Index: /release-kits/shared/ant/INSTALL
===================================================================
--- /release-kits/shared/ant/INSTALL	(revision 16139)
+++ /release-kits/shared/ant/INSTALL	(revision 16139)
@@ -0,0 +1,2 @@
+For installation instructions see the manual in the docs subdirectory
+or online at <http://ant.apache.org/manual/index.html>.
Index: /release-kits/shared/ant/KEYS
===================================================================
--- /release-kits/shared/ant/KEYS	(revision 16139)
+++ /release-kits/shared/ant/KEYS	(revision 16139)
@@ -0,0 +1,310 @@
+This file contains the PGP keys of various developers.
+
+Users: pgp < KEYS
+       gpg --import KEYS
+Developers: 
+        pgp -kxa <your name> and append it to this file.
+        (pgpk -ll <your name> && pgpk -xa <your name>) >> this file.
+        (gpg --list-sigs <your name>
+             && gpg --armor --export <your name>) >> this file.
+
+Type Bits/KeyID    Date       User ID
+pub  1024/FEECAAED 1998/11/11 Stefan Bodewig <bodewig@bost.de>
+                              Stefan Bodewig <bodewig@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: 2.6.3i
+
+mQCNAzZJoiMAAAEEAMzhUxTOC20Nprp6K4nLTiARt+EXii/dovNWWcfzZcYXi/lX
+r3zpUTTZxlKQpd4RaHjFmGgoOraZE4jCRFARVcFJgYmGUKpcWJZO7YKL36WUizTM
++dyB2ycOtzlty1W5VmRL3FGqo67pKA9F/QHg3NSu9hY1W9xPPK7Kq3f+7KrtAAUR
+tCBTdGVmYW4gQm9kZXdpZyA8Ym9kZXdpZ0Bib3N0LmRlPokAlQIFEDZSrGXHcgyK
+jiW9zQEBshEEAMlG4qVjKp4/agdJG56M6izx9oaKecFLXHQJrFUy3w2PvZHFYtXc
+osXKorX6bPrE8uB57MxbY2WapKeVRodlG0+j39vAf501duK8q2rktfWt9Cl4JjJ4
+DbWhSWfV1ci62u2gCxwYQe22F9Wh+vhOR5NK9RTbSKhupdlFsnrk/i7xiQCVAwUQ
+NkmiI67Kq3f+7KrtAQGdxQQAlcFOzSv7G6M4uGbgvw7IGgrhx7rawtIyv9hLXgVC
+7ua9xaZV8G0Fl9gh8RnbdcZ4R/aT+KIiAFaslfZ3t6hlC4MTbnAJqvdS/NO98ZkJ
+YvnzZSKHflAbd5gyE7IVxBC9/xRlF/Wls5sYNwb6RjoRCaOjxN/y3WCLa3Va101v
+zNy0I1N0ZWZhbiBCb2Rld2lnIDxib2Rld2lnQGFwYWNoZS5vcmc+iQCVAwUQOxIo
+BK7Kq3f+7KrtAQGn6gP/SBACdHakA4H//otpyESSrk4PmyOaYF0Kyok43Gee2mT+
+m9+jZ3jLcC0oav6iH+otL/lhk9t/JDM8LjD2kAkdWWoIvvuPyCx97gOzojIo0Ve2
+1wuxJTF/VIjwyOtE8FzE7p4tkc6EubVpeZkV9Pq9HFRBCUcyKJDLnF4tbstScLU=
+=OBLe
+-----END PGP PUBLIC KEY BLOCK-----
+
+
+pub  1024D/51898504 2001-05-29 Conor MacNeill <conor@cortexebusiness.com.au>
+sig 3       51898504 2001-05-29   Conor MacNeill <conor@cortexebusiness.com.au>
+sig         5F6B8B72 2002-01-11   Stefan Bodewig <bodewig@apache.org>
+uid                            Conor MacNeill <conor@apache.org>
+sig 3       51898504 2001-05-29   Conor MacNeill <conor@cortexebusiness.com.au>
+sig         5F6B8B72 2002-01-11   Stefan Bodewig <bodewig@apache.org>
+sub  1024g/D1ECBA5D 2001-05-29
+sig         51898504 2001-05-29   Conor MacNeill <conor@cortexebusiness.com.au>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.2.0 (GNU/Linux)
+
+mQGiBDsTqygRBACiZckNdclTlQFonLaIKBFGhMy0KKByw0x8XA4iwdbCXuF6xNIc
+HIFHajJ74AHchQ4d8xtomBy6b8yvFgWVeaZensvn69BlLeqGdyeJRzfPt6TgRnzZ
+2eWhb0HXdG3JwxL/2BabDhHfs4YJOrgB/vhRFQku6oCMRiBPtJj2werD6wCg0/zp
+jacYTw6+CR+sVvneCNyySFED/R3j10c4RnR8djgv1jKT8CKPuHYraupI9INEe+I6
+7qWjtJ02GzvMO6TElAtUsf4aysu45GgwkwEBnuG6mYb3Pq0V2c5tJc2A3Tj3DrdR
+i3HUNwurbus76I0sPyyENPu12QPeC6mvWLEsxVJ9o0hRKFayGvYUmrwWK9UFLjvp
+p9cXBACDHgLn7MAVLqUdYhRrUj/M+GOUpvBikEgoJJrEUmb5X4+++dffMh5HBIO4
+5LA11qEKuM2xnKqOilP7NLYXz1Fe0ocqv0jsHB4SprFTTai7ma31uwuRQvCQXVv1
+yJ5CLqYda64h/UA2kmmR2dfopmvDogYEMz/HU5voozxe7BEI7bQhQ29ub3IgTWFj
+TmVpbGwgPGNvbm9yQGFwYWNoZS5vcmc+iFcEExECABcFAjsTqygFCwcKAwQDFQMC
+AxYCAQIXgAAKCRCBBGRPUYmFBDgIAKCZztXqKhK6kXOnoGy7opCNmWU0lwCgsPDa
+4m+ruW3ch0rfbqtR75S52R+IRgQQEQIABgUCPD6eTgAKCRCiEVrhX2uLcqCcAKCr
+1Wylqju9YjBi5Twej9ze69JFBQCeOaMgo6yjnkcV3PnVInLlaMwPr5q0LUNvbm9y
+IE1hY05laWxsIDxjb25vckBjb3J0ZXhlYnVzaW5lc3MuY29tLmF1PohXBBMRAgAX
+BQI7E6xMBQsHCgMEAxUDAgMWAgECF4AACgkQgQRkT1GJhQTY0ACgmZmKheHzjPJs
+5hybpyvnvEiPYqYAn2+ryxdtz8XyOMExGRmHNlhG7svsiEYEEBECAAYFAjw+nlUA
+CgkQohFa4V9ri3JYYACg1WN+NCptfKVP1mbrIa+0ajztsiIAnAn+m70iwRRFZCxr
+jcULoY5SjyTLuQENBDsTqy0QBACfsCxJ6iCtgX8zjlVtMsMfDqu72x5sYatWKn8c
+u+4Oj5mi0x6azZIhwCa+K3ihLVOyG1mCRnzztGTIxWYRhq3TESIVOfgm+NgLGrmA
+XUTFyCT+21TExLCpuVZKmUHsWXLxDtfQ1diPeQpiQ8+Fvb/4jLGFjFIrQ2VjtFQn
+kumkSwADBgP9H0bF4hdMuVEcSJ9imxSoJshcOOA3Vd2+YiCTZhBygWM49wY5jNos
+/DArIjNCE53IlOu/UtHB2jqkSqjF0soGYsUjeCWouiTP9hLuMKPjnqj9ryJPTDKz
+nTCZ4TuB5CtzrKTlWLmPCPpFsGqe4KjMeKg/mqGqjPKl97xgStK9N9GIRgQYEQIA
+BgUCOxOrLQAKCRCBBGRPUYmFBMM6AJsF3FFyZGEmbt9aGG1W/u0oI9mcLgCfQyJ+
+aalbspazea4J9zgi59SSwOM=
+=cBdR
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub  1024D/5F6B8B72 2001-05-28 Stefan Bodewig <bodewig@apache.org>
+sig 3       5F6B8B72 2001-05-28   Stefan Bodewig <bodewig@apache.org>
+sig         51898504 2002-01-11   Conor MacNeill <conor@cortexebusiness.com.au>
+sig 3       F88341D9 2003-03-17   Lars Eilebrecht <lars@eilebrecht.org>
+sig 3       2261D073 2003-03-17   Astrid Keáler (Kess) <kess@kess-net.de>
+sig         21D0A71B 2003-03-17   Dirk-Willem van Gulik <dirkx@apache.org>
+sig         75A67692 2003-03-18   Erik Abele <erik@codefaktor.de>
+sig         B3B2A12C 2003-05-20   ct magazine CERTIFICATE <pgpCA@ct.heise.de>
+sig 3       8103A37E 2003-04-04   Andre Malo <nd@apache.org>
+uid                            Stefan Bodewig <bodewig@bost.de>
+sig 3       5F6B8B72 2001-05-28   Stefan Bodewig <bodewig@apache.org>
+sig         51898504 2002-01-11   Conor MacNeill <conor@cortexebusiness.com.au>
+sig 3       F88341D9 2003-03-17   Lars Eilebrecht <lars@eilebrecht.org>
+sig 3       2261D073 2003-03-17   Astrid Keáler (Kess) <kess@kess-net.de>
+sig         21D0A71B 2003-03-17   Dirk-Willem van Gulik <dirkx@apache.org>
+sig         75A67692 2003-03-18   Erik Abele <erik@codefaktor.de>
+sig         B3B2A12C 2003-05-20   ct magazine CERTIFICATE <pgpCA@ct.heise.de>
+sig 3       8103A37E 2003-04-04   Andre Malo <nd@apache.org>
+uid                            Stefan Bodewig <stefan.bodewig@freenet.de>
+sig 3       5F6B8B72 2003-03-07   Stefan Bodewig <bodewig@apache.org>
+sig 3       F88341D9 2003-03-17   Lars Eilebrecht <lars@eilebrecht.org>
+sig 3       2261D073 2003-03-17   Astrid Keáler (Kess) <kess@kess-net.de>
+sig         21D0A71B 2003-03-17   Dirk-Willem van Gulik <dirkx@apache.org>
+sig         75A67692 2003-03-18   Erik Abele <erik@codefaktor.de>
+sig         B3B2A12C 2003-05-20   ct magazine CERTIFICATE <pgpCA@ct.heise.de>
+sig 3       8103A37E 2003-04-04   Andre Malo <nd@apache.org>
+sub  1024g/24774157 2001-05-28
+sig         5F6B8B72 2001-05-28   Stefan Bodewig <bodewig@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.2.0 (GNU/Linux)
+
+mQGiBDsSIk4RBADSCj6rUjV64tYCGT1DYKYR7GthyWpNdGHSYLbETBcDatAe1dzQ
+5NsCgfrlybfyeY+y1lxr3T9bqf6zJWDw/718wff96qmmv1qzexSYtmIrj+h53V82
+EXwWOFuYMJisuxdT940iQzosm3GOv4MJdEg3oI2SgfEyRQQ6vO4Ob5rHDwCg5taZ
+nrHOrXx2dIGHxpxRZ0SUl30D/jmtttFjYOQ3LBMriikz5mh2sK3ZnoSRF4o5O0zW
+Ve6e2SFXOEjVjImKsH6KCbdQNelrAdgiyOoXClyQKsQ27pncbdWo6bO0E3POJZVm
+XaeW7iudHVr63rU5PViXObIQrdQl0D59j5brKj4vdlTyUw8kaHPvbKPDEOwvZq4Y
+LJQ5BACA1YilTeXRJqwFsNlpcxCHwlULD4QUVP496prQWf1B7Z6g0KvLGrQsO0Vn
+Jcn+fEqukysTJixSXCPebosltd4RalJIupVYkp4w6MJ7biaDAlLuNhDcI/AiXTmV
+dXUedVXIaM8I3Ne23gucwbAyc0Hvb+3cSAKRhl/azFQhuHBvlrQjU3RlZmFuIEJv
+ZGV3aWcgPGJvZGV3aWdAYXBhY2hlLm9yZz6IWgQTEQIAGgULBwoDBAMVAwIDFgIB
+AheAAhkBBQI7EiJPAAoJEKIRWuFfa4tyjzgAn3a+nFeNb3mAZQR4lRcXy3F+8eTh
+AJ4iuLt4No6TUB9xNETrm3Y/0TvkU4hGBBARAgAGBQI8PtWPAAoJEIEEZE9RiYUE
+nZIAnjOkQx1BfxN9+E7Yiz4MOxqZjSUpAKDQi0Alv/VR61fmR7KHZ11J2g+HSIiZ
+BBMBAgAGBQI+dd8MAAoJED6Pt/L4g0HZJ2QD5RzYjJ0fi1Gr6NsjmIiU/lvfvPqj
+bNoK2A2flkmLo2lk9jSa1AmF0ETOoCg8hZZHCbxyPZRdsmDJFXrZZWVTdUyZHhHb
+Wx53DR3HavmVEEdDZBSkxfXdSu1CNq31sbdcumtd03FI6SV827cPzl1/RGUiOkEL
+eK0hZb3IvSd5iEYEExECAAYFAj512sEACgkQ7tHqOSJh0HNFuACfb7quMJZHOk5p
+pmTxf4GVS5bh3E4AniLa5788ktdcZCBE5Or+G+yuIGooiD8DBRA+daoa/W+IxiHQ
+pxsRAkQFAKDmlWVuQnWfaGK9z8CP4vESTfiXyQCgpnf1yJWDgqWvUHOAvOeaTPDR
+cOmIRgQQEQIABgUCPnekEAAKCRAVP6DNdaZ2ki/rAJ9Q0x5m4SE3hMNGUoMgS0MN
+mYXDOQCdGa0QW2fZW4B1wSjiUIi6VgY+F7GIRgQQEQIABgUCPsoulgAKCRDb0kX8
+s7KhLBfwAKDV6uwqf9AMBJlr4aE45rWOnlWAeQCgjBYQVHWFjV1cmpT06vE4bMDV
+J6+IRgQTEQIABgUCPo3OLAAKCRDKaTl0gQOjfpFKAJ9TY+mKmq0INLC9iqxbEETj
+NwOn0gCgspeNEqX1SUjt932Lj3IvXR6fkAi0IFN0ZWZhbiBCb2Rld2lnIDxib2Rl
+d2lnQGJvc3QuZGU+iFcEExECABcFAjsSOYEFCwcKAwQDFQMCAxYCAQIXgAAKCRCi
+EVrhX2uLcsm6AKDaA//SYJv2pcXwGBC89OSzGvxy3gCg1kx6qu6zJKaWaE1L9oFq
+Vc65OUSIRgQQEQIABgUCPD7VlQAKCRCBBGRPUYmFBNCzAJ9tv7tNS6NwaOZQ8cR0
+pGslJGKtuQCgpttMmhhRrUwyVuEXc+dKX7rdQMKImQQTAQIABgUCPnXfDwAKCRA+
+j7fy+INB2Vm6A+IDxiYtMvd/mad+7hRoKXvD5AddB1SwnvQdJe0ewStFBYZxZdNO
+LXdkVngCwAJziPGZcQmocEHVMrN+Mxe+3OX0PEZ+3KOOsAUi+fTov2xvf8EWKgX9
+d0li9iEytVA4M0c52a3bPTC0Kki/KAY3m8oNvAmTwWB9dBSooFFDMIhGBBMRAgAG
+BQI+ddrHAAoJEO7R6jkiYdBzruYAnAzvTrvDQNvoAoaDQJ+o/ydqMA8ZAJwNEOQD
+Hsz+6ynbMPoQmKmUqfEISIg/AwUQPnWqIf1viMYh0KcbEQIkpwCeInf4o3C5Ykh/
+xBodVImrp2Cvcy8AoMn3me5p4Sl2rm2tjVxTJZt2sUr7iEYEEBECAAYFAj53pBoA
+CgkQFT+gzXWmdpIpGgCgiTARP6Cf+JKOxUaVFr0L9DC5oUcAoIxF7D5mOVK/AFcp
+1cxNk3BLtdq3iEYEEBECAAYFAj7KLjIACgkQ29JF/LOyoSwAbACglNp7V2Pw4U57
+5IuooM677bZf8pwAn2U2TcHj7PYprU+qYRkyJ3pa7FyyiEYEExECAAYFAj6NzjUA
+CgkQymk5dIEDo35YPwCeJIU/monN96nP4jJjAuflEXMbemUAoJXq7oVCYuOCqWr/
+T3VW0DM3W+dntCpTdGVmYW4gQm9kZXdpZyA8c3RlZmFuLmJvZGV3aWdAZnJlZW5l
+dC5kZT6IXAQTEQIAHAUCPmiBXAIbAwQLBwMCAxUCAwMWAgECHgECF4AACgkQohFa
+4V9ri3JPKACfXhFOr4gMMIQwEGvUb6J/X4asuzIAnje2H9T1r/jSVi1NbIEtQfbp
+myQXiJkEEwECAAYFAj513w8ACgkQPo+38viDQdmGkgPnUYJfHlDrQ2jYJWuPDve9
+n4pV9OvyIllKyLdfULjsLWXw4Jm+fMQJgWr2GEov1ZYIyS2+rGDZpbz/vxaBVbgq
+M8TPdiXzxwU8AFq4k+Em4fKXR58ByLSJjvLLza61spNHJennEUFp3tTpEsr4bNuu
+aoRYhtiKzTqNuoC+0x+IRgQTEQIABgUCPnXaxwAKCRDu0eo5ImHQc5lcAJ9FOoAL
+4uHMuqHXrrxsEVPJvEJ2/ACggaOR62EziUqcqpdm/1Zy3P4T22iIPwMFED51qjj9
+b4jGIdCnGxECWssAoKnkR63bWGCFDB5YJtfZJ+nQq3TAAKD1bzG/kITQpIHsT2vC
+Y1yBmlWbBYhGBBARAgAGBQI+d6QaAAoJEBU/oM11pnaS+9IAn0kFlXAzOhhu4r5f
+glMVuw5bTAd0AKCtYRX2ESU/+tsE8vuXWtsMCGeI5YhGBBARAgAGBQI+yi67AAoJ
+ENvSRfyzsqEsSccAoKqG39X2y6xoQa56nviF5iCj/oTFAJwLKx9GyKEUbSM9f/IQ
+7AsZPhHT54hGBBMRAgAGBQI+jc41AAoJEMppOXSBA6N+xYgAn1HbGamcXTDYSFjn
+3U5ik2vQa8nuAKC/uUM01gsdn+71EiAkmrjlH6H9MLkBDQQ7EiJUEAQAzVKWS8Qr
+kysydbTJu2/14wIbz2Coi93aAGelwCwXSxf50JpYdY3Lkcvd0FqT8bcEnz43MCSx
+8vlKubQtUpx9WMGIb4ixtShLJ4lAa6FJldhychz/dnxSNyz5N8W6sby4dTVxac0r
+loxjAOurGanhG7TMtgfDi0cEEoXRyAVoKyMAAwUD/j1pJm4Npq2mlJoE7MK3vAhg
+KwYHFflmJusmqvSAtRuFdT15pbMJrA5bAK+lA3SVOOhhWTCItlphSR2qxJCAcBTe
+OMqUi5ohFcCkSRNvwmDtH+80B3BehlEsEKNk8Z3moa2ch7Oxnb6XEXH5tGJ5Qvx9
+Qid6ZfBaXx7bc8yKyCb4iEYEGBECAAYFAjsSIlQACgkQohFa4V9ri3INMgCgnc22
+kj8TfjktU6u4SUUqud25ZZcAn0B2b0zPjKjGuiwdKSnkFbNcFS3g
+=9dY9
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub  1024R/697ECEDD 2000-04-06 Henri Gomez <hgomez@slib.fr>
+sig         697ECEDD 2000-04-06   Henri Gomez <hgomez@slib.fr>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.2.0 (GNU/Linux)
+
+mQCNAzjsydwAAAEEAMpwFU/ci3/wR3ryCGp9Exr+Rx/hTQ7hWAJcVw8ejlBXlT4T
+yITlRUs0HGfRWxME2J55PuXXsIEPZzjfozNtvOyq8WlLlJ7iaiyWxVRoPJ25sSEj
+C9etm6wjj4E66ZgzuElZkm1m69uEsCHPPNuz1oQ/g1O+SmIVxIYirlxpfs7dAAUR
+tBxIZW5yaSBHb21leiA8aGdvbWV6QHNsaWIuZnI+iQCVAwUQOOzJ3IYirlxpfs7d
+AQFQyQQAj0D9G0hEL7SQGaSCkkoXwvamQw42N8+tNm+jfWHWdE4HAiVlhJmI2GyD
+sdcXVAcR8R7ILIRB5AY7a3bF+qMk0r+vO6oR878RKKn9AvtaAIOnrh6tr0tiPwf5
+XDUMySxIWJEF3SmJAy9Lq3bAl5GMzZCFHiS0NW2gtWgmr/u1RuM=
+=6l+I
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub  1024R/397DCAD5 2002-07-03 Henri Gomez <hgomez@users.sourceforge.net>
+sig         397DCAD5 2002-07-03   Henri Gomez <hgomez@users.sourceforge.net>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.2.0 (GNU/Linux)
+
+mQCNAz0i0UUAAAEEALXePN6IHne0W96YRMnR+4EXB6402QY5f8ZLcnSUZUH55Fb1
+qcZGBc3WRKlPiUvwBD+eVYgCpNSXF/H+oV1mawxNJN5XwqBEpfYmY8MRIPcqa61h
+bJc3LBSm1qNf05G5Cwxeed+OgVm0r1HBy6DAgDHAqNlkC8DZ/BVgqMA5fcrVAAUR
+tCpIZW5yaSBHb21leiA8aGdvbWV6QHVzZXJzLnNvdXJjZWZvcmdlLm5ldD6JAJUD
+BRA9ItFFFWCowDl9ytUBASNyA/458T84LUVhqq6Y9fPBAfcFMWY2kehuDWsQEXkO
+46XoK+AnFZCkKuyDLqPHchVUO5pK/gZYsDK/xJkfh3u4FTDTsecb9wFmIeyayVIN
+SI8o6l8EZzDX/PGwqEwzxHrbQkIgIktNn5ApIoazvdBpbkQfNj1vr2wWoMYPLAWM
+8xzecA==
+=UmZ8
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub  1024D/307A10A5 2002-07-18 Henri Gomez <hgomez@users.sourceforge.net>
+sig 3       307A10A5 2002-07-18   Henri Gomez <hgomez@users.sourceforge.net>
+sub  2048g/862B8F70 2002-07-18
+sig         307A10A5 2002-07-18   Henri Gomez <hgomez@users.sourceforge.net>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.2.0 (GNU/Linux)
+
+mQGiBD02vbERBAC1v8fR6gjERpaz4UMfdy0hRVWCPSbOdF+Swm/IenjVzErco6zb
+MTa13umUNrDPBy/tTWiCCZrOnqi7fgDzWqPEqrXJjKAFVLEWE6MmKylPPEPG1/bm
+idkNGERSAZduvhKv777PzvEJJ/8eGe3wy/O8NbgIjCPtr4UklwCZS8cFuwCg8oMO
+UdT8qZRtzdxdAyu1m5fUb+MD/3IKJYWXsdtb6iBphCU4f/BoyjVC9EZJ1ywLuiVM
+siKbuaDUaXU9nWcbNKv+fx8uZ1NaadpfLokqqhnWcpnSiqw8HNR7SwsF1D33rkXK
+O4FSuVss/tIoqGdWFcJyPkP4yP5shxqR335narVw2vDa0+BiWkALbA2qVsSIdZDB
+LeFZA/47AMBS0U2BRk2rQT8LmMuFl7mR+wNBM4n7FUGdxsGn3TcYd4pXTNrEQPrV
+YNdooKlikgGk4hgFnIFX09Spmimqgq0goFue81rttVdZZ4uep8dTghY6gwmvcOxX
+jATbhWStBhdu9B35kzfHc+1QihD5Z94u4uyWIVBIzikcdiY8LbQqSGVucmkgR29t
+ZXogPGhnb21lekB1c2Vycy5zb3VyY2Vmb3JnZS5uZXQ+iFcEExECABcFAj02vbEF
+CwcKAwQDFQMCAxYCAQIXgAAKCRAZMdaEMHoQpYijAKCCP68ndU/kTXR9XAKLvibC
+3S8+1QCfUFQYte3Jo+MHKaWjsu9JGptRzo+5Ag0EPTa93RAIAKlsRJ5gOGTFsmaR
+W9k6MIh4c/MCy7J7HUxT5xTdHROa+3zUh+FAE/JaOx9ZtZtH863DFHA8cP4L+tpi
+PjBT6g2E94dwGcuH/OiSSCT4JSBukbGbOuLLdmFXqUl8+4gsL90Xal67FtNLwyLG
+1n7geLir0byD+OT7VLA5w+6G0NOpJEveV/FIa2qLgdRZ8vz73ybgMh18hBUrUmro
+jncp0rln2VU7VCH1C2aClKm7kK4mGAjIFIzKbguK+kM3b8NDHmXKpT6syyCtIM3h
+prkV1TUCAFqLI32aSdlTN79lpeA2zDga9k4/4X/RDHsFpRN2neRFGTNUtuUgYpQQ
+E5zWBmMAAwUH/RiGxyeBsad923IwE1+GAjxFl2tqF9xWk0J6yTnSK4nfhYAE9evV
+jwDEok9jRl4ILCcXx6YN/d/lWNuSbARKHz/3hLiTouPpwd3SSJ8is2x9PgpJz5JX
+cD0y1SkbPLvs3jH3ZmdcxZpuAmJeI/typqFKK5pWP44oXIH+XH/8nWDtmLEBkgKQ
+/ATQWenMTmZ6MIJ6aWKWGkO9QS6iYRz3PPPGQ1O8W02CeprM2wBtlb8J1Z3RxNhM
+rZcg/1Qi3V3D1HI4zw6tAFmDeBb8J4PaBQzqlhzx2EBTbfwNPhV8AlPvpxHEeGGn
+v+O1yhZr33SnyZdINNoNDn+owVMdmkobe9GIRgQYEQIABgUCPTa93QAKCRAZMdaE
+MHoQpRsTAJ4qst3MhLm48fBAEnzuzi/BIKr+AgCfYaCB/AvPoncQbHc8BcNGRimR
+P9A=
+=hQhz
+-----END PGP PUBLIC KEY BLOCK-----
+
+pub  1024D/EDF62C35 2002-04-10 Magesh Umasankar <umagesh@apache.org>
+sig 3       EDF62C35 2002-04-10   Magesh Umasankar <umagesh@apache.org>
+sig 3       5F6B8B72 2003-03-07   Stefan Bodewig <bodewig@apache.org>
+sub  1024g/B5FFC53F 2002-04-10
+sig         EDF62C35 2002-04-10   Magesh Umasankar <umagesh@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.2.0 (GNU/Linux)
+
+mQGiBDy0ebgRBADuKIKD8PuJ4wKEV1h2AprwJjxCRx8vn48XNwfLZuvhw8cpArtK
+rZwhoGPPUPEEXgtTNerlKq4VwpAwcnvRz7oC/7aWkUbcR2sAyhfe2scohwPgw7Xv
++isWC0NDPdrxvXG/PUOG/cnELunr51ymybBqBxUd2gMhYIxPo67D+YPYLwCgwcZp
+yc/6kJa116ESWHrti342GD8D/1srpnRs9CiS1DQF1uZ1wW4vzj4VD61tKsjdWD8D
+V573R22iMDLSj4oMB536WxUH7snz8XsAKm/peqJ6G9m0smtmWA1ago5yzQj70WqF
+xzWBhHn2I/YfAQ8pb2s9q1lClj8elnCxT65L27ydBAZteejb2VqjtQ6iGy86PUT2
+wRUvBADZmoV1eIZJEM5NnxBv1EtvRYZtIQEzZ8dO2A1LOS7qlVr8IypljNPLGhzX
+VHNvVsjC9QMUSWeBsDedvQHQ3hJpIMnTI32XE1V4gX06gfVTZdhf2fLTtwnsHZp0
+oumqshGDVRhNJJdDYLikxWOxOfkNveKEqJFvtuBR+ZqqluQKebQlTWFnZXNoIFVt
+YXNhbmthciA8dW1hZ2VzaEBhcGFjaGUub3JnPohXBBMRAgAXBQI8tHm4BQsHCgME
+AxUDAgMWAgECF4AACgkQ76Pnee32LDWSRwCfeASWXvpdt7bSFPMtszU/7uPEktsA
+n23mYUN5WKJA1ZreW+0CcZ2ESnOviEYEExECAAYFAj5ogYgACgkQohFa4V9ri3IW
+YACgsxGig0PL0M86rJsA/IpXjBdg3ysAoJzsoUZ/7s2BxDfzF/FRTVIzS+TMuQEN
+BDy0eb8QBACBVb9YDJRp9Irzmq71Jf9FIPw+4g/cWpF3t/Eb7eSzMcOvTAXyNIWz
+aaOjHre7lFctHfq8ls/6gR7uqajiAnfQcfTcu7pp+F5KsU0Embt83SFzZ3aoJwET
+mB/LqUyrrGDiue3lU+flJO7UmcsRvtk0+BDkyCeB9HgfdpXbBLCyuwADBQP+PNxX
+4e1tg3ZJo/xNEnD2Re3HjmQRrr0RYJLUGjgQrAEONSgowx3IW8/JssmNJVjnYm0q
+jSKsb8rergCFJhPNZ8Dd/k00pKcrq+IN6j7WTYLqPce87zrGAZUtmDwDSp5mxy5E
+xWJJxsgBPk4YBQLzJt21A3BgK/i24Sze2VLbaZuIRgQYEQIABgUCPLR5vwAKCRDv
+o+d57fYsNa8xAJ4mLfonZbd64+YY9rfvhIh3Vsl3AACeLPPKtma2K6XCfhTBEDnj
+hzSr4vo=
+=lBfF
+-----END PGP PUBLIC KEY BLOCK-----
+pub  1024D/265B4C63 2003-08-18 Antoine Levy-Lambert (Apache Ant Committer) <antoine@apache.org>
+sig 3       265B4C63 2003-12-06   Antoine Levy-Lambert (Apache Ant Committer) <antoine@apache.org>
+sig 3       5F6B8B72 2003-12-12   Stefan Bodewig <bodewig@apache.org>
+uid                            Antoine Levy-Lambert (Apache Ant Committer) <antoine@antbuild.com>
+sig 3       265B4C63 2003-08-18   Antoine Levy-Lambert (Apache Ant Committer) <antoine@apache.org>
+sig 3       5F6B8B72 2003-12-12   Stefan Bodewig <bodewig@apache.org>
+sub  1024g/A3060393 2003-08-18
+sig         265B4C63 2003-08-18   Antoine Levy-Lambert (Apache Ant Committer) <antoine@apache.org>
+
+-----BEGIN PGP PUBLIC KEY BLOCK-----
+Version: GnuPG v1.2.2 (MingW32)
+
+mQGiBD9AzmcRBACMqgb7IFvC/nLxw7mUAgHENeZXY3JOQJ8wVBevIbbMEeFvzHE2
+diFydqUXocPexduYr0ahkf033WvWdAiNqDLfVW/HFOsc1TpjbHkqPUHtJ62Ya5tg
+nH4UGN9BYZfMbfVDOSz41lYwmfK5HYgpZN/sBQBSKf2qgoFB+LxYaae8YwCgrWlu
+fYhf7fkKbbdSf3BGS67ggNkD/0VvkXkw1SEnPaqrkKGkPKomCTb2auGxcYYI3/rP
+1m+SGRf0gE1NtocmIEitiR6WvfKUjoMAXSCp5KdnUXmO9rwzkM002KCA7K5CY+e/
+2bLDuiQ3rNiD4mFfG6M+UnmZ+GMFba1p9Cp4PqLNLsCHz67t7hEsscTZQ8mZ9xKM
++GCsA/9P+XFM7JDn9MLhYab9qo1CkceBkthUP6jWGjuAZ00elmBCkpkzNv5aIzss
+xih2GpaU/tmcMjw8FGp0dTwzqdpmbZBLjunRnuBTir4m4l5G9rwl4JbfK1PVqk4a
+VKkh9W7/yqBcEfNcsfJO9cqaQ+PzcnCoDiD9UHEBYzUhrCKwhLRCQW50b2luZSBM
+ZXZ5LUxhbWJlcnQgKEFwYWNoZSBBbnQgQ29tbWl0dGVyKSA8YW50b2luZUBhbnRi
+dWlsZC5jb20+iFsEExECABsFAj9AzmcGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQ
+hhTWqyZbTGMnOgCeJV2gI+1CIxMR7dcIhXjPkiusreIAn2SGOsPS0o89l0EIVuc9
+J2F03aB+iEYEExECAAYFAj/ZwioACgkQohFa4V9ri3J8CACgiGlh+td0BAnJPq19
+ovEQR2DJ02MAoLP203EF+BLPpykeTcGp8UEWmeGQtEBBbnRvaW5lIExldnktTGFt
+YmVydCAoQXBhY2hlIEFudCBDb21taXR0ZXIpIDxhbnRvaW5lQGFwYWNoZS5vcmc+
+iF4EExECAB4FAj/SSwYCGwMGCwkIBwMCAxUCAwMWAgECHgECF4AACgkQhhTWqyZb
+TGOh/QCfbrSoipMlizyGT6c8BOPEd9zzfi0AnRRTqTDzXzsGk4CG/BJME9CCm7Ts
+iEYEExECAAYFAj/ZwiMACgkQohFa4V9ri3IOVACgh2QHS1+cVjvM4eLjls1bJwWi
+o5IAoLtAszHrwHS5qrhvr2rtnlyyyV2guQENBD9AzmkQBACNpBfqi2PweozCtxoX
+PqzKdRDKqCw7TOrDR6lbP3z/ov/1SN5Vc0XqNzBSGqwBcmGWtneABkX7n4uzJrC5
+wvX/TZ/DkMvypPyeNVMu+/ZMlzdbx06OUDKr6Fy/NtaI7cN1rw+Igyv22Q8ilhhg
+s/5Rdj84EtJkWQjVRml6wXSAZwADBwP/TqyhOC4sA5YrePYGH8i46h0SU9dXjfEF
+JijC8vXZ+BNQHQVK3fjEXedZL8Zvvp1bsMu7Muz9SMnqygM1unp2NGxLRd/9315p
+Qh0VStYKn0xq13ybOKr0Gsyx5yyl3Nzlu/1qAR0es/zN4jV7/IQi3R2GOvezcpHX
+mkbsYiZAhu2IRgQYEQIABgUCP0DOaQAKCRCGFNarJltMYxcsAKCSJJUABlXYnLBj
+b+WtmAzu0JjZNACdFJ4KIATJDK/5ajFq5+irOpodoHc=
+=UomR
+-----END PGP PUBLIC KEY BLOCK-----
Index: /release-kits/shared/ant/LICENSE
===================================================================
--- /release-kits/shared/ant/LICENSE	(revision 16139)
+++ /release-kits/shared/ant/LICENSE	(revision 16139)
@@ -0,0 +1,203 @@
+/*
+ *                                 Apache License
+ *                           Version 2.0, January 2004
+ *                        http://www.apache.org/licenses/
+ *
+ *   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+ *
+ *   1. Definitions.
+ *
+ *      "License" shall mean the terms and conditions for use, reproduction,
+ *      and distribution as defined by Sections 1 through 9 of this document.
+ *
+ *      "Licensor" shall mean the copyright owner or entity authorized by
+ *      the copyright owner that is granting the License.
+ *
+ *      "Legal Entity" shall mean the union of the acting entity and all
+ *      other entities that control, are controlled by, or are under common
+ *      control with that entity. For the purposes of this definition,
+ *      "control" means (i) the power, direct or indirect, to cause the
+ *      direction or management of such entity, whether by contract or
+ *      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ *      outstanding shares, or (iii) beneficial ownership of such entity.
+ *
+ *      "You" (or "Your") shall mean an individual or Legal Entity
+ *      exercising permissions granted by this License.
+ *
+ *      "Source" form shall mean the preferred form for making modifications,
+ *      including but not limited to software source code, documentation
+ *      source, and configuration files.
+ *
+ *      "Object" form shall mean any form resulting from mechanical
+ *      transformation or translation of a Source form, including but
+ *      not limited to compiled object code, generated documentation,
+ *      and conversions to other media types.
+ *
+ *      "Work" shall mean the work of authorship, whether in Source or
+ *      Object form, made available under the License, as indicated by a
+ *      copyright notice that is included in or attached to the work
+ *      (an example is provided in the Appendix below).
+ *
+ *      "Derivative Works" shall mean any work, whether in Source or Object
+ *      form, that is based on (or derived from) the Work and for which the
+ *      editorial revisions, annotations, elaborations, or other modifications
+ *      represent, as a whole, an original work of authorship. For the purposes
+ *      of this License, Derivative Works shall not include works that remain
+ *      separable from, or merely link (or bind by name) to the interfaces of,
+ *      the Work and Derivative Works thereof.
+ *
+ *      "Contribution" shall mean any work of authorship, including
+ *      the original version of the Work and any modifications or additions
+ *      to that Work or Derivative Works thereof, that is intentionally
+ *      submitted to Licensor for inclusion in the Work by the copyright owner
+ *      or by an individual or Legal Entity authorized to submit on behalf of
+ *      the copyright owner. For the purposes of this definition, "submitted"
+ *      means any form of electronic, verbal, or written communication sent
+ *      to the Licensor or its representatives, including but not limited to
+ *      communication on electronic mailing lists, source code control systems,
+ *      and issue tracking systems that are managed by, or on behalf of, the
+ *      Licensor for the purpose of discussing and improving the Work, but
+ *      excluding communication that is conspicuously marked or otherwise
+ *      designated in writing by the copyright owner as "Not a Contribution."
+ *
+ *      "Contributor" shall mean Licensor and any individual or Legal Entity
+ *      on behalf of whom a Contribution has been received by Licensor and
+ *      subsequently incorporated within the Work.
+ *
+ *   2. Grant of Copyright License. Subject to the terms and conditions of
+ *      this License, each Contributor hereby grants to You a perpetual,
+ *      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ *      copyright license to reproduce, prepare Derivative Works of,
+ *      publicly display, publicly perform, sublicense, and distribute the
+ *      Work and such Derivative Works in Source or Object form.
+ *
+ *   3. Grant of Patent License. Subject to the terms and conditions of
+ *      this License, each Contributor hereby grants to You a perpetual,
+ *      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ *      (except as stated in this section) patent license to make, have made,
+ *      use, offer to sell, sell, import, and otherwise transfer the Work,
+ *      where such license applies only to those patent claims licensable
+ *      by such Contributor that are necessarily infringed by their
+ *      Contribution(s) alone or by combination of their Contribution(s)
+ *      with the Work to which such Contribution(s) was submitted. If You
+ *      institute patent litigation against any entity (including a
+ *      cross-claim or counterclaim in a lawsuit) alleging that the Work
+ *      or a Contribution incorporated within the Work constitutes direct
+ *      or contributory patent infringement, then any patent licenses
+ *      granted to You under this License for that Work shall terminate
+ *      as of the date such litigation is filed.
+ *
+ *   4. Redistribution. You may reproduce and distribute copies of the
+ *      Work or Derivative Works thereof in any medium, with or without
+ *      modifications, and in Source or Object form, provided that You
+ *      meet the following conditions:
+ *
+ *      (a) You must give any other recipients of the Work or
+ *          Derivative Works a copy of this License; and
+ *
+ *      (b) You must cause any modified files to carry prominent notices
+ *          stating that You changed the files; and
+ *
+ *      (c) You must retain, in the Source form of any Derivative Works
+ *          that You distribute, all copyright, patent, trademark, and
+ *          attribution notices from the Source form of the Work,
+ *          excluding those notices that do not pertain to any part of
+ *          the Derivative Works; and
+ *
+ *      (d) If the Work includes a "NOTICE" text file as part of its
+ *          distribution, then any Derivative Works that You distribute must
+ *          include a readable copy of the attribution notices contained
+ *          within such NOTICE file, excluding those notices that do not
+ *          pertain to any part of the Derivative Works, in at least one
+ *          of the following places: within a NOTICE text file distributed
+ *          as part of the Derivative Works; within the Source form or
+ *          documentation, if provided along with the Derivative Works; or,
+ *          within a display generated by the Derivative Works, if and
+ *          wherever such third-party notices normally appear. The contents
+ *          of the NOTICE file are for informational purposes only and
+ *          do not modify the License. You may add Your own attribution
+ *          notices within Derivative Works that You distribute, alongside
+ *          or as an addendum to the NOTICE text from the Work, provided
+ *          that such additional attribution notices cannot be construed
+ *          as modifying the License.
+ *
+ *      You may add Your own copyright statement to Your modifications and
+ *      may provide additional or different license terms and conditions
+ *      for use, reproduction, or distribution of Your modifications, or
+ *      for any such Derivative Works as a whole, provided Your use,
+ *      reproduction, and distribution of the Work otherwise complies with
+ *      the conditions stated in this License.
+ *
+ *   5. Submission of Contributions. Unless You explicitly state otherwise,
+ *      any Contribution intentionally submitted for inclusion in the Work
+ *      by You to the Licensor shall be under the terms and conditions of
+ *      this License, without any additional terms or conditions.
+ *      Notwithstanding the above, nothing herein shall supersede or modify
+ *      the terms of any separate license agreement you may have executed
+ *      with Licensor regarding such Contributions.
+ *
+ *   6. Trademarks. This License does not grant permission to use the trade
+ *      names, trademarks, service marks, or product names of the Licensor,
+ *      except as required for reasonable and customary use in describing the
+ *      origin of the Work and reproducing the content of the NOTICE file.
+ *
+ *   7. Disclaimer of Warranty. Unless required by applicable law or
+ *      agreed to in writing, Licensor provides the Work (and each
+ *      Contributor provides its Contributions) on an "AS IS" BASIS,
+ *      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ *      implied, including, without limitation, any warranties or conditions
+ *      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ *      PARTICULAR PURPOSE. You are solely responsible for determining the
+ *      appropriateness of using or redistributing the Work and assume any
+ *      risks associated with Your exercise of permissions under this License.
+ *
+ *   8. Limitation of Liability. In no event and under no legal theory,
+ *      whether in tort (including negligence), contract, or otherwise,
+ *      unless required by applicable law (such as deliberate and grossly
+ *      negligent acts) or agreed to in writing, shall any Contributor be
+ *      liable to You for damages, including any direct, indirect, special,
+ *      incidental, or consequential damages of any character arising as a
+ *      result of this License or out of the use or inability to use the
+ *      Work (including but not limited to damages for loss of goodwill,
+ *      work stoppage, computer failure or malfunction, or any and all
+ *      other commercial damages or losses), even if such Contributor
+ *      has been advised of the possibility of such damages.
+ *
+ *   9. Accepting Warranty or Additional Liability. While redistributing
+ *      the Work or Derivative Works thereof, You may choose to offer,
+ *      and charge a fee for, acceptance of support, warranty, indemnity,
+ *      or other liability obligations and/or rights consistent with this
+ *      License. However, in accepting such obligations, You may act only
+ *      on Your own behalf and on Your sole responsibility, not on behalf
+ *      of any other Contributor, and only if You agree to indemnify,
+ *      defend, and hold each Contributor harmless for any liability
+ *      incurred by, or claims asserted against, such Contributor by reason
+ *      of your accepting any such warranty or additional liability.
+ *
+ *   END OF TERMS AND CONDITIONS
+ *
+ *   APPENDIX: How to apply the Apache License to your work.
+ *
+ *      To apply the Apache License to your work, attach the following
+ *      boilerplate notice, with the fields enclosed by brackets "[]"
+ *      replaced with your own identifying information. (Don't include
+ *      the brackets!)  The text should be enclosed in the appropriate
+ *      comment syntax for the file format. We also recommend that a
+ *      file or class name and description of purpose be included on the
+ *      same "printed page" as the copyright notice for easier
+ *      identification within third-party archives.
+ *
+ *   Copyright [yyyy] [name of copyright owner]
+ *
+ *   Licensed 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.
+ */
Index: /release-kits/shared/ant/LICENSE.dom
===================================================================
--- /release-kits/shared/ant/LICENSE.dom	(revision 16139)
+++ /release-kits/shared/ant/LICENSE.dom	(revision 16139)
@@ -0,0 +1,71 @@
+This license came from:
+http://www.w3.org/Consortium/Legal/copyright-software-19980720
+
+
+W3C® SOFTWARE NOTICE AND LICENSE
+Copyright © 1994-2001 World
+Wide Web Consortium, <a href="http://www.w3.org/">World
+Wide Web Consortium</a>, (<a href=
+"http://www.lcs.mit.edu/">Massachusetts Institute of
+Technology</a>, <a href="http://www.inria.fr/">Institut National de
+Recherche en Informatique et en Automatique</a>, <a href=
+"http://www.keio.ac.jp/">Keio University</a>). All Rights Reserved.
+http://www.w3.org/Consortium/Legal/
+
+This W3C work (including software, documents, or other related
+items) is being provided by the copyright holders under the
+following license. By obtaining, using and/or copying this work,
+you (the licensee) agree that you have read, understood, and will
+comply with the following terms and conditions:
+Permission to use, copy, modify, and distribute this software
+and its documentation, with or without modification,  for any
+purpose and without fee or royalty is hereby granted, provided that
+you include the following on ALL copies of the software and
+documentation or portions thereof, including modifications, that
+you make:
+
+The full text of this NOTICE in a location viewable to users of
+the redistributed or derivative work.
+
+Any pre-existing intellectual property disclaimers, notices, or
+terms and conditions. If none exist, a short notice of the
+following form (hypertext is preferred, text is permitted) should
+be used within the body of any redistributed or derivative code:
+"Copyright © [$date-of-software] World Wide Web Consortium, (Massachusetts Institute of
+Technology, Institut National de
+Recherche en Informatique et en Automatique, Keio University). All Rights Reserved.
+http://www.w3.org/Consortium/Legal/"
+
+Notice of any changes or modifications to the W3C files,
+including the date changes were made. (We recommend you provide 
+URIs to the location from which the code is derived.)
+
+THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND
+COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES OF
+MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE
+USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD
+PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS.
+COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT,
+SPECIAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE
+SOFTWARE OR DOCUMENTATION.
+
+The name and trademarks of copyright holders may NOT be used in
+advertising or publicity pertaining to the software without
+specific, written prior permission. Title to copyright in this
+software and any associated documentation will at all times remain
+with copyright holders.
+____________________________________
+This formulation of W3C's notice and license became active on
+August 14 1998 so as to improve compatibility with GPL. This
+version ensures that W3C software licensing terms are no more
+restrictive than GPL and consequently W3C software may be
+distributed in GPL packages. See the older formulation for the
+policy prior to this date. Please see our Copyright FAQ for common 
+questions about using materials from
+our site, including specific terms and conditions for packages like
+libwww, Amaya, and Jigsaw. 
+Other questions about this notice can be
+directed to site-policy@w3.org.
+
+webmaster
Index: /release-kits/shared/ant/LICENSE.sax
===================================================================
--- /release-kits/shared/ant/LICENSE.sax	(revision 16139)
+++ /release-kits/shared/ant/LICENSE.sax	(revision 16139)
@@ -0,0 +1,20 @@
+This license came from: http://www.megginson.com/SAX/copying.html
+  However please note future versions of SAX may be covered 
+  under http://saxproject.org/?selected=pd
+
+
+This page is now out of date -- see the new SAX site at 
+http://www.saxproject.org/ for more up-to-date
+releases and other information. Please change your bookmarks.
+
+
+SAX2 is Free!
+
+I hereby abandon any property rights to SAX 2.0 (the Simple API for
+XML), and release all of the SAX 2.0 source code, compiled code, and
+documentation contained in this distribution into the Public Domain.
+SAX comes with NO WARRANTY or guarantee of fitness for any
+purpose.
+
+David Megginson, david@megginson.com
+2000-05-05
Index: /release-kits/shared/ant/LICENSE.xerces
===================================================================
--- /release-kits/shared/ant/LICENSE.xerces	(revision 16139)
+++ /release-kits/shared/ant/LICENSE.xerces	(revision 16139)
@@ -0,0 +1,56 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ *
+ * Copyright (c) 1999-2002 The Apache Software Foundation.  All rights 
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution,
+ *    if any, must include the following acknowledgment:  
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowledgment may appear in the software itself,
+ *    if and wherever such third-party acknowledgments normally appear.
+ *
+ * 4. The names "Xerces" and "Apache Software Foundation" must
+ *    not be used to endorse or promote products derived from this
+ *    software without prior written permission. For written 
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache",
+ *    nor may "Apache" appear in their name, without prior written
+ *    permission of the Apache Software Foundation.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation and was
+ * originally based on software copyright (c) 1999, International
+ * Business Machines, Inc., http://www.ibm.com.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
Index: /release-kits/shared/ant/NOTICE
===================================================================
--- /release-kits/shared/ant/NOTICE	(revision 16139)
+++ /release-kits/shared/ant/NOTICE	(revision 16139)
@@ -0,0 +1,15 @@
+   =========================================================================
+   ==  NOTICE file corresponding to the section 4 d of                    ==
+   ==  the Apache License, Version 2.0,                                   ==
+   ==  in this case for the Apache Ant distribution.                      ==
+   =========================================================================
+
+   This product includes software developed by
+   The Apache Software Foundation (http://www.apache.org/).
+
+   This product includes also software developed by :
+     - the W3C consortium (http://www.w3c.org) ,
+     - the SAX project (http://www.saxproject.org)
+
+   Please read the different LICENSE files present in the root directory of
+   this distribution.
Index: /release-kits/shared/ant/README
===================================================================
--- /release-kits/shared/ant/README	(revision 16139)
+++ /release-kits/shared/ant/README	(revision 16139)
@@ -0,0 +1,70 @@
+
+                                 A     N     T
+ 
+
+  What is it? 
+  -----------
+  
+  Ant is a Java based build tool. In theory it is kind of like "make" 
+  without makes wrinkles and with the full portability of pure java code.
+
+  
+  Why?
+  ----
+  
+  Why another build tool when there is already make, gnumake, nmake, jam, 
+  and others? Because all of those tools have limitations that its original 
+  author couldn't live with when developing software across multiple platforms. 
+  
+  Make-like tools are inherently shell based. They evaluate a set of 
+  dependencies and then execute commands not unlike what you would issue on a 
+  shell. This means that you can easily extend these tools by using or writing 
+  any program for the OS that you are working on. However, this also means that 
+  you limit yourself to the OS, or at least the OS type such as Unix, that you 
+  are working on.
+  
+  Makefiles are inherently evil as well. Anybody who has worked on them for any 
+  time has run into the dreaded tab problem. "Is my command not executing 
+  because I have a space in front of my tab!!!" said the original author of Ant 
+  way too many times. Tools like Jam took care of this to a great degree, but 
+  still use yet another format to use and remember.
+  
+  Ant is different. Instead a model where it is extended with shell based 
+  commands, it is extended using Java classes. Instead of writing shell 
+  commands, the configuration files are XML based calling out a target tree 
+  where various tasks get executed. Each task is run by an object which 
+  implements a particular Task interface.
+  
+  Granted, this removes some of the expressive power that is inherent by being 
+  able to construct a shell command such as `find . -name foo -exec rm {}` but 
+  it gives you the ability to be cross platform. To work anywhere and 
+  everywhere. And hey, if you really need to execute a shell command, Ant has 
+  an exec rule that allows different commands to be executed based on the OS 
+  that it is executing on.
+
+  The Latest Version
+  ------------------
+
+  Details of the latest version can be found on the Apache Ant
+  Project web site <http://ant.apache.org/>.
+
+
+  Documentation
+  -------------
+
+  Documentation is available in HTML format, in the docs/ directory.
+  For information about building and installing Ant, see
+  docs/manual/index.html
+
+
+  Licensing
+  ---------
+
+  This software is licensed under the terms you may find in the file 
+  named "LICENSE" in this directory.
+  
+
+  Thanks for using Ant.
+
+                                          The Apache Ant Project
+                                         <http://ant.apache.org/>
Index: /release-kits/shared/ant/TODO
===================================================================
--- /release-kits/shared/ant/TODO	(revision 16139)
+++ /release-kits/shared/ant/TODO	(revision 16139)
@@ -0,0 +1,9 @@
+Things that are scheduled for Ant 1.6.2.  Once this list is empty, we
+can roll the first beta.
+
+Please go ahead and assign things that are not already assigned to
+anybody else (assignments look like [Stefan]) to yourself - and please
+remove items from this list once they are complete.
+
+
+
Index: /release-kits/shared/ant/WHATSNEW
===================================================================
--- /release-kits/shared/ant/WHATSNEW	(revision 16139)
+++ /release-kits/shared/ant/WHATSNEW	(revision 16139)
@@ -0,0 +1,2950 @@
+Changes from Ant 1.6.4 to Ant 1.6.5
+===================================
+
+Changes that could break older environments:
+--------------------------------------------
+
+Fixed bugs:
+-----------
+
+* <move> was unable to replace existing files or write into
+  existing directories.  Bugzilla report 34962.
+
+* <macrodef> with redefined default values was incorrect. (Fix for
+   31215 had a bug). Bugzilla report 35109.
+
+* <javadoc> will convert backslashes to forwardslashes when generating file
+  list by useexternalfile. Bugzilla report 27814.
+
+Changes from Ant 1.6.3 to Ant 1.6.4
+===================================
+
+Changes that could break older environments:
+--------------------------------------------
+
+Fixed bugs:
+-----------
+
+* Sun javah failed with java.lang.NoClassDefFoundError.
+  Bugzilla report 34681.
+
+* DirectoryScanner.slowScan() was broken. Bugzilla report 34722.
+
+* DirectoryScanner.scan() could throw a NullPointerException on
+  case-insensitive filesystems (read Windows or MacOS X).
+
+* Get w/authentication failed with ArrayOutOfBoundsExceptions.
+  Bugzilla report 34734.
+
+* Granularity attribute for <sync> task was undocumented.
+  Bugzilla report 34871.
+
+* <unzip> and <untar> could leave file handles open on invalid
+  archives.  Bugzilla report 34893.
+
+* propertyset threw NPE with nested, mapped propertysets.
+
+* <ftp> up to date calculations were wrong.
+  Bugzilla report 34941.
+
+Other changes:
+--------------
+
+* AntXMLContext.setCurrentTargets() is now public. Bugzilla report 34680.
+
+Changes from Ant 1.6.2 to Ant 1.6.3
+===================================
+
+Changes that could break older environments:
+--------------------------------------------
+
+* The subant task used the canonical version of a file path. This
+  has been changed to use the absolute path. Bugzilla 30438.
+
+* Tar now writes two EOF blocks rather than one.
+  Bugzilla report 28776
+
+* The Reference object now has a project field which it uses in preference
+  to the project passed in. This allows composite references to be
+  handled to nested projects.
+  Bugzilla report 25777
+
+* <junit> with filtertrace="true" will now also swallow lines for the
+  sun.reflect package.  If you need to see them in your stack trace,
+  you must set filtertrace to false.
+  Bugzilla Report 22758
+
+* The jikes compiler adapter now supports -bootclasspath, -extdirs and
+  -sourcepath and also uses the same logic for debug flags as javac.
+  This means, the jikes compiler adapter now requires Jikes 1.15 or later.
+  Bugzilla Reports 25868, 26404 and 32609.
+
+* Targets with identical name work in imported project. Bugzilla Report 34566.
+
+* The gcj compiler adapter used to include the Java runtime classes
+  even if includeJavaRuntime was set to false, unless the
+  bootclasspath has been specified as well.  It will now always adhere
+  to includeJavaRuntime, you may need to set it to true explicitly now
+  if you relied on the old behavior.
+
+Other changes:
+--------------
+
+* <javadoc> can now take an attribute 'executable'. Bugzilla report 30606.
+
+* New attribute ignorecontents for <different> selector
+
+* Javadoc fixes for Location, Project, and RuntimeConfigurable
+  Bugzilla 30160.
+
+* Enable to choose the regexp implementation without system property.
+  Bugzilla Report 15390.
+
+* Expose objects and methods in IntrospectionHelper. Bugzilla Report 30794.
+
+* Allow file attribute of <move> to rename a directory.
+  Bugzilla Report 22863.
+
+* Add xmlcatalog nested element to XmlProperty. Bugzilla report 27053.
+
+* New attribute alwayslog for <redirector> type.
+
+* Added <target> nested elements to <ant> and <antcall> to allow
+  specification of multiple sub-build targets, which are executed
+  with a single dependency analysis.
+
+* Refactored Target invocation into org.apache.tools.ant.Executor
+  implementations.  Bugzilla Reports 21421, 29248.
+
+* <rmic> now also supports Kaffe's rmic version shipping with Kaffe
+  1.1.2 and above.
+
+* added casesensitive attribute to <globmapper> and <regexpmapper>
+  Bugzilla report 16686
+
+* added handledirsep attribute to <globmapper> and <regexpmapper>
+  Bugzilla report 32487
+
+* added a new mapper <filtermapper>
+
+* When a BuildListener tried to access System.err or System.out, Ant
+  would have thrown an exception - this has been changed.  Ant now
+  silently ignores the message.  BuildListeners still should avoid
+  accessing either stream.
+
+* Added a comment attribute to the zip task.
+  Bugzilla report 22793.
+
+* Overloaded FileUtils.createNewFile with a boolean mkdirs attribute
+  to create nonexistent parent directories.
+
+* <apply> has a new "force" attribute that, when true, disables
+  checking of target files.
+
+* Made the dest attribute of the apply task optional; mapped target
+  filenames will be interpreted as absolute pathnames when dest is omitted.
+
+* Changed default tempdir for <javac> from user.dir to java.io.tmpdir.
+
+* Added searchpath attribute to <exec> for searching path variable(s)
+  when resolveexecutable = true.
+
+* Added revision and userid attributes to <pvcs> documentation.
+
+* Added support to the touch task for a mkdirs attribute to create
+  nonexistent parent directories before touching new files.
+
+* Added support to the touch task for a pattern attribute to allow
+  alternate datetime formats.
+
+* Added support to the touch task to map touched files using a nested
+  mapper element.
+
+* Added support to the touch task for a verbose attribute to suppress
+  logging of new file creation.
+
+* bad link in docs to the enhancement page in bugzilla.
+  Bugzilla report 33252.
+
+* Added length task to get strings' and files' lengths.
+
+* <native2ascii> and <javah> now also support Kaffe's versions.
+
+* Recursive token expansion in a filterset can now be disabled by
+  setting its recurse attribute to false.
+
+* Pathconvert no longer requires that one of (targetos|pathsep|dirsep)
+  be set; platform defaults are used when this is the case.
+
+* Added preservelastmodified attribute to fixcrlf task. Bugzilla 25770.
+
+* Added isfileselected condition.
+
+* Added verbose="true|false" attribute to <subant>. When verbose is enabled,
+  the directory name is logged on entry and exit of the sub-build. Bugzilla 33787.
+
+* Added -nouserlib option to allow running ant without automatically loading
+  up ${user.home}/.lib/ant. This is useful when compiling ant, and antlibs.
+  Modified the build.sh and build.bat to use the option.
+
+* Added -noclasspath option to allow running ant WITHOUT using CLASSPATH env
+  variable. Modified ant.bat to do this so that %CLASSPATH% is not looked at.
+
+* Add else attribute to the condition task, which specifies an
+  optional alternate value to set the property to if the nested
+  condition evaluates to false. Bugzilla report 33074.
+
+* Ant generated jar files should now be detected as jar files by
+  Solaris.  Bugzilla Report 32649.
+
+* Added two tutorials
+  - beginner: introduction into Ant
+  - task developers: using path, fileset etc
+
+* <rpm> has new attributes failonerror and quiet.
+
+Fixed bugs:
+-----------
+
+* Do not pass on ThreadDeath when halting <java fork="false">. Bugzilla
+  32941.
+
+* Killing a thread running <java fork="true"> (e.g. from an IDE) would
+  not stop the forked process. Bugzilla 31928.
+
+* Programs run with <java fork="true"> can now accept standard input
+  from the Ant console.  (Programs run with <java fork="false"> could
+  already do so.)  Bugzilla 24918.
+
+* AbstractCvsTask prematurely closed its outputStream and errorStream.
+  Bugzilla 30097.
+
+* Impossible to use implicit classpath for <taskdef>
+  when Ant core loader != Java application loader and
+  Path.systemClassPath taken from ${java.class.path} Bugzilla 30161.
+
+* MacroInstance did not clean up nested elements correctly in the execute 
+  method, causing multiple use of the same macro instance with nested
+  elements to fail.
+
+* checksum fileext property doc wrong. Bugzilla 30787.
+
+* FTP task, getTimeDiff method was returning wrong value. Bugzilla 30595.
+
+* Zip task was not zipping when only empty directories were found.
+  Bugzilla 30365.
+
+* Jar task was not including manifest files when duplicate="preserve" was
+  chosen. Bugzilla 32802.
+
+* Classpath was treated in the same way as -lib options. Bugzilla 28046.
+
+* Manual page for cvsversion contained incorrect attributes and did not
+  say since 1.6.1. Bugzilla 31408.
+
+* Typo in definition of <cvsversion> task causing it not to be defined.
+  Bugzilla 31403.
+
+* Execution of top level tasks in imported files get delayed by targets.
+  Bugzilla report 31487.
+
+* ExecTask executes checkConfiguration() even though os does not match.
+  Bugzilla report 31805.
+
+* Concat task instance could not be run twice.
+  Bugzilla report 31814.
+
+* NPE using XmlLogger and antlib.
+  Bugzilla report 31840.
+
+* Properties.propertyNames() should be used instead of .keys().
+  Bugzilla report 27261.
+
+* Target location is not set for default target.
+  Bugzilla report 32267.
+
+* Incorrect classloader parent in junittask when using with
+  ant-junit.jar and junit.jar not in the project classloader. Bugzilla
+  report 28474.
+
+* getResources() on the classloader returned by ClasspathUtils would
+  see each resource twice - if the resource is in the project
+  classpath and if the classloader is requested with a null path.
+
+* XMLValidate used URL#getFile rather than the ant method FileUtils#fromURI
+  Bugzilla report 32508
+
+* fixed Regexp-Mapper docs which gave outdated instructions (optional.jar)
+  Bugzilla report 28584
+
+* <scp> using <fileset> didn't work with OpenSSH 3.9 and later.
+  Bugzilla report 31939
+
+* <setproxy> failed to set user/password on some JDKs.
+  Bugzilla report 32667
+
+* untar would go into infinite loop for some invalid tar files.
+  Bugzilla report 29877
+
+* forked <javac> won't pass -source to a JDK 1.1 or 1.2 javac anymore.
+  Bugzilla report 32948
+
+* propertyset references did not handle nested propertyset references.
+
+* oata.types.Description.getDescription(Project) would throw a
+  NullPointerException when the "ant.targets" reference was unset.
+
+* Wrapper scripts did not detect WINNT value of dynamic OS environment
+  variable when logged into workstations using Novell authentication.
+  Bugzilla Report 30366.
+
+* DependScanner.getResource() always returned nonexistent resources,
+  even when the resource actually existed.  Bugzilla Report 30558.
+
+* <apply> was broken with classfilesets.  Bugzilla Report 30567.
+
+* <available> returned false positives when checking a file
+  passed in with the current basedir leading twice:
+  e.g. ${basedir}${file.separator}${basedir}${file.separator}foo .
+
+* The first file open that took place when using input files with the
+  <exec>, <apply>, or <java> tasks was always logged to System.out
+  instead of to the managing Task.
+
+* <telnet> and <rexec> would try to disconnect from servers they never
+  connetced to, potentially leading to exceptions in commons-net.
+  Bugzilla Report 33618.
+
+* <zip> would drop files matched by defaultexcludes during updates.
+  Bugzilla Report 33412.
+
+* <zip> couldn't store files with size between 2GB and 4GB (the
+  upper limit set by the ZIP format itself).  Bugzilla Report 33310.
+
+* NPE when when <presetdef> tries to configure a task that
+  cannot be instantiated. Bugzilla Report 33689.
+
+* <javac debug="false"> created an invalid command line when running
+  the Symantec Java compiler.
+
+* Get with usetimestamp did not work on Java 1.2.
+
+* Get with usetimestamp did not work when local timestamp roughly >= now.
+
+* The framed JUnit report now handles multiple reports for the same
+  testcase properly.  Bugzilla Report 32745.
+
+* <cab> didn't work for files with spaces in their names on Windows.
+  Bugzilla Report 17182.
+
+* The VAJ tasks could fail if the project name contained characters
+  that need to get URL encoded.  Bugzilla Report 23322.
+  
+* The FTP task could crash with an ArrayIndexOutOfBoundsException
+  Bugzilla Report 28797
+  
+* Improved handling of paths on cygwin
+  Bugzilla Report 31245
+
+* TarInputStream#read() wasn't implemented correctly.  Bugzilla Report
+  34097.
+
+* <xslt> failed to process file-hierarchies of more than one level if
+  scanincludeddirectories was true.  Bugzilla Report 24866.
+
+* forkmode="perBatch" or "once" would ignore extension attributes that
+  had been specified for <formatter>s.  Bugzilla Report 32973.
+  
+* minor performance update of Locator.fromUri. Bugzilla Report 34529.
+
+* The refid attribute of the I/O redirector was not functional.
+
+* SQL task would try access result sets of statements that didn't
+  return any, causing problems with Informix IDS 9.2 and IBM DB2 8.1
+  FixPak 6 (or later). Bugzilla Reports 27162 and 29954.
+
+* make sure that Zip and its derivates call the createEmptyZip method when
+ there are no resources to zip/jar/...
+
+* ant.bat was missing runAntNoClasspath label for goto.
+  Bugzilla 34510.
+
+
+Changes from Ant 1.6.1 to Ant 1.6.2
+===================================
+
+Changes that could break older environments:
+--------------------------------------------
+
+* The import task used the canonical version of a file path. This
+  has been changed to use the absolute path. Bugzilla 28505.
+
+* ant-xalan2.jar has been removed since the only class contained in it
+  didn't depend on Xalan-J 2 at all.  Its sole dependency has always
+  been TraX and so it has been merged into ant-trax.jar.
+
+* All exceptions thrown by tasks are now wrapped in a buildexception
+  giving the location in the buildfile of the task.
+
+* Nested elements for namespaced tasks and types may belong to the
+  Ant default namespace as well as the task's or type's namespace.
+
+* <junitreport> will very likely no longer work with Xalan-J 1.
+
+  Note that Xalan-J 1 has been deprecated for a very long time and we
+  highly recommend that you upgrade.
+
+  If you really need to continue using Xalan-J 1, please copy the
+  junit-frames-xalan1.xsl from the distribution's etc directory as
+  junit-frames.xsl into a new directory and use the task's styledir
+  attribute to point to.  This is the last version of the XSLT
+  stylesheet that is expected to be compatible with Xalan-J 1.
+
+* <csc>'s extraoptions can now contain multiple arguments.
+  Bugzilla Report 23599.
+
+* <macrodef> with default values set by properties would be
+  seen as new definitions when called twice with different properties.
+  This was confusing so the definitions are now treated as similar.
+  Bugzilla Report 31215.
+
+* <javadoc> has a new attribute "includenosourcepackages" that can be
+  used to document packages that don't hold source files but a
+  package.html file.  Bugzilla Report 25339.
+
+Fixed bugs:
+-----------
+
+* eliminate memory leak in AntClassLoader. Bugzilla Report 8689.
+
+* subant haltonfailure=false did not catch all failures. Bugzilla Report 27007.
+
+* macrodef @@ escaping was broken.  Bugzilla Report 27069.
+
+* SQL task did not work with Informix IDS 9.2. Bugzilla Report 27162.
+
+* MacroDef did not allow attributes named 'description'. Bugzilla Report 27175.
+
+* Throw build exception if name attribute missing from patternset#NameEntry.
+  Bugzilla Report 25982.
+
+* Throw build exception if target repeated in build file, but allow targets
+  to be repeated in imported files. 
+
+* <apply> didn't compare timestamps of source and targetfiles when
+  using a nested <filelist>.  Bugzilla Report 26985.
+
+* tagdiff.xml was broken in ant 1.6.1. Bugzilla Report 27057.
+
+* if the basedir contained .. or . dirs, and the build file name contained
+  .. or ., the basedir was set incorrectly. Bugzilla Report 26765.
+
+* regression from ant 1.5, exec task outputted two redundant trailing newlines.
+  Bugzilla Report 27546.
+
+* NPE when running commons listener. Bugzilla Report 27373.
+
+* <java> swallowed the stack trace of exceptions thrown by the
+  executed program if run in the same VM.
+
+* -projecthelp swallowed (configuration) errors silently.
+  Bugzilla report 27732.
+
+* filterset used by filtertask doesn't respect loglevel. Bugzilla Report 27568.
+
+* wrong compare used in ProjectComponent for logging. Bugzilla Report 28070.
+
+* failOnAny attribute for <parallel> was broken. Bugzilla Report 28122.
+
+* If <javac> uses gcj and any of the nested <compilerarg>s implies
+  compilation to native code (like -o or --main), Ant will not pass
+  the -C switch to gcj.  This means you can now compile to native code
+  with gcj which has been impossible in Ant < 1.6.2.
+
+* <import optional="false"> and <import optional="true">
+  behaved identically.
+
+* <xslt> now sets the context classloader if you've specified a nested
+  <classpath>.  Bugzilla Report 24802.
+
+* <zip> and friends would delete the original file when trying to update
+  a read-only archive.  Bugzilla Report 28419.
+
+* <junit> and <assertions> are working together. Bugzilla report 27218
+
+* AntClassLoader#getResource could return invalid URLs.  Bugzilla
+  Report 28060.
+
+* Ant failed to locate tools.jar if the jre directory name wasn't all
+  lowercase.  Bugzilla Report 25798.
+
+* Redirector exhibited inconsistent behavior with regard to split
+  output.  When sent to file only, files would be created in all
+  cases; when split file-property, files were only created if
+  writes were performed.
+
+* fixed case handling of scriptdef attributes and elements.
+
+* UNC pathnames did not work for ANT_HOME or -lib locations on Windows.
+  Bugzilla report 27922.
+
+* replacestring tokenfilter only replaced the first occurrence.
+
+* AntLikeTasksAtTopLevelTest failed on cygwin.
+
+* I/O-intensive processes hung when executed via <exec spawn="true">.
+  Bugzilla reports 23893/26852.
+
+* JDependTask did not close an output file. Bugzilla Report 28557.
+
+* Using <macrodef> could break XmlLogger. Bugzilla Report 28993.
+
+* <genkey> no longer requires keytool to be in your PATH.  Bugzilla
+  Report 29382.
+
+* <symlink> could create cyclic links.  Bugzilla Report 25181.
+
+* <zip whenempty="skip"> didn't work in a common situation.  Bugzilla
+  Report 22865.
+
+* <scp> now properly handles remote files and directories with spaces
+  in their names.  Bugzilla Report 26097.
+
+* <scp> now has (local|remote)tofile attributes to rename files on the
+  fly.  Bugzilla Report 26758.
+
+* <telnet> and <rexec> didn't close the session.  Bugzilla Report 25935.
+
+* <subant> and XmlLogger didn't play nicley together.
+
+Other changes:
+--------------
+* doc fix concerning the dependencies of the ftp task
+  Bugzilla Report 29334.
+
+* <xmlvalidate> has now a property nested element,
+  allowing to set string properties for the parser
+  Bugzilla Report 23395.
+
+* Docs fixes for xmlvalidate.html, javadoc.html, starteam.
+  Bugzilla Reports 27092, 27284, 27554.
+
+* <pathconvert> now accepts nested <mapper>s.  Bugzilla Report 26364.
+
+* Shipped XML parser is now Xerces-J 2.6.2.
+
+* Added nested file element to filelist.
+
+* spelling fixes, occurred. Bugzilla Report 27282.
+
+* add uid and gid to tarfileset. Bugzilla Report 19120.
+
+* <scp> has a verbose attribute to get some feedback during the
+  transfer and new [local|remote][File|Todir] alternatives to file and
+  todir that explicitly state the direction of the transfer.
+
+* The OS/2 wrapper scripts have been adapted to use the new launcher.
+  Bugzilla Report 28226.
+
+* <sshexec> now also captures stderr output.  Bugzilla Report 28349.
+
+* <xslt> now supports a nested <mapper>.  Bugzilla Report 11249.
+
+* <touch> has filelist support.
+
+* <nice> task lets you set the priority of the current thread; non-forking
+  <java> code will inherit this priority in their main thread.
+
+* New attribute "negate" on <propertyset> to invert selection criteria.
+
+* Target now supports a Location member.  Bugzilla Report 28599.
+
+* New "pattern" attribute for <date> selector.
+
+* <junit> has a new forkmode attribute that controls the number of
+  Java VMs that get created when forking tests.  This allows you to
+  run all tests in a single forked JVM reducing the overhead of VM
+  creation a lot.  Bugzilla Report 24697.
+
+* <jar> can now optionally create an index for jars different than the
+  one it currently builds as well.  See the new <indexjars> element
+  for details.  Bugzilla Report 14255.
+
+* Permit building under JDK 1.5. Bugzilla Report 28996.
+
+* minor Javadoc changes. Bugzilla Report 28998.
+
+* Misc. corrections in SignJar.java. Bugzilla Report 28999.
+
+* Remove redundant <hr> from javah.html. Bugzilla Report 28995.
+
+* Ignore built distributions. Bugzilla Report 28997.
+
+* A new roundup attribute on <zip> and related task can be used to
+  control whether the file modification times inside the archive will
+  be rounded up or down (since zips only store modification times with
+  a granularity of two seconds).  The default remains to round up.
+  Bugzilla Report 17934.
+
+* A binary option has been added to <concat>. Bugzilla Report 26312.
+
+* Added DynamicConfiguratorNS, an namespace aware version of
+  DynamicConfigurator. Bugzilla Report 28436.
+
+* Add implicit nested element to <macrodef>. Bugzilla Report 25633.
+
+* Add deleteonexit attribute to <delete>.
+
+* Added Target.getIf/Unless().  Bugzilla Report 29320.
+
+* <fail> has a status attribute that can be used to pass an exit
+  status back to the command line.
+
+* <fail> accepts a nested <condition>.
+
+* <loadproperties> supports loading from a resource.
+  Bugzilla Report 28340.
+
+* Nested file mappers and a container mapper implementation have been
+  introduced.  Additionally, the <mapper> element now accepts "defined"
+  nested FileNameMapper implementations directly, allowing a usage
+  comparable to those of <condition>, <filter>, and <selector>.
+
+* New <redirector> type introduced to provide extreme I/O flexibility.
+  Initial support for <exec>, <apply>, and <java> tasks.
+
+* <apply> has a new ignoremissing attribute (default true for BC)
+  which will allow nonexistent files specified via <filelist>s to
+  be passed to the executable.  Bugzilla Report 29585.
+
+* <junitreport> now also works with Xalan XSLTC and/or JDK 1.5.
+  Bugzilla Report 27541.
+
+* <jspc> doesn't work properly with Tomcat 5.x.  We've implemented a
+  work-around but don't intend to support future changes in Tomcat
+  5.x.  Please use the jspc task that ships with Tomcat instead of
+  Ant's.
+
+Changes from Ant 1.6.0 to Ant 1.6.1
+=============================================
+
+Changes that could break older environments:
+--------------------------------------------
+
+* License is now Apache Software License 2.0
+  see http://www.apache.org/licenses/ for more information
+
+Fixed bugs:
+-----------
+* Remove a recursive template call in the junit xsls that could trigger a stack
+  overflow. It now uses Xalan extensions to call a Java class directly.
+  Bugzilla Report 19301
+  
+* Fix spurious infinite loop detection for filters (introduced in ant 1.6.0).
+  Bugzilla Report 23154.
+
+* Fix handling of default ant namespace for nested elements.
+
+* Fix jboss element of ejb task (introduced in ant 1.6.0).
+
+* <whichresource> failed to load classes correctly.
+
+* Ant could fail to start with a NullPointerException if
+  ANT_HOME/lib/ant-launcher.jar was part of the system CLASSPATH.
+
+* presetdef'ed types did not work with the ant-type attribute
+
+* fixed case handling of macrodef attributes and elements. Bugzilla
+  Reports 25687 and 26225.
+
+* <java> ignored the append attribute, Bugzilla Report 26137.
+
+* The gcj compiler adapter for <javac> failed if the destination
+  directory didn't exist.  Bugzilla Report 25856.
+
+* Ant now fails with a more useful message if a new process will be
+  forked in a directory and that directory doesn't exist.
+
+* <splash> used to break the build on non-GUI environments.  Bugzilla
+  report 11482.
+
+* Ant 1.6.0 cannot run build scripts in directories with non-ASCII names.
+  Bugzilla Report 26642.
+
+Other changes:
+--------------
+* Shipped XML parser is now Xerces-J 2.6.1
+
+* Translate task logs a debug message specifying the number of files
+  that it processed.  Bugzilla Report 13938.
+
+* <fixcrlf> has a new attribute - fixlast. Bugzilla Report 23262.
+
+* <p4submit> has 2 new attributes, needsresolveproperty and changeproperty.
+  Bugzilla Report 25711.
+
+* add description attributes to macrodef attributes and elements.
+  Bugzilla Report 24711.
+
+* Extending ClearCase Tasks :
+ - Added an extra option to 'failonerr' to each ClearCase task/command.
+ - Extended the functionality of cccheckout. It can check (notco) to see if
+  the desired element is already checked out to the current view. Thus it
+   won't attempt to check it out again.
+ - Added three new ClearCase commands: ccmkattr, ccmkdir, ccmkelem
+  Bugzilla Report 26253.
+
+* added nested text support to <macrodef>
+  
+* added initial support for Java 1.5.  Java 1.5 is now correctly
+  detected by Ant and treated just like Java 1.4.  You can now specify
+  source="1.5" in the <javac> task.
+
+* created new task <cvsversion>
+
+* added support for branch logging via the tag attribute in <cvschangelog>
+  Bugzilla Report 13510.
+
+* added support the groovy language in the script and scriptdef tasks
+
+Changes from Ant 1.5.4 to Ant 1.6.0
+===================================
+
+Changes that could break older environments:
+--------------------------------------------
+
+* This version of Ant can not be built with JDK 1.1 and requires at
+  least Java 1.2 at runtime as well.  Compiling for a 1.1 target is
+  still supported.
+
+* Targets cannot have the empty string as their name any longer.
+
+* ant.jar's manifest does no longer include a Class-Path entry, so it
+  is no longer possible to run Ant via "java -jar ant.jar" without
+  manually altering the CLASSPATH.  Instead of that a file
+  ant-bootstrap.jar is included in the etc directory of the binary
+  distribution, copy this to the lib directory and use
+  "java -jar ant-bootstrap.jar" instead if you want to run Ant without
+  the wrapper script (not recommended).
+
+* The <script> task now requires Apache BSF instead of the older IBM
+  version.  See <http://jakarta.apache.org/bsf/>
+
+* <xmlproperty> will no longer fail if the file to be loaded doesn't exist.
+
+* XML namespaces are now enabled in the XML parser, meaning XML namespace
+  declarations no longer cause errors. However task names containing colons
+  will cause errors unless there is a corresponding namespace uri.
+
+* The <ftp> and <telnet> tasks now require Jakarta Commons Net instead
+  of the older ORO Netcomponents version.  See
+  <http://jakarta.apache.org/commons/net/index.html>.
+
+* <input> will no longer prompt the user and wait for input if the
+  addproperty attribute is set to a property that has already been
+  defined in the project.  If you rely on the task waiting for input,
+  don't use the addproperty attribute.
+
+* The Class-Path attribute in manifests will no longer merge the
+  entries of all manifests found, but will be treated like all other
+  manifest attributes - the most recent attribute(s) will be used.
+
+* New Launch mechanism implemented. This moves some functionality from
+  the batch files / shell scripts into Java. This removes environment
+  limitations, for command issues, directory depth issues on Windows. Also
+  allows a per-user library location to be used if the main Ant install
+  is locked down.
+
+* The Entry nested element of PropertyFile will not any more have its value
+  attribute (actually increment) overwritten with the new value of the entry
+  after execution.
+
+* Output stored from a <java> or <exec> task is now exactly as generated. No
+  conversion to platform end-of-line characters is performed.
+
+* <translate> will now preserve line endings.
+
+* <ftp> followsymlinks="false" in nested fileset definitions is explicitly
+  required in order to exclude remote symbolic links (when doing a get, chmod,
+  delete, rmdir).
+
+* The values of the Copy#fileCopyMap variable has changed from String to
+  String[]. (In java 1.5 terms it was Hashtable<String, String> and
+  is now Hashtable<String, String[]>). This will affect third party code
+  that extend Copy and override Copy#doFileOperations.
+
+* <loadproperties> didn't expand properties while <property file="..."/>
+  does, so they were not equivalent.  This has been fixed, which means
+  that propetries may get expanded twice if you use an
+  <expandproperties> filterreader.  Bugzilla Report 17782.
+
+* User defined tasks and typedefs are now handled internally in the
+  same way as predefined tasks and typedefs. Also tasks and typedefs
+  are resolved at a later stage. This causes some
+  differences especially for user defined task containers.
+
+* <checksum> log message "Calculating checksum ..." has been degraded
+  from INFO to VERBOSE.
+
+Fixed bugs:
+-----------
+* Filter readers were not handling line endings properly.  Bugzilla
+  Report 18476.
+
+* Filtersets were also not handling line endings properly.
+
+* Expand tasks did not behave as expected with PatternSets.
+
+* <property environment=... /> now works on OS/400.
+
+* <cab> could hang listcab on large <fileset>s.
+
+* The starteam stcheckout, stcheckin tasks now correctly compute
+  status of files against whatever local tree they are run against
+  and, optionally, will not process a file if it is current.
+  Previously you had to process everything unless you ran against the
+  default folder which wasn't the normal use-case for ant-starteam.
+  The stlist task now similarly displays that status correctly making
+  it a more generally useful tool.
+
+* entity includes would cause exceptions if path names included spaces.
+
+* addConfiguredXXX would not work for TaskAdapter wrapped tasks
+
+* Fix <ilasm> outputfile testing so that the output file does not need
+  to exist beforehand.
+
+* Ant will now exit with a return code of 1 if it encounters problems
+  with the command line arguments.
+
+* ClassLoader creation changes to use a factory method in Project. A new
+  class AntClassLoader2 implemented for 1.2+ specific features including
+  Package information and addition of classes specified in the Class-Path
+  element of a Jar's manifest.
+
+* It is now possible in <exec> to resolve the executable to a project
+  basedir or execution dir relative executable. The resolveExecutable
+  must be used to pick up such executables.
+
+* splash screen wouldn't disappear when build was finished.
+
+* <exec> output and error streams can now be redirected independently
+  to either a property or a file (or both)
+
+* TarEntry's File-arg constructor would fail with a
+  StringIndexOutOfBoundsException on all OSes where os.name is shorter
+  than seven characters.  Bugzilla Report 18105.
+
+* <copy> and <move>'s failonerror didn't apply to filesets pointing to
+  non-existant directories.  Bugzilla Report 18414.
+
+* The <stripjavacomments> filter sometimes removed parts of string
+  constants.  Bugzilla Report 17441.
+
+* <antlr> will now recompile your grammar if the supergrammar has
+  changed.  Bugzilla Report 12691.
+
+* <property env> will now work on Unices with /bin/env instead of
+  /usr/bin/env.  Bugzilla Report 17642.
+
+* <jar index="on"> could include multiple index lists.  Bugzilla 10262.
+
+* The index created by <jar> didn't conform to the spec as it didn't
+  include the top-level entries.  Bugzilla Report 16972.
+
+* <tar> and <zip> didn't honor the defaultexcludes attribute for the
+  implicit fileset.  Bugzilla Report 18637.
+
+* The <replacetokens> filter would throw an exception if the token's
+  value was an empty string.  Bugzilla Report 18625.
+
+* Perforce tasks relying on output from the server such as <p4change>
+  and <p4label> were hanging. Bugzilla Reports 18129 and 18956.
+
+* Improve exception and logging behavior of Perforce tasks.
+  Bugzilla report 18154.
+
+* build.sh install had a problem on cygwin (with REALANTHOME).
+  Bugzilla Report 17257
+
+* <replaceregexp> didn't work for multi-byte encodings if byline was false.
+  Bugzilla Report 19187.
+
+* <replaceregexp> was altering unnecessarily the timestamp of the directories
+  containing the files to process
+  Bugzilla Report 22541.
+
+* file names that include spaces need to be quoted inside the @argfile
+  argument using forked <javac> and (all JDKS).  Bugzilla Report 10499.
+  NB : a first correction was only introducing quotes for JDK 1.4
+  It has been changed to quote for all external compilers when paths
+  contain spaces.
+  Also the backslashes need to be converted to forward slashes
+  Bugzilla Report 17683.
+
+* Setting filesonly to true in <zip> and related tasks would cause the
+  archives to be always recreated.  Bugzilla Report 19449.
+
+* file names that include spaces need to be quoted inside the @argfile
+  argument using <javadoc> and JDK 1.4.  Bugzilla Report 16871.
+
+* <junit> didn't work with custom formatters that were only available
+  on the user specified classpath when a timeout occured.  Bugzilla
+  Report 19953.
+
+* <different> selector : make ignoreFileTimes effectively default to true
+  and fix a bug in the comparison of timestamps. Bugzilla Report 20205.
+
+* <different> selector can now be nested directly under a fileset
+  Bugzilla Report 20220.
+
+* <cvstagdiff> had a problem with "dd-MM-yy hh:mm:ss" formats
+  Bugzilla Report 15995.
+
+* <cvstagdiff> cvsroot and package attributes added to the root
+  element tagdiff of the xml output
+  Bugzilla Report 16081.
+
+* <cvstagdiff> had a problem with aliased modules and with requests for multiple modules
+  Bugzilla Reports 21373 and 22877.
+
+* <cvstagdiff> could not parse properly the revision number of new files with CVS 1.11.9 or higher
+  Bugzilla Report 24406.
+
+* <fixcrlf> make fixcrlf create its temporary files in the default directory
+  of FileUtils#createTempFile instead of the destination dir of fixcrlf.
+  Bugzilla Report 20870.
+
+* <ejbjar> implementation for Borland.
+  Prevent the task from being blocked by error messages coming from java2iiop.
+  Bugzilla Report 19385.
+
+* <unzip>'s and <untar>'s nested patternsets didn't work as documented
+  when the pattern ended in a slash or backslash.  Bugzilla Report 20969.
+
+* <fixcrlf> will now create the parent directories for the destination
+  files if necessary.  Bugzilla Report 20840.
+
+* <xmlproperty> now handles CDATA sections. BugZilla Report 17195
+
+* <translate> now translate tokens that are placed close together.
+  Bugzilla Report 17297
+
+* Nested websphere element for ejbjar does not support spaces in file name.
+  Bugzilla Report 21298
+
+* Don't multiply Class-Path attributes when updating jars.  Bugzilla
+  Report 21170.
+
+* Do not overwrite the value (increment) attribute of PropertyFile nested Entry element.
+  Bugzilla Report 21505.
+
+* Prevent sysproperties with no key or no value from being added in <junit>.
+  Bugzilla Report 21684.
+
+* Allow references to be properly inherited via antcall
+  Bugzilla Report 21724.
+
+* ftp chmod failed when the remote system was UNIX and local system Windows
+  Bugzilla Report 21865.
+
+* ftp put with chmod failed when the remote system was UNIX and local system Windows
+  Bugzilla Report 23143.
+
+* ftp did not set the ascii mode explicity, causing problems with ftp servers
+  having binary as default
+
+* ftp was not able to download files when they were pointed to by symbolic links
+  Bugzilla Report 14063.
+
+* ftp is able to download also directories pointed to by symbolic links.
+
+* replace would change \r\n into \r\r\n under Windows.
+
+* junitreport with frames did not display a link for classes without a package
+  or in the top package.
+  Bugzilla Report 21915.
+
+* Project.toBoolean(String) now handles null as argument and does not throw a
+  NullPointerException any more.
+
+* The socket condition will now close the socket created to test.
+  Bugzilla Report 23040.
+
+* <junit includeantruntime="true" fork="true"> replaced the CLASSPATH instead
+  of adding to it.  Bugzilla Report 14971.
+
+* <splash> could fail on JVMs that use null to indicate the system classloader.
+  Bugzilla Report 23320.
+
+* <xmlcatalog>s only worked when defined inside of tasks.  Bugzilla
+  Report 20965.
+
+* <csc> and siblings (<vbc> <jsharpc>) handle large filesets by
+automatic use of response files.  Bugzilla report #19630
+
+Other changes:
+--------------
+
+* Shipped XML parser is now Xerces 2.6.0
+
+* All tasks can be used outside of <target>s.  Note that some tasks
+  will not work at all outside of targets as they would cause infinite
+  loops (<antcall> as well as <ant> and <subant> if they invoke the
+  current build file).
+
+* Six new Clearcase tasks added.
+
+* A new filter reader namely tokenfilter has been added.  Bugzilla
+  Report 18312.
+
+* A new attribute named skip is added to the TailFilter and
+  HeadFilter filter readers.
+
+* The filesetmanifest attribute of <jar> has been reenabled.
+
+* The start and end tokens for <translate> may now be longer than a
+  single character.
+
+* <setproxy> lets you set the username and password for proxies that
+  want authentication
+
+* <loadproperties> has a new encoding attribute.
+
+* <echoproperties> can now create XML output.
+
+* <echoproperties> has a new srcfile attribute that can make it read
+  properties files and output them instead of Ant's properties.
+
+* <filterset> will now resolve filters recursively.
+
+* <input> has a new attribute that allows you to specify a default value.
+
+* Added <image> task (requires JAI).
+
+* <image> task has now proportions attribute in the <scale/> nested element
+  instead of keepproportions (bringing in more functionality)
+
+* New condition <isreference>
+
+* <ftp> now has a preservelastmodified attribute to preserve the
+  timestamp of a downloaded file.
+
+* new rmdir action for <ftp> that removes directories from a fileset.
+
+* <ftp> has attributes timediffauto and timediffmillis to use together
+  with the newer attribute to tell ant to take into account a time difference
+  between client and remote side.
+  Bugzilla Report 19358.
+
+* <ftp> has been optimized to go directly to the include patterns.
+  This reduces scanning time under UNIX when followsymlinks="true"
+  and casesensitive="true" (the default)
+  Bugzilla Report 20103.
+
+* The SOS and VSS tasks will no longer unconditionally prepend a $ to
+  vsspath or projectpath.
+
+* OS/400 now gets detected by the os condition.
+
+* <arg> has a new attribute pathref that can be used to reference
+  previously defined paths.
+
+* <xmlproperty> has been improved, you can now expand ${properties},
+  define ids or paths and use Ant's location magic for filename resolutions
+  in the XML file.
+
+* <xmlcatalog> will now support external catalogs according to the
+  OASIS "Open Catalog" standard - if resolver.jar (newer than version
+  1.0) from Apache's xml-commons is in your CLASSPATH.
+
+* Starteam tasks now have support for revision labels and build labels.
+  Checkouts now have the option of using repository timestamps, instead
+  of current.
+
+* new task <symlink> that creates and maintains symbolic links.
+
+* new tasks <chown> and <chgrp> which are wrappers of the Unix commands.
+
+* new task <attrib> to change file attributes on Windows systems.
+
+* <style> has a new attribute reloadstylesheet to work around a
+  bug in widespread Xalan versions.
+
+* <tarfileset> has a new dirmode attribute to specify the permissions
+  for directories.
+
+* <fixcrlf>'s eol attribute now also understands "mac", "unix" and "dos".
+
+* <classfileset> now picks up dependencies of the form MyClass.class. This
+  works for the code generated by the Sun java compiler. It may not work for
+  all compilers.
+
+* a new attribute "globalopts" can be added to all Perforce tasks.
+  You can put in it all the strings described by p4 help usage. Refer to
+  the docs for more information.
+
+* new Perforce tasks <p4integrate> , <p4resolve>, and <p4labelsync>
+
+* <p4submit> will change the property p4.change if the Perforce server
+  renumbers the change list.
+  It will set the property p4.needsresolve if the submit fails,
+  and the message says that file(s) need to be resolved.
+
+* <replaceregexp> now has an optional encoding attribute to support
+  replacing in files that are in a different encoding than the
+  platform's default.
+
+* The <exec> task may now have its input redirected from either a file
+  or a string from the build file. The error output can be separated
+  to a different file when outut is redirected. standard error may be
+  logged to the Ant log when redirecting output to a file
+
+* The <java> task also supports the input redirection and separate
+  error streams introduced to the <exec> task. In addition, it is now
+  possible to save the output into a property for use within the build
+  file as was possible with <exec> in Ant 1.5
+
+* The <javadoc> task <tag> subelement has been enhanced to allow files
+  with tag mappings to be used.
+
+* New tasks: <scp> supports file transfers, <sshexec> executes a
+  command over SSH.  They require jsch, a BSD licensed SSH library that
+  can be found at http://www.jcraft.com/jsch/index.html
+
+* New filterreader <escapeunicode/>.
+
+* Support for HP's NonStop Kernel (Tandem) OS has been added.
+
+* <cab>'s basedir attribute is now optional if you specify nested
+  filesets.  Bugzilla Report 18046.
+
+* New task <sync> that synchronizes two directory trees.
+
+* <apply> has new forwardslash attribute that can force filenames to
+  use forward slashes (/) as file separators even on platforms with a
+  different separator.  This is useful if you want to run certain
+  ported Unix tools.
+
+* Copy has a new outputencoding attribute that can be used to change
+  the encoding while copying files.  Bugzilla Report 18217.
+
+* The xml formatter for JUnit will now honor test case names set with
+  setName.  Bugzilla Report 17040.
+
+* JUnit now has an attribute reloading, which, when set to false,
+  makes the task reuse the same class loader for a series of tests.
+
+* <concat> now supports filtering and can check timestamps before
+  overriding a file.  Bugzilla Report 18166.
+
+* <junit> has a new attribute tempdir that controls the placement of
+  temporary files.  Bugzilla Report 15454.
+
+* <jdepend> now supports a new nested element <classespath> which is
+  the same as <sourcespath> but point to compiled classes (the
+  prefered mode of operation for JDepend > 2.5).  Additionally, nested
+  <exclude> elements can be used to exclude certain packages from
+  being parsed.  Bugzilla Report 17134.
+
+* The JProbe tasks now also work with JProbe 4.x.  Bugzilla Report 14849.
+
+* <javacc> and <jjtree> will now autodetect JavaCC 3.x and can use it.
+
+* <sql> has a new attribute to control escape processing.
+
+* <sql> is able to display properly several resultsets if you are
+  running a compound sql statement. Bugzilla Report 21594.
+
+* A new <containsregexp> selector has been added, that selects files
+  if their content matches a certain regular expression.
+
+* <antlr>'s debug attribute has been enabled.  Bugzilla Report 19051.
+
+* <mail> has a new attribute charset. Bugzilla Report 15434.
+
+* <mail> has new attributes user and password for SMTP auth.
+  maillogger can also use this.
+  The implementation only works with JavaMail (encoding="MIME").
+  Implementation with plain mail remains to do.
+  Bugzilla Report 5969.
+
+* <mail> and mailloger support SMTP over TLS/SSL
+  Bugzilla Report 19180.
+
+* <mail> the attributes from, replyto ,tolist, cclist, bcclist
+  can now contain email addresses of the form name <address@xyz.com>
+  or (name) address@xyz.com
+  Bugzilla Report 22474.
+
+* <mail> (version PlainMail)
+  prevent blank headers from being sent,
+  make the order of the headers of plain mail messages predictable
+  Bugzilla Report 22088.
+
+* <zipfileset> can now be defined in the main body of a project
+  and referred to with refid="xyz". Bugzilla Report 17007.
+
+* A wrapper script for OS/2 has been added.
+
+* <unzip> will now detect and successfully extract self-extracting
+  archives.  Bugzilla Report 16213.
+
+* <stcheckout> has a new attribute "converteol" that can be used to
+  control the automatic line-end conversion performed on ASCII files.
+  Bugzilla Report 18884.
+
+* Users can now modify the list of default excludes using the new
+  defaultexcludes task.  Bugzilla Report 12700.
+
+* There is a new data type <propertyset> that can be used to collect
+  properties.  It is supported by <ant>, <antcall>, <subant>, <java>,
+  <echoproperties> and <junit>.
+
+* <concat> can now control the encoding of the output as well and optionally
+  add new-line characters at the end of files that get concatenated but
+  don't end in newlines.  Bugzilla Report 12511.
+
+* <rpm> will detect the rpmbuild executable of RedHat 8.0 and newer
+  and use that if it is on your PATH.  Bugzilla Report 14650.
+
+* A new task <rexec> has been added that requires commons-net to work.
+  Bugzilla Report 19541.
+
+* <javadoc> now supports a nested <arg> element in addition to the
+  additionalparams attribute.
+
+* You can now determine the order of standard tags in <javadoc> via
+  <tag> elements - you must not use the description attribute for them.
+  Bugzilla Report 18912.
+
+* <javadoc> now supports the -noqualifier switch.  Bugzilla Report 19288.
+
+* <javac>'s executable attribute can now also be used to specify the
+  executable for jikes, jvc, sj or gcj.  Bugzilla Report 13814.
+
+* <javac> has a new attribute tempdir that can control the placement
+  of temporary files.  Bugzilla Report 19765.
+
+* A new magic property build.compiler.jvc.extensions has been added
+  that can be used to turn of Microsoft extensions while using the jvc
+  compiler.  Bugzilla Report 19826.
+
+* You can now limit the parallelism of <apply> and <chmod> by using the new
+  maxparallel attribute.
+
+* With the new addsourcefile attribute, you can make <apply> ommit the
+  source file names from the command line.  Bugzilla Report 13654.
+
+* <apply> and <chmod> now support nested <filelist>s as well as <dirset>s.
+  Bugzilla Reports 15929 and 20687.
+
+* <apply> and <chmod> will display a summary if you set the new
+  verbose attribute to true.  Bugzilla Report 19883.
+
+* <copy>/<move>'s failonerror attribute can now also be used to
+  continue the build if an I/O error caused a problem.  Bugzilla
+  Report 12999.
+
+* new selector <type/> allowing to select only files or only directories.
+  Bugzilla Report 20222.
+
+* <java> and <junit> now support a nested <bootclasspath> element that
+  will be ignored if not forking a new VM.
+
+* <junit>'s nested <formatter> elements now support if/unless clauses.
+
+* <ejbjar>
+  cmpversion attribute added
+  jboss element will look for jbosscmp-jdbc.xml descriptor
+  if ejbjar has cmpversion="2.0" set
+  Bugzilla Reports 14707 and 14709.
+
+* <pvcs> config attribute added to set the location of a specific PVCS
+  .cfg file
+  Bugzilla Report 9752
+
+* <mapper> has an "unpackage" mapper
+  Bugzilla Report 18908
+
+* Added <scriptdef> task allowing tasks to be defined using any BSF-supported
+  scripting language.
+
+* <touch>'s datetime attribute can now accept time with a granularity
+  of seconds as well.  Bugzilla Report 21014.
+
+* <checksum> has two new properties: totalproperty and todir.
+
+* FileUtils#createTempFile will now create temporary files in the
+  directory pointed to by the property java.io.tmpdir
+
+* <unzip> and friends now supports an optional encoding attribute to
+  enable it to expand archives created with filenames using an encoding
+  other than UTF8.  Bugzilla Report 10504.
+
+* <patch> has a new attribute destfile that can be used to create a new
+  file instead of patching files in place.
+
+* OpenVMS is detected as a valid OS family.
+
+* DirectoryScanner has been optimized for cases where include patterns do not
+  start with wildcards.  Bugzilla Report 20103.
+
+* DirectoryScanner begins to be optimized not to scan excluded directories.
+  Bugzilla Report 21941.
+
+* Added keep-going feature. Bugzilla Report 21144
+
+* The archives generated by <zip> and friends will now contain CRC and
+  size information in the "local file header", thereby providing this
+  information to applications that read the archives using
+  java.util.ZipInputStream.  Bugzilla Report 19195.
+
+* <copy> and <move> can now handle mappers that return multiple
+  mappings per source path. This behaviour is enabled by using
+  an enablemultiplemapping attribute. Bugzilla Report 21320.
+
+* <exec> will now work on OpenVMS (please read the notes in
+  <exec>'s manual page).  Bugzilla Report 21877.
+
+* <exec> will now have a new attribute spawn (default false).
+  If set to true, the process will be spawned. Bugzilla Report 5907.
+
+* <java> will now have a new attribute spawn (default false).
+  If set to true, the process will be spawned. Bugzilla Report 5907.
+
+* <parallel> now supports a timeout which can be used to recover
+  from deadlocks, etc in the parallel threads. <parallel> also
+  now supports a <daemons> nested element. This can be used to
+  run tasks in daemon threads which the parallel task will not
+  wait for before completing. A new attribute failonany will cause
+  <parallel> to throw an exception if any thread fails without
+  waiting for all other threads to complete.
+
+* <zip> and friends will consume far less memory than they used to
+  when run with compress="false".  Bugzilla Report 21899.
+
+* <if/> and <unless/> attributes added to <param/> element of <style>
+   Bugzilla Report 22044
+
+* <zip> and friends have a new attribute "keepcompression" that can be
+  used to incrementally build an archive mixing compressed and uncompressed
+  entries.
+
+* <junit>'s XML formatter adds a new classname attribute to the <testcase>
+  elements.
+
+* new <permissions> type add permission handling to the code
+  this type can be nested in the <java> and <junit> tasks.
+  Bugzilla Report 22533.
+
+* additional shortcuts for ant options (-d --> -debug, -e --> -emacs,
+  -h --> -help, -p --> -projecthelp, -s --> -find).
+
+* new selector <modified>. "cache" was renamed to "modified".
+  Bugzilla Report 20474.
+
+* <stcheckout> and <stlist> have a new asofdate attribute that can be
+  used to checkout/list files based on a date instead of a label.
+  Bugzilla Report 20578.
+
+* New filter <concatfilter>. Adds the content of file at the beginning
+  or end of a file. Discussion started at
+  http://marc.theaimsgroup.com/?l=ant-user&m=106366791228585&w=2
+
+* New task <import>
+
+* New task <macrodef>
+
+* New task <presetdef>
+
+* Ant libraries that can make use of namespaces to avoid name
+  clashes of custom tasks
+
+
+* <java> and <junit> now support <assertions>, which let you enable
+  and disable Java1.4 assertions on a package or class basis. These
+  only work when fork=true, currently.
+
+* .NET tasks expanded with VB support <vbc> and J#, via <jsharp>,
+  <importtypelib> and <ilasm>. <csc> supports nested <src> types,
+  <defines> for (potentially conditional) definitions, <reference>
+  filesets for references. The executable attribute lets you switch to
+  mono or other implementations -<csc> has been tested with Mono on
+  Linux and OSX.
+
+
+Changes from Ant 1.5.3 to Ant 1.5.4
+===================================
+
+Changes that could break older environments:
+--------------------------------------------
+
+* If the Visual Age tasks used to work for you, they may stop doing so
+  now - and we'd like to know about it.  The current set of tasks is
+  supposed to work with any version of VAJ starting with 3.0.
+
+Fixed bugs:
+-----------
+
+* The Visual Age for Java tasks didn't work (at least for versions 3.0
+  and higher).  Bugzilla Report 10016.
+
+* URL-encoding in <vaj*port> didn't work properly.
+
+* VAJRemoteUtil called getAbsolutePath instead of getPath
+  causing problems when using a Windows VAJ server from a UNIX server.
+  Bugzilla Report 20457.
+
+* VAJImport task failed with NullPointerException when using DirectoryScanner.
+  Bugzilla Report 22080.
+
+Other changes:
+--------------
+
+* Shipped XML parser is now Xerces 2.5.0
+
+* <javah> will invoke oldjavah on JDK 1.4.2.  Bugzilla Report 18667.
+
+* The VAJ tasks now support a haltonfailure attribute to conditionally
+  keep building even if they fail.
+
+* It is now possible to use the latest (versioned or unversioned) edition
+  in <vajload> by using special wildcard characters.  Also fixes
+  Bugzilla Report 2236.
+
+Changes from Ant 1.5.2 to Ant 1.5.3
+===================================
+
+Changes that could break older environments:
+--------------------------------------------
+
+* The <zip> task and friends have again changed a method signature
+  (sorry, was necessary to fix bug 17780).  The return type of
+  getResourcesToAdd has changed.
+
+Fixed bugs:
+-----------
+
+* <zipfileset>'s filemode would get ignored and the dirmode was used
+  for the included files as well.  As a side effect, WinZIP was unable
+  to extract or display the files, so they seemed to be missing from
+  the archive.  Bugzilla Report 17648.
+
+* <ftp> could use the wrong path separator when trying to change the
+  remote working directory.  Bugzilla Report 17735.
+
+* <jar update="true"> would loose all original files if you didn't
+  specify any nested <(zip)fileset>s and the manifest had changed.
+  Bugzilla Report 17780.
+
+* If you used a value starting with \ on Windows for the appxml
+  attribute of <ear> or the webxml attribute of <war>, it would be
+  ignored.  Bugzilla Report 17871.
+
+* Ant will no longer implicitly add Sun's rt.jar in <javac> when you
+  use jvc and don't specify a bootclasspath.  Bugzilla Report 18055.
+
+* The prefix attribute of <zipfileset> would not generate directory
+  entries for the prefix itself.  Bugzilla Report 18403.
+
+* starteam checkout can now handle deleted labels.  Bugzilla Report 17646.
+
+* The Unix wrapper script failed if you invoked it as a relative
+  symlink and ANT_HOME has not been set.  Bugzilla Report 17721.
+
+Other Changes:
+--------------
+* Added ability to specify manifest encoding for the <jar> and
+  <manifest> tasks
+
+Changes from Ant 1.5.1 to Ant 1.5.2
+=============================================
+
+Changes that could break older environments:
+--------------------------------------------
+* ANT_OPTS environment variable is now applied at the start of the
+  Java command line, allowing position specific parameters of some
+  JVMs, such as -classic to be specified.
+
+* ZipScanner#getIncludedFiles will now return the names of the ZipEntries
+  that have been matched instead of the name of the archive.
+
+* The <zip> task and friends have been heavily modified, almost every
+  method signature of the Zip class has changed.  If you have subclassed
+  Zip (or one of its subclasses), your class will most likely not
+  compile against the current code base.  If it still compiles, it will
+  probably not work as in Ant 1.5.1.
+
+Fixed bugs:
+-----------
+* <translate> was not ignoring comment lines.
+
+* <manifest> wouldn't update an existing manifest if only an attribute
+  of an existing section changed.
+
+* ant.bat now supports the ANT_ARGS and JAVACMD environment variables
+  again (like Ant 1.5 did).
+
+* The "plain" <junit> <formatter> could throw a NullPointerException
+  if an error occured in setUp.
+
+* <junit> will now produce output when a test times out as well.
+
+* <replace> would count some internal character replacements when
+  reporting the number of replaced tokens.
+
+* <concat> would cause an exception if a <filelist> pointed to files
+  that do not exist.
+
+* <javadoc> will now pass -source to custom doclets as well.
+
+* <cvstagdiff> would throw a NullPointException if there had been no
+  differences.
+
+* <cvschangelog> could miss today's changes.
+
+* <concat> could append newline characters between concatenated files.
+
+* <xmlvalidate> ignored the specified encoding of the files to
+  validate.
+
+* the errorsbeginat attribute of the <http> condition didn't work.
+
+* Ant will try to force loading of certain packages like com.sun.*
+  from the system classloader.  The packages are determined by the
+  version of the JVM running Ant.
+
+* Ant didn't find the runtime libraries on IBM's JDK 1.4 for Linux.
+
+* random component of temporary files is now always a positive integer.
+
+* Ant could incorrectly try to use the 1.4 regexp implementation even
+  if it isn't available if you run the JVM with -Xverify:none.
+
+* Ant would die with an exception if you used nested <reference>
+  elements in Ant and the refid attribute didn't point to an existing
+  project reference.
+
+* The <get> task can now be compiled (and Ant thus bootstrapped) using
+  Kaffee.
+
+* build.sysclasspath will now be honored by more tasks.
+
+* The signjar keystore attribute has been reverted to a String allowing
+  it to once again accept URLs. This should not affect current File based usage
+  unless you are extending the Signjar task.
+
+* <jar update="true"> would remove the original manifest.
+
+* fix up folder creation in PVCS task
+
+* <tar>'s up-to-date check didn't work for nested <(tar)fileset>s.
+
+* Corrected a problem in XMLLogger where it would not associated
+  messages with a taskdef'd task
+
+* <uptodate> now works when using attributes (i.e. not filesets) and pointing
+  to the same file
+
+* Java task (and output system) now stores output which doos not end
+  with a line feed.
+
+* splash screen wouldn't disappear when build was finished.
+
+* <exec> now supports OS/2.
+
+* <zip> and friends would only update/recreate existing archives if
+  the files to add/update have been newer than the archive.
+
+* <javadoc>'s <link> element could fail for offline="true" on some JDKs.
+
+Other changes:
+--------------
+
+* MailLogger now sets the Date header correctly.
+
+* Shipped XML parser is now Xerces 2.3.0
+
+* signjar now accepts a maxmemory attribute to allow the memory allocated to the
+  jarsigner tool to be specified. The jarsigner from the JDK's JAVA_HOME bin
+  dir is now used rather than the first jarsigner on the path.
+
+* **/.DS_Store has been added to the list of default pattern excludes.
+
+* The Created-By header in the default manifest now contains the JVM
+  vendor and version according to the jar specification. A new header,
+  Ant-Version provides the Ant version used to create the jar.
+
+* <zip> can now store Unix permissions in a way that can be
+  reconstructed by Info-Zip's unzip command.
+
+Changes from Ant 1.5.1Beta1 to 1.5.1
+====================================
+
+Fixed bugs:
+-----------
+
+* <tstamp>'s prefix attribute failed to apply to nested <format> elements.
+
+* <junitreport> created an empty junit-noframes.html if no format had
+  been specified.
+
+* <basename> would remove more than it should if the file name
+  contained more than one dot.
+
+* <filterset>s nested into <filterset>s didn't work.
+
+Other changes:
+--------------
+
+* Shipped XML parser is now Xerces 2.2.0
+
+* Filesets now support a 'file' attribute, allowing a single-file
+  fileset to be constructed without having to specify its parent
+  directory separately.
+
+* <junit> will now return the result of a call to getName instead of
+  "unknown" for Test implementations that don't extend TestCase but have
+  a public String getName() method.
+
+Changes from Ant 1.5 to 1.5.1Beta1
+==================================
+
+Fixed bugs:
+-----------
+* Date/time in CvsChangeLog was in local timezone and 12 hour format leading
+  to a problem when sorting by time. It is now UTC (GMT) and in 24-hour
+  format as per cvs 'specifications'.
+
+* CvsTagDiff now supports ampersand modules or modules that have a different
+  root directory than their name.
+
+* EjbJar threw NPEs for the Websphere element. The property 'websphere.home'
+  was not documented.
+
+* Mail example in the documentation was not correct.
+
+* Checksum was broken in the following scenario:
+  (using verifyproperty OR in a condition) AND using filesets
+  with multiple files.
+
+* The ExpandProperties filter threw NPEs when defined using
+  the <filterreader> format.
+
+* The sh wrapper script didn't work under Cygwin if ANT_HOME wasn't
+  set with a Unix style filename.
+
+* The sh wrapper script could fail if you started Ant from a directory
+  with whitespace in its name.
+
+* ant -diagnostics was not working properly when the task dependency
+  was missing and was just printing the missing dependency.
+
+* If a task got redefined via <taskdef>, it lost its child elements.
+
+* <property>'s classpathref attribute was broken.
+
+* <arg line="''" /> would result in no command line argument, will now
+  be a single empty argument.  Use <arg value="''"/> if you need the
+  quotes literally.
+
+* <replaceregexp> could append a newline character at the end of the
+  file.
+
+Other changes:
+--------------
+
+* Appendix E of Java Development with Ant (Loughran/Hatcher) was
+  contributed to the docs.
+
+* <available> will only print deprecration warnings if it is actually
+  used to change the value of a property.
+
+Changes from Ant 1.5beta3 to Ant 1.5
+====================================
+
+Changes that could break older environments:
+--------------------------------------------
+
+* The filesetmanifest attribute added to <jar> after the 1.4.1
+  release has been removed for now.  This change may affect only
+  the 1.5Beta/1.6Alpha users.  An attempt will be made to add this
+  feature back into Ant 1.6.
+
+Fixed bugs:
+-----------
+
+* <zip> and friends would always update existing archive if you set
+  the update attribute to true.
+
+* To support backward compatibility with older versions, <pathconvert>
+  will once again set the property, even if the result is the empty
+  string, unless the new 'setonempty' attribute is set to false|no|off
+  (default is "true").
+
+* The manifest task would crash XmlLogger
+
+Other changes:
+--------------
+
+* added **/.svn and **/.svn/** to the default excludes
+
+Changes from Ant 1.5beta2 to Ant 1.5beta3
+=========================================
+
+Changes that could break older environments:
+--------------------------------------------
+
+* <pvcs> default filenameformat has been different from Ant 1.4.1.
+  Now it is different from 1.5beta1 and 1.5beta2.
+
+* <pathconvert> won't set the property if the result is the empty string.
+
+Fixed bugs:
+-----------
+
+* <available> could fail to find files or directories that happen to
+  start with the name of the project's basedir but are not children of
+  the basedir.
+
+* Nested <property>'s inside <ant> can now be overriden by subsequent
+  <ant> and <antcall> tasks.
+
+* <xslt>'s outputtype attribute wouldn't do anything.
+
+* <linecontains> filterreader could swallow lines.
+
+* <sequential> used to configure the tasks (set their attributes)
+  before the first task has been executed.  This means that properties
+  that have been set by nested task seemed to be unset for the other
+  tasks in the same <sequential> element.
+
+* <javac>'s sourcepath setting has been ignored by some compiler
+  implementations.
+
+* <javadoc>'s packagelist attribute didn't work.
+
+* the plain mailer would always use port 25 in <mail>.
+
+* Ant's default logger could swallow empty lines.
+
+* ejbjar's iPlanet nested element now can process multiple descriptors.
+
+* IPlanetEjbc was looking in the wrong place for four iiop files.
+
+* <javac> would pass the -source switch to JDK 1.3's javac, even
+  though it doesn't support it.
+
+Other changes:
+--------------
+
+* <checksum> now uses a buffer (of configurable size).
+
+* The "Trying to override task definition" warning has been degraded
+  to verbose level if the two task definitions only differ in the class
+  loader instance that has loaded the definition.
+
+* Add a jvmargs to the ejbjar's weblogic element to allow additional
+  arguments to be provided to the VM runnign ejbc. Document the
+  jvmdebuglevel attribute which can be used to avoid warnings about
+  interface classess being found on the classpath. Document the new
+  <sysproperty> element which allows JVM properties to be defined.
+  Added an outputdir attribute to allow the destination to be a
+  directory into which the exploded jar is written.
+
+* ejbjar now supports Borland Enterprise Server 5 and Jonas 2.5
+
+Changes from Ant 1.5beta1 to Ant 1.5beta2
+=========================================
+
+Changes that could break older environments:
+--------------------------------------------
+
+* Properties will now be expanded in mail message bodies.  This means
+that one $ sign will be stripped if your mail message contains the text
+$$.
+
+* org.apache.tools.ant.taskdefs.Expand no longer extends MatchingTask.
+
+* Available#setFile now again uses a File argument as it did in 1.4,
+this may break environments that have been adapted to the String
+argument version present in 1.5beta1.
+
+Fixed bugs:
+-----------
+* When <move> attempts a rename, it deletes the destination file, if it
+  exists, before renaming the source file.  However, <move> was not
+  checking if the destination file was actually a directory before
+  trying to delete it.
+
+* Make CVS Tasks to work under Cygwin.
+
+* Fix LineContains to handle huge files elegantly without causing
+Stack Overflows.
+
+* if you ask for the "classic" compiler on Java1.4, you get upgraded to
+"modern" because there is no classic compiler any more.
+
+* the <http> condition was viewing 404 'not found' exceptions as success. Now
+it defaults to viewing any response >=400 as an error, and has an errorsBeginAt
+attribute you can use if you want a higher or lower value.
+
+* <get> throws a build exception on an http authorization error, unless you
+have set ignoreerrors to true.
+
+* <wsdltodotnet> was spelt in Wintel case: <WsdlToDotnet>. It is now lower
+case, though the old spelling is retained for anyone who used it.
+
+* Merging of Manifests in jar now works as documented.
+
+* paths that have been separated by colons would be incorrectly parsed
+on NetWare.
+
+* runant.pl now supports NetWare.
+
+* <tempfile> and <setproxy> tasks were in beta1, but not defined by
+default; They now are. <tempfile> fills a property with the name of a
+temporary file; <setproxy> lets you set the JVM's http, ftp and socks proxy
+settings.
+
+* <available classname="foo" ignoresystemclasses="true"> failed for
+JDK 1.1 and 1.2, even if the class could be found on the
+user-specified classpath.
+
+* <property environment=... /> now works on z/OS.
+
+* forked <javac> failed for the wrong reason on JDK 1.1 - Ant would
+use a temporary file to hold the names of the files to compile under
+some conditons, but 1.1 doesn't support this feature.  Ant will no
+longer try this, but you may run into problems with the length of the
+command line now.
+
+* the refid attribute for <property>s nested into <ant> or <param>s
+nested into <antcall> didn't work.
+
+* <replaceregexp> didn't work for nested <fileset>s.
+
+* <javadoc> dropped sourcepath entries if no "interesting" .java
+source files  could be found below them.  This has been backwards
+incompatible and caused problems with custom doclets like xdoclet.
+
+* Using the doclet, docletpath or docletpathref attributes of
+<javadoc> may have caused NullPointerExceptions.
+
+* nested <filesets> of <javadoc> would include too much.
+
+* <dependset> will no longer choke on <targetfileset>s that point to
+non-existing directories.
+
+* <patch> didn't work at all.
+
+* <replace> and <replaceregexp> now fail if the file they are working
+on is locked.
+
+* <javadoc> would pick up the wrong executable in the combination JDK
+1.2  and AIX.
+
+Other changes:
+--------------
+
+* z/OS now gets detected by the os condition.
+
+* <fileset> and <dirset> now have an optional followsymlink attribute
+that can prevent Ant from following symbolic links on some platforms.
+
+* BeanShell is now supported in the <script> task.
+
+* <ejbjar> under Weblogic attempts to use the ejbc20 compiler for 2.0 beans
+  based on the deployment descriptor's DTD reference. Under weblogic 7.00 Beta
+  this ejbc class has been deprecated. To avoid the deprecation warning use
+  ejbcclass="weblogic.ejbc".
+
+* <ejbjar> will add a manifest to the generated jar based on the naming
+  convention in use. This overrides the manifest specified in the
+  <ejbjar> attribute
+
+
+Changes from Ant 1.4.1 to 1.5beta1
+==================================
+
+Changes that could break older environments:
+--------------------------------------------
+
+* Important: Single $ signs are no longer silently stripped!
+  Before you panic that we have broken all your build files, we have kept
+  the old "$$" -> "$" behaviour. So only build files which accidentally had
+  a $ sign in a string that was being silently stripped may break.
+  We added this fix to stop newbie confusion; if you want to write a
+  build file which works on ant versions 1.4.1 or earlier, stay with
+  the double $$ sign rule.
+
+* Project.getBuildListeners now returns a clone of the listener
+  list. Changes to the returned list will not affect the listeners
+  currently attached to the Project. It also means that it is safe to
+  iterate over the returned list if listeners are added or removed
+  during the traversal.
+
+* <pvcs> default filenameformat has been different from Ant 1.4.1.
+
+* Some messages that are printed during startup will not be
+  written to the logfile specified via -logfile as they might destroy
+  the format of the file for special BuildLoggers (like XmlLogger).
+
+* The filesetmanifest attribute added to <jar> after the 1.4.1
+  release has been removed for now.  This change may affect only
+  the 1.5Beta/1.6Alpha users.  An attempt will be made to add this
+  feature back into Ant 1.6.
+* Shipped XML parser is now Xerces 2.0.1 along with the XML Parser APIs.
+  XML Parser APIs is a separate jar that contains the necessary
+  JAXP/DOM/SAX classes.
+
+* <telnet> was fixed to expand properties inside nested <read> and
+  <write> elements; before this only happened when you assigned the text
+  to the string attribute. If you had $ signs in the string, they may
+  need escaping.
+
+* the RegexpMatcher interface has been extended to support case
+  insensitive matches and other options - custom implementations of
+  this interface won't work any longer.  We recommend to use the new
+  Regexp interface that also supports substitution instead of the
+  RegexpMatcher interface in the future.
+
+* <gzip> will throw an exception if your src attribute points to a directory.
+
+* Unjar, Unzip and Unwar will throw an exception if the Src attribute
+  represents a directory.  Support for nested filesets is provided
+  instead.
+
+* It is no longer possible to overwrite a property using tasks like
+  <condition>, <exec>, <pathconvert>, or <tstamp>. In some exceptional
+  cases it will generate a warning if you attempt to overwrite an
+  existing property.
+
+* Taskwriters please note: Whenever tasks had any overloaded set* methods,
+  Ant's introspection mechanism would select the last overloaded method
+  provided to it by the Java Runtime.  A modification has now been made such
+  that when the Java Runtime provides a method with a String as its argument,
+  a check is made to see if there is another overloaded method that takes in
+  some other type of argument.  If there is one such method, then the method
+  that takes in String as an argument is not selected by the Introspector.
+
+* The pattern definition **/._* has been included into the Default
+  Excludes list.
+
+* <propertyfile>'s <entry> element was modified to remove "never" as a value
+  as its behavior was undocumented and flakey.
+
+* The -projecthelp flag now only prints out targets that include the
+  'description' attribute, unless the -verbose or -debug flag is included
+  on the Ant command line.
+
+* Ant's testcases now require JUnit 3.7 or above, as they now use the new
+  assertTrue method instead of assert.
+
+* If the 'output' attribute of <ant> is set to a simple filename or a
+  relative path, the file is created relative to ${basedir}, not ${user.dir}.
+
+* The default value for build.compiler is now javac1.x with x
+  depending on the JDK that is running Ant instead of classic/modern.
+
+Fixed bugs:
+-----------
+
+* <available> could fail to find files or directories that happen to
+  start with the name of the project's basedir but are not children of
+  the basedir.
+
+* Nested <property>'s inside <ant> can now be overriden by subsequent
+  <ant> and <antcall> tasks.
+
+* <xslt>'s outputtype attribute wouldn't do anything.
+
+* <linecontains> filterreader could swallow lines.
+
+* <sequential> used to configure the tasks (set their attributes)
+  before the first task has been executed.  This means that properties
+  that have been set by nested task seemed to be unset for the other
+  tasks in the same <sequential> element.
+
+* <javac>'s sourcepath setting has been ignored by some compiler
+  implementations.
+
+* <javadoc>'s packagelist attribute didn't work.
+
+* the plain mailer would always use port 25 in <mail>.
+
+* Ant's default logger could swallow empty lines.
+
+* ejbjar's iPlanet nested element now can process multiple descriptors.
+
+* IPlanetEjbc was looking in the wrong place for four iiop files.
+
+* <javac> would pass the -source switch to JDK 1.3's javac, even
+  though it doesn't support it.
+
+* <zip> and friends would always update existing archive if you set
+  the update attribute to true.
+
+* To support backward compatibility with older versions, <pathconvert>
+  will once again set the property, even if the result is the empty
+  string, unless the new 'setonempty' attribute is set to false|no|off
+  (default is "true").
+
+* The manifest task would crash XmlLogger
+
+* A bug existed that prevented generated log files from being deleted as
+  part of the build process itself.  This has now been fixed.
+
+* Fixed bug where <move> ignored <filterset>s.
+
+* Ant works properly with the combination of Java1.4/WindowsXP.
+
+* Fixed bug where <java> used to sometimes invoke class constructors twice.
+
+* Fixed bug with 4NT shell support.
+
+* Fixed bug where ant would not perform ftp without remotedir being
+  specified even though this was not mandatory.
+
+* Fixed bug where ant would not copy system properties into new Project
+  in ant/antcall tasks when inheritall="false" is set.
+
+* <propertyfile> would not close the original property file.
+
+* <ant> will no longer override a subbuild's basedir with inheritall="true".
+
+* Fixed problem with the built-in <junit> formatters which assumed
+  that only one test could be running at the same time - this is not
+  necessarily true, see junit.extensions.ActiveTestSuite.
+
+* <jar>'s whenEmpty attribute is useless as JARs are never empty, they
+  contain at least a manifest file, therefore it will now print a
+  warning and do nothing.
+
+* <typedef> hasn't been all that useful as it couldn't be used outside
+  of targets (it can now) and nested "unknown" elements have always
+  been considered to be tasks (changed as well).
+
+* <fixcrlf> would fail for files that contained lines longer than 8kB.
+
+* Some junit formatters incorrectly assumed that all testcases would
+  inherit from junit.framework.TestCase.
+
+* <fixcrlf> dropped the first characters from Mac files.
+
+Other changes:
+--------------
+
+* <checksum> now uses a buffer (of configurable size).
+
+* The "Trying to override task definition" warning has been degraded
+  to verbose level if the two task definitions only differ in the class
+  loader instance that has loaded the definition.
+
+* Add a jvmargs to the ejbjar's weblogic element to allow additional
+  arguments to be provided to the VM runnign ejbc. Document the
+  jvmdebuglevel attribute which can be used to avoid warnings about
+  interface classess being found on the classpath. Document the new
+  <sysproperty> element which allows JVM properties to be defined.
+  Added an outputdir attribute to allow the destination to be a
+  directory into which the exploded jar is written.
+
+* ejbjar now supports Borland Enterprise Server 5 and Jonas 2.5
+
+* added **/.svn and **/.svn/** to the default excludes
+* Selector Elements now provide a way to create filesets based on
+  sophisticated selection criteria.
+
+* Gzip and Bzip2 files can now be constructed in the fly when using
+  the tar task without having to create the intermediate tar file on
+  disk.  The Untar task can also untar GZip and BZip2 files on the fly
+  without creating the intermediate tar file.
+
+* New optional type, <classfileset> added.
+
+* <ejbjar> now allows control over which additional classes and interfaces
+  are added to the generated EJB jars. A new attribute "dependency" can be
+  defined which controls what classes are added. The addition of classes now uses
+  the Jakarta-BCEL library rather than reflection, meaning bean classes are
+  no longer loaded into Ant's JVM. The default dependency analyzer is known as
+  the ancestor analyzer. It provides the same behaviour as the 1.4.1 version of
+  <ejbjar>. If the BCEL library is not present, a warning will be issued stating
+  the ancestor analyzer is not available. In this case <ejbjar> will continue
+  to function but will not add super classes to the jar.
+
+* <available> has a new attribute named ignoreSystemClasses.
+
+* New task <cvschangelog/> generates an XML report of changes that occur
+  on CVS repository.
+
+* New filter readers: ClassConstants, ExpandProperties, HeadFilter,
+  LineContains, LineContainsRegExp, PrefixLines, ReplaceTokens,
+  StripJavaComments, StripLineBreaks, StripLineComments, TabsToSpaces,
+  TailFilter.
+
+* <copy>, <loadfile>, <loadproperties>, <move> support FilterChains
+  of FilterReaders.
+
+* New task <loadproperties> to load contents of file as Ant properties,
+  with nested <filterchain> elements.
+
+* New task <loadfile> to load a whole file into a property.
+
+* New task <echoproperties> to list your current properties to the screen
+  or a file.
+
+* New tasks <bzip2> and <bunzip2> to pack and unpack files using the
+  BZip2 alogrithm.
+
+* New tasks <replaceregexp>, <checksum>, <translate>, <waitfor>,
+  <manifest>, <vsscp>, <vssadd>, <vsscreate>, <splash>, <basename>, <dirname>,
+  <concat>, <sourceoffsite>, <jarlib-available>, <jarlib-display>,
+  <jarlib-manifest>, <jarlib-resolve>.
+
+* A new combined <mail> task, which replaces the old <mail> and
+  <mimemail> tasks, has been added.  The <mimemail> task, and
+  old SendEmail and MimeMail classes have been deprecated.
+
+* Mail task allows specification of port number.
+
+* Users can control what <zip> and <jar> must do when duplicate files
+  are found.  A new element <zipgroupfileset> allows for multiple zip
+  files to be merged into the archive.  In addition, <jar> also has
+  another new attribute: filesetmanifest.  The existing manifest
+  attribute of <jar> now also accepts the name of a jar added through
+  a fileset.
+
+* gzip now checks that the zipfile is older than the source file
+  before rebuilding the zipfile.
+
+* TarFileset takes in three new attributes - fullpath, prefix
+  and preserveLeadingSlashes.
+
+* <move> attempts to rename the directory, if everything inside it is
+  included, before performing file-by-file moves.  This attempt will
+  be done only if filtering is off and if mappers are not used.  This
+  is a performance improvement and there is no change otherwise in
+  the funtionality of this task.
+
+* Exec task has extra attribute "resultproperty" to get the return code
+  into a property.
+
+* Exec task prints a message when a timed-out process is killed.
+
+* Added optional attributes - name, arch and version to the <os> task.
+
+* Unjar, Untar, Unwar and Unzip now support patternsets to
+  select files from an archive for extraction.  Filesets may be
+  used to select archived files for unarchival.
+
+* Javac task allows debug levels to be specified.  Debug levels
+  will have an effect only when the modern compiler or the
+  classic compiler (version 1.2 and higher) is used and debugging
+  is enabled.
+
+* Added support for specifying CVS_RSH in the <cvs/> task
+
+* The attributes zipfile, jarfile, warfile and earfile (from the Zip,
+  Jar, War and Ear tasks) have been deprecated and superseded by a
+  new attribute "destfile".
+
+* Added new conditions <isset>, <checksum>, <http>, <socket>, <contains>,
+  <filesmatch>.
+
+* <taskdef> and <typedef> will now emit a warning if a task/type of
+  the given name already exists.
+
+* A new revision of VAJ tasks: The most important new feature
+  is the ability to execute VAJ tasks from the command line by
+  exploiting the Remote Tool Access feature of VAJ.
+
+* Improved support for Novell NetWare.
+
+* Added an optional encoding attribute to <fixcrlf>.
+
+* <apply> has a new attribute relative that allows users to pass the
+  filenames as relative instead of absolute paths on the command line.
+
+* References can now be copied into the child build by <ant> and
+  <antcall> using nested <reference> elements or the new inheritRefs
+  attribute.
+
+* <fail> now supports builds to fail based on conditions via if and
+  unless attributes.
+
+* Ant now comes with two new BuildLogger implementations - one that
+  can send emails containing a log of the build process (MailLogger),
+  and one that colorizes the output based on message levels, using
+  ANSI color code escape sequences (AnsiColorLogger).
+
+* A "package" mapper type has been added to allow package directory
+  names replaced with the dotted form.
+
+* You can now specify environment variables in the <java> and <junit> tasks
+  if the fork attribute has been set to true.
+
+* -propertyfile command-line option has been added to load an entire
+  property file just as -D properties are declared (as user properties).
+  -D properties take precedence over -propertyfile specified ones.
+
+* You can now set an ANT_ARGS environment variable to hold arguments you
+  always want passed to the 'ant' command -- for example, if you always
+  want to use a different logger or the -find flag.
+
+* <tstamp> now supports a new "prefix" attribute to prefix properties set.
+
+* You can now specify the -sourcepath for <javac> explicitly.
+
+* <javac> now supports a new "listfiles" attribute to list the source
+  files it's handing off to the compiler.
+
+* The compiler implementation for <javac> can now be chosen on a task by
+  task basis.  The new "compiler" attribute of <javac> can be used to override
+  the value of the build.compiler property, if set.
+
+* <javac> has a new nested element, <compilerarg>, which allows you
+  to specify additional args for the specific compiler you're using.
+
+* <javac>'s "source" attribute is now enabled for jikes as well.
+
+* <propertyfile>'s <entry> now has a 'unit' attribute to specify the
+  increment/decrement unit on date operations.
+
+* <property> now supports a 'prefix' attribute when loading from a file
+  or resource.
+
+* In Ant 1.4, a feature has been added to the <junit> task that would
+  add ant.jar, optional.jar and junit.jar implicitly to the classpath -
+  this feature can now be disabled by setting the new includeantruntime
+  attribute to false.
+
+* <style> behaves differently from any other directory-based task, as it
+  processes all files that it finds in included directories in
+  addition to the files matched by your patterns.  There is now a new
+  attribute, 'scanincludeddirectories', to suppress this behavior.
+
+* <javadoc> now supports a <tag> nested element to provide the -tag option
+  to the standard Java 1.4 doclet. The element is ignored when not running
+  on Java 1.4.
+
+* <ftp> can now chmod files on a remote server that supports
+  "site chmod", as well as set the umask before transferring files, if
+  the server supports "site umask".
+
+* New <serverdeploy> "optional" task.
+
+* <patternset> now supports nested patternsets.
+
+* Perforce tasks now support a "failonerror" attribute (defaults to "true").
+
+* Open Source application server JOnAS support:
+    EJB hot deploy and deploy with <serverdeploy> and <ejbjar>
+
+* Added new DirSet (<dirset>) datatype.
+
+* <path> now supports nested <dirset> and <filelist> elements.
+
+* <pathconvert> now supports nested <dirset> and <filelist> elements.
+
+* <pathconvert>'s "dirsep" and "pathsep" attributes now accept
+  multi-character values.
+
+* <copy> task now has a 'failonerror' attribute to allow keep-going
+  behaviour when the file to be copied is not found (defaults to "true").
+
+* <uptodate> now has a 'srcfile' attribute to allow specifying a
+  full-path filename.
+
+* <exec>, <sql> and <java> now support append attributes to allow
+  appending the output to an existing file.
+
+* <java> now supports a timeout attribute analog to <exec> - it is
+  highly recommended to only use it together with fork="true".
+
+* <javadoc> now supports a source attribute to enable javadoc to
+  handle assertions present in JDK 1.4 source code.
+
+* <replace> supports a new replacefilterfile attribute that
+  automatically turns all properties of a given file into
+  replacefilters.
+
+* An alias of <xslt> has been added to refer to the <style> task.
+
+* The compiler implementation for <rmic> can now be chosen on a task by
+  task basis.  The new "compiler" attribute of <rmic> can be used to override
+  the value of the build.rmic property, if set.
+
+* <rmic> has a new nested element, <compilerarg>, which allows you
+  to specify additional args for the specific compiler you're using.
+
+* org.apache.tools.ant.XmlLogger now is a BuildLogger, rather than just
+  a BuildListener. It can operate in either mode successfully.
+
+* <junit> has a new attribute "showoutput".  If set to true, output
+  generated by tests will be sent to Ant's logging system as well as
+  to the formatters (instead of sending it to the formatters
+  exclusively).
+
+* Ant has now a pluggable way to prompt users for input, which is used
+  by the new <input> task.  IDE integrators can provide an
+  implementation of the InputHandler interface to decouple Ant's input
+  from the console.  An implementation that gets its input from a file
+  for unattended builds is part of Ant's distribution.
+
+  For more details see docs/manual/inputhandler.html.
+
+* <patch> has a new attribute that selects the directory in which to
+  run the command.
+
+* <javadoc> now supports two new nested elements, <fileset> and <packageset>.
+
+
+Changes from Ant 1.4 to Ant 1.4.1
+===========================================
+
+Fixed bugs:
+-----------
+
+* <ant>'s antfile attribute will now also be considered an absolute path on
+  Windows systems, if it starts with a \ and no drive specifier.
+
+* The fullpath attribute of <zipfileset> has been ignored if you used
+  the src attribute at the same time.
+
+* The manifest file is now always placed as the second entry (after /META-INF)
+  in generated jars. This allows the manifest to be read by JarInputStreams
+
+* Fixed bug in depend task which would fail with a NullPointerException if no
+  dependency cache was specified.
+
+* sql task now handles REM statements correctly so that lines starying with rem
+  but which are not comments are actually processed.
+
+* XMLLogger now uses the task's name rather than the classname
+
+* <mapper>s will now work as expected if the to pattern expands to an
+  absolute pathname.
+
+* <javac> didn't ignore memory settings in non-fork mode
+
+* <cab> didn't split the options attribute into several command line
+  arguments correctly.
+
+Other changes:
+--------------
+
+* New source attribute for <javac> to enable assertion in JDK 1.4
+
+* XmlLogger and <antstructure> now add an encoding declaration to the
+  XML files they generate.
+
+* <fileset> has a new attribute "casesensitive" to make it match
+  filenames in a case insensitive way (if you set it to false) - by
+  default filesets remain case sensitive.
+
+Changes from Ant 1.3 to Ant 1.4
+===========================================
+
+Changes that could break older environments:
+--------------------------------------------
+* JUnitReport now uses the xalan redirect extension for multi-output.
+  With Xalan 1.2.2 it forces the use of bsf.jar in the classpath.
+  (Available in the xalan distribution). It is recommended to switch
+  to Xalan 2.x that do not need it.
+
+* Zip.setWhenempty() has changed its signature.
+
+* <rmic> is now implemented using a factory. This makes extending
+  rmic to use a new compiler a lot easier but may break custom
+  versions of this task that rely on the old implementation.
+
+* several Zip methods have changed their signature as we now use a Zip
+  package of our own that handles Unix permissions for directories.
+  Furthermore <zip> will now use the platform's default character
+  encoding for filenames - this is consistent with the command line
+  ZIP tools, but causes problems if you try to open them from within
+  Java and your filenames contain non US-ASCII characters. Use the new
+  encoding attribute of the task and set it to UTF8 to get the old
+  behavior.
+
+* The <pvcs> task has been moved to a package of its own.
+
+* JUnitResultFormater has two additional methods that must be
+  implemented by custom formatters.
+
+* Ant will no longer use the canonical version of a path internally -
+  this may yield different results on filesystems that support
+  symbolic links.
+
+* The output generated by the xml formatter for <junit> has changed
+  again, it doesn't format the numeric value in the time attribute anymore.
+
+* Pattern matching rules have changes slightly, the pattern foo*
+  doesn't match files contained in a directory named foo - use foo/*
+  instead.
+
+* <fixcrlf> will not remove trailing whitespace at the end of lines anymore.
+
+* The Classloader usage has been changed for the taskdef, property, available
+  and sql tasks so that it delegates to the parent classloader. This may cause
+  ClassNotFoundExceptions to be thrown if a system class attempts to load a
+  class in the taskdef's classpath (typically factory objects).
+
+* Ant now allows multithreading of tasks and the containment of tasks within
+  other tasks. This can break customer listeners which do not expect messages
+  from a task before the previous task has finished.
+
+* Ant now installs its own ouput stream into System.out to route output to the
+  task currently executing on the current thread. This also means that all
+  output is now routed as Ant message events. Customer listeners and loggers
+  should not call System.out at any time. This has always been true but such
+  usage now will cause problems due to possible recursion.
+
+* Invalid manifest files will now cause build failures in the <jar> task.
+
+* Ant Introspection now looks for methods with method names starting with
+  addConfigured. When called these methods are passed an argument after it has
+  been configured from the build file. Custom tasks supporting nested elements
+  starting with the name configured will no longer function.
+
+* The environment variable JAVACMD that can be used to specify the
+  java executable to Ant's wrapper scripts must not contain additional
+  command line parameters any longer - please use the environment
+  variable ANT_OPTS for such parameters now.
+
+* Ant's wrapper scripts now quote the CLASSPATH environment variable, thus
+  supporting classpaths which refer to directories containing spaces. This means
+  that the CLASSPATH environment variable cannot have quotes. Any quotes should
+  be removed. This will not affect the operation of the CLASSPATH environment
+  variable in other contexts.
+
+* A delete task like
+  <delete includeEmptyFilesets="true">
+    <fileset dir="somedir" />
+  </delete>
+  will now remove "somedir" as well, unless there are still files left
+  in it (matched by the default excludes).
+
+* The copy task will now fail if the file to be copied is not found.
+
+* Ant properties defined in properties files now behave the same way as
+  properties defined in the build file. In particular the $ character needs
+  to be escaped in property values by doubling it to $$. So, to define a
+  property with the value $hello, you need to define it in a properties file
+  as
+    test.prop=$$hello
+  This was not the case in Ant 1.3
+
+Other changes:
+--------------
+
+* New tasks: ear, p4counter, record, cvspass, vsscheckin, vsscheckout,
+  typedef, sleep, mimemail, set of tasks for Continuus/Synergy, dependset,
+  condition, maudit, mmetrics, jpcoverage, jpcovreport, jpcovmerge
+
+* Ant now uses JAXP 1.1
+
+* rmic now supports Kaffe's and Weblogic's version of rmic.
+
+* new magic property build.rmic to chose the rmic implementation
+
+* <tar> will now add empty directories as well
+
+* you can now specify a description for <p4change>
+
+* <touch> can now work on <fileset>s
+
+* <uptodate> now supports a value attribute
+
+* <fail> supports nested text
+
+* <fixcrlf> won't override files that are already in the correct
+   format.
+
+* <sql> now supports REM comments as well as // and --
+
+* <jar> now has a nested <metainf> element following the same idea as
+  <war>'s <webinf>.
+
+* <pvcs> can now handle multiple projects.
+
+* <available> now has a "type" attribute you can use in conjunction
+  with the "file" attribute to specify whether the "file" you're
+  looking for is a file or a directory.
+
+* New <junit> formatter named "brief"
+
+* <ejbjar> changes
+  * Add support for Borland Application Server to the <ejbjar> task using
+    a <borland> nested element.
+  * Add support for iPlanet Application Server to the <ejbjar> task. Also
+    includes some iPlanet utility tasks
+  * Add support for JBoss Application Server to the <ejbjar> task.
+  * Add a naming attribute to control the naming scheme that
+    ejbjar uses to name the generated EJB jars.
+  * Weblogic element now sets the compiler class for EJB 2.0 beans
+  * <dtd> elements can be specified at the <ejbjar> level for building generic
+    beans
+  * <dtd> elements can now be URLs
+  * Allow the manifest to be specified for the generated jars
+  * The weblogic element now supprts an attribte noEJBC to skip the processing
+    of the jar by ejbc. The ejbc step will then occur at deployment
+  * weblogic will tell ejbc to use Jikes compiler if build.compiler is set to
+    jikes. It can be restored to the default, javac, operation if desired.
+
+* Allow the <sql> Delimiter to be set in the so that Oracle stored procs may be
+  entered
+
+* <execon> and <apply> can now optionally skip empty filesets.
+
+* <javadoc> has a new useexternalfile attribute that makes it use a
+  temporary file for sourcefile and package names - helps to defeat
+  command line length limitations.
+
+* Data types like <path> can now be defined inside of <target>s
+
+* you can now specify a classpath for <style> - the XSLZ processor
+  will be loaded from this path
+
+* added a force attribute to <style> to support dependencies that the
+  task cannot determine itself (dependency on parameters, not file
+  modification times for example)
+
+* added vmlauncher attribute to exec tasks. This defaults to true. If
+  it is set to false, the VM's ability to launch commands in bypassed
+  and the OS shell, either directly or through the auxillary antRun
+  scripts is used.
+
+* regexp mapper now supports the java.util.regex package of JDK 1.4.
+
+* New filesonly attribute for <zip> and friends to suppress directory
+  entries.
+
+* New update attribute for <zip> and friends - update an existing
+  archive instead of creating a new one.
+
+* <apply> and <execon> have been merged into a single task.
+
+* added vssver.scc to the default excludes
+
+* <available> has a new filepath attribute/nested element that allows
+  you top search for a file in a given path.
+
+* <junit> can now optionally set a property on test failure.
+
+* <taskdef> can now define several tasks at once, reading the
+  name/classname pairs from a property file or resource.
+
+* <unzip/unjar/unwar> and <untar> now have an overwrite attribute that
+  defaults to true.  If set to false, files that are newer than the
+  files in the archive will not be replaced.
+
+* <patternset> and <fileset> now support nested <in/excludesfile>
+  elements - using these you can have more than one in/excludes file
+  per <patternset>.
+
+* Three new supported compilers for javac: kjc for kopi, gcj for the
+  gcc frontend and sj for Symantec's compiler.
+  In addition extJavac or the new fork attribute can be
+  used to run the JDK's javac in a JVM separate from Ant.
+
+* <fixrlf> can now with CR only line-ends and can use an arbitraty
+  between 2 and 80.
+
+* The .NET tasks have been adapted to the beta2 release of the framework.
+
+* <move> will now try to rename() files before copying them byte by
+  byte - only if filtering is of, of course.
+
+* <ant> and <antcall> tasks now support a new attribute inheritAll. When set to
+  false, only user properties are passed through to the target Ant instance.
+  This includes properties set on the command line and properties explicitly
+  passed
+
+* <javadoc> now skips off line links if the package list cannot be found.
+
+* <wlrun> now allows the security policy file to exist outside the weblogic
+  directory.
+
+* <java> task will set the Thread contextClassLoader under JDKs 1.2+ to the
+  classloader for the class being executed.
+
+* Introduce the concept of a TaskContainer - a task or element which can contain
+  Ant Tasks.
+
+* Add new tasks implementing the TaskContainer interface <parallel> and
+  <sequential> which allow parallel execution of tasks to be specified.
+
+* <depend> task will now take into account dependencies on jar files and class
+  files from a given classpath.
+
+* <jar> manifest entries may now be specified in the build file either
+  completely or to be merged with a manifest file.
+
+* <tstamp> task custom formats now support locales.
+
+* Added a listner which will forward events to Log4J. The log4j configuration
+  file should be in the directory from which Ant is run or passed as a system
+  property using a JVM argument.
+
+* Introduced the concept of <filtersets> to allow for more control in which
+  filters get applied in a <copy> or <move> operation.
+
+* Added nowarn attribute to javac and deprecated the Jikes-magic property
+  build.compiler.warnings.
+
+* The <depend> task cache format has changed and all dependency information is
+  now stored in a single file.
+
+Fixed bugs:
+-----------
+
+* Testcases have been made independent of current working directory.
+
+* Input ZIP-Files will be closed when using a <zipfileset>.
+
+* p4 tasks now don't fail if user, port or client have been omitted
+  (and this is acceptable for the context of the command).
+
+* <javah>'s outputfile attribute will be resolved as relative to the
+  projects basedir.
+
+* <antstructure> should create a valid DTD for propertyfile.operation.entry
+  and omit tasks it fails to load.
+
+* won't try to pass a -bootclasspath flag to javac 1.1 anymore
+
+* <style>'s style attribute no handles absolute paths correctly.
+
+* <delete includeemptydirs="true"> now deletes more than just the leaf
+  directories.
+
+* You can now specify a <fileset> for a directory that doesn't exist at
+  declaration time but will created before the fileset gets used for the
+  first time.
+
+* If the quiet attribute has been set, <delete> will handle <fileset>s
+  with non-existing directories gracefully.
+
+* Output written by testcases will now be captured by the <junit> task
+  and passed to the formatters.
+
+* Quote the -group parameter to Javadoc as per the specification
+
+* Initialise classes when loaded through the AntClassLoader - that is, run
+  static initializers
+
+* Implement getResource() and getResources() in AntClassLoader
+
+* Create the <ejbjar> weblogic command line as a set of arguments rather than
+  as a single line. Avoids problems with paths which contain spaces.
+
+* <ejbjar> now fails when the weblogic ejbc compiler reports an error.
+
+* Make the AntClassLoader load resources in the same order as it currently
+  loads classes.
+
+* Handle classpaths with spaces
+
+* Make sure XSLT processors close their output files in <style>.
+
+* perform proper uptodate check in <rmic> when compiling for IIOP.
+
+* <jjtree>'s uptodate test works even if outputdirectory is not the
+  parent dir of target
+
+* <copy> will remove target file (if it exists) before writing to it -
+  this avoids problems with links on filesystems that support them.
+
+* <ftp> now properly recurses remote directories.
+
+* <ftp> closes remote connection when it's done.
+
+* <junit> tries to include all necessary classes for the task itself
+  to the classpath when running in fork mode - doesn't work for JDK 1.1
+
+* <apply> and <execon> do now execute the command only once, if you
+  specify the parallel attribute - instead of once per fileset.
+
+* directory based tasks and fileset could miss some included files in
+  directories that have been excluded
+
+* <fixcrlf> failed for large files.
+
+* <move> removed files you tried to move to themselves.
+
+* <sql> task will not trty to print the result set unless the query succeeded.
+
+* Ant classloader will now ignore paths which are invalid relative to the
+  project base
+
+* <ejbjar> weblogic elements check for jar file changes has been fixed.
+  Previously some changes would not be included.
+
+* properties loaded from properties files are now resolved internally. This
+  removes the spurious warnings about usage of properties which have not been
+  set.
+
+* <jar> task and friends now process the JAR manifest to ensure it is valid.
+
+* The task finished event now includes any exception thrown by the task.
+
+* <java> task now supports a jvmVersion attribute so that if another JVM is
+  being used, Ant can determine which options to use for features such as the
+  VM memory limits
+
+
+Changes from Ant 1.2 to Ant 1.3
+===========================================
+
+Changes that could break older environments:
+--------------------------------------------
+
+* Ant doesn't search for the buildfile anymore, unless you use the new
+  -find argument.
+
+* <perforce> has been replaced by a number of new tasks.
+
+* <javac> is now implemented using a factory. This makes extending
+  javac to use a new compiler a lot easier but may break custom
+  versions of this task that rely on the old implementation.
+
+* The output generated by the xml formatter for <junit> has changed a
+  little, it doesn't append " sec" in the time attribute anymore.
+
+Other changes:
+--------------
+
+* A GUI Frontend: Antidote. This is currently in development. At this
+  time, this is not part of the Ant release, although the source is
+  included if you are interested.
+
+* New tasks: stylebook, propertyfile, depend, antlr, telnet, csc,
+  ilasm, apply, javah, several clearcase tasks, junitreport, sound
+
+* Added output attribute to <java>.
+
+* Added nested zipfileset element to <zip>
+
+* Changed <sql> so that printing is at the task level rather than
+  the statement level.
+
+* javadoc task will pass -d flag to any doclet if the destDir attribute is
+  given. If the doclet does not accept the -d flag then omit the destdir
+  attribute.
+
+* <cab> can work on non-Windows platforms with the help of libcabinet.
+  See http://trill.cis.fordham.edu/~barbacha/cabinet_library/.
+
+* <ftp> now supports passive mode.
+
+* New <mapper> data type that can be used to get influence on the
+  target files for some tasks like <copy> or enable new types of tasks
+  like <apply>.
+
+* <execon> provides more control over the command line now, the names
+  of the source files are no longer required to be at the end of the
+  command.
+
+* Style tasks will now support TraX compliant XSL processors if one is present
+  in your classpath.
+
+* Added a failonerror to the javac task. If set to false, the build will
+  continue even if there are compilation errors.
+
+* Added nested format elements to the tstamp task allowing additional time
+  formats to be defined for arbitrary properties.
+
+* Added classpath attribute and nested classpath element to <property>
+  to make the resource attribute more powerful.
+
+* ${} property expansion will now be performed on the patterns read
+  from files specified as includesfile or excludesfile attributes.
+
+* The <tar> and <untar> tasks now support GNU format for handling paths
+  which are greater than 100 characters in length. In addition the <tar>
+  task now supports nested filesets through which the file permissions
+  may be controlled.
+
+* wlrun, wlstop and ejbjar now support Weblogic 6.0
+
+* The MPasre task has been updated to work with MParse 2.0
+
+* The documentation has been significantly updated.
+
+
+Fixed bugs:
+-----------
+
+* <signjar> doesn't use deprectated methods anymore.
+
+* javadoc's failonerror attribute works again
+
+* javadoc's additionalparam attribute will now be split into separate
+  parameters (on spaces) to allow for more than one parameter.
+
+* Changed <sql> task so that printing result sets works on Oracle
+
+* Changes to ddcreator and ejbc helper to respect the descriptor hierarchy
+  keppgenerated in ejbc can now be turned off
+
+* ejbjar now correctly ignores <ejb-ref> elements in the deployment descriptor.
+  CMP files are included by parsing the weblogic deployment descriptor rather
+  than relying on the naming convention used in ant 1.2
+
+* ejbjar includes super classes and super interfaces into the generated ejb
+  jar files. The <support> nested element allows support classes to be
+  included in the EJB jar. The toplink element should now correctly locate
+  the toplink descriptor.
+
+* <vssget> now correctly deals with spaces in arguments
+
+* <jar> fails early if a given manifest file doesn't exist
+
+* <rmic> doesn't search for the _Skel file anymore when stubversion is
+  set to 1.2.
+
+* <rmic> uses the the same classpath to verify a class can be rmic'd
+  as it passes to the compiler.
+
+* org.apache.tools.mail.MailMessage (and therefore <mail>) can now
+  handle SMTP servers sending multi line responses.
+
+* nested <classpath> elements of <taskdef> now work for <taskdef>s not
+  nested into <target> as well.
+
+* <property> and <available> will search for the resource "foo" instead
+  of "/org/apache/tools/ant/taskdefs/foo" when given a relative resource
+  name foo.
+
+* Handle build files in directories whose name contained a "#" character
+
+* <junit> can now log to files whose name contains a comma as well.
+
+* The AntClassLoader now refers to the loader which loaded it, any
+  requests it does not handle itself. Previously these went to the
+  primordial loader.
+
+Changes from Ant 1.1 to Ant 1.2
+===============================
+
+Changes that could break older environments:
+--------------------------------------------
+
+* Semantics of <property> has changed again in the hope to be more
+intuitive. ${} expansion now happens at runtime and <property> tags
+living inside of targets only take effect if they are visited at
+runtime.
+
+As a side effect of this change, task's attributes get set at runtime
+not at parser time as well, which might change the results of
+<script>s or other custom tasks that reference other tasks by their id
+attribute.
+
+* copying of support files in <javac> has been removed - as well as
+the filtering attribute.
+
+* the <expand> and <keysubst> tasks have been removed.
+
+* the ignore and items attributes of directory based tasks have been removed.
+
+* the command line switches _not_ starting with - have been removed.
+
+* Path and EnumeratedAttribute have been moved from
+org.apache.tools.ant to org.apache.tools.ant.types.
+
+* the class attributes of <available>, <java>, <rmic> and <taskdef>
+have been removed.
+
+* the src attribute of <chmod> has been removed.
+
+* <patch> and <javadoc> have lost some of their attributes.
+
+* <java> and <cvs> have lost some undocumented attributes.
+
+* the Unix antRun script would search for command.sh in the directory
+it changed to and invoke this instead of command if present. This
+behavior has been dropped.
+
+* <ejbjar> task syntax has been changed significantly
+
+* <exec> is no longer implemented by org.apache.tool.ant.taskdefs.Exec.
+Custom tasks that rely on Project.createTask("exec") to return an
+instance of this class are going to fail.
+
+* nested <include> and <exclude> elements expect the value of their
+name attribute to be a single pattern, they don't accept multiple
+patterns anymore. Split them into multiple elements of the same type.
+
+* <delete dir="somedir" /> will now delete the directory itself as
+well as all included files. If you just want to clean out the
+directory and keep the empty one, use a nested fileset.
+
+Other changes:
+--------------
+
+* New tasks: antstructure, cab, execon, fail, ftp, genkey, jlink,
+junit, sql, javacc, jjtree, starteam, war, unwar, uptodate,
+native2ascii, copy, move, mparse.
+
+* copydir, copyfile, deltree and rename are now deprecated. They
+should be replaced with the new copy, delete and move tasks.
+
+* <java> uses a ClassLoader of its own in no-fork mode if a classpath is
+specified.
+
+* <style> will create the necessary target directories and reprocess
+all files if the stylesheet changes.
+
+* New data types fileset and patternset - expected to get a broader use.
+They, as well as PATH like structures, can now be defined on a global
+level and later be referenced by their id attribute.
+
+* You can specify environment variables to <exec>.
+
+* <get> can check whether a remote file is actually newer than a local
+copy before it starts a download (HTTP only).
+
+* Added a -logger option to allow the class which performs logging to be
+specified on the command line.
+
+* Added a -emacs option to tell the logger to leave out taskname adornments
+on log output.
+
+* <chmod> works on all files in parallel and supports multiple filesets.
+
+* <replace> can now use tokens and/or values that cross line boundaries.
+
+* build.compiler supports now jvc as well.
+
+* project specific help can now be obtained with the -projecthelp option.
+
+* Added a -debug option to make -verbose less verbose (and more useful)
+
+* Ant will now search for a file named build.xml in the parent directory
+and above (towards the root of the filesystem) if you didn't specify
+-buildfile and there is no build.xml in the current directory.
+
+* <echo> can now write to a file and accepts nested text.
+
+Fixed bugs:
+-----------
+
+* <chmod> didn't work when used as a directory based task.
+
+* Path, Available, Property didn't resolve relative filenames with
+respect to the Project's basedir.
+
+* Project didn't interpret the basedir attribute correctly in all
+cases.
+
+* Nested <src> in <javac> caused NullPointerException.
+
+* Corrupt Zip- and Jar-files ar now deleted if the task fails.
+
+* many more fixes we've forgotten to document here ...
+
+* The packagelistloc attribute of <javadoc>'s <link> child will be
+  resolved as a file (i.e. it is either absolute or relative to
+  basedir).
Index: /release-kits/shared/ant/bin/ant
===================================================================
--- /release-kits/shared/ant/bin/ant	(revision 16139)
+++ /release-kits/shared/ant/bin/ant	(revision 16139)
@@ -0,0 +1,299 @@
+#! /bin/sh
+
+#   Copyright 2001-2005 The Apache Software Foundation
+#
+#   Licensed 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.
+
+# Extract launch and ant arguments, (see details below).
+ant_exec_args=
+no_config=false
+use_jikes_default=false
+ant_exec_debug=false
+show_help=false
+for arg in "$@" ; do
+  if [ "$arg" = "--noconfig" ] ; then
+    no_config=true
+  elif [ "$arg" = "--usejikes" ] ; then
+    use_jikes_default=true
+  elif [ "$arg" = "--execdebug" ] ; then
+    ant_exec_debug=true
+  elif [ my"$arg" = my"--h"  -o my"$arg" = my"--help"  ] ; then
+    show_help=true
+    ant_exec_args="$ant_exec_args -h"
+  else
+    if [  my"$arg" = my"-h"  -o  my"$arg" = my"-help" ] ; then
+      show_help=true
+    fi
+    ant_exec_args="$ant_exec_args \"$arg\""
+  fi
+done
+
+# Source/default ant configuration
+if $no_config ; then
+  rpm_mode=false
+  usejikes=$use_jikes_default
+else
+  # load system-wide ant configuration
+#  if [ -f "/etc/ant.conf" ] ; then
+#    . /etc/ant.conf
+#  fi
+
+  # load user ant configuration
+  if [ -f "$HOME/.ant/ant.conf" ] ; then
+    . $HOME/.ant/ant.conf
+  fi
+  if [ -f "$HOME/.antrc" ] ; then
+    . "$HOME/.antrc"
+  fi
+
+  # provide default configuration values
+  if [ -z "$rpm_mode" ] ; then
+    rpm_mode=false
+  fi
+  if [ -z "$usejikes" ] ; then
+    usejikes=$use_jikes_default
+  fi
+fi
+
+# Setup Java environment in rpm mode
+if $rpm_mode ; then
+  if [ -f /usr/share/java-utils/java-functions ] ; then
+    . /usr/share/java-utils/java-functions
+    set_jvm
+    set_javacmd
+  fi
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  Darwin*) darwin=true
+           if [ -z "$JAVA_HOME" ] ; then
+             JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home
+           fi
+           ;;
+esac
+
+if [ -z "$ANT_HOME" -o ! -d "$ANT_HOME" ] ; then
+  ## resolve links - $0 may be a link to ant's home
+  PRG="$0"
+  progname=`basename "$0"`
+
+  # need this for relative symlinks
+  while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+    PRG="$link"
+    else
+    PRG=`dirname "$PRG"`"/$link"
+    fi
+  done
+
+  ANT_HOME=`dirname "$PRG"`/..
+
+  # make it fully qualified
+  ANT_HOME=`cd "$ANT_HOME" && pwd`
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$ANT_HOME" ] &&
+    ANT_HOME=`cygpath --unix "$ANT_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# set ANT_LIB location
+ANT_LIB="${ANT_HOME}/lib"
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD=`which java 2> /dev/null `
+    if [ -z "$JAVACMD" ] ; then
+        JAVACMD=java
+    fi
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly."
+  echo "  We cannot execute $JAVACMD"
+  exit 1
+fi
+
+# Build local classpath using just the launcher in non-rpm mode or
+# use the Jpackage helper in rpm mode with basic and default jars
+# specified in the ant.conf configuration. Because the launcher is
+# used, libraries linked in ANT_HOME will also be include, but this
+# is discouraged as it is not java-version safe. A user should
+# request optional jars and their dependencies via the OPT_JAR_LIST
+# variable
+if $rpm_mode && [ -f /usr/bin/build-classpath ] ; then
+  LOCALCLASSPATH="$(/usr/bin/build-classpath ant ant-launcher jaxp_parser_impl xml-commons-apis)"
+  # If the user requested to try to add some other jars to the classpath
+  if [ -n "$OPT_JAR_LIST" ] ; then
+    _OPTCLASSPATH="$(/usr/bin/build-classpath $OPT_JAR_LIST 2> /dev/null)"
+    if [ -n "$_OPTCLASSPATH" ] ; then 
+      LOCALCLASSPATH="$LOCALCLASSPATH:$_OPTCLASSPATH"
+    fi
+  fi
+
+  # Explicitly add javac path to classpath, assume JAVA_HOME set
+  # properly in rpm mode
+  if [ -f "$JAVA_HOME/lib/tools.jar" ] ; then
+    LOCALCLASSPATH="$LOCALCLASSPATH:$JAVA_HOME/lib/tools.jar"
+  fi
+  if [ -f "$JAVA_HOME/lib/classes.zip" ] ; then
+    LOCALCLASSPATH="$LOCALCLASSPATH:$JAVA_HOME/lib/classes.zip"
+  fi
+
+  # if CLASSPATH_OVERRIDE env var is set, LOCALCLASSPATH will be
+  # user CLASSPATH first and ant-found jars after.
+  # In that case, the user CLASSPATH will override ant-found jars
+  #
+  # if CLASSPATH_OVERRIDE is not set, we'll have the normal behaviour
+  # with ant-found jars first and user CLASSPATH after
+  if [ -n "$CLASSPATH" ] ; then
+    # merge local and specified classpath 
+    if [ -z "$LOCALCLASSPATH" ] ; then 
+      LOCALCLASSPATH="$CLASSPATH"
+    elif [ -n "$CLASSPATH_OVERRIDE" ] ; then
+      LOCALCLASSPATH="$CLASSPATH:$LOCALCLASSPATH"
+    else
+      LOCALCLASSPATH="$LOCALCLASSPATH:$CLASSPATH"
+    fi
+
+    # remove class path from launcher -cp option
+    CLASSPATH=""
+  fi
+else
+  # not using rpm_mode; use launcher to determine classpaths
+  if [ -z "$LOCALCLASSPATH" ] ; then
+      LOCALCLASSPATH=$ANT_LIB/ant-launcher.jar
+  else
+      LOCALCLASSPATH=$ANT_LIB/ant-launcher.jar:$LOCALCLASSPATH
+  fi
+fi
+
+if [ -n "$JAVA_HOME" ] ; then
+  # OSX hack to make Ant work with jikes
+  if $darwin ; then
+    OSXHACK="${JAVA_HOME}/../Classes"
+    if [ -d "${OSXHACK}" ] ; then
+      for i in "${OSXHACK}"/*.jar
+      do
+        JIKESPATH="$JIKESPATH:$i"
+      done
+    fi
+  fi
+fi
+
+# Allow Jikes support (off by default)
+if $usejikes; then
+  ANT_OPTS="$ANT_OPTS -Dbuild.compiler=jikes"
+fi
+
+# For Cygwin, switch paths to appropriate format before running java
+# For PATHs convert to unix format first, then to windows format to ensure
+# both formats are supported. Probably this will fail on directories with ;
+# in the name in the path. Let's assume that paths containing ; are more
+# rare than windows style paths on cygwin.
+if $cygwin; then
+  if [ "$OS" = "Windows_NT" ] && cygpath -m .>/dev/null 2>/dev/null ; then
+    format=mixed
+  else
+    format=windows
+  fi
+  ANT_HOME=`cygpath --$format "$ANT_HOME"`
+  ANT_LIB=`cygpath --$format "$ANT_LIB"`
+  JAVA_HOME=`cygpath --$format "$JAVA_HOME"`
+  LCP_TEMP=`cygpath --path --unix "$LOCALCLASSPATH"`
+  LOCALCLASSPATH=`cygpath --path --$format "$LCP_TEMP"`
+  if [ -n "$CLASSPATH" ] ; then
+    CP_TEMP=`cygpath --path --unix "$CLASSPATH"`
+    CLASSPATH=`cygpath --path --$format "$CP_TEMP"`
+  fi
+  CYGHOME=`cygpath --$format "$HOME"`
+fi
+
+# Show script help if requested
+if $show_help ; then
+  echo $0 '[script options] [options] [target [target2 [target3] ..]]'
+  echo 'Script Options:'
+  echo '  --help, --h            print this message and ant help'
+  echo '  --noconfig             suppress sourcing of /etc/ant.conf,'
+  echo '                         $HOME/.ant/ant.conf, and $HOME/.antrc'
+  echo '                         configuration files'
+  echo '  --usejikes             enable use of jikes by default, unless'
+  echo '                         set explicitly in configuration files'
+  echo '  --execdebug            print ant exec line generated by this'
+  echo '                         launch script'
+  echo '  '
+fi
+# add a second backslash to variables terminated by a backslash under cygwin
+if $cygwin; then
+  case "$ANT_HOME" in
+    *\\ )
+    ANT_HOME="$ANT_HOME\\"
+    ;;
+  esac
+  case "$CYGHOME" in
+    *\\ )
+    CYGHOME="$CYGHOME\\"
+    ;;
+  esac
+  case "$JIKESPATH" in
+    *\\ )
+    JIKESPATH="$JIKESPATH\\"
+    ;;
+  esac
+  case "$LOCALCLASSPATH" in
+    *\\ )
+    LOCALCLASSPATH="$LOCALCLASSPATH\\"
+    ;;
+  esac
+  case "$CLASSPATH" in
+    *\\ )
+    CLASSPATH="$CLASSPATH\\"
+    ;;
+  esac
+fi
+# Execute ant using eval/exec to preserve spaces in paths,
+# java options, and ant args
+ant_sys_opts=
+if [ -n "$CYGHOME" ]; then
+  if [ -n "$JIKESPATH" ]; then
+    ant_sys_opts="-Djikes.class.path=\"$JIKESPATH\" -Dcygwin.user.home=\"$CYGHOME\""
+  else
+    ant_sys_opts="-Dcygwin.user.home=\"$CYGHOME\""
+  fi
+else
+  if [ -n "$JIKESPATH" ]; then
+    ant_sys_opts="-Djikes.class.path=\"$JIKESPATH\""
+  fi
+fi
+ant_exec_command="exec \"$JAVACMD\" $ANT_OPTS -classpath \"$LOCALCLASSPATH\" -Dant.home=\"$ANT_HOME\" -Dant.library.dir=\"$ANT_LIB\" $ant_sys_opts org.apache.tools.ant.launch.Launcher $ANT_ARGS -cp \"$CLASSPATH\" $ant_exec_args"
+if $ant_exec_debug ; then
+    echo $ant_exec_command
+fi
+eval $ant_exec_command
Index: /release-kits/shared/ant/bin/ant.bat
===================================================================
--- /release-kits/shared/ant/bin/ant.bat	(revision 16139)
+++ /release-kits/shared/ant/bin/ant.bat	(revision 16139)
@@ -0,0 +1,126 @@
+@echo off
+
+REM  Copyright 2001,2004-2005 The Apache Software Foundation
+REM
+REM  Licensed under the Apache License, Version 2.0 (the "License");
+REM  you may not use this file except in compliance with the License.
+REM  You may obtain a copy of the License at
+REM
+REM      http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM  Unless required by applicable law or agreed to in writing, software
+REM  distributed under the License is distributed on an "AS IS" BASIS,
+REM  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM  See the License for the specific language governing permissions and
+REM  limitations under the License.
+
+if exist "%HOME%\antrc_pre.bat" call "%HOME%\antrc_pre.bat"
+
+if "%OS%"=="Windows_NT" @setlocal
+if "%OS%"=="WINNT" @setlocal
+
+rem %~dp0 is expanded pathname of the current script under NT
+set DEFAULT_ANT_HOME=%~dp0..
+
+if "%ANT_HOME%"=="" set ANT_HOME=%DEFAULT_ANT_HOME%
+set DEFAULT_ANT_HOME=
+
+set _USE_CLASSPATH=yes
+
+rem Slurp the command line arguments. This loop allows for an unlimited number
+rem of arguments (up to the command line limit, anyway).
+set ANT_CMD_LINE_ARGS=%1
+if ""%1""=="""" goto doneStart
+shift
+:setupArgs
+if ""%1""=="""" goto doneStart
+if ""%1""==""-noclasspath"" goto clearclasspath
+set ANT_CMD_LINE_ARGS=%ANT_CMD_LINE_ARGS% %1
+shift
+goto setupArgs
+
+rem here is there is a -noclasspath in the options
+:clearclasspath
+set _USE_CLASSPATH=no
+shift
+goto setupArgs
+
+rem This label provides a place for the argument list loop to break out
+rem and for NT handling to skip to.
+
+:doneStart
+rem find ANT_HOME if it does not exist due to either an invalid value passed
+rem by the user or the %0 problem on Windows 9x
+if exist "%ANT_HOME%\lib\ant.jar" goto checkJava
+
+rem check for ant in Program Files
+if not exist "%ProgramFiles%\ant" goto checkSystemDrive
+set ANT_HOME=%ProgramFiles%\ant
+goto checkJava
+
+:checkSystemDrive
+rem check for ant in root directory of system drive
+if not exist %SystemDrive%\ant\lib\ant.jar goto checkCDrive
+set ANT_HOME=%SystemDrive%\ant
+goto checkJava
+
+:checkCDrive
+rem check for ant in C:\ant for Win9X users
+if not exist C:\ant\lib\ant.jar goto noAntHome
+set ANT_HOME=C:\ant
+goto checkJava
+
+:noAntHome
+echo ANT_HOME is set incorrectly or ant could not be located. Please set ANT_HOME.
+goto end
+
+:checkJava
+set _JAVACMD=%JAVACMD%
+
+if "%JAVA_HOME%" == "" goto noJavaHome
+if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
+if "%_JAVACMD%" == "" set _JAVACMD=%JAVA_HOME%\bin\java.exe
+goto checkJikes
+
+:noJavaHome
+if "%_JAVACMD%" == "" set _JAVACMD=java.exe
+
+:checkJikes
+if not "%JIKESPATH%"=="" goto runAntWithJikes
+
+:runAnt
+if "%_USE_CLASSPATH%"=="no" goto runAntNoClasspath
+if not "%CLASSPATH%"=="" goto runAntWithClasspath
+"%_JAVACMD%" %ANT_OPTS% -classpath "%ANT_HOME%\lib\ant-launcher.jar" "-Dant.home=%ANT_HOME%" org.apache.tools.ant.launch.Launcher %ANT_ARGS% %ANT_CMD_LINE_ARGS%
+goto end
+
+:runAntNoClasspath
+"%_JAVACMD%" %ANT_OPTS% -classpath "%ANT_HOME%\lib\ant-launcher.jar" "-Dant.home=%ANT_HOME%" org.apache.tools.ant.launch.Launcher %ANT_ARGS% %ANT_CMD_LINE_ARGS%
+goto end
+
+:runAntWithClasspath
+"%_JAVACMD%" %ANT_OPTS% -classpath "%ANT_HOME%\lib\ant-launcher.jar" "-Dant.home=%ANT_HOME%" org.apache.tools.ant.launch.Launcher %ANT_ARGS% -cp "%CLASSPATH%" %ANT_CMD_LINE_ARGS%
+goto end
+
+:runAntWithJikes
+if "%_USE_CLASSPATH%"=="no" goto runAntWithJikesNoClasspath
+if not "%CLASSPATH%"=="" goto runAntWithJikesAndClasspath
+
+:runAntWithJikesNoClasspath
+"%_JAVACMD%" %ANT_OPTS% -classpath "%ANT_HOME%\lib\ant-launcher.jar" "-Dant.home=%ANT_HOME%" "-Djikes.class.path=%JIKESPATH%" org.apache.tools.ant.launch.Launcher %ANT_ARGS% %ANT_CMD_LINE_ARGS%
+goto end
+
+:runAntWithJikesAndClasspath
+"%_JAVACMD%" %ANT_OPTS% -classpath "%ANT_HOME%\lib\ant-launcher.jar" "-Dant.home=%ANT_HOME%" "-Djikes.class.path=%JIKESPATH%" org.apache.tools.ant.launch.Launcher %ANT_ARGS%  -cp "%CLASSPATH%" %ANT_CMD_LINE_ARGS%
+goto end
+
+:end
+set _JAVACMD=
+set ANT_CMD_LINE_ARGS=
+
+if "%OS%"=="Windows_NT" @endlocal
+if "%OS%"=="WINNT" @endlocal
+
+:mainEnd
+if exist "%HOME%\antrc_post.bat" call "%HOME%\antrc_post.bat"
+
Index: /release-kits/shared/ant/bin/ant.cmd
===================================================================
--- /release-kits/shared/ant/bin/ant.cmd	(revision 16139)
+++ /release-kits/shared/ant/bin/ant.cmd	(revision 16139)
@@ -0,0 +1,92 @@
+/* 
+    Copyright 2003-2004 The Apache Software Foundation
+  
+    Licensed 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.
+ 
+    Run ant
+*/
+
+'@echo off'
+parse arg mode envarg '::' antarg
+
+if mode\='.' & mode\='..' & mode\='/' then do
+  envarg = mode envarg
+  mode = ''
+end
+
+if antarg = '' then do
+  antarg = envarg
+  envarg = ''
+end
+
+x = setlocal()
+
+env="OS2ENVIRONMENT"
+antenv = _getenv_('antenv')
+if _testenv_() = 0 then interpret 'call "' || antenv || '"' '"' || envarg || '"'
+
+if mode = '' then mode = _getenv_('ANT_MODE' '..')
+if mode \= '/' then do
+  runrc = _getenv_('runrc')
+  antrc = _getenv_('antrc' 'antrc.cmd')
+  if mode = '..' then mode = '-r'
+  else mode = ''
+  interpret 'call "' || runrc || '"' antrc '"' || mode || '"'
+end
+
+if _testenv_() = 0 then do
+  say 'Ant environment is not set properly'
+  x = endlocal()
+  exit 16
+end
+
+settings = '-Dant.home=' || ANT_HOME '-Djava.home=' || JAVA_HOME
+
+java = _getenv_('javacmd' 'java')
+opts = value('ANT_OPTS',,env)
+args = value('ANT_ARGS',,env)
+lcp = value('LOCALCLASSPATH',,env)
+cp = value('CLASSPATH',,env)
+if value('ANT_USE_CP',,env) \= '' then do
+  if lcp \= '' & right(lcp, 1) \= ';' then lcp = lcp || ';'
+  lcp = lcp || cp
+  'SET CLASSPATH='
+end
+if lcp\='' then lcp = '-classpath' lcp
+
+cmd = java opts lcp '-jar' ANT_HOME ||'\lib\ant-launcher.jar' settings args antarg
+launcher = stream(ANT_HOME ||'\lib\ant-launcher.jar', 'C', 'query exists')
+if launcher = '' then entry = 'org.apache.tools.ant.Main'
+else entry = 'org.apache.tools.ant.launch.Launcher'
+java opts lcp entry settings args antarg
+
+x = endlocal()
+
+return rc
+
+_testenv_: procedure expose env ANT_HOME JAVA_HOME
+ANT_HOME = value('ANT_HOME',,env)
+if ANT_HOME = '' then return 0
+JAVA_HOME = value('JAVA_HOME',,env)
+if JAVA_HOME = '' then return 0
+cp = translate(value('CLASSPATH',,env))
+if pos(translate(ANT_HOME), cp) = 0 then return 0
+if pos(translate(JAVA_HOME), cp) = 0 then return 0
+return 1
+
+_getenv_: procedure expose env
+parse arg envar default
+if default = '' then default = envar
+var = value(translate(envar),,env)
+if var = '' then var = default
+return var
Index: /release-kits/shared/ant/bin/antRun
===================================================================
--- /release-kits/shared/ant/bin/antRun	(revision 16139)
+++ /release-kits/shared/ant/bin/antRun	(revision 16139)
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+#
+#  Copyright  2001-2002,2004 The Apache Software Foundation
+# 
+#   Licensed 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.
+# 
+#
+
+# Args: DIR command
+cd "$1"
+CMD="$2"
+shift
+shift
+
+exec "$CMD" "$@"
Index: /release-kits/shared/ant/bin/antRun.bat
===================================================================
--- /release-kits/shared/ant/bin/antRun.bat	(revision 16139)
+++ /release-kits/shared/ant/bin/antRun.bat	(revision 16139)
@@ -0,0 +1,47 @@
+@echo off
+
+REM
+REM Copyright  2001-2002,2004-2005 The Apache Software Foundation
+REM
+REM  Licensed under the Apache License, Version 2.0 (the "License");
+REM  you may not use this file except in compliance with the License.
+REM  You may obtain a copy of the License at
+REM
+REM      http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM  Unless required by applicable law or agreed to in writing, software
+REM  distributed under the License is distributed on an "AS IS" BASIS,
+REM  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM  See the License for the specific language governing permissions and
+REM  limitations under the License.
+REM
+REM
+
+if "%OS%"=="Windows_NT" @setlocal
+if "%OS%"=="WINNT" @setlocal
+
+if ""%1""=="""" goto runCommand
+
+rem Change drive and directory to %1
+if "%OS%"=="Windows_NT" cd /d ""%1""
+if not "%OS%"=="Windows_NT" cd ""%1""
+shift
+
+rem Slurp the command line arguments. This loop allows for an unlimited number
+rem of agruments (up to the command line limit, anyway).
+set ANT_RUN_CMD=%1
+if ""%1""=="""" goto runCommand
+shift
+:loop
+if ""%1""=="""" goto runCommand
+set ANT_RUN_CMD=%ANT_RUN_CMD% %1
+shift
+goto loop
+
+:runCommand
+rem echo %ANT_RUN_CMD%
+%ANT_RUN_CMD%
+
+if "%OS%"=="Windows_NT" @endlocal
+if "%OS%"=="WINNT" @endlocal
+
Index: /release-kits/shared/ant/bin/antRun.pl
===================================================================
--- /release-kits/shared/ant/bin/antRun.pl	(revision 16139)
+++ /release-kits/shared/ant/bin/antRun.pl	(revision 16139)
@@ -0,0 +1,65 @@
+#!/usr/bin/perl
+#
+# Copyright 2001,2003-2004 The Apache Software Foundation
+#
+#  Licensed 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.
+#
+#######################################################################
+#
+# antRun.pl
+#
+# wrapper script for invoking commands on a platform with Perl installed
+# this is akin to antRun.bat, and antRun the SH script 
+#
+# created:         2001-10-18
+# author:          Jeff Tulley jtulley@novell.com 
+#######################################################################
+#be fussy about variables
+use strict;
+
+#turn warnings on during dev; generates a few spurious uninitialised var access warnings
+#use warnings;
+
+#and set $debug to 1 to turn on trace info (currently unused)
+my $debug=1;
+
+#######################################################################
+# change drive and directory to "%1"
+my $ANT_RUN_CMD = @ARGV[0];
+
+# assign current run command to "%2"
+chdir (@ARGV[0]) || die "Can't cd to $ARGV[0]: $!\n";
+if ($^O eq "NetWare") {
+    # There is a bug in Perl 5 on NetWare, where chdir does not
+    # do anything.  On NetWare, the following path-prefixed form should 
+    # always work. (afaict)
+    $ANT_RUN_CMD .= "/".@ARGV[1];
+}
+else {
+    $ANT_RUN_CMD = @ARGV[1];
+}
+
+# dispose of the first two arguments, leaving only the command's args.
+shift;
+shift;
+
+# run the command
+my $returnValue = system $ANT_RUN_CMD, @ARGV;
+if ($returnValue eq 0) {
+    exit 0;
+}
+else {
+    # only 0 and 1 are widely recognized as exit values
+    # so change the exit value to 1
+    exit 1;
+}
Index: /release-kits/shared/ant/bin/antenv.cmd
===================================================================
--- /release-kits/shared/ant/bin/antenv.cmd	(revision 16139)
+++ /release-kits/shared/ant/bin/antenv.cmd	(revision 16139)
@@ -0,0 +1,99 @@
+/* 
+    Copyright 2003-2004 The Apache Software Foundation
+  
+    Licensed 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.
+
+    Ant environment
+*/
+
+'@echo off'
+call RxFuncAdd "SysLoadFuncs", "RexxUtil", "SysLoadFuncs"
+call SysLoadFuncs
+
+/* Prepare the parameters for later use */
+parse arg argv
+mode = ''
+args = ''
+opts = ''
+cp = ''
+lcp = ''
+
+do i = 1 to words(argv)
+  param = word(argv, i)
+  select
+    when param='-lcp' then mode = 'l'
+    when param='-cp' | param='-classpath' then mode = 'c'
+    when abbrev('-opts', param, 4) then mode = 'o'
+    when abbrev('-args', param, 4) then mode = 'a'
+  otherwise
+    select
+      when mode = 'a' then args = space(args param, 1)
+      when mode = 'c' then cp = space(cp param, 1)
+      when mode = 'l' then lcp = space(lcp param, 1)
+      when mode = 'o' then opts = space(opts param, 1)
+    otherwise
+      say 'Option' param 'ignored'
+    end
+  end
+end
+
+env="OS2ENVIRONMENT"
+antconf = _getenv_('antconf' 'antconf.cmd')
+runrc = _getenv_('runrc')
+interpret 'call "' || runrc || '"' '"' || antconf || '"' 'ETC'
+ANT_HOME = value('ANT_HOME',,env)
+JAVA_HOME = value('JAVA_HOME',,env)
+classpath = value('CLASSPATH',,env)
+classes = stream(JAVA_HOME || "\lib\classes.zip", "C", "QUERY EXISTS")
+if classes \= '' then classpath = prepend(classpath classes)
+classes = stream(JAVA_HOME || "\lib\tools.jar", "C", "QUERY EXISTS")
+if classes \= '' then classpath = prepend(classpath classes)
+
+classpath = prepend(classpath ANT_HOME || '\lib\ant-launcher.jar')
+'SET CLASSPATH=' || classpath
+
+/* Setting classpathes, options and arguments */
+envset = _getenv_('envset')
+if cp\=''   then interpret 'call "' || envset || '"' '"; CLASSPATH"' '"' || cp || '"'
+if lcp\=''  then interpret 'call "' || envset || '"' '"; LOCALCLASSPATH"' '"' || lcp || '"'
+if opts\='' then interpret 'call "' || envset || '"' '"-D ANT_OPTS"' '"' || opts || '"'
+if args\='' then interpret 'call "' || envset || '"' '"ANT_ARGS"' '"' || args || '"'
+
+exit 0
+
+addpath: procedure
+parse arg path elem
+if elem = '' then do
+  if path\='' & right(path, 1)\=';' then path = path || ';'
+  return path
+end
+if substr(path, length(path)) = ';' then glue = ''
+else glue = ';'
+if pos(translate(elem), translate(path)) = 0 then path = path || glue || elem || ';'
+return path
+
+prepend: procedure
+parse arg path elem
+if elem = '' then do
+  if path\='' & right(path, 1)\=';' then path = path || ';'
+  return path
+end
+if pos(translate(elem), translate(path)) = 0 then path = elem || ';' || path
+return path
+
+_getenv_: procedure expose env
+parse arg envar default
+if default = '' then default = envar
+var = value(translate(envar),,env)
+if var = '' then var = default
+return var
Index: /release-kits/shared/ant/bin/complete-ant-cmd.pl
===================================================================
--- /release-kits/shared/ant/bin/complete-ant-cmd.pl	(revision 16139)
+++ /release-kits/shared/ant/bin/complete-ant-cmd.pl	(revision 16139)
@@ -0,0 +1,113 @@
+#!/usr/bin/perl
+#
+# Copyright 2001,2004 The Apache Software Foundation
+#
+#  Licensed 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.
+#
+# A script to allow Bash or Z-Shell to complete an Ant command-line.  
+#
+# To install for Bash 2.0 or better, add the following to ~/.bashrc:
+# 
+#     $ complete -C complete-ant-cmd ant build.sh
+#
+# To install for Z-Shell 2.5 or better, add the following to ~/.zshrc:
+#
+#     function ant_complete () {
+#         local args_line args
+#         read -l args_line
+#         set -A args $args_line
+#         set -A reply $(COMP_LINE=$args_line complete-ant-cmd ${args[1]} $1)
+#     }
+#     compctl -K ant_complete ant build.sh
+#     
+# @author Mike Williams <mikew@cortexebusiness.com.au>
+
+my $cmdLine = $ENV{'COMP_LINE'};
+my $antCmd = $ARGV[0];
+my $word = $ARGV[1];
+
+my @completions;
+if ($word =~ /^-/) {
+    list( restrict( $word, getArguments() ));
+} elsif ($cmdLine =~ /-(f|buildfile)\s+\S*$/) {
+    list( getBuildFiles($word) );
+} else {
+    list( restrict( $word, getTargets() ));
+}
+
+exit(0);
+
+sub list {
+    for (@_) {
+        print "$_\n";
+    }
+}
+
+sub restrict {
+    my ($word, @completions) = @_;
+    grep( /^\Q$word\E/, @completions );
+}
+
+sub getArguments {
+    qw(-buildfile -debug -emacs -f -find -help -listener -logfile 
+       -logger -projecthelp -quiet -verbose -version); 
+}
+
+
+sub getBuildFiles {
+    my ($word) = @_;
+    grep( /\.xml$/, glob( "$word*" ));
+}
+
+sub getTargets {
+
+    # Look for build-file
+    my $buildFile = 'build.xml';
+    if ($cmdLine =~ /-(f|buildfile)\s+(\S+)/) {
+        $buildFile = $2;
+    }
+    return () unless (-f $buildFile);
+
+    # Run "ant -projecthelp" to list targets.  Keep a cache of results in a
+    # cache-file.
+    my $cacheFile = $buildFile;
+    $cacheFile =~ s|(.*/)?(.*)|${1}.ant-targets-${2}|;
+    if ((!-e $cacheFile) || (-M $buildFile) < (-M $cacheFile)) {
+        open( CACHE, '>'.$cacheFile ) || die "can\'t write $cacheFile: $!\n";
+        open( HELP, "$antCmd -projecthelp -f '$buildFile'|" ) || return(); 
+        my %targets;
+        while( <HELP> ) {
+            if (/^\s+(\S+)/) {
+                $targets{$1}++;
+            }
+        }
+        my @targets = sort keys %targets;
+        for (@targets) { print CACHE "$_\n"; }
+        return @targets;
+    }
+    
+    # Read the target-cache
+    open( CACHE, $cacheFile ) || die "can\'t read $cacheFile: $!\n";
+    my @targets;
+    while (<CACHE>) {
+        chop;
+        s/\r$//;  # for Cygwin
+        push( @targets, $_ );
+    }
+    close( CACHE );
+    @targets;
+
+}
+
+
+
Index: /release-kits/shared/ant/bin/envset.cmd
===================================================================
--- /release-kits/shared/ant/bin/envset.cmd	(revision 16139)
+++ /release-kits/shared/ant/bin/envset.cmd	(revision 16139)
@@ -0,0 +1,130 @@
+/*
+
+    Copyright 2003-2004 The Apache Software Foundation
+  
+    Licensed 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.
+
+SET environment variables
+First optional parameter:
+   ;     parameters are considered parts of a path variable, semicolons are
+         appended to each element if not already present
+   -D    parameters are properties for Java or Makefile etc., -D will be
+         prepended and the parameters will be separated by a space
+   =D    the same as above but equal sign is not required
+   ,     parameters should be comma separated in the environment variable
+   -     parameters should be separated by the next parameter
+   Other values mean that the first parameter is missing and the environment
+   variable will be set to the space separated parameters
+
+Second parameter: name of the environment variable
+
+Next parameters: values
+; implies that the equal sign is considered a part of the parameter and is
+not interpreted
+
+-D requires parameters in the form name=value. If the equal sign is not found,
+the parameters are changed to name=expanded_name
+
+Other options have optional equal sign. If it is found, only the part after
+the equal sign will be oprionally expanded.
+
+If the parameter is the minus sign, the next parameter will not be expanded.
+If the parameter is a single dot, it will be replaced with the value of the
+environment variable as it existed before envset was invoked.
+
+For other parameters the batch looks for the environment variable with the
+same name (in uppercase). If it is found, it forms the expanded_name. If
+the environment variable with such a name does not exist, the expanded_name
+will hold the parameter name without case conversion.
+*/
+
+parse arg mode envar args
+
+equal = 0
+sep = ' '
+
+/* Parse command line parameters */
+select
+  when mode='-' then do
+    sep = envar
+    parse var args envar args
+  end
+  when mode=';' then do
+    sep = ''
+    equal = -1
+  end
+  when mode='-D' then equal = 1
+  when mode='=D' then mode = '-D'
+  when mode=',' then sep = ','
+otherwise
+  args = envar args
+  envar = mode
+  mode = ''
+end
+
+env = 'OS2ENVIRONMENT'
+envar = translate(envar)
+orig = value(envar,,env)
+newval = ''
+expand = 1
+
+/* for each parameter... */
+do i = 1 to words(args)
+  if expand > 0 & word(args, i) = '-' then expand = 0
+  else call addval word(args, i)
+end
+
+/* Optionally enclose path variable by quotes */
+if mode = ';' & pos(' ', newval) > 0 then newval = '"' || newval || '"'
+
+/* Set the new value, 'SET' cannot be used since it does not allow '=' */
+x = value(envar, newval, env)
+exit 0
+
+addval: procedure expose sep equal orig expand newval mode env
+parse arg var
+
+if var = '.' then expvar = orig
+else do
+  if equal >= 0 then do
+    parse var var name '=' val
+    if val = '' then var = name
+    else var = val
+  end
+  if expand = 0 then expvar = var
+  else expvar = value(translate(var),,env)
+  if expvar = '' then expvar = var
+  if equal >= 0 then do
+    if val = '' then do
+      parse var expvar key '=' val
+      if val <> '' then name = key
+      else do
+        if equal > 0 then val = key
+        else name = key
+      end
+    end
+    else val = expvar
+    if pos(' ', val) > 0 | pos('=', val) > 0 then val = '"' || val || '"'
+    if val = '' then expvar = name
+    else expvar = name || '=' || val
+  end
+  if mode = '-D' then expvar = '-D' || expvar
+  if mode = ';' then do
+    if right(expvar, 1) <> ';' then expvar = expvar || ';'
+  end
+end
+
+if newval = '' then newval = expvar
+else newval = newval || sep || expvar
+expand = 1
+return
Index: /release-kits/shared/ant/bin/lcp.bat
===================================================================
--- /release-kits/shared/ant/bin/lcp.bat	(revision 16139)
+++ /release-kits/shared/ant/bin/lcp.bat	(revision 16139)
@@ -0,0 +1,30 @@
+REM
+REM Copyright  2001-2004 The Apache Software Foundation
+REM
+REM  Licensed under the Apache License, Version 2.0 (the "License");
+REM  you may not use this file except in compliance with the License.
+REM  You may obtain a copy of the License at
+REM
+REM      http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM  Unless required by applicable law or agreed to in writing, software
+REM  distributed under the License is distributed on an "AS IS" BASIS,
+REM  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM  See the License for the specific language governing permissions and
+REM  limitations under the License.
+REM
+REM
+
+set _CLASSPATHCOMPONENT=%1
+if ""%1""=="""" goto gotAllArgs
+shift
+
+:argCheck
+if ""%1""=="""" goto gotAllArgs
+set _CLASSPATHCOMPONENT=%_CLASSPATHCOMPONENT% %1
+shift
+goto argCheck
+
+:gotAllArgs
+set LOCALCLASSPATH=%_CLASSPATHCOMPONENT%;%LOCALCLASSPATH%
+
Index: /release-kits/shared/ant/bin/runant.pl
===================================================================
--- /release-kits/shared/ant/bin/runant.pl	(revision 16139)
+++ /release-kits/shared/ant/bin/runant.pl	(revision 16139)
@@ -0,0 +1,152 @@
+#!/usr/bin/perl
+#
+# Copyright 2000-2004 The Apache Software Foundation
+#
+#  Licensed 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.
+#
+#######################################################################
+#
+# runant.pl
+#
+# wrapper script for invoking ant in a platform with Perl installed
+# this may include cgi-bin invocation, which is considered somewhat daft.
+# (slo: that should be a separate file which can be derived from this
+# and returns the XML formatted output)
+#
+# the code is not totally portable due to classpath and directory splitting
+# issues. oops. (NB, use File::Spec::Functions  will help and the code is
+# structured for the catfile() call, but because of perl version funnies
+# the code is not included. 
+#
+# created:         2000-8-24
+# author:          Steve Loughran steve_l@sourceforge.net
+#######################################################################
+#
+# Assumptions:
+#
+# - the "java" executable/script is on the command path
+# - ANT_HOME has been set
+# - target platform uses ":" as classpath separator or perl indicates it is dos/win32
+# - target platform uses "/" as directory separator.
+
+#be fussy about variables
+use strict;
+
+#platform specifics (disabled)
+#use File::Spec::Functions;
+
+#turn warnings on during dev; generates a few spurious uninitialised var access warnings
+#use warnings;
+
+#and set $debug to 1 to turn on trace info
+my $debug=1;
+
+#######################################################################
+#
+# check to make sure environment is setup
+#
+
+my $HOME = $ENV{ANT_HOME};
+if ($HOME eq "")
+        {
+    die "\n\nANT_HOME *MUST* be set!\n\n";
+        }
+
+my $JAVACMD = $ENV{JAVACMD};
+$JAVACMD = "java" if $JAVACMD eq "";
+
+my $onnetware = 0;
+if ($^O eq "NetWare")
+{
+  $onnetware = 1;
+}
+
+my $oncygwin = ($^O eq "cygwin");
+
+#ISSUE: what java wants to split up classpath varies from platform to platform 
+#and perl is not too hot at hinting which box it is on.
+#here I assume ":" 'cept on win32, dos, and netware. Add extra tests here as needed.
+my $s=":";
+if(($^O eq "MSWin32") || ($^O eq "dos") || ($^O eq "cygwin") ||
+   ($onnetware == 1))
+        {
+        $s=";";
+        }
+
+#build up standard classpath
+my $localpath = "$HOME/lib/ant-launcher.jar";
+#set JVM options and Ant arguments, if any
+my @ANT_OPTS=split(" ", $ENV{ANT_OPTS});
+my @ANT_ARGS=split(" ", $ENV{ANT_ARGS});
+
+#jikes
+if($ENV{JIKESPATH} ne "")
+        {
+        push @ANT_OPTS, "-Djikes.class.path=$ENV{JIKESPATH}";
+        }
+
+#construct arguments to java
+my @ARGS;
+push @ARGS, @ANT_OPTS;
+
+my $CYGHOME = "";
+
+my $classpath=$ENV{CLASSPATH};
+if ($oncygwin == 1) {
+  $localpath = `cygpath --path --windows $localpath`;
+  chomp ($localpath);
+  if (! $classpath eq "")
+  {
+    $classpath = `cygpath --path --windows "$classpath"`;
+    chomp ($classpath);
+  }
+  $HOME = `cygpath --path --windows $HOME`;
+  chomp ($HOME);
+  $CYGHOME = `cygpath --path --windows $ENV{HOME}`;
+  chomp ($CYGHOME);
+}
+push @ARGS, "-classpath", "$localpath";
+push @ARGS, "-Dant.home=$HOME";
+if ( ! $CYGHOME eq "" )
+{
+  push @ARGS, "-Dcygwin.user.home=\"$CYGHOME\""
+}
+push @ARGS, "org.apache.tools.ant.launch.Launcher", @ANT_ARGS;
+push @ARGS, @ARGV;
+if (! $classpath eq "")
+{
+  if ($onnetware == 1)
+  {
+    # make classpath literally $CLASSPATH
+    # this is to avoid pushing us over the 512 character limit
+    # even skip the ; - that is already in $localpath
+    push @ARGS, "-lib", "\$CLASSPATH";
+  }
+  else
+  {
+    push @ARGS, "-lib", "$classpath";
+  }
+}
+print "\n $JAVACMD @ARGS\n\n" if ($debug);
+
+my $returnValue = system $JAVACMD, @ARGS;
+if ($returnValue eq 0)
+        {
+        exit 0;
+        }
+else
+        {
+        # only 0 and 1 are widely recognized as exit values
+        # so change the exit value to 1
+        exit 1;
+        }
Index: /release-kits/shared/ant/bin/runant.py
===================================================================
--- /release-kits/shared/ant/bin/runant.py	(revision 16139)
+++ /release-kits/shared/ant/bin/runant.py	(revision 16139)
@@ -0,0 +1,100 @@
+#!/usr/bin/python
+# Copyright 2001,2003-2004 The Apache Software Foundation
+#
+#  Licensed 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.
+#
+
+"""
+
+ runant.py
+
+    This script is a translation of the runant.pl written by Steve Loughran.
+    It runs ant with/out arguments, it should be quite portable (thanks to
+    the python os library)
+    This script has been tested with Python2.0/Win2K
+
+ created:         2001-04-11
+ author:          Pierre Dittgen pierre.dittgen@criltelecom.com
+
+ Assumptions:
+
+ - the "java" executable/script is on the command path
+"""
+import os, os.path, string, sys
+
+# Change it to 1 to get extra debug information
+debug = 0
+
+#######################################################################
+
+# If ANT_HOME is not set default to script's parent directory
+if os.environ.has_key('ANT_HOME'):
+    ANT_HOME = os.environ['ANT_HOME']
+else:
+    ANT_HOME = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
+
+# set ANT_LIB location
+ANT_LIB = os.path.join(ANT_HOME, 'lib')
+
+# set JAVACMD (check variables JAVACMD and JAVA_HOME)
+JAVACMD = None
+if not os.environ.has_key('JAVACMD'):
+    if os.environ.has_key('JAVA_HOME'):
+        if not os.path.exists(os.environ['JAVA_HOME']):
+            print "Warning: JAVA_HOME is not defined correctly."
+        else:
+            JAVACMD = os.path.join(os.environ['JAVA_HOME'], 'bin', 'java')
+    else:
+        print "Warning: JAVA_HOME not set."
+else:
+    JAVACMD = os.environ['JAVACMD']
+if not JAVACMD:
+    JAVACMD = 'java'
+
+launcher_jar = os.path.join(ANT_LIB, 'ant-launcher.jar')
+if not os.path.exists(launcher_jar):
+    print 'Unable to locate ant-launcher.jar. Expected to find it in %s' % \
+        ANT_LIB
+
+# Build up standard classpath (LOCALCLASSPATH)
+LOCALCLASSPATH = launcher_jar
+if os.environ.has_key('LOCALCLASSPATH'):
+    LOCALCLASSPATH += os.pathsep + os.environ['LOCALCLASSPATH']
+
+ANT_OPTS = ""
+if os.environ.has_key('ANT_OPTS'):
+    ANT_OPTS = os.environ['ANT_OPTS']
+
+OPTS = ""
+if os.environ.has_key('JIKESPATH'):
+    OPTS = '-Djikes.class.path=\"%s\"' % os.environ['JIKESPATH']
+
+ANT_ARGS = ""
+if os.environ.has_key('ANT_ARGS'):
+    ANT_ARGS = os.environ['ANT_ARGS']
+
+CLASSPATH = ""
+if os.environ.has_key('CLASSPATH'):
+    CLASSPATH = os.environ['CLASSPATH']
+
+# Builds the commandline
+cmdline = ('%s %s -classpath %s -Dant.home=%s %s ' + \
+    'org.apache.tools.ant.launch.Launcher %s -lib %s %s') \
+     % (JAVACMD, ANT_OPTS, LOCALCLASSPATH, ANT_HOME, OPTS, ANT_ARGS, \
+        CLASSPATH, string.join(sys.argv[1:], ' '))
+
+if debug:
+    print '\n%s\n\n' % (cmdline)
+
+# Run the biniou!
+os.system(cmdline)
Index: /release-kits/shared/ant/bin/runrc.cmd
===================================================================
--- /release-kits/shared/ant/bin/runrc.cmd	(revision 16139)
+++ /release-kits/shared/ant/bin/runrc.cmd	(revision 16139)
@@ -0,0 +1,59 @@
+/* 
+    Copyright 2003-2004 The Apache Software Foundation
+  
+    Licensed 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.
+
+    Run RC file, name is in the first arg, second arg is either PATH
+    ENV  or -r or nothing 
+*/
+
+parse arg name path rest
+
+if name = '' then do
+  say 'RC file name is missing'
+  exit 1
+end
+
+if rest \= '' then do
+  say 'Too many parameters'
+  exit 1
+end
+
+call runit name path
+exit 0
+
+runit: procedure
+parse arg name path dir
+
+if path \= '' & path \= '-r' then do
+  dir = value(translate(path),,'OS2ENVIRONMENT')
+  if dir = '' then return
+  dir = translate(dir, '\', '/') /* change UNIX-like path to OS/2 */
+end
+
+if dir = '' then dir = directory()
+
+if path = '-r' then do /* recursive call */
+  subdir = filespec('path', dir)
+  if subdir \= '\' then do
+    subdir = left(subdir, length(subdir)-1)
+    call runit name path filespec('drive', dir) || subdir
+  end
+end
+
+/* Look for the file and run it */
+if right(dir, 1) \= '\' then dir = dir || '\'
+rcfile = stream(dir || name, 'c', 'query exists')
+if rcfile \= '' then interpret 'call "' || rcfile || '"'
+
+return
Index: /release-kits/shared/ant/bootstrap.bat
===================================================================
--- /release-kits/shared/ant/bootstrap.bat	(revision 16139)
+++ /release-kits/shared/ant/bootstrap.bat	(revision 16139)
@@ -0,0 +1,124 @@
+@echo off
+
+REM You will need to specify JAVA_HOME if compiling with 1.2 or later.
+
+REM    Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
+REM    reserved.
+
+set OLDJAVA=%JAVA%
+set OLDJAVAC=%JAVAC%
+set BOOTOLDCLASSPATH=%CLASSPATH%
+set OLDANTHOME=%ANT_HOME%
+
+set ANT_HOME=.
+
+if "" == "%JAVA%"  if "" == "%JAVA_HOME%" set JAVA=java
+if "" == "%JAVA%"                         set JAVA=%JAVA_HOME%\bin\java
+
+if "" == "%JAVAC%" if "" == "%JAVA_HOME%" set JAVAC=javac
+if "" == "%JAVAC%"                        set JAVAC=%JAVA_HOME%\bin\javac
+
+echo.
+echo ... Bootstrapping Ant Distribution
+
+if     "%OS%" == "Windows_NT" if exist bootstrap\nul rmdir/s/q bootstrap
+if not "%OS%" == "Windows_NT" if exist bootstrap\nul deltree/y bootstrap
+if     "%OS%" == "Windows_NT" if exist build\nul rmdir/s/q build
+if not "%OS%" == "Windows_NT" if exist build\nul deltree/y build
+
+SET LOCALCLASSPATH=lib\xercesImpl.jar;lib\xml-apis.jar
+for %%i in (lib\optional\*.jar) do call src\script\lcp.bat %%i
+if exist "%JAVA_HOME%\lib\tools.jar" call src\script\lcp.bat %JAVA_HOME%\lib\tools.jar
+if exist "%JAVA_HOME%\lib\classes.zip" call src\script\lcp.bat %JAVA_HOME%\lib\classes.zip
+
+set TOOLS=src\main\org\apache\tools
+set CLASSDIR=build\classes
+
+SET CLASSPATH=%LOCALCLASSPATH%;%CLASSDIR%;src\main;%CLASSPATH%
+
+echo JAVA_HOME=%JAVA_HOME%
+echo JAVA=%JAVA%
+echo JAVAC=%JAVAC%
+echo CLASSPATH=%CLASSPATH%
+
+if     "%OS%" == "Windows_NT" if exist %CLASSDIR%\nul rmdir/s/q %CLASSDIR%
+if not "%OS%" == "Windows_NT" if exist %CLASSDIR%\nul deltree/y %CLASSDIR%
+
+if not exist build\nul mkdir build
+if not exist build\classes\nul mkdir build\classes
+
+echo.
+echo ... Compiling Ant Classes
+
+"%JAVAC%" %BOOTJAVAC_OPTS% -d %CLASSDIR% %TOOLS%\bzip2\*.java %TOOLS%\tar\*.java %TOOLS%\zip\*.java %TOOLS%\ant\*.java %TOOLS%\ant\types\*.java %TOOLS%\ant\taskdefs\*.java %TOOLS%\ant\util\regexp\RegexpMatcher.java %TOOLS%\ant\util\regexp\RegexpMatcherFactory.java %TOOLS%\ant\taskdefs\condition\*.java %TOOLS%\ant\taskdefs\compilers\*.java 
+
+echo.
+echo ... Copying Required Files
+
+copy %TOOLS%\ant\taskdefs\*.properties %CLASSDIR%\org\apache\tools\ant\taskdefs
+copy %TOOLS%\ant\types\*.properties %CLASSDIR%\org\apache\tools\ant\types
+
+echo.
+echo ... Building Ant Distribution
+
+if not "%OS%"=="Windows_NT" goto win9xStart
+:winNTStart
+@setlocal
+
+REM parse command line arguments
+rem Need to check if we are using the 4NT shell...
+if "%eval[2+2]" == "4" goto setup4NT
+
+rem On NT/2K grab all arguments at once
+set ANT_CMD_LINE_ARGS=%*
+goto doneStart
+
+:setup4NT
+set ANT_CMD_LINE_ARGS=%$
+goto doneStart
+
+:win9xStart
+rem Slurp the command line arguments.  This loop allows for an unlimited number of 
+rem agruments (up to the command line limit, anyway).
+
+set ANT_CMD_LINE_ARGS=
+
+:setupArgs
+if %1a==a goto doneStart
+set ANT_CMD_LINE_ARGS=%ANT_CMD_LINE_ARGS% %1
+shift
+goto setupArgs
+
+:doneStart
+rem This label provides a place for the argument list loop to break out 
+rem and for NT handling to skip to.
+
+"%JAVA%" %ANT_OPTS% org.apache.tools.ant.Main -emacs %ANT_CMD_LINE_ARGS% bootstrap
+
+set ANT_CMD_LINE_ARGS=
+if not "%OS%"=="Windows_NT" goto mainEnd
+:winNTend
+@endlocal
+
+:mainEnd
+
+echo.
+echo ... Cleaning Up Build Directories
+
+if     "%OS%" == "Windows_NT" if exist %CLASSDIR%\nul rmdir/s/q %CLASSDIR%
+if not "%OS%" == "Windows_NT" if exist %CLASSDIR%\nul deltree/y %CLASSDIR%
+
+echo.
+echo ... Done Bootstrapping Ant Distribution
+
+set JAVA=%OLDJAVA%
+set JAVAC=%OLDJAVAC%
+set CLASSPATH=%BOOTOLDCLASSPATH%
+set ANT_HOME=%OLDANTHOME%
+set OLDJAVA=
+set OLDJAVAC=
+set BOOTOLDCLASSPATH=
+set LOCALCLASSPATH=
+set OLDANTHOME=
+set TOOLS=
+
Index: /release-kits/shared/ant/bootstrap.sh
===================================================================
--- /release-kits/shared/ant/bootstrap.sh	(revision 16139)
+++ /release-kits/shared/ant/bootstrap.sh	(revision 16139)
@@ -0,0 +1,166 @@
+#!/bin/sh
+
+#   Copyright 2000-2004 Apache Software Foundation
+#
+#   Licensed 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.
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  Darwin*) darwin=true
+           if [ -z "$JAVA_HOME" ] ; then
+             JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home   
+           fi
+           ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=`cygpath --path --unix "$CLASSPATH"`
+fi
+
+# You will need to specify JAVA_HOME if compiling with 1.2 or later.
+
+if [ -n "$JAVA_HOME" ] ; then
+  if [ -f "$JAVA_HOME/lib/tools.jar" ] ; then
+    CLASSPATH=$CLASSPATH:$JAVA_HOME/lib/tools.jar
+  fi
+
+  if [ -f "$JAVA_HOME/lib/classes.zip" ] ; then
+    CLASSPATH=$CLASSPATH:$JAVA_HOME/lib/classes.zip
+  fi
+else
+  echo "Warning: JAVA_HOME environment variable not set."
+  echo "  If build fails because sun.* classes could not be found"
+  echo "  you will need to set the JAVA_HOME environment variable"
+  echo "  to the installation directory of java."
+fi
+
+# IBM's JDK on AIX uses strange locations for the executables:
+# JAVA_HOME/jre/sh for java and rmid
+# JAVA_HOME/sh for javac and rmic
+if [ -z "$JAVAC" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/sh/javac" ] ; then
+      JAVAC=${JAVA_HOME}/sh/javac;
+    else
+      JAVAC=${JAVA_HOME}/bin/javac;
+    fi
+  else
+    JAVAC=javac
+  fi
+fi
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      JAVACMD=$JAVA_HOME/jre/sh/java
+    else
+      JAVACMD=$JAVA_HOME/bin/java
+    fi
+  else
+    JAVACMD=java
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly."
+  echo "  We cannot execute $JAVACMD"
+  exit
+fi
+
+ANT_HOME=.
+export ANT_HOME
+
+echo ... Bootstrapping Ant Distribution
+
+if [ -d "bootstrap" ] ; then
+  rm -r bootstrap
+fi
+
+if [ -d "build" ] ; then
+  rm -r build
+fi
+
+CLASSPATH=lib/xercesImpl.jar:lib/xml-apis.jar:${CLASSPATH}
+
+DIRLIBS=lib/optional/*.jar
+for i in ${DIRLIBS}
+do
+    # if the directory is empty, then it will return the input string
+    # this is stupid, so case for it
+    if [ "$i" != "${DIRLIBS}" ] ; then
+        CLASSPATH=$CLASSPATH:"$i"
+    fi
+done
+
+TOOLS=src/main/org/apache/tools
+CLASSDIR=build/classes
+
+CLASSPATH=${CLASSDIR}:src/main:${CLASSPATH}
+
+# For Cygwin, switch to Windows format before running java
+if $cygwin; then
+  CLASSPATH=`cygpath --path --windows "$CLASSPATH"`
+fi
+
+export CLASSPATH
+
+mkdir -p build
+mkdir -p ${CLASSDIR}
+mkdir -p bin
+
+echo ... Compiling Ant Classes
+
+"${JAVAC}" $BOOTJAVAC_OPTS -d ${CLASSDIR} ${TOOLS}/bzip2/*.java ${TOOLS}/tar/*.java ${TOOLS}/zip/*.java \
+    ${TOOLS}/ant/util/regexp/RegexpMatcher.java \
+    ${TOOLS}/ant/util/regexp/RegexpMatcherFactory.java \
+    ${TOOLS}/ant/types/*.java \
+    ${TOOLS}/ant/*.java ${TOOLS}/ant/taskdefs/*.java \
+    ${TOOLS}/ant/taskdefs/compilers/*.java \
+    ${TOOLS}/ant/taskdefs/condition/*.java
+ret=$?
+if [ $ret != 0 ]; then  
+  echo ... Failed compiling Ant classes !
+  exit $ret
+fi
+
+echo ... Copying Required Files
+
+cp src/main/org/apache/tools/ant/taskdefs/defaults.properties \
+    ${CLASSDIR}/org/apache/tools/ant/taskdefs
+cp src/main/org/apache/tools/ant/types/defaults.properties \
+    ${CLASSDIR}/org/apache/tools/ant/types
+cp src/script/antRun bin
+chmod +x bin/antRun
+
+echo ... Building Ant Distribution
+
+"${JAVACMD}" -classpath "${CLASSPATH}" -Dant.home=. $ANT_OPTS org.apache.tools.ant.Main -emacs "$@" bootstrap
+ret=$?
+if [ $ret != 0 ]; then  
+  echo ... Failed Building Ant Distribution !
+  exit $ret
+fi
+
+
+echo ... Cleaning Up Build Directories
+
+rm -rf ${CLASSDIR}
+rm -rf bin
+
+echo ... Done Bootstrapping Ant Distribution
Index: /release-kits/shared/ant/build.bat
===================================================================
--- /release-kits/shared/ant/build.bat	(revision 16139)
+++ /release-kits/shared/ant/build.bat	(revision 16139)
@@ -0,0 +1,26 @@
+@echo off
+
+REM   Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
+REM   reserved.
+
+set REAL_ANT_HOME=%ANT_HOME%
+set ANT_HOME=%~dp0\bootstrap
+if exist bootstrap\lib\ant.jar if exist bootstrap\bin\ant.bat if exist bootstrap\bin\lcp.bat if exist bootstrap\bin\antRun.bat goto runAnt
+call bootstrap.bat
+if exist bootstrap\lib\ant.jar if exist bootstrap\bin\ant.bat if exist bootstrap\bin\lcp.bat if exist bootstrap\bin\antRun.bat goto runAnt
+echo Bootstrap FAILED
+goto cleanup
+
+:runAnt
+if not "%REAL_ANT_HOME%" == "" goto install_ant
+call bootstrap\bin\ant.bat -lib lib/optional %1 %2 %3 %4 %5 %6 %7 %8 %9
+goto cleanup
+
+:install_ant
+call bootstrap\bin\ant.bat -lib lib/optional -Dant.install="%REAL_ANT_HOME%" %1 %2 %3 %4 %5 %6 %7 %8 %9
+
+rem clean up
+:cleanup
+set ANT_HOME=%REAL_ANT_HOME%
+set REAL_ANT_HOME=
+
Index: /release-kits/shared/ant/build.sh
===================================================================
--- /release-kits/shared/ant/build.sh	(revision 16139)
+++ /release-kits/shared/ant/build.sh	(revision 16139)
@@ -0,0 +1,45 @@
+#!/bin/sh
+
+#   Copyright (c) 2000-2003 The Apache Software Foundation.  All rights
+#   reserved.
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  Darwin*) darwin=true
+           if [ -z "$JAVA_HOME" ] ; then
+             JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home
+           fi
+           ;;
+esac
+
+REALANTHOME=$ANT_HOME
+if [ -z "$PWD" ]; then
+    ANT_HOME=./bootstrap
+else
+    ANT_HOME="$PWD"/bootstrap
+fi
+export ANT_HOME
+
+if test ! -f bootstrap/lib/ant.jar -o  ! -x bootstrap/bin/ant -o ! -x bootstrap/bin/antRun ; then
+  /bin/sh ./bootstrap.sh
+fi
+
+if test ! -f bootstrap/lib/ant.jar -o  ! -x bootstrap/bin/ant -o ! -x bootstrap/bin/antRun ; then
+  echo Bootstrap FAILED
+  exit
+fi
+
+if [ "$REALANTHOME" != "" ] ; then
+  if $cygwin; then
+     REALANTHOME=`cygpath --windows "$REALANTHOME"`
+  fi
+  ANT_INSTALL="-Dant.install=$REALANTHOME"
+else
+  ANT_INSTALL="-emacs"
+fi
+
+bootstrap/bin/ant -lib lib/optional "$ANT_INSTALL" $*
+
Index: /release-kits/shared/ant/build.xml
===================================================================
--- /release-kits/shared/ant/build.xml	(revision 16139)
+++ /release-kits/shared/ant/build.xml	(revision 16139)
@@ -0,0 +1,1676 @@
+<?xml version="1.0"?>
+
+<!--
+  =======================================================================
+    Apache Ant own build file
+
+   Copyright (c) 2000-2005 The Apache Software Foundation.  All rights
+   reserved.
+
+  =======================================================================
+-->
+<project name="apache-ant" default="main" basedir=".">
+
+  <!-- Give user a chance to override without editing this file
+       (and without typing -D each time it compiles it) -->
+  <property file=".ant.properties"/>
+  <property file="${user.home}/.ant.properties"/>
+  <property environment="env"/>
+
+
+  <!--
+       ===================================================================
+         Set the properties that control names and versions
+       ===================================================================
+  -->
+  <property name="Name" value="Apache Ant"/>
+  <property name="name" value="ant"/>
+  <property name="version" value="1.6.5"/>
+  <property name="manifest-version" value="1.6.5"/>
+  <property name="bootstrap.jar" value="ant-bootstrap.jar"/>
+
+  <property name="ant.package" value="org/apache/tools/ant"/>
+  <property name="optional.package" value="${ant.package}/taskdefs/optional"/>
+  <property name="optional.type.package" value="${ant.package}/types/optional"/>
+  <property name="apache.resolver.type.package" value="${ant.package}/types/resolver"/>
+  <property name="util.package" value="${ant.package}/util"/>
+  <property name="regexp.package" value="${util.package}/regexp"/>
+
+  <property name="optional.jars.prefix" value="ant"/>
+
+  <!--
+       ===================================================================
+         Set the properties that control various build options
+       ===================================================================
+  -->
+  <property name="debug" value="true"/>
+  <property name="chmod.fail" value="true"/>
+  <property name="chmod.maxparallel" value="250"/>
+  <property name="deprecation" value="false"/>
+  <property name="optimize" value="true"/>
+  <property name="javac.target" value="1.2"/>
+  <property name="javac.source" value="1.2"/>
+  <property name="junit.fork" value="false"/>
+  <property name="junit.filtertrace" value="off"/>
+  <property name="junit.summary" value="no"/>
+  <property name="test.haltonfailure" value="yes" />
+  <property name="junit.forkmode" value="once"/>
+  <property name="unfiltered.files" value="**/*.gif,**/*.jpg,**/*.ico,**/*.pdf,**/*.zip"/>
+
+  <!--
+       ===================================================================
+         Set the properties related to the source tree
+       ===================================================================
+  -->
+  <property name="src.dir" value="src"/>
+  <property name="java.dir" value="${src.dir}/main"/>
+  <property name="script.dir" value="${src.dir}/script"/>
+  <property name="lib.dir" value="lib"/>
+  <property name="docs.dir" value="docs"/>
+  <property name="etc.dir" value="src/etc"/>
+  <property name="tests.dir" value="src/testcases"/>
+  <property name="tests.etc.dir" value="src/etc/testcases"/>
+  <property name="manifest" value="src/etc/manifest"/>
+
+  <!--
+       ===================================================================
+         Set the properties for the build area
+       ===================================================================
+  -->
+  <property name="build.dir" value="build"/>
+  <property name="bootstrap.dir" value="bootstrap"/>
+  <property name="build.classes" value="${build.dir}/classes"/>
+  <property name="build.lib" value="${build.dir}/lib"/>
+  <property name="build.javadocs" value="${build.dir}/javadocs"/>
+  <property name="build.tests" value="${build.dir}/testcases"/>
+  <property name="build.tests.javadocs" value="${build.dir}/javadocs.test/"/>
+  <property name="manifest.tmp" value="${build.dir}/optional.manifest"/>
+  <!-- the absolute path -->
+  <property name="build.tests.value" location="${build.tests}"/>
+
+  <!--
+       ===================================================================
+         Set the paths used in the build
+       ===================================================================
+  -->
+  <path id="classpath">
+  </path>
+
+  <path id="tests-classpath">
+    <pathelement location="${build.classes}"/>
+    <pathelement location="${build.tests}"/>
+    <!--
+        include the test source and test data dirs
+        so that we can pick resources via getResource(AsStream)
+     -->
+    <pathelement location="${tests.dir}"/>
+    <pathelement location="${tests.etc.dir}"/>
+    <path refid="classpath"/>
+  </path>
+
+  <!-- turn this path into a string which is passed to the tests -->
+  <property name="tests-classpath.value"
+            refid="tests-classpath"/>
+
+ <!--
+       ===================================================================
+         Set up properties for the distribution area
+       ===================================================================
+  -->
+  <property name="dist.name" value="apache-${name}-${version}"/>
+  <property name="dist.base" value="distribution"/>
+  <property name="dist.base.source" value="${dist.base}/source"/>
+  <property name="dist.base.binaries" value="${dist.base}/binaries"/>
+  <property name="dist.dir" value="dist"/>
+  <property name="dist.bin" value="${dist.dir}/bin"/>
+  <property name="dist.lib" value="${dist.dir}/lib"/>
+  <property name="dist.docs" value="${dist.dir}/docs"/>
+  <property name="dist.etc" value="${dist.dir}/etc"/>
+  <property name="dist.javadocs" value="${dist.dir}/docs/manual/api"/>
+
+  <property name="src.dist.dir" value="dist-src"/>
+  <property name="src.dist.src" value="${src.dist.dir}/src"/>
+  <property name="src.dist.docs" value="${src.dist.dir}/docs"/>
+  <property name="src.dist.lib" value="${src.dist.dir}/lib"/>
+
+  <property name="java-repository.dir" value="java-repository/ant/jars"/>
+
+  <!--
+       ===================================================================
+         Set up selectors to be used by javac, junit and jar to exclude
+         files that have dependencies that are not available
+       ===================================================================
+  -->
+  <!-- depends on JDK version -->
+  <selector id="needs.jdk1.3+">
+    <or>
+      <filename name="${ant.package}/taskdefs/TestProcess*"/>
+      <filename name="${optional.package}/extension/**"/>
+    </or>
+  </selector>
+
+  <selector id="needs.jdk1.4+">
+    <or>
+      <filename name="${regexp.package}/Jdk14Regexp*"/>
+      <filename name="${ant.package}/types/AssertionsTest.java"/>
+    </or>
+  </selector>
+
+  <selector id="needs.sun.uue">
+    <filename name="${ant.package}/taskdefs/email/UUMailer*"/>
+  </selector>
+
+  <selector id="needs.sun.b64">
+    <or>
+      <filename name="${optional.package}/splash/SplashTask*"/>
+      <filename name="${optional.package}/splash/SplashScreenTest.java"/>
+    </or>
+  </selector>
+
+  <!-- depends on external libraries -->
+  <selector id="needs.trax">
+    <or>
+      <filename name="${optional.package}/junit/Xalan2Executor*"/>
+      <filename name="${optional.package}/TraXLiaison*"/>
+      <filename name="${optional.package}/sitraka/**"/>
+      <filename name="${optional.package}/metamata/MMetrics*"/>
+      <filename name="${optional.package}/XsltTest*"/>
+      <filename name="${ant.package}/types/XMLCatalogBuildFileTest*"/>
+    </or>
+  </selector>
+
+  <selector id="needs.xalan1">
+    <or>
+      <filename name="${optional.package}/XalanLiaison*"/>
+      <filename name="${optional.package}/junit/Xalan1Executor*"/>
+    </or>
+  </selector>
+
+  <selector id="needs.xslp">
+    <filename name="${optional.package}/XslpLiaison*"/>
+  </selector>
+
+  <selector id="needs.apache-resolver">
+    <filename name="${apache.resolver.type.package}/**"/>
+  </selector>
+
+  <selector id="needs.junit">
+    <filename name="${optional.package}/junit/**"/>
+  </selector>
+
+  <selector id="needs.apache-regexp">
+    <filename name="${regexp.package}/JakartaRegexp*"/>
+  </selector>
+
+  <selector id="needs.apache-oro">
+    <or>
+      <filename name="${regexp.package}/JakartaOro*"/>
+      <filename name="${optional.package}/perforce/*"/>
+      <filename name="${optional.package}/metamata/MAudit*"/>
+    </or>
+  </selector>
+
+  <selector id="needs.apache-bcel">
+    <or>
+      <filename name="${ant.package}/filters/util/JavaClassHelper*"/>
+      <filename name="${util.package}/depend/bcel/*"/>
+      <filename name="${optional.type.package}/depend/ClassFileSetTest*"/>
+    </or>
+  </selector>
+
+  <selector id="needs.apache-log4j">
+    <filename name="${ant.package}/listener/Log4jListener*"/>
+  </selector>
+
+  <selector id="needs.commons-logging">
+    <filename name="${ant.package}/listener/CommonsLoggingListener*"/>
+  </selector>
+
+  <selector id="needs.apache-bsf">
+    <or>
+      <filename name="${optional.package}/Script*"/>
+      <filename name="${optional.package}/script/**/*"/>
+      <filename name="${optional.type.package}/Script*"/>
+      <filename name="${util.package}/Script*"/>
+    </or>
+  </selector>
+
+  <selector id="needs.stylebook">
+    <filename name="${optional.package}/StyleBook*"/>
+  </selector>
+
+  <selector id="needs.javamail">
+    <or>
+      <filename name="${ant.package}/taskdefs/email/MimeMailer*"/>
+    </or>
+  </selector>
+
+  <selector id="needs.icontract">
+    <filename name="${optional.package}/IContract*"/>
+  </selector>
+
+  <selector id="needs.netrexx">
+    <filename name="${optional.package}/NetRexxC*"/>
+  </selector>
+
+  <selector id="needs.weblogic.ejbc">
+    <filename name="${optional.package}/ejb/Ejbc*"/>
+  </selector>
+
+  <selector id="needs.weblogic.ddcreator">
+    <filename name="${optional.package}/ejb/DDCreator*"/>
+  </selector>
+
+  <selector id="needs.weblogic.server">
+    <or>
+      <filename name="${optional.package}/ejb/WLRun*"/>
+      <filename name="${optional.package}/ejb/WLStop*"/>
+    </or>
+  </selector>
+
+  <selector id="needs.commons-net">
+    <or>
+      <filename name="${optional.package}/net/FTP*"/>
+      <filename name="${optional.package}/net/RExec*"/>
+      <filename name="${optional.package}/net/TelnetTask*"/>
+    </or>
+  </selector>
+
+  <selector id="needs.starteam">
+    <or>
+      <filename name="${optional.package}/scm/AntStarTeam*"/>
+      <filename name="${optional.package}/starteam/*"/>
+    </or>
+  </selector>
+
+  <selector id="needs.vaj">
+    <filename name="${optional.package}/ide/VAJ*"/>
+  </selector>
+
+  <selector id="needs.antlr">
+    <filename name="${optional.package}/ANTLR*"/>
+  </selector>
+
+  <selector id="needs.jmf">
+    <filename name="${optional.package}/sound/*"/>
+  </selector>
+
+  <selector id="needs.jai">
+    <or>
+      <filename name="${optional.package}/image/*"/>
+      <filename name="${ant.package}/types/optional/image/*"/>
+    </or>
+  </selector>
+
+  <selector id="needs.jdepend">
+    <filename name="${optional.package}/jdepend/*"/>
+  </selector>
+
+  <selector id="needs.swing">
+    <filename name="${optional.package}/splash/*"/>
+  </selector>
+
+  <selector id="needs.jsch">
+    <filename name="${optional.package}/ssh/*"/>
+  </selector>
+
+  <selector id="ant.launcher">
+    <filename name="${ant.package}/launch/**/*"/>
+  </selector>
+
+  <patternset id="onlinetests">
+    <exclude name="**/GetTest.java" if="offline"/>
+    <exclude name="**/SignJarTest.java" if="offline"/>
+  </patternset>
+
+  <patternset id="teststhatfail">
+    <!-- Property 'run.failing.tests' should force Ant to run these tests. -->
+    <!-- Because the whole patternset can not be excluded, you have to add -->
+    <!-- an unless-attribute on each exclude-element.                      -->
+    <exclude unless="run.failing.tests" name="${optional.package}/BeanShellScriptTest.java"/>
+    <exclude unless="run.failing.tests" name="${optional.package}/jdepend/JDependTest.java"/>
+  </patternset>
+
+  <!--
+       ===================================================================
+         Set up a patternsets that matches the parts of our JUnit testsuite
+         that may be useful for task developers.
+       ===================================================================
+  -->
+  <patternset id="useful.tests">
+    <include name="${ant.package}/BuildFileTest*"/>
+    <include name="${regexp.package}/RegexpMatcherTest*"/>
+    <include name="${regexp.package}/RegexpTest*"/>
+    <include name="${optional.package}/AbstractXSLTLiaisonTest*"/>
+    <include name="${ant.package}/types/AbstractFileSetTest*"/>
+  </patternset>
+
+  <!--
+       ===================================================================
+         Set up a patternsets that matches the parts of our site that
+         should not be part of the distribution.
+       ===================================================================
+  -->
+  <patternset id="site.excludes">
+    <exclude name="bindownload.html"/>
+    <exclude name="srcdownload.html"/>
+    <exclude name="*.cgi"/>
+  </patternset>
+
+  <!--
+       ===================================================================
+         Check to see what optional dependencies are available
+       ===================================================================
+  -->
+  <target name="check_for_optional_packages">
+    <available property="jdk1.3+" classname="java.lang.StrictMath"/>
+    <available property="jdk1.4+" classname="java.lang.CharSequence"/>
+    <available property="jdk1.5+" classname="java.lang.Readable"/>
+    <available property="kaffe" classname="kaffe.util.NotImplemented"/>
+    <available property="bsf.present"
+               classname="org.apache.bsf.BSFManager"
+               classpathref="classpath"/>
+    <available property="netrexx.present"
+               classname="netrexx.lang.Rexx"
+               classpathref="classpath"/>
+    <available property="trax.present"
+               classname="javax.xml.transform.Transformer"
+               classpathref="classpath"/>
+    <condition property="trax.impl.present">
+      <or>
+        <and>
+          <isset property="javax.xml.transform.TransformerFactory"/>
+          <available classname="${javax.xml.transform.TransformerFactory}"
+                     classpathref="classpath"/>
+        </and>
+        <available resource="META-INF/services/javax.xml.transform.TransformerFactory"/>
+      </or>
+    </condition>
+    <available property="xslp.present"
+               classname="com.kvisco.xsl.XSLProcessor"
+               classpathref="classpath"/>
+    <available property="apache.resolver.present"
+               classname="org.apache.xml.resolver.tools.CatalogResolver"
+               classpathref="classpath" />
+    <available property="xalan.present"
+               classname="org.apache.xalan.xslt.XSLTProcessorFactory"
+               classpathref="classpath"/>
+    <available property="xalan2.present"
+               classname="org.apache.xalan.transformer.TransformerImpl"
+               classpathref="classpath"/>
+    <available property="ejb.ejbc.present"
+               classname="weblogic.ejbc"
+               classpathref="classpath"/>
+    <available property="ejb.DDCreator.present"
+               classname="weblogic.ejb.utils.DDCreator"
+               classpathref="classpath"/>
+    <available property="ejb.wls.present"
+               classname="weblogic.Server"
+               classpathref="classpath"/>
+    <available property="junit.present"
+               classname="junit.framework.TestCase"
+               classpathref="classpath"/>
+    <available property="commons.net.present"
+               classname="org.apache.commons.net.ftp.FTPClient"
+               classpathref="classpath"/>
+    <available property="starteam.present"
+               classname="com.starbase.util.Platform"
+               classpathref="classpath"/>
+    <available property="antlr.present"
+               classname="antlr.Tool"
+               classpathref="classpath"/>
+    <available property="vaj.present"
+               classname="com.ibm.ivj.util.base.Workspace"
+               classpathref="classpath"/>
+    <available property="stylebook.present"
+               classname="org.apache.stylebook.Engine"
+               classpathref="classpath"/>
+    <available property="apache.regexp.present"
+               classname="org.apache.regexp.RE"
+               classpathref="classpath"/>
+    <available property="apache.oro.present"
+               classname="org.apache.oro.text.regex.Perl5Matcher"
+               classpathref="classpath"/>
+    <available property="jmf.present"
+               classname="javax.sound.sampled.Clip"
+               classpathref="classpath"/>
+    <available property="jai.present"
+               classname="javax.media.jai.JAI"
+               classpathref="classpath"/>
+    <available property="icontract.present"
+               classname="com.reliablesystems.iContract.IContracted"
+               classpathref="classpath"/>
+    <available property="jdepend.present"
+               classname="jdepend.framework.JDepend"
+               classpathref="classpath"/>
+    <available property="log4j.present"
+               classname="org.apache.log4j.Logger"
+               classpathref="classpath"/>
+    <available property="commons.logging.present"
+               classname="org.apache.commons.logging.LogFactory"
+               classpathref="classpath"/>
+    <available property="xalan.envcheck"
+               classname="org.apache.xalan.xslt.EnvironmentCheck"
+               classpathref="classpath"/>
+    <available property="which.present"
+               classname="org.apache.env.Which"
+               classpathref="classpath"/>
+
+    <available property="xerces.present"
+               classname="org.apache.xerces.parsers.SAXParser"
+               classpathref="classpath"/>
+    <available property="bcel.present"
+               classname="org.apache.bcel.Constants"
+               classpathref="classpath"/>
+    <available property="sunuue.present"
+               classname="sun.misc.UUEncoder"
+               classpathref="classpath"/>
+
+    <condition property="javamail.complete">
+      <and>
+        <available classname="javax.activation.DataHandler"
+                   classpathref="classpath"/>
+        <available classname="javax.mail.Transport"
+                   classpathref="classpath"/>
+      </and>
+    </condition>
+
+    <condition property="some.regexp.support">
+      <or>
+        <isset property="jdk1.4+"/>
+        <isset property="apache.regexp.present"/>
+        <isset property="apache.oro.present"/>
+      </or>
+    </condition>
+
+    <condition property="tests.and.ant.share.classloader">
+      <or>
+        <equals arg1="${junit.fork}" arg2="true"/>
+        <equals arg1="${build.sysclasspath}" arg2="only"/>
+      </or>
+    </condition>
+
+    <condition property="sun.tools.present">
+      <and>
+        <available classname="sun.tools.native2ascii.Main"/>
+        <available classname="com.sun.tools.javah.Main"/>
+      </and>
+    </condition>
+
+    <available property="base64.present" classname="sun.misc.BASE64Encoder"/>
+
+    <property name="build.tests.resolved" location="${build.tests}"/>
+    <condition property="tests.are.on.system.classpath">
+      <or>
+        <!-- relative paths in CLASSPATH -->
+        <contains string="${java.class.path}"
+                  substring="${build.tests}"/>
+        <!-- absolute paths in CLASSPATH -->
+        <contains string="${java.class.path}"
+                  substring="${build.tests.resolved}"/>
+        <istrue value="${junit.fork}"/>
+      </or>
+    </condition>
+
+    <condition property="jasper.present">
+      <and>
+        <available classname="org.apache.jasper.compiler.Compiler"/>
+        <available classname="org.apache.jasper.JasperException"/>
+      </and>
+    </condition>
+
+    <condition property="swing.present">
+      <or>
+        <not>
+          <isset property="kaffe"/>
+        </not>
+        <available classname="javax.swing.ImageIcon"
+                   classpathref="classpath"/>
+      </or>
+    </condition>
+    <condition property="wsdl.found">
+      <or>
+        <available file="wsdl"     filepath="${env.PATH}"/>
+        <available file="wsdl.exe" filepath="${env.PATH}"/>
+        <available file="wsdl.exe" filepath="${env.Path}"/>
+      </or>
+    </condition>
+    <echo level="verbose"> wsdl.found=${wsdl.found}</echo>
+    <condition property="csc.found">
+      <or>
+        <available file="csc"     filepath="${env.PATH}"/>
+        <available file="csc.exe" filepath="${env.PATH}"/>
+        <available file="csc.exe" filepath="${env.Path}"/>
+      </or>
+    </condition>
+    <echo level="verbose"> csc.found=${csc.found}</echo>
+    <condition property="dotnetapps.found">
+      <and>
+        <isset property="csc.found"/>
+        <isset property="wsdl.found"/>
+      </and>
+    </condition>
+    <echo level="verbose"> dotnetapps.found=${dotnetapps.found}</echo>
+
+    <available property="rhino.present"
+               classname="org.mozilla.javascript.Scriptable"
+               classpathref="classpath"/>
+    <available property="beanshell.present"
+               classname="bsh.StringUtil"
+               classpathref="classpath"/>
+    <available property="xerces1.present"
+               classname="org.apache.xerces.framework.XMLParser"
+               classpathref="classpath"/>
+    <available property="jsch.present"
+               classname="com.jcraft.jsch.Session"
+               classpathref="classpath"/>
+  </target>
+
+
+  <!--
+       ===================================================================
+         Prepare the build
+       ===================================================================
+  -->
+  <target name="prepare">
+    <tstamp>
+      <format property="year" pattern="yyyy"/>
+    </tstamp>
+    <filterchain id="ant.filters">
+      <replacetokens>
+        <token key="VERSION" value="${version}"/>
+        <token key="DATE" value="${TODAY}"/>
+        <token key="TIME" value="${TSTAMP}"/>
+      </replacetokens>
+    </filterchain>
+  </target>
+
+  <!--
+       ===================================================================
+         Build the code
+       ===================================================================
+  -->
+  <target name="build"
+          depends="prepare, check_for_optional_packages"
+          description="--> compiles the source code">
+    <mkdir dir="${build.dir}"/>
+    <mkdir dir="${build.classes}"/>
+    <mkdir dir="${build.lib}"/>
+
+    <javac srcdir="${java.dir}"
+           destdir="${build.classes}"
+           debug="${debug}"
+           deprecation="${deprecation}"
+           target="${javac.target}"
+           source="${javac.source}"
+           optimize="${optimize}" >
+      <classpath refid="classpath"/>
+
+      <selector id="conditional-patterns">
+        <not>
+          <or>
+            <selector refid="needs.jdk1.3+" unless="jdk1.3+"/>
+            <selector refid="needs.jdk1.4+" unless="jdk1.4+"/>
+            <selector refid="needs.sun.uue" unless="sunuue.present"/>
+            <selector refid="needs.sun.b64" unless="base64.present"/>
+
+            <selector refid="needs.trax" unless="trax.present"/>
+            <selector refid="needs.xalan1" unless="xalan.present"/>
+            <selector refid="needs.xslp" unless="xslp.present"/>
+            <selector refid="needs.apache-resolver" unless="apache.resolver.present"/>
+            <selector refid="needs.junit" unless="junit.present"/>
+            <selector refid="needs.apache-regexp"
+                      unless="apache.regexp.present"/>
+            <selector refid="needs.apache-oro" unless="apache.oro.present"/>
+            <selector refid="needs.apache-bcel" unless="bcel.present"/>
+            <selector refid="needs.apache-log4j" unless="log4j.present"/>
+            <selector refid="needs.commons-logging"
+                      unless="commons.logging.present"/>
+            <selector refid="needs.apache-bsf" unless="bsf.present"/>
+            <selector refid="needs.stylebook" unless="stylebook.present"/>
+            <selector refid="needs.javamail" unless="javamail.complete"/>
+            <selector refid="needs.icontract" unless="icontract.present"/>
+            <selector refid="needs.netrexx" unless="netrexx.present"/>
+            <selector refid="needs.weblogic.ejbc" unless="ejb.ejbc.present"/>
+            <selector refid="needs.weblogic.ddcreator"
+                      unless="ejb.DDCreator.present"/>
+            <selector refid="needs.weblogic.server" unless="ejb.wls.present"/>
+            <selector refid="needs.commons-net" unless="commons.net.present"/>
+            <selector refid="needs.starteam" unless="starteam.present"/>
+            <selector refid="needs.vaj" unless="vaj.present"/>
+            <selector refid="needs.antlr" unless="antlr.present"/>
+            <selector refid="needs.jmf" unless="jmf.present"/>
+            <selector refid="needs.jai" unless="jai.present"/>
+            <selector refid="needs.jdepend" unless="jdepend.present"/>
+            <selector refid="needs.swing"  unless="swing.present"/>
+            <selector refid="needs.jsch" unless="jsch.present"/>
+          </or>
+        </not>
+      </selector>
+    </javac>
+
+    <copy todir="${build.classes}">
+      <fileset dir="${java.dir}">
+        <include name="**/*.properties"/>
+        <include name="**/*.dtd"/>
+      </fileset>
+    </copy>
+
+    <copy todir="${build.classes}"
+          overwrite="true" encoding="UTF-8">
+      <fileset dir="${java.dir}">
+        <include name="**/version.txt"/>
+        <include name="**/defaultManifest.mf"/>
+      </fileset>
+      <filterchain refid="ant.filters"/>
+    </copy>
+
+    <copy todir="${build.classes}/${optional.package}/junit/xsl">
+       <fileset dir="${etc.dir}">
+         <include name="junit-frames.xsl"/>
+         <include name="junit-noframes.xsl"/>
+       </fileset>
+    </copy>
+  </target>
+
+  <!--
+       ===================================================================
+         Create the all of the Apache Ant jars
+       ===================================================================
+  -->
+  <target name="jars"
+          depends="build"
+          description="--> creates the Apache Ant jars">
+
+    <copy todir="${build.dir}">
+      <fileset dir=".">
+        <include name="LICENSE"/>
+        <include name="LICENSE.xerces"/>
+        <include name="LICENSE.dom"/>
+        <include name="LICENSE.sax"/>
+        <include name="NOTICE"/>
+      </fileset>
+      <mapper type="glob" from="*" to="*.txt"/>
+    </copy>
+
+    <copy file="${manifest}" tofile="${manifest.tmp}"/>
+    <manifest file="${manifest.tmp}">
+      <section name="${optional.package}/">
+        <attribute name="Extension-name"
+                   value="org.apache.tools.ant"/>
+        <attribute name="Specification-Title"
+                   value="Apache Ant"/>
+        <attribute name="Specification-Version"
+                   value="${manifest-version}"/>
+        <attribute name="Specification-Vendor"
+                   value="Apache Software Foundation"/>
+        <attribute name="Implementation-Title"
+                   value="org.apache.tools.ant"/>
+        <attribute name="Implementation-Version"
+                   value="${manifest-version}"/>
+        <attribute name="Implementation-Vendor"
+                   value="Apache Software Foundation"/>
+      </section>
+    </manifest>
+
+    <jar destfile="${build.lib}/${name}-launcher.jar"
+         basedir="${build.classes}">
+      <selector refid="ant.launcher"/>
+      <manifest>
+        <attribute name="Main-Class" value="org.apache.tools.ant.launch.Launcher"/>
+      </manifest>
+    </jar>
+
+    <jar destfile="${build.lib}/${name}.jar"
+         basedir="${build.classes}"
+         manifest="${manifest}">
+      <not>
+        <selector id="non-core">
+          <or>
+            <filename name="${optional.package}/**"/>
+            <filename name="${optional.type.package}/**"/>
+            <filename name="${util.package}/depend/**"/>
+            <filename name="${util.package}/optional/**"/>
+            <selector refid="needs.apache-log4j"/>
+            <selector refid="needs.commons-logging"/>
+            <selector refid="needs.apache-regexp"/>
+            <selector refid="needs.apache-oro"/>
+            <selector refid="needs.jdk1.4+"/>
+            <selector refid="needs.javamail"/>
+            <selector refid="ant.launcher"/>
+          </or>
+        </selector>
+      </not>
+      <metainf dir="${build.dir}">
+        <include name="LICENSE.txt"/>
+        <include name="NOTICE.txt"/>
+      </metainf>
+
+      <manifest>
+        <section name="${ant.package}/">
+          <attribute name="Extension-name"
+                     value="org.apache.tools.ant"/>
+          <attribute name="Specification-Title"
+                     value="Apache Ant"/>
+          <attribute name="Specification-Version"
+                     value="${manifest-version}"/>
+          <attribute name="Specification-Vendor"
+                     value="Apache Software Foundation"/>
+          <attribute name="Implementation-Title"
+                     value="org.apache.tools.ant"/>
+          <attribute name="Implementation-Version"
+                     value="${manifest-version}"/>
+          <attribute name="Implementation-Vendor"
+                     value="Apache Software Foundation"/>
+        </section>
+      </manifest>
+
+      <fileset dir="${docs.dir}">
+        <include name="images/ant_logo_large.gif"/>
+      </fileset>
+    </jar>
+
+    <jar destfile="${build.lib}/${bootstrap.jar}"
+         basedir="${build.classes}"
+         manifest="${manifest}">
+      <include name="${ant.package}/Main.class"/>
+      <metainf dir="${build.dir}">
+        <include name="LICENSE.txt"/>
+        <include name="NOTICE.txt"/>
+      </metainf>
+      <manifest>
+        <attribute name="Class-Path"
+                   value="ant.jar xml-apis.jar xercesImpl.jar xalan.jar"/>
+      </manifest>
+    </jar>
+
+    <jar destfile="${build.lib}/ant-nodeps.jar"
+         basedir="${build.classes}"
+         manifest="${manifest.tmp}">
+      <and>
+        <selector refid="non-core"/>
+        <not>
+          <or>
+            <selector refid="needs.trax"/>
+            <selector refid="needs.xalan1"/>
+            <selector refid="needs.xslp"/>
+            <selector refid="needs.apache-resolver"/>
+            <selector refid="needs.junit"/>
+            <selector refid="needs.apache-regexp"/>
+            <selector refid="needs.apache-oro"/>
+            <selector refid="needs.apache-bcel"/>
+            <selector refid="needs.apache-log4j"/>
+            <selector refid="needs.commons-logging"/>
+            <selector refid="needs.apache-bsf"/>
+            <selector refid="needs.stylebook"/>
+            <selector refid="needs.javamail"/>
+            <selector refid="needs.icontract"/>
+            <selector refid="needs.netrexx"/>
+            <selector refid="needs.weblogic.ejbc"/>
+            <selector refid="needs.weblogic.ddcreator"/>
+            <selector refid="needs.weblogic.server"/>
+            <selector refid="needs.commons-net"/>
+            <selector refid="needs.starteam"/>
+            <selector refid="needs.vaj"/>
+            <selector refid="needs.antlr"/>
+            <selector refid="needs.jmf"/>
+            <selector refid="needs.jai"/>
+            <selector refid="needs.jdepend"/>
+            <selector refid="needs.swing"/>
+            <selector refid="needs.jsch"/>
+          </or>
+        </not>
+      </and>
+      <metainf dir="${build.dir}">
+        <include name="LICENSE.txt"/>
+        <include name="NOTICE.txt"/>
+      </metainf>
+    </jar>
+
+    <macrodef name="optional-jar">
+      <attribute name="dep"/>
+      <sequential>
+        <jar destfile="${build.lib}/${optional.jars.prefix}-@{dep}.jar"
+          basedir="${build.classes}"
+          manifest="${manifest.tmp}">
+          <selector refid="needs.@{dep}"/>
+        </jar>
+      </sequential>
+    </macrodef>
+
+    <optional-jar dep="trax"/>
+    <optional-jar dep="xalan1"/>
+    <optional-jar dep="xslp"/>
+    <optional-jar dep="apache-resolver"/>
+    <optional-jar dep="junit"/>
+    <optional-jar dep="apache-regexp"/>
+    <optional-jar dep="apache-oro"/>
+    <optional-jar dep="apache-bcel"/>
+    <optional-jar dep="apache-log4j"/>
+    <optional-jar dep="commons-logging"/>
+    <optional-jar dep="apache-bsf"/>
+    <optional-jar dep="stylebook"/>
+    <optional-jar dep="javamail"/>
+    <optional-jar dep="icontract"/>
+    <optional-jar dep="netrexx"/>
+    <optional-jar dep="commons-net"/>
+    <optional-jar dep="starteam"/>
+    <optional-jar dep="vaj"/>
+    <optional-jar dep="antlr"/>
+    <optional-jar dep="jmf"/>
+    <optional-jar dep="jai"/>
+    <optional-jar dep="swing"/>
+    <optional-jar dep="jsch"/>
+    <optional-jar dep="jdepend"/>
+
+    <jar destfile="${build.lib}/${optional.jars.prefix}-weblogic.jar"
+         basedir="${build.classes}"
+         manifest="${manifest.tmp}">
+      <or>
+        <selector refid="needs.weblogic.ejbc"/>
+        <selector refid="needs.weblogic.ddcreator" />
+        <selector refid="needs.weblogic.server"/>
+      </or>
+    </jar>
+
+  </target>
+
+  <!--   Creates jar of test utility classes -->
+  <target name="test-jar"
+          depends="compile-tests"
+          description="--> creates the Apache Ant Test Utilties jar">
+
+    <fail unless="junit.present">
+      We cannot build the test jar unless JUnit is present,
+      as JUnit is needed to compile the test classes.
+    </fail>
+    <jar destfile="${build.lib}/${name}-testutil.jar"
+         basedir="${build.tests}">
+      <patternset refid="useful.tests"/>
+    </jar>
+  </target>
+
+  <!--
+       ===================================================================
+         Create the essential distribution that can run Apache Ant
+       ===================================================================
+  -->
+  <target name="dist-lite"
+          depends="jars"
+          description="--> creates a minimum distribution to run Apache Ant">
+
+    <mkdir dir="${dist.dir}"/>
+    <mkdir dir="${dist.bin}"/>
+    <mkdir dir="${dist.lib}"/>
+
+    <copy todir="${dist.lib}">
+      <fileset dir="${build.lib}">
+        <exclude name="${bootstrap.jar}"/>
+      </fileset>
+    </copy>
+
+    <copy todir="${dist.lib}">
+      <fileset dir="${lib.dir}">
+        <include name="*.jar"/>
+        <include name="*.zip"/>
+      </fileset>
+    </copy>
+
+    <copy todir="${dist.bin}">
+      <fileset dir="${script.dir}/"/>
+    </copy>
+
+    <fixcrlf srcdir="${dist.bin}" eol="dos" includes="*.bat,*.cmd"/>
+    <fixcrlf srcdir="${dist.bin}" eol="unix">
+      <include name="ant"/>
+      <include name="antRun"/>
+      <include name="*.pl"/>
+    </fixcrlf>
+
+    <chmod perm="ugo+rx" dir="${dist.dir}" type="dir" includes="**"
+           failonerror="${chmod.fail}"/>
+    <chmod perm="ugo+r" dir="${dist.dir}" type="file" includes="**"
+           failonerror="${chmod.fail}" maxparallel="${chmod.maxparallel}"/>
+    <chmod perm="ugo+x" type="file" failonerror="${chmod.fail}">
+       <fileset dir="${dist.bin}">
+         <include name="**/ant"/>
+         <include name="**/antRun"/>
+         <include name="**/*.pl"/>
+         <include name="**/*.py"/>
+       </fileset>
+    </chmod>
+
+  </target>
+
+ <!--
+       ===================================================================
+         Create the complete distribution
+       ===================================================================
+  -->
+  <target name="dist" description="--> creates a complete distribution">
+    <antcall inheritAll="false" target="internal_dist">
+      <param name="dist.dir" value="${dist.name}"/>
+    </antcall>
+  </target>
+
+  <target name="dist_javadocs" depends="javadocs">
+    <mkdir dir="${dist.javadocs}"/>
+    <copy todir="${dist.javadocs}" overwrite="true">
+      <fileset dir="${build.javadocs}"/>
+    </copy>
+  </target>
+
+
+  <target name="internal_dist" depends="dist-lite,dist_javadocs">
+    <mkdir dir="${dist.docs}"/>
+    <mkdir dir="${dist.etc}"/>
+
+    <copy todir="${dist.lib}" file="${lib.dir}/README"/>
+
+    <copy todir="${dist.docs}">
+      <fileset dir="${docs.dir}" excludes="${unfiltered.files}">
+        <patternset refid="site.excludes"/>
+      </fileset>
+      <filterchain refid="ant.filters"/>
+    </copy>
+
+    <copy todir="${dist.docs}" filtering="false">
+      <fileset dir="${docs.dir}" includes="${unfiltered.files}">
+        <patternset refid="site.excludes"/>
+      </fileset>
+    </copy>
+
+    <copy todir="${dist.dir}">
+      <fileset dir=".">
+        <include name="README"/>
+        <include name="INSTALL"/>
+        <include name="LICENSE"/>
+        <include name="LICENSE.xerces"/>
+        <include name="LICENSE.dom"/>
+        <include name="LICENSE.sax"/>
+        <include name="NOTICE"/>
+        <include name="TODO"/>
+        <include name="WHATSNEW"/>
+        <include name="KEYS"/>
+        <include name="welcome.html"/>
+      </fileset>
+    </copy>
+
+    <chmod perm="ugo+rx" dir="${dist.dir}" type="dir" includes="**"
+           failonerror="${chmod.fail}"/>
+    <chmod perm="ugo+r" dir="${dist.dir}" type="file" includes="**"
+           failonerror="${chmod.fail}" maxparallel="${chmod.maxparallel}"/>
+    <chmod perm="ugo+x" type="file" failonerror="${chmod.fail}">
+       <fileset dir="${dist.bin}">
+         <include name="**/ant"/>
+         <include name="**/antRun"/>
+         <include name="**/*.pl"/>
+         <include name="**/*.py"/>
+       </fileset>
+    </chmod>
+
+    <!-- publish some useful stylesheets -->
+    <copy todir="${dist.etc}">
+        <fileset dir="${etc.dir}">
+            <include name="junit-frames.xsl"/>
+            <include name="junit-noframes.xsl"/>
+            <include name="junit-frames-xalan1.xsl"/>
+            <include name="coverage-frames.xsl"/>
+            <include name="maudit-frames.xsl"/>
+            <include name="mmetrics-frames.xsl"/>
+            <include name="changelog.xsl"/>
+            <include name="jdepend.xsl"/>
+            <include name="jdepend-frames.xsl"/>
+            <include name="checkstyle/*.xsl"/>
+            <include name="log.xsl"/>
+            <include name="tagdiff.xsl"/>
+        </fileset>
+      <fileset dir="${build.lib}">
+        <include name="${bootstrap.jar}"/>
+      </fileset>
+    </copy>
+
+  </target>
+
+
+  <!--
+       ===================================================================
+         Target to create bootstrap build
+       ===================================================================
+  -->
+  <target name="bootstrap" description="--> creates a bootstrap build">
+    <antcall inheritAll="false" target="dist-lite">
+      <param name="dist.dir" value="${bootstrap.dir}"/>
+    </antcall>
+  </target>
+
+
+  <!--
+       ===================================================================
+         Create the source distribution
+       ===================================================================
+  -->
+  <target name="src-dist"
+          description="--> creates a source distribution">
+
+    <mkdir dir="${src.dist.dir}"/>
+
+    <copy todir="${src.dist.lib}">
+      <fileset dir="${lib.dir}">
+        <include name="*.jar"/>
+        <include name="*.zip"/>
+        <include name="README"/>
+      </fileset>
+    </copy>
+
+    <copy todir="${src.dist.src}">
+      <fileset dir="${src.dir}"/>
+    </copy>
+
+    <copy todir="${src.dist.docs}">
+      <fileset dir="${docs.dir}">
+        <exclude name="manual/api/**"/>
+        <patternset refid="site.excludes"/>
+      </fileset>
+    </copy>
+
+    <copy todir="${src.dist.dir}">
+      <fileset dir=".">
+        <include name="README"/>
+        <include name="INSTALL"/>
+        <include name="LICENSE"/>
+        <include name="LICENSE.xerces"/>
+        <include name="LICENSE.dom"/>
+        <include name="LICENSE.sax"/>
+        <include name="NOTICE"/>
+        <include name="TODO"/>
+        <include name="WHATSNEW"/>
+        <include name="KEYS"/>
+        <include name="build.bat"/>
+        <include name="build.sh"/>
+        <include name="bootstrap.bat"/>
+        <include name="bootstrap.sh"/>
+        <include name="build.xml"/>
+        <include name="welcome.html"/>
+      </fileset>
+    </copy>
+
+    <fixcrlf srcdir="${src.dist.dir}" eol="dos" includes="*.bat,*.cmd"/>
+    <fixcrlf srcdir="${src.dist.dir}" eol="unix">
+      <include name="**/*.sh"/>
+      <include name="**/*.pl"/>
+      <include name="**/ant"/>
+      <include name="**/antRun"/>
+    </fixcrlf>
+    <fixcrlf srcdir="${src.dist.dir}">
+      <include name="**/*.java"/>
+      <exclude name="src/etc/testcases/taskdefs/fixcrlf/expected/Junk?.java"/>
+      <exclude name="src/etc/testcases/taskdefs/fixcrlf/input/Junk?.java"/>
+    </fixcrlf>
+
+    <chmod perm="ugo+x" dir="${src.dist.dir}" type="dir"
+           failonerror="${chmod.fail}"/>
+    <chmod perm="ugo+r" dir="${src.dist.dir}" failonerror="${chmod.fail}"/>
+    <chmod perm="ugo+x" failonerror="${chmod.fail}">
+       <fileset dir="${src.dist.dir}">
+         <include name="**/.sh"/>
+         <include name="**/.pl"/>
+         <include name="**/.py"/>
+         <include name="**/ant"/>
+         <include name="**/antRun"/>
+       </fileset>
+    </chmod>
+
+  </target>
+
+  <!--
+       ===================================================================
+         Create the binary distribution
+       ===================================================================
+  -->
+  <target name="main_distribution"
+          description="--> creates the zip and tar distributions">
+    <delete dir="${dist.name}"/>
+    <delete dir="${java-repository.dir}"/>
+    <mkdir dir="${dist.base}"/>
+    <mkdir dir="${dist.base.source}"/>
+    <mkdir dir="${dist.base.binaries}"/>
+    <mkdir dir="${java-repository.dir}"/>
+
+    <antcall inheritAll="false" target="internal_dist">
+      <param name="dist.dir" value="${dist.name}"/>
+    </antcall>
+    <zip destfile="${dist.base.binaries}/${dist.name}-bin.zip">
+      <zipfileset dir="${dist.name}/.." filemode="755">
+        <include name="${dist.name}/bin/ant"/>
+        <include name="${dist.name}/bin/antRun"/>
+        <include name="${dist.name}/bin/*.pl"/>
+        <include name="${dist.name}/bin/*.py"/>
+      </zipfileset>
+      <fileset dir="${dist.name}/..">
+        <include name="${dist.name}/**"/>
+        <exclude name="${dist.name}/bin/ant"/>
+        <exclude name="${dist.name}/bin/antRun"/>
+        <exclude name="${dist.name}/bin/*.pl"/>
+        <exclude name="${dist.name}/bin/*.py"/>
+      </fileset>
+    </zip>
+    <tar longfile="gnu"
+         destfile="${dist.base.binaries}/${dist.name}-bin.tar">
+      <tarfileset dir="${dist.name}/.." mode="755" username="ant" group="ant">
+        <include name="${dist.name}/bin/ant"/>
+        <include name="${dist.name}/bin/antRun"/>
+        <include name="${dist.name}/bin/*.pl"/>
+        <include name="${dist.name}/bin/*.py"/>
+      </tarfileset>
+      <tarfileset dir="${dist.name}/.." username="ant" group="ant">
+        <include name="${dist.name}/**"/>
+        <exclude name="${dist.name}/bin/ant"/>
+        <exclude name="${dist.name}/bin/antRun"/>
+        <exclude name="${dist.name}/bin/*.pl"/>
+        <exclude name="${dist.name}/bin/*.py"/>
+      </tarfileset>
+    </tar>
+    <gzip destfile="${dist.base.binaries}/${dist.name}-bin.tar.gz"
+          src="${dist.base.binaries}/${dist.name}-bin.tar"/>
+    <bzip2 destfile="${dist.base.binaries}/${dist.name}-bin.tar.bz2"
+           src="${dist.base.binaries}/${dist.name}-bin.tar"/>
+    <delete file="${dist.base.binaries}/${dist.name}-bin.tar"/>
+
+    <copy todir="${java-repository.dir}">
+      <fileset dir="${dist.name}/lib">
+        <include name="ant*.jar"/>
+      </fileset>
+      <mapper type="glob" from="*.jar" to="*-${version}.jar"/>
+    </copy>
+    <checksum fileext=".md5">
+      <fileset dir="${java-repository.dir}" includes="*${version}.jar"/>
+    </checksum>
+    <checksum fileext=".sha1" algorithm="SHA">
+      <fileset dir="${java-repository.dir}" includes="*${version}.jar"/>
+    </checksum>
+    
+    <delete dir="${dist.name}"/>
+    <checksum fileext=".md5">
+      <fileset dir="${dist.base.binaries}/">
+        <include name="**/*"/>
+        <exclude name="**/*.asc"/>
+        <exclude name="**/*.md5"/>
+      </fileset>
+    </checksum>
+    <checksum fileext=".sha1" algorithm="SHA">
+      <fileset dir="${dist.base.binaries}/">
+        <include name="**/*"/>
+        <exclude name="**/*.asc"/>
+        <exclude name="**/*.md5"/>
+      </fileset>
+    </checksum>
+
+    <antcall inheritAll="false" target="src-dist">
+      <param name="src.dist.dir" value="${dist.name}"/>
+    </antcall>
+    <zip destfile="${dist.base.source}/${dist.name}-src.zip">
+      <zipfileset dir="${dist.name}/.." filemode="755">
+        <include name="${dist.name}/bootstrap.sh"/>
+        <include name="${dist.name}/build.sh"/>
+      </zipfileset>
+      <fileset dir="${dist.name}/..">
+        <include name="${dist.name}/**"/>
+        <exclude name="${dist.name}/bootstrap.sh"/>
+        <exclude name="${dist.name}/build.sh"/>
+      </fileset>
+    </zip>
+    <tar longfile="gnu"
+         destfile="${dist.base.source}/${dist.name}-src.tar" >
+      <tarfileset dir="${dist.name}/.." mode="755" username="ant" group="ant">
+        <include name="${dist.name}/bootstrap.sh"/>
+        <include name="${dist.name}/build.sh"/>
+      </tarfileset>
+      <tarfileset dir="${dist.name}/.." username="ant" group="ant">
+        <include name="${dist.name}/**"/>
+        <exclude name="${dist.name}/bootstrap.sh"/>
+        <exclude name="${dist.name}/build.sh"/>
+      </tarfileset>
+    </tar>
+    <gzip destfile="${dist.base.source}/${dist.name}-src.tar.gz"
+          src="${dist.base.source}/${dist.name}-src.tar"/>
+    <bzip2 destfile="${dist.base.source}/${dist.name}-src.tar.bz2"
+           src="${dist.base.source}/${dist.name}-src.tar"/>
+    <delete file="${dist.base.source}/${dist.name}-src.tar"/>
+    <delete dir="${dist.name}"/>
+    <checksum fileext=".md5">
+      <fileset dir="${dist.base.source}/">
+        <include name="**/*"/>
+        <exclude name="**/*.asc"/>
+        <exclude name="**/*.md5"/>
+      </fileset>
+    </checksum>
+    <checksum fileext=".sha1" algorithm="SHA">
+      <fileset dir="${dist.base.source}/">
+        <include name="**/*"/>
+        <exclude name="**/*.asc"/>
+        <exclude name="**/*.md5"/>
+      </fileset>
+    </checksum>
+  </target>
+
+  <target name="distribution" depends="main_distribution"
+          description="--> creates the full Apache Ant distribution">
+  </target>
+  <!--
+       ===================================================================
+         Upload the distribution to cvs.apache.org for final releases
+       ===================================================================
+  -->
+  <target name="upload" description="upload distribution">
+    <fail unless="apache.user" message="set a property apache.user with your apache user"/>
+    <fail unless="ssh.passphrase" message="set a property with your ssh passphrase"/>
+    <fail unless="ssh.keyfile" message="set a property with your ssh keyfile"/>
+    <fail unless="ssh.knownhosts" message="set a property with your ssh knownhosts"/>
+    <scp todir="${apache.user}@cvs.apache.org:/www/www.apache.org/dist/ant" keyfile="${ssh.keyfile}" passphrase="${ssh.passphrase}" knownhosts="${ssh.knownhosts}">
+      <fileset dir="${dist.base}">
+        <include name="**/*${version}*"/>
+      </fileset>
+     </scp>
+    <scp todir="${apache.user}@cvs.apache.org:/www/www.apache.org/dist/java-repository/ant/jars" keyfile="${ssh.keyfile}" passphrase="${ssh.passphrase}" knownhosts="${ssh.knownhosts}">
+      <fileset dir="java-repository/ant/jars">
+        <include name="*${version}*"/>
+      </fileset>
+     </scp>
+  </target>
+  <!--
+       ===================================================================
+         Cleans up build and distribution directories
+       ===================================================================
+  -->
+  <target name="clean"
+          description="--> cleans up build and dist directories">
+    <delete dir="${build.dir}"/>
+    <delete dir="${dist.base}"/>
+    <delete dir="${dist.dir}"/>
+    <delete>
+      <fileset dir="." includes="**/*~" defaultexcludes="no"/>
+    </delete>
+  </target>
+
+  <!--
+       ===================================================================
+         Cleans everything
+       ===================================================================
+  -->
+  <target name="allclean"
+          depends="clean"
+          description="--> cleans up everything">
+    <delete file="${bootstrap.dir}/bin/antRun"/>
+    <delete file="${bootstrap.dir}/bin/antRun.bat"/>
+    <delete file="${bootstrap.dir}/bin/*.pl"/>
+    <delete file="${bootstrap.dir}/bin/*.py"/>
+  </target>
+
+  <!--
+       ===================================================================
+         Installs Apache Ant
+       ===================================================================
+  -->
+  <target name="install" if="ant.install">
+    <antcall inheritAll="false" target="internal_dist">
+      <param name="dist.dir" value="${ant.install}"/>
+    </antcall>
+  </target>
+
+  <target name="install-lite" if="ant.install">
+    <antcall inheritAll="false" target="dist-lite">
+      <param name="dist.dir" value="${ant.install}"/>
+    </antcall>
+  </target>
+
+  <!--
+       ===================================================================
+         Creates the API documentation
+       ===================================================================
+  -->
+  <target name="javadoc_check">
+    <uptodate property="javadoc.notrequired"
+              targetfile="${build.javadocs}/packages.html" >
+        <srcfiles dir= "${java.dir}" includes="**/*.java"/>
+    </uptodate>
+    <uptodate property="tests.javadoc.notrequired"
+              targetfile="${build.tests.javadocs}/packages.html" >
+      <srcfiles dir= "${tests.dir}">
+        <patternset refid="useful.tests"/>
+      </srcfiles>
+    </uptodate>
+  </target>
+
+  <target name="javadocs" depends="prepare, javadoc_check"
+          unless="javadoc.notrequired"
+          description="--> creates the API documentation">
+    <mkdir dir="${build.javadocs}"/>
+    <javadoc useexternalfile="yes"
+             destdir="${build.javadocs}"
+             author="true"
+             version="true"
+             locale="en"
+             windowtitle="${Name} API"
+             doctitle="${Name}">
+
+      <packageset dir="${java.dir}"/>
+
+      <tag name="todo" description="To do:" scope="all"/>
+      <tag name="ant.task" enabled="false" description="Task:" scope="types"/>
+      <tag name="ant.datatype" enabled="false" description="Data type:" scope="types"/>
+      <tag name="ant.attribute" enabled="false" description="Attribute:" scope="types"/>
+      <tag name="ant.attribute.group" enabled="false" description="Attribute group:" scope="types"/>
+      <tag name="ant.element" enabled="false" description="Nested element:" scope="types"/>
+      <group title="Apache Ant Core" packages="org.apache.tools.ant*"/>
+      <group title="Core Tasks" packages="org.apache.tools.ant.taskdefs*"/>
+      <group title="Core Types" packages="org.apache.tools.ant.types*"/>
+      <group title="Optional Tasks" packages="org.apache.tools.ant.taskdefs.optional*"/>
+      <group title="Optional Types" packages="org.apache.tools.ant.types.optional*"/>
+      <group title="Ant Utilities" packages="org.apache.tools.ant.util*"/>
+
+      <bottom>Copyright &#169; 2000-${year} Apache Software Foundation. All Rights Reserved.</bottom>
+    </javadoc>
+  </target>
+
+  <target name="test-javadocs" depends="prepare, javadoc_check"
+          unless="tests.javadoc.notrequired"
+          description="--> creates the API documentation for test utilities">
+    <mkdir dir="${build.tests.javadocs}"/>
+    <javadoc useexternalfile="yes"
+             destdir="${build.tests.javadocs}"
+             author="true"
+             version="true"
+             locale="en"
+             windowtitle="${Name} Test Utilities"
+             doctitle="${Name}">
+
+      <fileset dir="${tests.dir}">
+        <patternset refid="useful.tests"/>
+      </fileset>
+
+      <bottom>Copyright &#169; 2000-${year} Apache Software Foundation. All Rights Reserved.</bottom>
+    </javadoc>
+  </target>
+
+  <!--
+       ===================================================================
+         Compile testcases
+       ===================================================================
+  -->
+  <target name="compile-tests" depends="build" if="junit.present">
+    <mkdir dir="${build.tests}"/>
+
+    <javac srcdir="${tests.dir}"
+           destdir="${build.tests}"
+           debug="${debug}"
+           target="${javac.target}"
+           source="${javac.source}"
+           deprecation="${deprecation}" >
+      <classpath refid="tests-classpath"/>
+
+      <selector refid="conditional-patterns"/>
+    </javac>
+
+    <!-- Used by AntlibTest.testAntlibResource: -->
+    <jar jarfile="${build.tests}/org/apache/tools/ant/taskdefs/test2-antlib.jar">
+        <zipfileset dir="${tests.etc.dir}" fullpath="taskdefs/test.antlib.xml">
+            <include name="taskdefs/test2.antlib.xml"/>
+        </zipfileset>
+    </jar>
+  </target>
+
+  <target name="dump-info" depends="dump-sys-properties,run-which"/>
+
+  <target name="dump-sys-properties" unless="which.present"
+          depends="xml-check">
+    <echo message="java.vm.info=${java.vm.info}"/>
+    <echo message="java.vm.name=${java.vm.name}"/>
+    <echo message="java.vm.vendor=${java.vm.vendor}"/>
+    <echo message="java.vm.version=${java.vm.version}"/>
+    <echo message="os.arch=${os.arch}"/>
+    <echo message="os.name=${os.name}"/>
+    <echo message="os.version=${os.version}"/>
+    <echo message="file.encoding=${file.encoding}"/>
+    <echo message="user.language=${user.language}"/>
+  </target>
+
+  <!-- helper class from Xalan2 to check for jar versioning of xml/xsl processors -->
+  <target name="xml-check" depends="check_for_optional_packages"
+          if="xalan.envcheck" unless="which.present">
+      <java classname="org.apache.xalan.xslt.EnvironmentCheck"/>
+  </target>
+
+  <target name="run-which" depends="check_for_optional_packages"
+          if="which.present">
+      <java classname="org.apache.env.Which" taskname="which"/>
+  </target>
+
+  <!-- test to see if we are online or not. can take a while when we are off line, so
+    setting the property is a good shortcut-->
+  <target name="probe-offline">
+      <condition property="offline">
+      <or>
+        <isset property="offline"/>
+        <not>
+          <http  url="http://www.apache.org/"/>
+        </not>
+      </or>
+    </condition>
+    <echo level="verbose" > offline=${offline}</echo>
+  </target>
+
+  <!--
+       ===================================================================
+         Run testcase
+       ===================================================================
+  -->
+  <target name="test" depends="run-tests,run-single-test" description="--> run JUnit tests"/>
+
+  <target name="run-tests" depends="dump-info,compile-tests,probe-offline" if="junit.present" unless="testcase">
+
+    <condition property="run.junitreport">
+      <or>
+        <isset property="xalan2.present"/>
+        <and>
+          <isset property="xalan.present"/>
+          <isset property="bsf.present"/>
+        </and>
+      </or>
+    </condition>
+
+    <junit printsummary="${junit.summary}" haltonfailure="${test.haltonfailure}"
+           filtertrace="${junit.filtertrace}"
+           fork="${junit.fork}" forkmode="${junit.forkmode}"
+           failureproperty="tests.failed">
+<!--      <jvmarg value="-classic"/> -->
+      <classpath refid="tests-classpath"/>
+
+      <sysproperty key="ant.home" value="${ant.home}"/>
+      <sysproperty key="build.tests" file="${build.tests}"/>
+      <sysproperty key="build.tests.value" value="${build.tests.value}"/>
+      <sysproperty key="tests-classpath.value"
+                   value="${tests-classpath.value}"/>
+
+      <formatter type="brief" usefile="false"/>
+
+      <batchtest>
+        <fileset dir="${tests.dir}">
+          <include name="**/*Test*"/>
+
+          <!-- abstract classes, not testcases -->
+          <exclude name="${ant.package}/taskdefs/TaskdefsTest.java"/>
+          <exclude name="${ant.package}/BuildFileTest.java"/>
+          <exclude name="${regexp.package}/RegexpMatcherTest.java"/>
+          <exclude name="${regexp.package}/RegexpTest.java"/>
+          <exclude name="${optional.package}/AbstractXSLTLiaisonTest.java"/>
+          <exclude name="${ant.package}/types/AbstractFileSetTest.java"/>
+          <exclude name="${ant.package}/types/selectors/BaseSelectorTest.java"/>
+
+          <!-- helper classes, not testcases -->
+          <exclude name="org/example/**"/>
+          <exclude name="${ant.package}/taskdefs/TaskdefTest*Task.java"/>
+
+          <!-- interactive tests -->
+          <exclude name="${ant.package}/taskdefs/TestProcess.java"/>
+          <exclude name="${optional.package}/splash/SplashScreenTest.java"/>
+
+          <!-- only run these tests if their required libraries are
+               installed -->
+          <selector refid="conditional-patterns"/>
+
+          <!-- tests excluded if the test is run in offline mode -->
+          <patternset refid="onlinetests"/>
+
+          <!-- failing tests excluded unless run.failing.tests is set -->
+          <patternset refid="teststhatfail"/>
+
+          <!-- runtime dependencies that are different from compile
+               time dependencies -->
+          <exclude name="${optional.package}/ReplaceRegExpTest.java"
+                   unless="some.regexp.support"/>
+          <exclude name="${optional.package}/sitraka/*.java"
+                   unless="some.regexp.support"/>
+          <exclude name="${ant.package}/types/selectors/ContainsRegexpTest.java"
+                   unless="some.regexp.support"/>
+          <exclude name="${optional.package}/Rhino*.java"
+                   unless="bsf.present"/>
+          <exclude name="${optional.package}/Rhino*.java"
+                   unless="rhino.present"/>
+          <exclude name="${optional.package}/script/*.java"
+                   unless="bsf.present"/>
+          <exclude name="${optional.package}/script/*.java"
+                   unless="rhino.present"/>
+          <exclude name="${optional.package}/BeanShellScriptTest.java"
+                   unless="bsf.present"/>
+          <exclude name="${optional.package}/BeanShellScriptTest.java"
+                   unless="beanshell.present"/>
+
+          <!-- fail if testcases can be loaded from the system classloader -->
+          <exclude name="${ant.package}/AntClassLoaderDelegationTest.java"
+                   if="tests.are.on.system.classpath"/>
+          <exclude name="${optional.package}/junit/JUnitClassLoaderTest.java"
+                   if="tests.are.on.system.classpath"/>
+          <exclude name="${optional.package}/sitraka/XMLReportTest.java"
+                   if="tests.are.on.system.classpath"/>
+
+          <!-- these tests need to be localised before being ran???? -->
+          <exclude name="${optional.package}/PvcsTest.java"/>
+
+          <!-- These tests need a TraX implementation like xalan2 or saxon -->
+          <exclude name="${optional.package}/TraXLiaisonTest.java"
+                   unless="trax.impl.present"/>
+          <exclude name="${optional.package}/XsltTest.java"
+                   unless="trax.impl.present"/>
+          <exclude name="${ant.package}/types/XMLCatalogBuildFileTest.java"
+                   unless="trax.impl.present"/>
+          <exclude name="${optional.package}/junit/JUnitReportTest.java"
+                   unless="run.junitreport"/>
+          <exclude name="${ant.package}/taskdefs/StyleTest.java"
+                   unless="trax.impl.present"/>
+
+          <!-- needs BSF to work -->
+          <exclude name="${optional.package}/XalanLiaisonTest.java"
+                   unless="bsf.present"/>
+          <exclude name="${optional.package}/XalanLiaisonTest.java"
+                   unless="xerces1.present"/>
+          <exclude name="${optional.package}/XslpLiaisonTest.java"
+                   unless="xerces1.present"/>
+
+          <!-- needs resolver.jar to work -->
+          <exclude name="${optional.package}/XmlValidateCatalogTest.java"
+                   unless="apache.resolver.present"/>
+
+          <!-- needs jasperc -->
+          <exclude name="${optional.package}/JspcTest.java"
+                   unless="jasper.present"/>
+
+          <!-- misc oneoff tests -->
+          <exclude name="${optional.package}/WsdlToDotnetTest.java"
+                   unless="dotnetapps.found"/>
+          <exclude name="${optional.package}/DotnetTest.java"
+                   unless="dotnetapps.found"/>
+
+          <!--  These tests only passes if testcases and Ant classes have
+          been loaded by the same classloader - will throw
+          IllegalAccessExceptions otherwise.  -->
+          <exclude name="${ant.package}/DirectoryScannerTest.java"
+                   unless="tests.and.ant.share.classloader"/>
+          <exclude name="${ant.package}/taskdefs/SQLExecTest.java"
+                   unless="tests.and.ant.share.classloader"/>
+          <exclude name="${optional.package}/sos/SOSTest.java"
+                   unless="tests.and.ant.share.classloader"/>
+          <exclude name="${optional.package}/vss/MSVSSTest.java"
+                   unless="tests.and.ant.share.classloader"/>
+          <exclude name="${optional.package}/TraXLiaisonTest.java"
+                   unless="tests.and.ant.share.classloader"/>
+          <exclude name="${optional.package}/metamata/MAuditParserTest.java"
+                   unless="tests.and.ant.share.classloader"/>
+          <exclude name="${ant.package}/taskdefs/ProcessDestroyerTest.java"
+                   unless="tests.and.ant.share.classloader"/>
+          <exclude name="${ant.package}/taskdefs/ProtectedJarMethodsTest.java"
+                   unless="tests.and.ant.share.classloader"/>
+
+          <!-- can only run if cvs is installed on your machine
+               enable by setting the property have.cvs
+          -->
+          <exclude name="${ant.package}/taskdefs/AbstractCvsTaskTest.java"
+                   unless="have.cvs"/>
+
+          <!-- needs a local ftp server and the entry of a user/password combination -->
+          <exclude name="${optional.package}/net/FTPTest.java"/>
+
+          <!-- test needs special setup -->
+          <exclude name="${optional.package}/ssh/ScpTest.java"/>
+
+          <!-- test fails if build/classes and ant.jar are using the same
+               classloader -->
+          <exclude name="${ant.package}/util/ClasspathUtilsTest.java"
+            if="tests.and.ant.share.classloader"/>
+        </fileset>
+      </batchtest>
+    </junit>
+    <fail if="tests.failed">At least one test has failed.</fail>
+  </target>
+
+  <target name="run-single-test" if="testcase" depends="compile-tests,run-single-test-only"
+    description="--> runs the single unit test defined in the testcase property"/>
+
+  <target name="run-single-test-only" if="testcase"
+    description="--> runs the single unit test defined in the testcase property">
+
+
+    <junit printsummary="${junit.summary}"
+          haltonfailure="${test.haltonfailure}"
+          fork="${junit.fork}"
+          filtertrace="${junit.filtertrace}">
+<!--      <jvmarg value="-classic"/> -->
+      <sysproperty key="ant.home" value="${ant.home}"/>
+      <sysproperty key="build.tests" file="${build.tests}"/>
+      <sysproperty key="build.tests.value" value="${build.tests.value}"/>
+      <sysproperty key="tests-classpath.value"
+                   value="${tests-classpath.value}"/>
+      <classpath refid="tests-classpath"/>
+      <formatter type="plain" usefile="false"/>
+      <test name="${testcase}"/>
+    </junit>
+  </target>
+
+  <target name="interactive-tests" description="--> runs interactive tests"
+          depends="compile-tests"
+          if="jdk1.3+">
+    <java classpathref="tests-classpath"
+          classname="org.apache.tools.ant.taskdefs.TestProcess"
+          fork="true"/>
+  </target>
+
+  <!--
+       ===================================================================
+         Main target - runs dist-lite by default
+       ===================================================================
+  -->
+  <target name="main"
+          description="--> creates a minimum distribution in ./dist"
+          depends="dist-lite"/>
+
+</project>
Index: /release-kits/shared/ant/debug.log
===================================================================
--- /release-kits/shared/ant/debug.log	(revision 16139)
+++ /release-kits/shared/ant/debug.log	(revision 16139)
@@ -0,0 +1,11 @@
+
+Log file created by MultiPlatform installer @Fri Sep 28 14:24:41 NZST 2007
+INSTALLER_PATH=/home/nzdl/installshieldX/InstallShieldUniversal
+Verifying resolved JVM
+JVM_HOME is not found in the jvm file /research/oranfry/temp/istemp29008271142441/sunjdk14.jvm.
+Checking the environment variables specifed in the JVM files to find the JVM...
+Verifying... /opt/jdk1.5.0_10/bin/java -cp /research/oranfry/temp/istemp29008271142441/Verify.jar Verify  java.version
+Verifying... /research/oranfry/programs/usr-bin/java -cp /research/oranfry/temp/istemp29008271142441/Verify.jar Verify  java.version
+Verification passed for /research/oranfry/programs/usr-bin/ using the JVM file /research/oranfry/temp/istemp29008271142441/sunjdk14.jvm.
+JavaHome is not resolved correctly in the jvm file /research/oranfry/temp/istemp29008271142441/sunjdk14.jvm. Failed to launch the application.
+Launch Script can't not be resolved. Run the installer with -is:log <logfile> to view the errors.
Index: /release-kits/shared/ant/etc/changelog.xsl
===================================================================
--- /release-kits/shared/ant/etc/changelog.xsl	(revision 16139)
+++ /release-kits/shared/ant/etc/changelog.xsl	(revision 16139)
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<xsl:stylesheet
+    xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
+    version='1.0'>
+
+<!--
+    Copyright  2002,2004-2005 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+  <xsl:param name="title"/>
+  <xsl:param name="module"/>
+  <xsl:param name="cvsweb"/>
+
+  <xsl:output method="html" indent="yes" encoding="US-ASCII"
+              doctype-public="-//W3C//DTD HTML 4.01//EN"
+              doctype-system="http://www.w3.org/TR/html401/strict.dtd"/>
+
+  <!-- Copy standard document elements.  Elements that
+       should be ignored must be filtered by apply-templates
+       tags. -->
+  <xsl:template match="*">
+    <xsl:copy>
+      <xsl:copy-of select="attribute::*[. != '']"/>
+      <xsl:apply-templates/>
+    </xsl:copy>
+  </xsl:template>
+
+  <xsl:template match="changelog">
+    <html>
+      <head>
+        <title><xsl:value-of select="$title"/></title>
+        <style type="text/css">
+          body, p {
+            font-family: Verdana, Arial, Helvetica, sans-serif;
+            font-size: 80%;
+            color: #000000;
+            background-color: #ffffff;
+          }
+          tr, td {
+            font-family: Verdana, Arial, Helvetica, sans-serif;
+            background: #eeeee0;
+          }
+          td {
+            padding-left: 20px;
+          }
+      .dateAndAuthor {
+            font-family: Verdana, Arial, Helvetica, sans-serif;
+            font-weight: bold;
+            text-align: left;
+            background: #a6caf0;
+            padding-left: 3px;
+      }
+          a {
+            color: #000000;
+          }
+          pre {
+            font-weight: bold;
+          }
+        </style>
+      </head>
+      <body>
+        <h1>
+          <a name="top"><xsl:value-of select="$title"/></a>
+        </h1>
+        <p style="text-align: right">Designed for use with <a href="http://ant.apache.org/">Apache Ant</a>.</p>
+        <hr/>
+        <table border="0" width="100%" cellspacing="1">
+          
+          <xsl:apply-templates select=".//entry">
+            <xsl:sort select="date" data-type="text" order="descending"/>
+            <xsl:sort select="time" data-type="text" order="descending"/>
+          </xsl:apply-templates>
+          
+        </table>
+        
+      </body>
+    </html>
+  </xsl:template>
+  
+  <xsl:template match="entry">
+    <tr>
+      <td class="dateAndAuthor">
+        <xsl:value-of select="date"/><xsl:text> </xsl:text><xsl:value-of select="time"/><xsl:text> </xsl:text><xsl:value-of select="author"/>
+      </td>
+    </tr>
+    <tr>
+      <td>
+        <pre>
+<xsl:apply-templates select="msg"/></pre>
+        <ul>
+          <xsl:apply-templates select="file"/>
+        </ul>
+      </td>
+    </tr>
+  </xsl:template>
+
+  <xsl:template match="date">
+    <i><xsl:value-of select="."/></i>
+  </xsl:template>
+
+  <xsl:template match="time">
+    <i><xsl:value-of select="."/></i>
+  </xsl:template>
+
+  <xsl:template match="author">
+    <i>
+      <a>
+        <xsl:attribute name="href">mailto:<xsl:value-of select="."/></xsl:attribute>
+        <xsl:value-of select="."/></a>
+    </i>
+  </xsl:template>
+
+  <xsl:template match="file">
+    <li>
+      <a>
+        <xsl:choose>
+          <xsl:when test="string-length(prevrevision) = 0 ">
+            <xsl:attribute name="href"><xsl:value-of select="$cvsweb"/><xsl:value-of select="$module" />/<xsl:value-of select="name" />?rev=<xsl:value-of select="revision" />&amp;content-type=text/x-cvsweb-markup</xsl:attribute>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:attribute name="href"><xsl:value-of select="$cvsweb"/><xsl:value-of select="$module" />/<xsl:value-of select="name" />?r1=<xsl:value-of select="revision" />&amp;r2=<xsl:value-of select="prevrevision"/></xsl:attribute>
+          </xsl:otherwise>
+        </xsl:choose>
+        <xsl:value-of select="name" /> (<xsl:value-of select="revision"/>)</a>
+    </li>
+  </xsl:template>
+
+  <!-- Any elements within a msg are processed,
+       so that we can preserve HTML tags. -->
+  <xsl:template match="msg">
+    <xsl:apply-templates/>
+  </xsl:template>
+  
+</xsl:stylesheet>
Index: /release-kits/shared/ant/etc/checkstyle/checkstyle-frames.xsl
===================================================================
--- /release-kits/shared/ant/etc/checkstyle/checkstyle-frames.xsl	(revision 16139)
+++ /release-kits/shared/ant/etc/checkstyle/checkstyle-frames.xsl	(revision 16139)
@@ -0,0 +1,293 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt"
+    xmlns:redirect="org.apache.xalan.lib.Redirect"
+    extension-element-prefixes="redirect">
+
+<!--
+    Copyright  2002-2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+
+    <xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+    <xsl:decimal-format decimal-separator="." grouping-separator="," />
+
+    <xsl:param name="output.dir" select="'.'"/>
+    <xsl:param name="basedir" select="'.'"/>
+
+    <xsl:template match="checkstyle">
+        <!-- create the index.html -->
+        <redirect:write file="{$output.dir}/index.html">
+            <xsl:call-template name="index.html"/>
+        </redirect:write>
+
+        <!-- create the stylesheet.css -->
+        <redirect:write file="{$output.dir}/stylesheet.css">
+            <xsl:call-template name="stylesheet.css"/>
+        </redirect:write>
+
+        <!-- create the overview-summary.html at the root -->
+        <redirect:write file="{$output.dir}/overview-frame.html">
+            <xsl:apply-templates select="." mode="overview"/>
+        </redirect:write>
+
+        <!-- create the all-classes.html at the root -->
+        <redirect:write file="{$output.dir}/allclasses-frame.html">
+            <xsl:apply-templates select="." mode="all.classes"/>
+        </redirect:write>
+
+        <!-- process all files -->
+        <xsl:apply-templates select="file[count(error) != 0]"/>
+    </xsl:template>
+
+    <xsl:template name="index.html">
+        <html>
+            <head>
+                <title>CheckStyle Audit</title>
+            </head>
+            <frameset cols="20%,80%">
+                <frame src="allclasses-frame.html" name="fileListFrame"/>
+                <frame src="overview-frame.html" name="fileFrame"/>
+            </frameset>
+            <noframes>
+                <h2>Frame Alert</h2>
+                <p>
+                    This document is designed to be viewed using the frames feature.
+                    If you see this message, you are using a non-frame-capable web client.
+                </p>
+            </noframes>
+        </html>
+    </xsl:template>
+
+    <xsl:template name="pageHeader">
+        <table border="0" cellpadding="0" cellspacing="0" width="100%">
+            <tr>
+                <td class="text-align:right"><h2>CheckStyle Audit</h2></td>
+            </tr>
+            <tr>
+                <td class="text-align:right">Designed for use with
+                  <a href='http://checkstyle.sourceforge.net/'>CheckStyle</a> and
+                  <a href='http://ant.apache.org/'>Ant</a>.</td>
+            </tr>
+        </table>
+        <hr size="1"/>
+    </xsl:template>
+
+    <xsl:template match="checkstyle" mode="overview">
+        <html>
+            <head>
+                <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+            </head>
+            <body>
+                <!-- page header -->
+                <xsl:call-template name="pageHeader"/>
+
+                <!-- Summary part -->
+                <xsl:apply-templates select="." mode="summary"/>
+                <hr size="1" width="100%" align="left"/>
+
+                <!-- File list part -->
+                <xsl:apply-templates select="." mode="filelist"/>
+            </body>
+        </html>
+    </xsl:template>
+
+    <xsl:template name="stylesheet.css">
+        .bannercell {
+        border: 0px;
+        padding: 0px;
+        }
+        body {
+        margin-left: 10;
+        margin-right: 10;
+        font:normal 80% arial,helvetica,sanserif;
+        background-color:#FFFFFF;
+        color:#000000;
+        }
+        .oddrow td {
+        background: #efefef;
+        }
+        .evenrow td {
+        background: #fff;
+        }
+        th, td {
+        text-align: left;
+        vertical-align: top;
+        }
+        th {
+        font-weight:bold;
+        background: #ccc;
+        color: black;
+        }
+        table, th, td {
+        font-size:100%;
+        border: none
+        }
+        table.log tr td, tr th {
+
+        }
+        h2 {
+        font-weight:bold;
+        font-size:140%;
+        margin-bottom: 5;
+        }
+        h3 {
+        font-size:100%;
+        font-weight:bold;
+        background: #525D76;
+        color: white;
+        text-decoration: none;
+        padding: 5px;
+        margin-right: 2px;
+        margin-left: 2px;
+        margin-bottom: 0;
+        }
+    </xsl:template>
+
+    <!--
+    Creates an all-classes.html file that contains a link to all files.
+    -->
+    <xsl:template match="checkstyle" mode="all.classes">
+        <html>
+            <head>
+                <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+            </head>
+            <body>
+                <h2>Files</h2>
+                <p>
+                    <table width="100%">
+                        <!-- For each file create its part -->
+                        <xsl:apply-templates select="file[count(error) != 0]" mode="all.classes">
+                            <xsl:sort select="substring-after(@name, $basedir)"/>
+                        </xsl:apply-templates>
+                    </table>
+                </p>
+            </body>
+        </html>
+    </xsl:template>
+
+    <xsl:template match="checkstyle" mode="filelist">
+        <h3>Files</h3>
+        <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+            <tr>
+                <th>Name</th>
+                <th>Errors</th>
+            </tr>
+            <xsl:apply-templates select="file[count(error) != 0]" mode="filelist">
+                <xsl:sort select="count(error)" order="descending" data-type="number"/>
+            </xsl:apply-templates>
+        </table>
+    </xsl:template>
+
+    <xsl:template match="file" mode="filelist">
+        <tr>
+            <xsl:call-template name="alternated-row"/>
+            <td nowrap="nowrap">
+                <a>
+                    <xsl:attribute name="href">
+                        <xsl:text>files/</xsl:text><xsl:value-of select="substring-after(@name, $basedir)"/><xsl:text>.html</xsl:text>
+                    </xsl:attribute>
+                    <xsl:value-of select="substring-after(@name, $basedir)"/>
+                </a>
+            </td>
+            <td><xsl:value-of select="count(error)"/></td>
+        </tr>
+    </xsl:template>
+
+    <xsl:template match="file" mode="all.classes">
+        <tr>
+            <td nowrap="nowrap">
+                <a target="fileFrame">
+                    <xsl:attribute name="href">
+                        <xsl:text>files/</xsl:text><xsl:value-of select="substring-after(@name, $basedir)"/><xsl:text>.html</xsl:text>
+                    </xsl:attribute>
+                    <xsl:value-of select="substring-after(@name, $basedir)"/>
+                </a>
+            </td>
+        </tr>
+    </xsl:template>
+
+    <!--
+    transform string like a/b/c to ../../../
+    @param path the path to transform into a descending directory path
+    -->
+    <xsl:template name="path">
+        <xsl:param name="path"/>
+        <xsl:if test="contains($path,'/')">
+            <xsl:text>../</xsl:text>
+            <xsl:call-template name="path">
+                <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'/')"/></xsl:with-param>
+            </xsl:call-template>
+        </xsl:if>
+        <xsl:if test="not(contains($path,'/')) and not($path = '')">
+            <xsl:text>../</xsl:text>
+        </xsl:if>
+    </xsl:template>
+
+    <xsl:template match="file">
+        <redirect:write file="{$output.dir}/files/{substring-after(@name, $basedir)}.html">
+            <html>
+                <head>
+                    <link rel="stylesheet" type="text/css">
+                        <xsl:attribute name="href"><xsl:call-template name="path"><xsl:with-param name="path" select="substring-after(@name, $basedir)"/></xsl:call-template><xsl:text>stylesheet.css</xsl:text></xsl:attribute>
+                    </link>
+                </head>
+                <body>
+                    <xsl:call-template name="pageHeader"/>
+                    <h3>File <xsl:value-of select="substring-after(@name, $basedir)"/></h3>
+                    <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+                        <tr>
+                            <th>Error Description</th>
+                            <th>Line:Column</th>
+                        </tr>
+                        <xsl:for-each select="error">
+                            <tr>
+                                <xsl:call-template name="alternated-row"/>
+                                <td><a title="{@source}"><xsl:value-of select="@message"/></a></td>
+                                <td align="center"><xsl:value-of select="@line"/><xsl:if test="@column">:<xsl:value-of select="@column"/></xsl:if></td>
+                            </tr>
+                        </xsl:for-each>
+                    </table>
+                </body>
+            </html>
+        </redirect:write>
+    </xsl:template>
+
+    <xsl:template match="checkstyle" mode="summary">
+        <h3>Summary</h3>
+        <xsl:variable name="fileCount" select="count(file)"/>
+        <xsl:variable name="errorCount" select="count(file/error)"/>
+        <xsl:variable name="fileErrorCount" select="count(file[count(error) != 0])"/>
+        <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+            <tr>
+                <th>Total Files</th>
+                <th>Files With Errors</th>
+                <th>Errors</th>
+            </tr>
+            <tr>
+                <xsl:call-template name="alternated-row"/>
+                <td><xsl:value-of select="$fileCount"/></td>
+                <td><xsl:value-of select="$fileErrorCount"/></td>
+                <td><xsl:value-of select="$errorCount"/></td>
+            </tr>
+        </table>
+    </xsl:template>
+
+    <xsl:template name="alternated-row">
+        <xsl:attribute name="class">
+            <xsl:if test="position() mod 2 = 1">oddrow</xsl:if>
+            <xsl:if test="position() mod 2 = 0">evenrow</xsl:if>
+        </xsl:attribute>
+    </xsl:template>
+</xsl:stylesheet>
Index: /release-kits/shared/ant/etc/checkstyle/checkstyle-text.xsl
===================================================================
--- /release-kits/shared/ant/etc/checkstyle/checkstyle-text.xsl	(revision 16139)
+++ /release-kits/shared/ant/etc/checkstyle/checkstyle-text.xsl	(revision 16139)
@@ -0,0 +1,33 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+<!--
+    Copyright  2003-2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+
+    <xsl:strip-space elements="checkstyle"/>
+    <xsl:preserve-space elements="file"/>
+    <xsl:output method="text"/>
+    <xsl:template match="checkstyle/file/error">
+        <xsl:value-of select="../@name"/>
+        <xsl:text>:</xsl:text>
+        <xsl:value-of select="@line"/>
+        <xsl:text>:</xsl:text>
+        <xsl:value-of select="@column"/>
+        <xsl:text> </xsl:text>
+        <xsl:value-of select="@message"/>
+    </xsl:template>
+</xsl:stylesheet>
+
Index: /release-kits/shared/ant/etc/checkstyle/checkstyle-xdoc.xsl
===================================================================
--- /release-kits/shared/ant/etc/checkstyle/checkstyle-xdoc.xsl	(revision 16139)
+++ /release-kits/shared/ant/etc/checkstyle/checkstyle-xdoc.xsl	(revision 16139)
@@ -0,0 +1,129 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt"
+    xmlns:redirect="org.apache.xalan.lib.Redirect"
+    extension-element-prefixes="redirect">
+
+<!--
+    Copyright  2003-2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+
+    <xsl:output method="xml" indent="yes"/>
+    <xsl:decimal-format decimal-separator="." grouping-separator="," />
+
+    <xsl:param name="output.dir" select="'.'"/>
+    <xsl:param name="basedir" select="'.'"/>
+
+    <xsl:template match="checkstyle">
+      <document>
+        <properties>
+          <title>Checkstyle Audit</title>
+        </properties>
+
+        <body>
+          <xsl:apply-templates select="." mode="summary"/>
+          <!-- File list part -->
+          <xsl:apply-templates select="." mode="filelist"/>
+          <xsl:apply-templates select="file[count(error) != 0]"/>
+        </body>
+      </document>
+    </xsl:template>
+
+    <xsl:template match="checkstyle" mode="filelist">
+      <section name="Files">
+        <table>
+            <tr>
+                <th>Name</th>
+                <th>Errors</th>
+            </tr>
+            <xsl:apply-templates select="file[count(error) != 0]" mode="filelist">
+                <xsl:sort select="count(error)" order="descending" data-type="number"/>
+            </xsl:apply-templates>
+        </table>
+      </section>
+    </xsl:template>
+
+    <xsl:template match="file" mode="filelist">
+        <tr>
+            <xsl:call-template name="alternated-row"/>
+            <td nowrap="nowrap">
+                <a>
+                    <xsl:attribute name="href">
+                        <xsl:text>files</xsl:text><xsl:value-of select="substring-after(@name, $basedir)"/><xsl:text>.html</xsl:text>
+                    </xsl:attribute>
+                    <xsl:value-of select="substring-after(@name, $basedir)"/>
+                </a>
+            </td>
+            <td><xsl:value-of select="count(error)"/></td>
+        </tr>
+    </xsl:template>
+
+    <xsl:template match="file">
+      <redirect:write file="{$output.dir}/files{substring-after(@name, $basedir)}.xml">
+        <document>
+          <properties>
+            <title>Checkstyle Audit</title>
+          </properties>
+
+          <body>
+            <section name="Details for {substring-after(@name, $basedir)}">
+              <table>
+                  <tr>
+                      <th>Error Description</th>
+                      <th>Line</th>
+                  </tr>
+                  <xsl:for-each select="error">
+                      <tr>
+                          <xsl:call-template name="alternated-row"/>
+                          <td><a title="{@source}"><xsl:value-of select="@message"/></a></td>
+                          <td><xsl:value-of select="@line"/></td>
+                      </tr>
+                  </xsl:for-each>
+              </table>
+            </section>
+          </body>
+        </document>
+      </redirect:write>
+    </xsl:template>
+
+    <xsl:template match="checkstyle" mode="summary">
+      <section name="Summary">
+        <xsl:variable name="fileCount" select="count(file)"/>
+        <xsl:variable name="errorCount" select="count(file/error)"/>
+        <xsl:variable name="fileErrorCount" select="count(file[count(error) != 0])"/>
+        <table>
+            <tr>
+                <th>Files</th>
+                <th>Files With Errors</th>
+                <th>Errors</th>
+            </tr>
+            <tr>
+                <xsl:call-template name="alternated-row"/>
+                <td><xsl:value-of select="$fileCount"/></td>
+                <td><xsl:value-of select="$fileErrorCount"/></td>
+                <td><xsl:value-of select="$errorCount"/></td>
+            </tr>
+        </table>
+      </section>
+    </xsl:template>
+
+    <xsl:template name="alternated-row">
+        <xsl:attribute name="class">
+            <xsl:if test="position() mod 2 = 1">oddrow</xsl:if>
+            <xsl:if test="position() mod 2 = 0">evenrow</xsl:if>
+        </xsl:attribute>
+    </xsl:template>
+</xsl:stylesheet>
+
Index: /release-kits/shared/ant/etc/coverage-frames.xsl
===================================================================
--- /release-kits/shared/ant/etc/coverage-frames.xsl	(revision 16139)
+++ /release-kits/shared/ant/etc/coverage-frames.xsl	(revision 16139)
@@ -0,0 +1,496 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt"
+    xmlns:redirect="org.apache.xalan.lib.Redirect"
+    extension-element-prefixes="redirect">
+<xsl:output method="html" indent="yes"/>
+<xsl:decimal-format decimal-separator="." grouping-separator="," />
+<!--
+    Copyright  2001-2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+
+<!--
+
+ Sample stylesheet to be used with JProbe 3.0 XML output.
+
+ It creates a set of HTML files a la javadoc where you can browse easily
+ through all packages and classes.
+
+ It is best used with JProbe Coverage Ant task that gives you the benefit
+ of a reference classpath so that you have the list of classes/methods
+ that are not used at all in a given classpath.
+
+ @author Stephane Bailliez <a href="mailto:sbailliez@apache.org"/>
+
+-->
+
+<!-- default output directory is current directory -->
+<xsl:param name="output.dir" select="'.'"/>
+
+<!-- ======================================================================
+    Root element
+    ======================================================================= -->
+<xsl:template match="/snapshot">
+    <!-- create the index.html -->
+    <redirect:write file="{$output.dir}/index.html">
+        <xsl:call-template name="index.html"/>
+    </redirect:write>
+
+    <!-- create the stylesheet.css -->
+    <redirect:write file="{$output.dir}/stylesheet.css">
+        <xsl:call-template name="stylesheet.css"/>
+    </redirect:write>
+
+    <!-- create the overview-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-summary.html">
+        <xsl:apply-templates select="." mode="overview.packages"/>
+    </redirect:write>
+
+    <!-- create the all-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-frame.html">
+        <xsl:apply-templates select="." mode="all.packages"/>
+    </redirect:write>
+
+    <!-- create the all-classes.html at the root -->
+    <redirect:write file="{$output.dir}/allclasses-frame.html">
+        <xsl:apply-templates select="." mode="all.classes"/>
+    </redirect:write>
+
+    <!-- process all packages -->
+    <xsl:apply-templates select="./package" mode="write"/>
+</xsl:template>
+
+<!-- =======================================================================
+    Frameset definition. Entry point for the report.
+    3 frames: packageListFrame, classListFrame, classFrame
+    ======================================================================= -->
+<xsl:template name="index.html">
+<html>
+    <head><title>Coverage Results.</title></head>
+    <frameset cols="20%,80%">
+        <frameset rows="30%,70%">
+            <frame src="overview-frame.html" name="packageListFrame"/>
+            <frame src="allclasses-frame.html" name="classListFrame"/>
+        </frameset>
+        <frame src="overview-summary.html" name="classFrame"/>
+    </frameset>
+    <noframes>
+        <h2>Frame Alert</h2>
+        <p>
+        This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+        </p>
+    </noframes>
+</html>
+</xsl:template>
+
+<!-- =======================================================================
+    Stylesheet CSS used
+    ======================================================================= -->
+<!-- this is the stylesheet css to use for nearly everything -->
+<xsl:template name="stylesheet.css">
+    .bannercell {
+      border: 0px;
+      padding: 0px;
+    }
+    body {
+      margin-left: 10;
+      margin-right: 10;
+      font:normal 80% arial,helvetica,sanserif;
+      background-color:#FFFFFF;
+      color:#000000;
+    }
+    .a td {
+      background: #efefef;
+    }
+    .b td {
+      background: #fff;
+    }
+    th, td {
+      text-align: left;
+      vertical-align: top;
+    }
+    th {
+      font-weight:bold;
+      background: #ccc;
+      color: black;
+    }
+    table, th, td {
+      font-size:100%;
+      border: none
+    }
+    table.log tr td, tr th {
+
+    }
+    h2 {
+      font-weight:bold;
+      font-size:140%;
+      margin-bottom: 5;
+    }
+    h3 {
+      font-size:100%;
+      font-weight:bold;
+      background: #525D76;
+      color: white;
+      text-decoration: none;
+      padding: 5px;
+      margin-right: 2px;
+      margin-left: 2px;
+      margin-bottom: 0;
+    }
+</xsl:template>
+
+<!-- =======================================================================
+    List of all classes in all packages
+    This will be the first page in the classListFrame
+    ======================================================================= -->
+<xsl:template match="snapshot" mode="all.classes">
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link"/>
+        </head>
+        <body>
+            <h2>Classes</h2>
+            <table width="100%">
+                <xsl:for-each select="package/class">
+                    <xsl:sort select="@name"/>
+                    <xsl:variable name="package.name" select="(ancestor::package)[last()]/@name"/>
+                    <xsl:variable name="link">
+                        <xsl:if test="not($package.name='')">
+                            <xsl:value-of select="translate($package.name,'.','/')"/><xsl:text>/</xsl:text>
+                        </xsl:if><xsl:value-of select="@name"/><xsl:text>.html</xsl:text>
+                    </xsl:variable>
+                    <tr>
+                        <td nowrap="nowrap">
+                            <a target="classFrame" href="{$link}"><xsl:value-of select="@name"/></a>
+                        </td>
+                    </tr>
+                </xsl:for-each>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+<!-- list of all packages -->
+<xsl:template match="snapshot" mode="all.packages">
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link"/>
+        </head>
+        <body>
+            <h2><a href="overview-summary.html" target="classFrame">Home</a></h2>
+            <h2>Packages</h2>
+            <table width="100%">
+                <xsl:for-each select="package">
+                    <xsl:sort select="@name" order="ascending"/>
+                    <tr>
+                        <td nowrap="nowrap">
+                            <a href="{translate(@name,'.','/')}/package-summary.html" target="classFrame">
+                                <xsl:value-of select="@name"/>
+                            </a>
+                        </td>
+                    </tr>
+                </xsl:for-each>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+<!-- overview of statistics in packages -->
+<xsl:template match="snapshot" mode="overview.packages">
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link"/>
+        </head>
+        <body onload="open('allclasses-frame.html','classListFrame')">
+        <xsl:call-template name="pageHeader"/>
+        <h3>Summary</h3>
+        <table class="log" cellpadding="5" cellspacing="2" width="100%">
+            <tr>
+                <!--th width="10%" nowrap="nowrap">Date</th>
+                <th width="10%" nowrap="nowrap">Elapsed time</th-->
+                <th width="10%" nowrap="nowrap">Reported Classes</th>
+                <th width="10%" nowrap="nowrap">Methods Hit</th>
+                <th width="10%" nowrap="nowrap">Lines Hit</th>
+            </tr>
+            <tr class="a">
+                <!--td nowrap="nowrap"><xsl:value-of select="execution_log/@program_start"/></td>
+                <td><xsl:value-of select="format-number(execution_log/@elapsed_time div 1000,'0.0')"/>secs</td-->
+                <td><xsl:value-of select="count(package/class)"/></td>
+                <td><xsl:value-of select="format-number(cov.data/@hit_methods div cov.data/@total_methods,'0.0%')"/></td>
+                <td><xsl:value-of select="format-number(cov.data/@hit_lines div cov.data/@total_lines,'0.0%')"/></td>
+            </tr>
+        </table>
+        <table border="0" width="100%">
+        <tr>
+        <td style="text-align: justify;">
+        To ensure accurate test runs on Java applications, developers need to know how much of
+        the code has been tested, and where to find any untested code. Coverage helps you
+        locate untested code, and measure precisely how much code has been exercised.
+        The result is a higher quality application in a shorter period of time.
+        <p/>
+        </td>
+        </tr>
+        </table>
+
+        <h3>Packages</h3>
+        <table class="log" cellpadding="5" cellspacing="2" width="100%">
+            <xsl:apply-templates select="package[1]" mode="stats.header"/>
+            <!-- display packages and sort them via their coverage rate -->
+            <xsl:for-each select="package">
+                <xsl:sort data-type="number" select="cov.data/@hit_lines div cov.data/@total_lines"/>
+                <tr>
+                  <xsl:call-template name="alternate-row"/>
+                    <td><a href="{translate(@name,'.','/')}/package-summary.html"><xsl:value-of select="@name"/></a></td>
+                    <td><xsl:value-of select="format-number(cov.data/@hit_methods div cov.data/@total_methods,'0.0%')"/></td>
+                    <td><xsl:value-of select="format-number(cov.data/@hit_lines div cov.data/@total_lines,'0.0%')"/></td>
+                </tr>
+            </xsl:for-each>
+        </table>
+        <xsl:call-template name="pageFooter"/>
+        </body>
+        </html>
+</xsl:template>
+
+<!--
+ detailed info for a package. It will output the list of classes
+, the summary page, and the info for each class
+-->
+<xsl:template match="package" mode="write">
+    <xsl:variable name="package.dir">
+        <xsl:if test="not(@name = '')"><xsl:value-of select="translate(@name,'.','/')"/></xsl:if>
+        <xsl:if test="@name = ''">.</xsl:if>
+    </xsl:variable>
+
+    <!-- create a classes-list.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-frame.html">
+        <xsl:apply-templates select="." mode="classes.list"/>
+    </redirect:write>
+
+    <!-- create a package-summary.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-summary.html">
+        <xsl:apply-templates select="." mode="package.summary"/>
+    </redirect:write>
+
+    <!-- for each class, creates a @name.html -->
+    <xsl:for-each select="class">
+        <redirect:write file="{$output.dir}/{$package.dir}/{@name}.html">
+            <xsl:apply-templates select="." mode="class.details"/>
+        </redirect:write>
+    </xsl:for-each>
+</xsl:template>
+
+<!-- list of classes in a package -->
+<xsl:template match="package" mode="classes.list">
+    <html>
+        <HEAD>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="@name"/>
+            </xsl:call-template>
+        </HEAD>
+        <BODY>
+            <table width="100%">
+                <tr>
+                    <td nowrap="nowrap">
+                        <H2><a href="package-summary.html" target="classFrame"><xsl:value-of select="@name"/></a></H2>
+                    </td>
+                </tr>
+            </table>
+
+            <H2>Classes</H2>
+            <TABLE WIDTH="100%">
+                <xsl:for-each select="class">
+                    <xsl:sort select="@name"/>
+                    <tr>
+                        <td nowrap="nowrap">
+                            <a href="{@name}.html" target="classFrame"><xsl:value-of select="@name"/></a>
+                        </td>
+                    </tr>
+                </xsl:for-each>
+            </TABLE>
+        </BODY>
+    </html>
+</xsl:template>
+
+<!-- summary of a package -->
+<xsl:template match="package" mode="package.summary">
+    <HTML>
+        <HEAD>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="@name"/>
+            </xsl:call-template>
+        </HEAD>
+        <!-- when loading this package, it will open the classes into the frame -->
+        <BODY onload="open('package-frame.html','classListFrame')">
+            <xsl:call-template name="pageHeader"/>
+            <h3>Package <xsl:value-of select="@name"/></h3>
+            <table class="log" cellpadding="5" cellspacing="2" width="100%">
+                <xsl:apply-templates select="." mode="stats.header"/>
+                <xsl:apply-templates select="." mode="stats"/>
+            </table>
+
+            <xsl:if test="count(class) &gt; 0">
+                <H3>Classes</H3>
+                <table class="log" cellpadding="5" cellspacing="2" width="100%">
+                    <xsl:apply-templates select="." mode="stats.header"/>
+                    <xsl:apply-templates select="class" mode="stats">
+                        <xsl:sort data-type="number" select="cov.data/@hit_lines div cov.data/@total_lines"/>
+                    </xsl:apply-templates>
+                </table>
+            </xsl:if>
+            <xsl:call-template name="pageFooter"/>
+        </BODY>
+    </HTML>
+</xsl:template>
+
+<!-- details of a class -->
+<xsl:template match="class" mode="class.details">
+    <xsl:variable name="package.name" select="(ancestor::package)[last()]/@name"/>
+    <HTML>
+        <HEAD>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$package.name"/>
+            </xsl:call-template>
+        </HEAD>
+        <BODY>
+            <xsl:call-template name="pageHeader"/>
+            <H3>Class <xsl:if test="not($package.name = '')"><xsl:value-of select="$package.name"/>.</xsl:if><xsl:value-of select="@name"/></H3>
+
+            <!-- class summary -->
+            <table class="log" cellpadding="5" cellspacing="2" width="100%">
+                <xsl:apply-templates select="." mode="stats.header"/>
+                <xsl:apply-templates select="." mode="stats"/>
+            </table>
+
+            <!-- details of methods -->
+            <H3>Methods</H3>
+            <table class="log" cellpadding="5" cellspacing="2" width="100%">
+                <xsl:apply-templates select="method[1]" mode="stats.header"/>
+                <xsl:apply-templates select="method" mode="stats">
+                    <xsl:sort data-type="number" select="cov.data/@hit_lines div cov.data/@total_lines"/>
+                </xsl:apply-templates>
+            </table>
+            <xsl:call-template name="pageFooter"/>
+        </BODY>
+    </HTML>
+
+</xsl:template>
+
+<!-- Page Header -->
+<xsl:template name="pageHeader">
+  <!-- jakarta logo -->
+  <table border="0" cellpadding="0" cellspacing="0" width="100%">
+  <tr>
+    <td class="bannercell" rowspan="2">
+      <a href="http://jakarta.apache.org/">
+      <img src="http://jakarta.apache.org/images/jakarta-logo.gif" alt="http://jakarta.apache.org" align="left" border="0"/>
+      </a>
+    </td>
+        <td style="text-align:right"><h2>Source Code Coverage</h2></td>
+        </tr>
+        <tr>
+        <td style="text-align:right">Designed for use with <a href='http://www.sitraka.com/jprobe'>Sitraka JProbe</a> and <a href='http://jakarta.apache.org'>Ant</a>.</td>
+        </tr>
+  </table>
+    <hr size="1"/>
+</xsl:template>
+
+<!-- Page Footer -->
+<xsl:template name="pageFooter">
+    <table width="100%">
+      <tr><td><hr noshade="yes" size="1"/></td></tr>
+      <tr><td>
+      <div align="center"><font color="#525D76" size="-1"><em>
+      Copyright &#169; 1999-2001, Apache Software Foundation
+      </em></font></div>
+      </td></tr>
+    </table>
+</xsl:template>
+
+
+<xsl:template name="table.header">
+    <tr>
+        <th width="80%">Name</th>
+        <th width="10%" nowrap="nowrap">Methods Hit</th>
+        <th width="10%" nowrap="nowrap">Lines Hit</th>
+    </tr>
+</xsl:template>
+
+<xsl:template match="method" mode="stats.header">
+    <tr>
+        <th width="90%">Name</th>
+        <th width="10%" nowrap="nowrap">Lines Hit</th>
+    </tr>
+</xsl:template>
+<xsl:template match="method" mode="stats">
+    <tr>
+      <xsl:call-template name="alternate-row"/>
+        <td><xsl:value-of select="@name"/></td>
+        <td>
+        <xsl:value-of select="format-number(cov.data/@hit_lines div cov.data/@total_lines,'0.0%')"/>
+        </td>
+    </tr>
+</xsl:template>
+
+<xsl:template match="package|class" mode="stats.header">
+    <tr>
+        <th width="80%">Name</th>
+        <th width="10%" nowrap="nowrap">Methods Hit</th>
+        <th width="10%" nowrap="nowrap">Lines Hit</th>
+    </tr>
+</xsl:template>
+<xsl:template match="package|class" mode="stats">
+    <tr>
+      <xsl:call-template name="alternate-row"/>
+        <td><xsl:value-of select="@name"/></td>
+        <td><xsl:value-of select="format-number(cov.data/@hit_methods div cov.data/@total_methods,'0.0%')"/></td>
+        <td><xsl:value-of select="format-number(cov.data/@hit_lines div cov.data/@total_lines,'0.0%')"/></td>
+    </tr>
+</xsl:template>
+
+<!--
+    transform string like a.b.c to ../../../
+    @param path the path to transform into a descending directory path
+-->
+<xsl:template name="path">
+    <xsl:param name="path"/>
+    <xsl:if test="contains($path,'.')">
+        <xsl:text>../</xsl:text>
+        <xsl:call-template name="path">
+            <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'.')"/></xsl:with-param>
+        </xsl:call-template>
+    </xsl:if>
+    <xsl:if test="not(contains($path,'.')) and not($path = '')">
+        <xsl:text>../</xsl:text>
+    </xsl:if>
+</xsl:template>
+
+
+<!-- create the link to the stylesheet based on the package name -->
+<xsl:template name="create.stylesheet.link">
+    <xsl:param name="package.name"/>
+    <LINK REL ="stylesheet" TYPE="text/css" TITLE="Style"><xsl:attribute name="href"><xsl:if test="not($package.name = 'unnamed package')"><xsl:call-template name="path"><xsl:with-param name="path" select="$package.name"/></xsl:call-template></xsl:if>stylesheet.css</xsl:attribute></LINK>
+</xsl:template>
+
+<!-- alternated row style -->
+<xsl:template name="alternate-row">
+<xsl:attribute name="class">
+  <xsl:if test="position() mod 2 = 1">a</xsl:if>
+  <xsl:if test="position() mod 2 = 0">b</xsl:if>
+</xsl:attribute>
+</xsl:template>
+
+</xsl:stylesheet>
+
+
Index: /release-kits/shared/ant/etc/jdepend-frames.xsl
===================================================================
--- /release-kits/shared/ant/etc/jdepend-frames.xsl	(revision 16139)
+++ /release-kits/shared/ant/etc/jdepend-frames.xsl	(revision 16139)
@@ -0,0 +1,485 @@
+<xsl:stylesheet  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+  xmlns:lxslt="http://xml.apache.org/xslt"
+  xmlns:redirect="org.apache.xalan.lib.Redirect"
+  extension-element-prefixes="redirect">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+<!--
+    Copyright  2002-2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+<!--
+
+ Sample stylesheet to be used with JDepend XML output.
+
+ It creates a set of HTML files a la javadoc where you can browse easily
+ through all packages and classes.
+
+ @author <a href="mailto:jtulley@novell.com">Jeff Tulley</a>
+
+  -->
+<xsl:param name="output.dir" select="'.'"/>
+
+<xsl:template match="JDepend">
+   <!-- create the index.html -->
+   <redirect:write file="{$output.dir}/index.html">
+      <xsl:call-template name="index.html"/>
+   </redirect:write>
+
+  <!-- create the stylesheet.css -->
+  <redirect:write file="{$output.dir}/stylesheet.css">
+    <xsl:call-template name="stylesheet.css"/>
+   </redirect:write>
+
+   <!-- create the overview-packages.html at the root -->
+  <redirect:write file="{$output.dir}/overview-summary.html">
+    <xsl:apply-templates select="." mode="overview.packages"/>
+  </redirect:write>
+
+   <!-- create the overview-packages.html at the root -->
+   <redirect:write file="{$output.dir}/overview-packages.html">
+    <xsl:apply-templates select="." mode="packages.details"/>
+  </redirect:write>
+
+   <!-- create the overview-cycles.html at the root -->
+   <redirect:write file="{$output.dir}/overview-cycles.html">
+    <xsl:apply-templates select="." mode="cycles.details"/>
+  </redirect:write>
+
+   <!-- create the overview-cycles.html at the root -->
+   <redirect:write file="{$output.dir}/overview-explanations.html">
+    <xsl:apply-templates select="." mode="explanations"/>
+  </redirect:write>
+
+  <!-- create the all-packages.html at the root -->
+   <redirect:write file="{$output.dir}/all-packages.html">
+    <xsl:apply-templates select="Packages" mode="all.packages"/>
+  </redirect:write>
+
+  <!-- create the all-cycles.html at the root -->
+  <redirect:write file="{$output.dir}/all-cycles.html">
+    <xsl:apply-templates select="Cycles" mode="all.cycles"/>
+  </redirect:write>
+</xsl:template>
+
+
+<xsl:template name="index.html">
+<html>
+   <head>
+      <title>JDepend Analysis</title>
+   </head>
+      <frameset cols="20%,80%">
+         <frameset rows="30%,70%">
+            <frame src="all-packages.html" name="packageListFrame"/>
+            <frame src="all-cycles.html" name="classListFrame"/>
+         </frameset>
+         <frame src="overview-summary.html" name="classFrame"/>
+      </frameset>
+      <noframes>
+         <h2>Frame Alert</h2>
+         <p>
+            This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+         </p>
+      </noframes>
+</html>
+</xsl:template>
+
+<!-- this is the stylesheet css to use for nearly everything -->
+<xsl:template name="stylesheet.css">
+   <style type="text/css">
+    body {
+    font:normal 68% verdana,arial,helvetica;
+    color:#000000;
+    }
+    table tr td, tr th {
+      font-size: 68%;
+    }
+    table.details tr th{
+    font-weight: bold;
+    text-align:left;
+    background:#a6caf0;
+    }
+    table.details tr td{
+    background:#eeeee0;
+    }
+
+    p {
+    line-height:1.5em;
+    margin-top:0.5em; margin-bottom:1.0em;
+    margin-left:2em;
+    margin-right:2em;
+    }
+    h1 {
+    margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
+    }
+    h2 {
+    margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
+    }
+    h3 {
+    margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
+    }
+    h4 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+    }
+    h5 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+    }
+    h6 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+    }
+    .Error {
+    font-weight:bold; color:red;
+    }
+    .Failure {
+    font-weight:bold; color:purple;
+    }
+    .Properties {
+    text-align:right;
+    }
+  </style>
+</xsl:template>
+
+<xsl:template match="JDepend" mode="overview.packages">
+   <html>
+      <head>
+         <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+      </head>
+      <body>
+         <xsl:call-template name="pageHeader"/>
+  <table width="100%"><tr align="left"><h2>Summary</h2><td>
+  </td><td align="right">
+  [summary]
+  [<a href="overview-packages.html">packages</a>]
+  [<a href="overview-cycles.html">cycles</a>]
+  [<a href="overview-explanations.html">explanations</a>]
+   </td></tr></table>
+         <table width="100%" class="details">
+            <tr>
+               <th>Package</th>
+               <th>Total Classes</th>
+               <th><a href="overview-explanations.html#EXnumber">Abstract Classes</a></th>
+               <th><a href="overview-explanations.html#EXnumber">Concrete Classes</a></th>
+               <th><a href="overview-explanations.html#EXafferent">Afferent Couplings</a></th>
+               <th><a href="overview-explanations.html#EXefferent">Efferent Couplings</a></th>
+               <th><a href="overview-explanations.html#EXabstractness">Abstractness</a></th>
+               <th><a href="overview-explanations.html#EXinstability">Instability</a></th>
+               <th><a href="overview-explanations.html#EXdistance">Distance</a></th>
+
+            </tr>
+            <xsl:for-each select="./Packages/Package">
+               <xsl:if test="count(error) = 0">
+                  <tr>
+                     <td align="left">
+                        <a>
+                           <xsl:attribute name="href">overview-packages.html#PK<xsl:value-of select="@name"/>
+                           </xsl:attribute>
+                           <xsl:value-of select="@name"/>
+                        </a>
+                     </td>
+                     <td align="right"><xsl:value-of select="Stats/TotalClasses"/></td>
+                     <td align="right"><xsl:value-of select="Stats/AbstractClasses"/></td>
+                     <td align="right"><xsl:value-of select="Stats/ConcreteClasses"/></td>
+                     <td align="right"><xsl:value-of select="Stats/Ca"/></td>
+                     <td align="right"><xsl:value-of select="Stats/Ce"/></td>
+                     <td align="right"><xsl:value-of select="Stats/A"/></td>
+                     <td align="right"><xsl:value-of select="Stats/I"/></td>
+                     <td align="right"><xsl:value-of select="Stats/D"/></td>
+                  </tr>
+               </xsl:if>
+            </xsl:for-each>
+            <xsl:for-each select="./Packages/Package">
+               <xsl:if test="count(error) &gt; 0">
+                  <tr>
+                     <td align="left">
+                        <xsl:value-of select="@name"/>
+                     </td>
+                     <td align="left" colspan="8"><xsl:value-of select="error"/></td>
+                  </tr>
+               </xsl:if>
+            </xsl:for-each>
+         </table>
+      </body>
+   </html>
+</xsl:template>
+
+<xsl:template match="JDepend" mode="packages.details">
+   <html>
+      <head>
+          <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+      </head>
+      <body>
+         <xsl:call-template name="pageHeader"/>
+  <table width="100%"><tr align="left"><h2>Packages</h2><td>
+  </td><td align="right">
+  [<a href="overview-summary.html">summary</a>]
+  [packages]
+  [<a href="overview-cycles.html">cycles</a>]
+  [<a href="overview-explanations.html">explanations</a>]
+   </td></tr></table>
+
+  <xsl:for-each select="./Packages/Package">
+    <xsl:if test="count(error) = 0">
+      <h3><a><xsl:attribute name="name">PK<xsl:value-of select="@name"/></xsl:attribute>
+      <xsl:value-of select="@name"/></a></h3>
+
+      <table width="100%"><tr>
+        <td><a href="overview-explanations.html#EXafferent">Afferent Couplings</a>: <xsl:value-of select="Stats/Ca"/></td>
+        <td><a href="overview-explanations.html#EXefferent">Efferent Couplings</a>: <xsl:value-of select="Stats/Ce"/></td>
+        <td><a href="overview-explanations.html#EXabstractness">Abstractness</a>: <xsl:value-of select="Stats/A"/></td>
+        <td><a href="overview-explanations.html#EXinstability">Instability</a>: <xsl:value-of select="Stats/I"/></td>
+        <td><a href="overview-explanations.html#EXdistance">Distance</a>: <xsl:value-of select="Stats/D"/></td>
+      </tr></table>
+
+      <table width="100%" class="details">
+        <tr>
+          <th>Abstract Classes</th>
+          <th>Concrete Classes</th>
+          <th>Used by Packages</th>
+          <th>Uses Packages</th>
+        </tr>
+        <tr>
+          <td valign="top" width="25%">
+          <xsl:if test="count(AbstractClasses/Class)=0">
+              <i>None</i>
+            </xsl:if>
+            <xsl:for-each select="AbstractClasses/Class">
+              <xsl:value-of select="node()"/><br/>
+            </xsl:for-each>
+          </td>
+          <td valign="top" width="25%">
+            <xsl:if test="count(ConcreteClasses/Class)=0">
+              <i>None</i>
+            </xsl:if>
+            <xsl:for-each select="ConcreteClasses/Class">
+              <xsl:value-of select="node()"/><br/>
+            </xsl:for-each>
+          </td>
+          <td valign="top" width="25%">
+            <xsl:if test="count(UsedBy/Package)=0">
+              <i>None</i>
+            </xsl:if>
+            <xsl:for-each select="UsedBy/Package">
+              <a>
+                        <xsl:attribute name="href">overview-packages.html#PK<xsl:value-of select="node()"/></xsl:attribute>
+                <xsl:value-of select="node()"/>
+              </a><br/>
+            </xsl:for-each>
+          </td>
+          <td valign="top" width="25%">
+            <xsl:if test="count(DependsUpon/Package)=0">
+              <i>None</i>
+            </xsl:if>
+            <xsl:for-each select="DependsUpon/Package">
+              <a>
+                        <xsl:attribute name="href">overview-packages.html#PK<xsl:value-of select="node()"/></xsl:attribute>
+                <xsl:value-of select="node()"/>
+              </a><br/>
+            </xsl:for-each>
+          </td>
+        </tr>
+      </table>
+    </xsl:if>
+  </xsl:for-each>
+  <!-- this is often a long listing; provide a lower navigation table also -->
+  <table width="100%"><tr align="left"><td></td><td align="right">
+  [<a href="overview-summary.html">summary</a>]
+  [packages]
+  [<a href="overview-cycles.html">cycles</a>]
+  [<a href="overview-explanations.html">explanations</a>]
+   </td></tr></table>
+      </body>
+   </html>
+</xsl:template>
+
+<xsl:template match="JDepend" mode="cycles.details">
+   <html>
+      <head>
+         <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+      </head>
+      <body>
+         <xsl:call-template name="pageHeader"/>
+  <table width="100%"><tr align="left"><h2>Cycles</h2><td>
+  </td><td align="right">
+  [<a href="overview-summary.html">summary</a>]
+  [<a href="overview-packages.html">packages</a>]
+  [cycles]
+  [<a href="overview-explanations.html">explanations</a>]
+   </td></tr></table>
+  <!--<table width="100%"><tr><td>
+  </td><td align="right">
+    [<a href="#NVsummary">summary</a>]
+  [<a href="#NVpackages">packages</a>]
+  [<a href="#NVcycles">cycles</a>]
+   [<a href="#NVexplanations">explanations</a>]
+  </td></tr></table> -->
+
+  <xsl:if test="count(Cycles/Package) = 0">
+    <p>There are no cyclic dependancies.</p>
+  </xsl:if>
+  <xsl:for-each select="Cycles/Package">
+     <h3><a><xsl:attribute name="name">#CY<xsl:value-of select="@Name"/></xsl:attribute><xsl:value-of select="@Name"/></a></h3><p>
+    <xsl:for-each select="Package">
+      <xsl:value-of select="."/><br/>
+    </xsl:for-each></p>
+  </xsl:for-each>
+  <!-- this is often a long listing; provide a lower navigation table also -->
+  <table width="100%"><tr align="left"><td></td><td align="right">
+  [<a href="overview-summary.html">summary</a>]
+  [<a href="overview-packages.html">packages</a>]
+  [cycles]
+  [<a href="overview-explanations.html">explanations</a>]
+   </td></tr></table>
+  </body>
+  </html>
+</xsl:template>
+
+<xsl:template match="JDepend" mode="explanations">
+   <html>
+      <head>
+         <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+      </head>
+      <body>
+         <xsl:call-template name="pageHeader"/>
+
+  <table width="100%"><tr align="left"><h2>Explanations</h2><td>
+  </td><td align="right">
+  [<a href="overview-summary.html">summary</a>]
+  [<a href="overview-packages.html">packages</a>]
+  [<a href="overview-cycles.html">cycles</a>]
+  [explanations]
+   </td></tr></table>
+
+  <p>The following explanations are for quick reference and are lifted directly from the original <a href="http://www.clarkware.com/software/JDepend.html">JDepend documentation</a>.</p>
+
+  <h3><a name="EXnumber">Number of Classes</a></h3>
+    <p>The number of concrete and abstract classes (and interfaces) in the package is an indicator of the extensibility of the package.</p>
+  <h3><a name="EXafferent">Afferent Couplings</a></h3>
+    <p>The number of other packages that depend upon classes within the package is an indicator of the package's responsibility. </p>
+  <h3><a name="EXefferent">Efferent Couplings</a></h3>
+    <p>The number of other packages that the classes in the package depend upon is an indicator of the package's independence. </p>
+  <h3><a name="EXabstractness">Abstractness</a></h3>
+    <p>The ratio of the number of abstract classes (and interfaces) in the analyzed package to the total number of classes in the analyzed package. </p>
+    <p>The range for this metric is 0 to 1, with A=0 indicating a completely concrete package and A=1 indicating a completely abstract package. </p>
+  <h3><a name="EXinstability">Instability</a></h3>
+    <p>The ratio of efferent coupling (Ce) to total coupling (Ce / (Ce + Ca)). This metric is an indicator of the package's resilience to change. </p>
+    <p>The range for this metric is 0 to 1, with I=0 indicating a completely stable package and I=1 indicating a completely instable package. </p>
+  <h3><a name="EXdistance">Distance</a></h3>
+    <p>The perpendicular distance of a package from the idealized line A + I = 1. This metric is an indicator of the package's balance between abstractness and stability. </p>
+    <p>A package squarely on the main sequence is optimally balanced with respect to its abstractness and stability. Ideal packages are either completely abstract and stable (x=0, y=1) or completely concrete and instable (x=1, y=0). </p>
+    <p>The range for this metric is 0 to 1, with D=0 indicating a package that is coincident with the main sequence and D=1 indicating a package that is as far from the main sequence as possible. </p>
+
+      </body>
+   </html>
+</xsl:template>
+
+
+<!--
+Creates an html file that contains a link to all package links in overview-packages.html.
+  @bug there will be a problem here, I don't know yet how to handle unnamed package :(
+-->
+<xsl:template match="JDepend/Packages" mode="all.packages">
+  <html>
+    <head>
+      <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+    </head>
+    <body>
+  <table width="100%"><tr align="left"><td></td><td nowrap="nowrap" align="right">
+  [<a href="overview-summary.html" target="classFrame">summary</a>]
+  [<a href="overview-packages.html" target="classFrame">packages</a>]
+  [<a href="overview-cycles.html" target="classFrame">cycles</a>]
+  [<a href="overview-explanations.html" target="classFrame">explanations</a>]
+   </td></tr></table>
+      <h2>Packages</h2>
+        <table width="100%">
+          <xsl:apply-templates select="Package[count(error)=0]" mode="all.packages.link">
+            <xsl:sort select="@name"/>
+          </xsl:apply-templates>
+          <xsl:apply-templates select="Package[count(error) &gt; 0]" mode="all.packages.nolink">
+            <xsl:sort select="@name"/>
+          </xsl:apply-templates>
+        </table>
+    </body>
+  </html>
+</xsl:template>
+
+<xsl:template match="JDepend/Packages/Package" mode="all.packages.link">
+  <tr>
+    <td nowrap="nowrap">
+         <a href="overview-packages.html#PK{@name}" target="classFrame">
+        <xsl:value-of select="@name"/>
+      </a>
+    </td>
+  </tr>
+</xsl:template>
+
+<!--
+I do not know JDepend enough to know if every error results in a non-analyzed package,
+but that is how I am presenting it to the viewer.  This may need to change.
+  @bug there will be a problem here, I don't know yet how to handle unnamed package :(
+-->
+<xsl:template match="JDepend/Packages/Package" mode="all.packages.nolink">
+  <tr>
+    <td nowrap="nowrap">
+       Not Analyzed: <xsl:value-of select="@name"/>
+    </td>
+  </tr>
+</xsl:template>
+
+<!--
+Creates an html file that contains a link to all package links in overview-cycles.html.
+  @bug there will be a problem here, I don't know yet how to handle unnamed package :(
+-->
+<xsl:template match="JDepend/Cycles" mode="all.cycles">
+  <html>
+    <head>
+      <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+    </head>
+    <body>
+  <table width="100%"><tr align="left"><td></td><td nowrap="nowrap" align="right">
+  [<a href="overview-summary.html" target="classFrame">summary</a>]
+  [<a href="overview-packages.html" target="classFrame">packages</a>]
+  [<a href="overview-cycles.html" target="classFrame">cycles</a>]
+  [<a href="overview-explanations.html" target="classFrame">explanations</a>]
+   </td></tr></table>
+      <h2>Cycles</h2>
+        <table width="100%">
+           <xsl:apply-templates select="Package" mode="all.cycles">
+            <xsl:sort select="@Name"/>
+          </xsl:apply-templates>
+        </table>
+    </body>
+  </html>
+</xsl:template>
+
+<xsl:template match="JDepend/Cycles/Package" mode="all.cycles">
+  <tr>
+    <td nowrap="nowrap">
+         <a href="overview-cycles.html#CY{@Name}" target="classFrame"><xsl:value-of select="@Name"/></a>
+    </td>
+  </tr>
+</xsl:template>
+
+<!-- Page HEADER -->
+<xsl:template name="pageHeader">
+   <h1>JDepend Analysis</h1>
+  <table width="100%">
+  <tr>
+    <td align="left"></td>
+      <td align="right">Designed for use with <a href="http://www.clarkware.com/software/JDepend.html">JDepend</a> and <a href="http://jakarta.apache.org">Ant</a>.</td>
+  </tr>
+  </table>
+  <hr size="1"/>
+</xsl:template>
+
+</xsl:stylesheet>
Index: /release-kits/shared/ant/etc/jdepend.xsl
===================================================================
--- /release-kits/shared/ant/etc/jdepend.xsl	(revision 16139)
+++ /release-kits/shared/ant/etc/jdepend.xsl	(revision 16139)
@@ -0,0 +1,275 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+<!--
+    Copyright  2002,2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+
+<xsl:output method="html" indent="yes"  encoding="US-ASCII"/>
+
+<xsl:template match="JDepend">
+    <html>
+    <head>
+        <title>JDepend Analysis</title>
+        
+    <style type="text/css">
+      body {
+        font:normal 68% verdana,arial,helvetica;
+        color:#000000;
+      }
+      table tr td, tr th {
+          font-size: 68%;
+      }
+      table.details tr th{
+        font-weight: bold;
+        text-align:left;
+        background:#a6caf0;
+      }
+      table.details tr td{
+        background:#eeeee0;
+      }
+      
+      p {
+        line-height:1.5em;
+        margin-top:0.5em; margin-bottom:1.0em;
+        margin-left:2em;
+        margin-right:2em;
+      }
+      h1 {
+        margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
+      }
+      h2 {
+        margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
+      }
+      h3 {
+        margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
+      }
+      h4 {
+        margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+      }
+      h5 {
+        margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+      }
+      h6 {
+        margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+      }
+      .Error {
+        font-weight:bold; color:red;
+      }
+      .Failure {
+        font-weight:bold; color:purple;
+      }
+      .Properties {
+        text-align:right;
+      }
+      </style>
+        
+        
+    </head>
+    <body>
+    <!--h1>JDepend Report</h1>
+    <ul>
+    <xsl:for-each select="./Packages/Package">
+                <xsl:sort select="@name"/>
+        <li><xsl:value-of select="@name"/></li>
+    </xsl:for-each>
+    </ul-->
+    
+    <h1><a name="top">JDepend Analysis</a></h1>
+    <p align="right">Designed for use with <a href="http://www.clarkware.com/software/JDepend.html">JDepend</a> and <a href="http://jakarta.apache.org">Ant</a>.</p>
+    <hr size="2" />
+    
+    <table width="100%"><tr><td>
+    <a name="NVsummary"><h2>Summary</h2></a>
+    </td><td align="right">
+    [<a href="#NVsummary">summary</a>]
+    [<a href="#NVpackages">packages</a>]
+    [<a href="#NVcycles">cycles</a>]
+    [<a href="#NVexplanations">explanations</a>]
+    </td></tr></table>
+    
+    <table width="100%" class="details">
+        <tr>
+            <th>Package</th>
+            <th>Total Classes</th>
+            <th><a href="#EXnumber">Abstract Classes</a></th>
+            <th><a href="#EXnumber">Concrete Classes</a></th>
+            <th><a href="#EXafferent">Afferent Couplings</a></th>
+            <th><a href="#EXefferent">Efferent Couplings</a></th>
+            <th><a href="#EXabstractness">Abstractness</a></th>
+            <th><a href="#EXinstability">Instability</a></th>
+            <th><a href="#EXdistance">Distance</a></th>
+            
+        </tr>
+    <xsl:for-each select="./Packages/Package">
+        <xsl:if test="count(error) = 0">
+            <tr>
+                <td align="left">
+                    <a>
+                    <xsl:attribute name="href">#PK<xsl:value-of select="@name"/>
+                    </xsl:attribute>
+                    <xsl:value-of select="@name"/>
+                    </a>
+                </td>
+                <td align="right"><xsl:value-of select="Stats/TotalClasses"/></td>
+                <td align="right"><xsl:value-of select="Stats/AbstractClasses"/></td>
+                <td align="right"><xsl:value-of select="Stats/ConcreteClasses"/></td>
+                <td align="right"><xsl:value-of select="Stats/Ca"/></td>
+                <td align="right"><xsl:value-of select="Stats/Ce"/></td>
+                <td align="right"><xsl:value-of select="Stats/A"/></td>
+                <td align="right"><xsl:value-of select="Stats/I"/></td>
+                <td align="right"><xsl:value-of select="Stats/D"/></td>
+                
+
+            </tr>
+        </xsl:if>
+    </xsl:for-each>
+    <xsl:for-each select="./Packages/Package">
+        <xsl:if test="count(error) &gt; 0">
+            <tr>
+                <td align="left">
+                    <xsl:value-of select="@name"/>
+                </td>
+                <td align="left" colspan="8"><xsl:value-of select="error"/></td>
+            </tr>
+        </xsl:if>
+    </xsl:for-each>
+    </table>
+    
+    <table width="100%"><tr><td>
+    <a name="NVpackages"><h2>Packages</h2></a>
+    </td><td align="right">
+    [<a href="#NVsummary">summary</a>]
+    [<a href="#NVpackages">packages</a>]
+    [<a href="#NVcycles">cycles</a>]
+    [<a href="#NVexplanations">explanations</a>]
+    </td></tr></table>
+    
+    <xsl:for-each select="./Packages/Package">
+        <xsl:if test="count(error) = 0">
+            <h3><a><xsl:attribute name="name">PK<xsl:value-of select="@name"/></xsl:attribute>
+            <xsl:value-of select="@name"/></a></h3>
+            
+            <table width="100%"><tr>
+                <td><a href="#EXafferent">Afferent Couplings</a>: <xsl:value-of select="Stats/Ca"/></td>
+                <td><a href="#EXefferent">Efferent Couplings</a>: <xsl:value-of select="Stats/Ce"/></td>
+                <td><a href="#EXabstractness">Abstractness</a>: <xsl:value-of select="Stats/A"/></td>
+                <td><a href="#EXinstability">Instability</a>: <xsl:value-of select="Stats/I"/></td>
+                <td><a href="#EXdistance">Distance</a>: <xsl:value-of select="Stats/D"/></td>
+            </tr></table>
+            
+            <table width="100%" class="details">
+                <tr>
+                    <th>Abstract Classes</th>
+                    <th>Concrete Classes</th>
+                    <th>Used by Packages</th>
+                    <th>Uses Packages</th>
+                </tr>
+                <tr>
+                    <td valign="top" width="25%">
+                    <xsl:if test="count(AbstractClasses/Class)=0">
+                            <i>None</i>
+                        </xsl:if>
+                        <xsl:for-each select="AbstractClasses/Class">
+                            <xsl:value-of select="node()"/><br/>
+                        </xsl:for-each>
+                    </td>
+                    <td valign="top" width="25%">
+                        <xsl:if test="count(ConcreteClasses/Class)=0">
+                            <i>None</i>
+                        </xsl:if>
+                        <xsl:for-each select="ConcreteClasses/Class">
+                            <xsl:value-of select="node()"/><br/>
+                        </xsl:for-each>
+                    </td>
+                    <td valign="top" width="25%">
+                        <xsl:if test="count(UsedBy/Package)=0">
+                            <i>None</i>
+                        </xsl:if>
+                        <xsl:for-each select="UsedBy/Package">
+                            <a>
+                                <xsl:attribute name="href">#PK<xsl:value-of select="node()"/></xsl:attribute>
+                                <xsl:value-of select="node()"/>
+                            </a><br/>
+                        </xsl:for-each>
+                    </td>
+                    <td valign="top" width="25%">
+                        <xsl:if test="count(DependsUpon/Package)=0">
+                            <i>None</i>
+                        </xsl:if>
+                        <xsl:for-each select="DependsUpon/Package">
+                            <a>
+                                <xsl:attribute name="href">#PK<xsl:value-of select="node()"/></xsl:attribute>
+                                <xsl:value-of select="node()"/>
+                            </a><br/>
+                        </xsl:for-each>
+                    </td>
+                </tr>
+            </table>
+        </xsl:if>
+    </xsl:for-each>
+    
+    <table width="100%"><tr><td>
+    <a name="NVcycles"><h2>Cycles</h2></a>
+    </td><td align="right">
+    [<a href="#NVsummary">summary</a>]
+    [<a href="#NVpackages">packages</a>]
+    [<a href="#NVcycles">cycles</a>]
+    [<a href="#NVexplanations">explanations</a>]
+    </td></tr></table>
+    
+    <xsl:if test="count(Cycles/Package) = 0">
+        <p>There are no cyclic dependancies.</p>
+    </xsl:if>
+    <xsl:for-each select="Cycles/Package">
+        <h3><xsl:value-of select="@Name"/></h3><p>
+        <xsl:for-each select="Package">
+            <xsl:value-of select="."/><br/>
+        </xsl:for-each></p>
+    </xsl:for-each>
+    
+    <table width="100%"><tr><td>
+    <a name="NVexplanations"><h2>Explanations</h2></a>
+    </td><td align="right">
+    [<a href="#NVsummary">summary</a>]
+    [<a href="#NVpackages">packages</a>]
+    [<a href="#NVcycles">cycles</a>]
+    [<a href="#NVexplanations">explanations</a>]
+    </td></tr></table>
+    
+    <p>The following explanations are for quick reference and are lifted directly from the original <a href="http://www.clarkware.com/software/JDepend.html">JDepend documentation</a>.</p>
+    
+    <h3><a name="EXnumber">Number of Classes</a></h3>
+        <p>The number of concrete and abstract classes (and interfaces) in the package is an indicator of the extensibility of the package.</p>
+    <h3><a name="EXafferent">Afferent Couplings</a></h3>
+        <p>The number of other packages that depend upon classes within the package is an indicator of the package's responsibility. </p>
+    <h3><a name="EXefferent">Efferent Couplings</a></h3>
+        <p>The number of other packages that the classes in the package depend upon is an indicator of the package's independence. </p>
+    <h3><a name="EXabstractness">Abstractness</a></h3> 
+        <p>The ratio of the number of abstract classes (and interfaces) in the analyzed package to the total number of classes in the analyzed package. </p>
+        <p>The range for this metric is 0 to 1, with A=0 indicating a completely concrete package and A=1 indicating a completely abstract package. </p>
+    <h3><a name="EXinstability">Instability</a></h3>
+        <p>The ratio of efferent coupling (Ce) to total coupling (Ce / (Ce + Ca)). This metric is an indicator of the package's resilience to change. </p>
+        <p>The range for this metric is 0 to 1, with I=0 indicating a completely stable package and I=1 indicating a completely instable package. </p>
+    <h3><a name="EXdistance">Distance</a></h3>
+        <p>The perpendicular distance of a package from the idealized line A + I = 1. This metric is an indicator of the package's balance between abstractness and stability. </p>
+        <p>A package squarely on the main sequence is optimally balanced with respect to its abstractness and stability. Ideal packages are either completely abstract and stable (x=0, y=1) or completely concrete and instable (x=1, y=0). </p>
+        <p>The range for this metric is 0 to 1, with D=0 indicating a package that is coincident with the main sequence and D=1 indicating a package that is as far from the main sequence as possible. </p>
+    
+    </body>
+    </html>
+</xsl:template>
+
+</xsl:stylesheet>
Index: /release-kits/shared/ant/etc/junit-frames-xalan1.xsl
===================================================================
--- /release-kits/shared/ant/etc/junit-frames-xalan1.xsl	(revision 16139)
+++ /release-kits/shared/ant/etc/junit-frames-xalan1.xsl	(revision 16139)
@@ -0,0 +1,716 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt"
+    xmlns:redirect="org.apache.xalan.lib.Redirect"
+    xmlns:stringutils="xalan://org.apache.tools.ant.util.StringUtils"
+    extension-element-prefixes="redirect">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+<xsl:decimal-format decimal-separator="." grouping-separator=","/>
+<!--
+   Copyright 2001-2004 The Apache Software Foundation
+
+   Licensed 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.
+ -->
+
+<!--
+
+ Sample stylesheet to be used with Ant JUnitReport output.
+
+ It creates a set of HTML files a la javadoc where you can browse easily
+ through all packages and classes.
+
+-->
+<xsl:param name="output.dir" select="'.'"/>
+
+
+<xsl:template match="testsuites">
+    <!-- create the index.html -->
+    <redirect:write file="{$output.dir}/index.html">
+        <xsl:call-template name="index.html"/>
+    </redirect:write>
+
+    <!-- create the stylesheet.css -->
+    <redirect:write file="{$output.dir}/stylesheet.css">
+        <xsl:call-template name="stylesheet.css"/>
+    </redirect:write>
+
+    <!-- create the overview-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-summary.html">
+        <xsl:apply-templates select="." mode="overview.packages"/>
+    </redirect:write>
+
+    <!-- create the all-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-frame.html">
+        <xsl:apply-templates select="." mode="all.packages"/>
+    </redirect:write>
+
+    <!-- create the all-classes.html at the root -->
+    <redirect:write file="{$output.dir}/allclasses-frame.html">
+        <xsl:apply-templates select="." mode="all.classes"/>
+    </redirect:write>
+
+    <!-- process all packages -->
+    <xsl:for-each select="./testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+        <xsl:call-template name="package">
+            <xsl:with-param name="name" select="@package"/>
+        </xsl:call-template>
+    </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template name="package">
+    <xsl:param name="name"/>
+    <xsl:variable name="package.dir">
+        <xsl:if test="not($name = '')"><xsl:value-of select="translate($name,'.','/')"/></xsl:if>
+        <xsl:if test="$name = ''">.</xsl:if>
+    </xsl:variable>
+    <!--Processing package <xsl:value-of select="@name"/> in <xsl:value-of select="$output.dir"/> -->
+    <!-- create a classes-list.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-frame.html">
+        <xsl:call-template name="classes.list">
+            <xsl:with-param name="name" select="$name"/>
+        </xsl:call-template>
+    </redirect:write>
+
+    <!-- create a package-summary.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-summary.html">
+        <xsl:call-template name="package.summary">
+            <xsl:with-param name="name" select="$name"/>
+        </xsl:call-template>
+    </redirect:write>
+
+    <!-- for each class, creates a @name.html -->
+    <!-- @bug there will be a problem with inner classes having the same name, it will be overwritten -->
+    <xsl:for-each select="/testsuites/testsuite[@package = $name]">
+        <redirect:write file="{$output.dir}/{$package.dir}/{@name}.html">
+            <xsl:apply-templates select="." mode="class.details"/>
+        </redirect:write>
+        <xsl:if test="string-length(./system-out)!=0">
+            <redirect:write file="{$output.dir}/{$package.dir}/{@name}-out.txt">
+                <xsl:value-of select="./system-out" />
+            </redirect:write>
+        </xsl:if>
+        <xsl:if test="string-length(./system-err)!=0">
+            <redirect:write file="{$output.dir}/{$package.dir}/{@name}-err.txt">
+                <xsl:value-of select="./system-err" />
+            </redirect:write>
+        </xsl:if>
+    </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="index.html">
+<html>
+    <head>
+        <title>Unit Test Results.</title>
+    </head>
+    <frameset cols="20%,80%">
+        <frameset rows="30%,70%">
+            <frame src="overview-frame.html" name="packageListFrame"/>
+            <frame src="allclasses-frame.html" name="classListFrame"/>
+        </frameset>
+        <frame src="overview-summary.html" name="classFrame"/>
+        <noframes>
+            <h2>Frame Alert</h2>
+            <p>
+                This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+            </p>
+        </noframes>
+    </frameset>
+</html>
+</xsl:template>
+
+<!-- this is the stylesheet css to use for nearly everything -->
+<xsl:template name="stylesheet.css">
+body {
+    font:normal 68% verdana,arial,helvetica;
+    color:#000000;
+}
+table tr td, table tr th {
+    font-size: 68%;
+}
+table.details tr th{
+    font-weight: bold;
+    text-align:left;
+    background:#a6caf0;
+}
+table.details tr td{
+    background:#eeeee0;
+}
+
+p {
+    line-height:1.5em;
+    margin-top:0.5em; margin-bottom:1.0em;
+}
+h1 {
+    margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
+}
+h2 {
+    margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
+}
+h3 {
+    margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
+}
+h4 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+h5 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+h6 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+.Error {
+    font-weight:bold; color:red;
+}
+.Failure {
+    font-weight:bold; color:purple;
+}
+.Properties {
+  text-align:right;
+}
+</xsl:template>
+
+
+<!-- ======================================================================
+    This page is created for every testsuite class.
+    It prints a summary of the testsuite and detailed information about
+    testcase methods.
+     ====================================================================== -->
+<xsl:template match="testsuite" mode="class.details">
+    <xsl:variable name="package.name" select="@package"/>
+    <xsl:variable name="class.name"><xsl:if test="not($package.name = '')"><xsl:value-of select="$package.name"/>.</xsl:if><xsl:value-of select="@name"/></xsl:variable>
+    <html>
+        <head>
+          <title>Unit Test Results: <xsl:value-of select="$class.name"/></title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$package.name"/>
+            </xsl:call-template>
+       <script type="text/javascript" language="JavaScript">
+        var TestCases = new Array();
+        var cur;
+        <xsl:apply-templates select="properties"/>
+       </script>
+       <script type="text/javascript" language="JavaScript"><![CDATA[
+        function displayProperties (name) {
+          var win = window.open('','JUnitSystemProperties','scrollbars=1,resizable=1');
+          var doc = win.document.open();
+          doc.write("<html><head><title>Properties of " + name + "</title>");
+          doc.write("<style type=\"text/css\">");
+          doc.write("body {font:normal 68% verdana,arial,helvetica; color:#000000; }");
+          doc.write("table tr td, table tr th { font-size: 68%; }");
+          doc.write("table.properties { border-collapse:collapse; border-left:solid 1 #cccccc; border-top:solid 1 #cccccc; padding:5px; }");
+          doc.write("table.properties th { text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#eeeeee; }");
+          doc.write("table.properties td { font:normal; text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#fffffff; }");
+          doc.write("h3 { margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica }");
+          doc.write("</style>");
+          doc.write("</head><body>");
+          doc.write("<h3>Properties of " + name + "</h3>");
+          doc.write("<div align=\"right\"><a href=\"javascript:window.close();\">Close</a></div>");
+          doc.write("<table class='properties'>");
+          doc.write("<tr><th>Name</th><th>Value</th></tr>");
+          for (prop in TestCases[name]) {
+            doc.write("<tr><th>" + prop + "</th><td>" + TestCases[name][prop] + "</td></tr>");
+          }
+          doc.write("</table>");
+          doc.write("</body></html>");
+          doc.close();
+          win.focus();
+        }
+      ]]>
+      </script>
+        </head>
+        <body>
+            <xsl:call-template name="pageHeader"/>
+            <h3>Class <xsl:value-of select="$class.name"/></h3>
+
+
+            <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+                <xsl:call-template name="testsuite.test.header"/>
+                <xsl:apply-templates select="." mode="print.test"/>
+            </table>
+
+            <h2>Tests</h2>
+            <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+        <xsl:call-template name="testcase.test.header"/>
+              <!--
+              test can even not be started at all (failure to load the class)
+              so report the error directly
+              -->
+                <xsl:if test="./error">
+                    <tr class="Error">
+                        <td colspan="4"><xsl:apply-templates select="./error"/></td>
+                    </tr>
+                </xsl:if>
+                <xsl:apply-templates select="./testcase" mode="print.test"/>
+            </table>
+            <div class="Properties">
+                <a>
+                    <xsl:attribute name="href">javascript:displayProperties('<xsl:value-of select="@package"/>.<xsl:value-of select="@name"/>');</xsl:attribute>
+                    Properties &#187;
+                </a>
+            </div>
+            <xsl:if test="string-length(./system-out)!=0">
+                <div class="Properties">
+                    <a>
+                        <xsl:attribute name="href">./<xsl:value-of select="@name"/>-out.txt</xsl:attribute>
+                        System.out &#187;
+                    </a>
+                </div>
+            </xsl:if>
+            <xsl:if test="string-length(./system-err)!=0">
+                <div class="Properties">
+                    <a>
+                        <xsl:attribute name="href">./<xsl:value-of select="@name"/>-err.txt</xsl:attribute>
+                        System.err &#187;
+                    </a>
+                </div>
+            </xsl:if>
+        </body>
+    </html>
+</xsl:template>
+
+  <!--
+   Write properties into a JavaScript data structure.
+   This is based on the original idea by Erik Hatcher (ehatcher@apache.org)
+   -->
+  <xsl:template match="properties">
+    cur = TestCases['<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>'] = new Array();
+    <xsl:for-each select="property">
+    <xsl:sort select="@name"/>
+        cur['<xsl:value-of select="@name"/>'] = '<xsl:call-template name="JS-escape"><xsl:with-param name="string" select="@value"/></xsl:call-template>';
+    </xsl:for-each>
+  </xsl:template>
+
+
+<!-- ======================================================================
+    This page is created for every package.
+    It prints the name of all classes that belongs to this package.
+    @param name the package name to print classes.
+     ====================================================================== -->
+<!-- list of classes in a package -->
+<xsl:template name="classes.list">
+    <xsl:param name="name"/>
+    <html>
+        <head>
+            <title>Unit Test Classes: <xsl:value-of select="$name"/></title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <table width="100%">
+                <tr>
+                    <td nowrap="nowrap">
+                        <h2><a href="package-summary.html" target="classFrame">
+                            <xsl:value-of select="$name"/>
+                            <xsl:if test="$name = ''">&lt;none&gt;</xsl:if>
+                        </a></h2>
+                    </td>
+                </tr>
+            </table>
+
+            <h2>Classes</h2>
+            <table width="100%">
+                <xsl:for-each select="/testsuites/testsuite[./@package = $name]">
+                    <xsl:sort select="@name"/>
+                    <tr>
+                        <td nowrap="nowrap">
+                            <a href="{@name}.html" target="classFrame"><xsl:value-of select="@name"/></a>
+                        </td>
+                    </tr>
+                </xsl:for-each>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+
+<!--
+    Creates an all-classes.html file that contains a link to all package-summary.html
+    on each class.
+-->
+<xsl:template match="testsuites" mode="all.classes">
+    <html>
+        <head>
+            <title>All Unit Test Classes</title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <h2>Classes</h2>
+            <table width="100%">
+                <xsl:apply-templates select="testsuite" mode="all.classes">
+                    <xsl:sort select="@name"/>
+                </xsl:apply-templates>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+<xsl:template match="testsuite" mode="all.classes">
+    <xsl:variable name="package.name" select="@package"/>
+    <tr>
+        <td nowrap="nowrap">
+            <a target="classFrame">
+                <xsl:attribute name="href">
+                    <xsl:if test="not($package.name='')">
+                        <xsl:value-of select="translate($package.name,'.','/')"/><xsl:text>/</xsl:text>
+                    </xsl:if><xsl:value-of select="@name"/><xsl:text>.html</xsl:text>
+                </xsl:attribute>
+                <xsl:value-of select="@name"/>
+            </a>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<!--
+    Creates an html file that contains a link to all package-summary.html files on
+    each package existing on testsuites.
+    @bug there will be a problem here, I don't know yet how to handle unnamed package :(
+-->
+<xsl:template match="testsuites" mode="all.packages">
+    <html>
+        <head>
+            <title>All Unit Test Packages</title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <h2><a href="overview-summary.html" target="classFrame">Home</a></h2>
+            <h2>Packages</h2>
+            <table width="100%">
+                <xsl:apply-templates select="testsuite[not(./@package = preceding-sibling::testsuite/@package)]" mode="all.packages">
+                    <xsl:sort select="@package"/>
+                </xsl:apply-templates>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+<xsl:template match="testsuite" mode="all.packages">
+    <tr>
+        <td nowrap="nowrap">
+            <a href="./{translate(@package,'.','/')}/package-summary.html" target="classFrame">
+                <xsl:value-of select="@package"/>
+                <xsl:if test="@package = ''">&lt;none&gt;</xsl:if>
+            </a>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<xsl:template match="testsuites" mode="overview.packages">
+    <html>
+        <head>
+            <title>Unit Test Results: Summary</title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+        <xsl:attribute name="onload">open('allclasses-frame.html','classListFrame')</xsl:attribute>
+        <xsl:call-template name="pageHeader"/>
+        <h2>Summary</h2>
+        <xsl:variable name="testCount" select="sum(testsuite/@tests)"/>
+        <xsl:variable name="errorCount" select="sum(testsuite/@errors)"/>
+        <xsl:variable name="failureCount" select="sum(testsuite/@failures)"/>
+        <xsl:variable name="timeCount" select="sum(testsuite/@time)"/>
+        <xsl:variable name="successRate" select="($testCount - $failureCount - $errorCount) div $testCount"/>
+        <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+        <tr valign="top">
+            <th>Tests</th>
+            <th>Failures</th>
+            <th>Errors</th>
+            <th>Success rate</th>
+            <th>Time</th>
+        </tr>
+        <tr valign="top">
+            <xsl:attribute name="class">
+                <xsl:choose>
+                    <xsl:when test="$errorCount &gt; 0">Error</xsl:when>
+                    <xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
+                    <xsl:otherwise>Pass</xsl:otherwise>
+                </xsl:choose>
+            </xsl:attribute>
+            <td><xsl:value-of select="$testCount"/></td>
+            <td><xsl:value-of select="$failureCount"/></td>
+            <td><xsl:value-of select="$errorCount"/></td>
+            <td>
+                <xsl:call-template name="display-percent">
+                    <xsl:with-param name="value" select="$successRate"/>
+                </xsl:call-template>
+            </td>
+            <td>
+                <xsl:call-template name="display-time">
+                    <xsl:with-param name="value" select="$timeCount"/>
+                </xsl:call-template>
+            </td>
+        </tr>
+        </table>
+        <table border="0" width="95%">
+        <tr>
+        <td style="text-align: justify;">
+        Note: <em>failures</em> are anticipated and checked for with assertions while <em>errors</em> are unanticipated.
+        </td>
+        </tr>
+        </table>
+
+        <h2>Packages</h2>
+        <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+            <xsl:call-template name="testsuite.test.header"/>
+            <xsl:for-each select="testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+                <xsl:sort select="@package" order="ascending"/>
+                <!-- get the node set containing all testsuites that have the same package -->
+                <xsl:variable name="insamepackage" select="/testsuites/testsuite[./@package = current()/@package]"/>
+                <tr valign="top">
+                    <!-- display a failure if there is any failure/error in the package -->
+                    <xsl:attribute name="class">
+                        <xsl:choose>
+                            <xsl:when test="sum($insamepackage/@errors) &gt; 0">Error</xsl:when>
+                            <xsl:when test="sum($insamepackage/@failures) &gt; 0">Failure</xsl:when>
+                            <xsl:otherwise>Pass</xsl:otherwise>
+                        </xsl:choose>
+                    </xsl:attribute>
+                    <td><a href="./{translate(@package,'.','/')}/package-summary.html">
+                        <xsl:value-of select="@package"/>
+                        <xsl:if test="@package = ''">&lt;none&gt;</xsl:if>
+                    </a></td>
+                    <td><xsl:value-of select="sum($insamepackage/@tests)"/></td>
+                    <td><xsl:value-of select="sum($insamepackage/@errors)"/></td>
+                    <td><xsl:value-of select="sum($insamepackage/@failures)"/></td>
+                    <td>
+                    <xsl:call-template name="display-time">
+                        <xsl:with-param name="value" select="sum($insamepackage/@time)"/>
+                    </xsl:call-template>
+                    </td>
+                    <td><xsl:value-of select="$insamepackage/@timestamp"/></td>
+                    <td><xsl:value-of select="$insamepackage/@hostname"/></td>
+                </tr>
+            </xsl:for-each>
+        </table>
+        </body>
+        </html>
+</xsl:template>
+
+
+<xsl:template name="package.summary">
+    <xsl:param name="name"/>
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <xsl:attribute name="onload">open('package-frame.html','classListFrame')</xsl:attribute>
+            <xsl:call-template name="pageHeader"/>
+            <h3>Package <xsl:value-of select="$name"/></h3>
+
+            <!--table border="0" cellpadding="5" cellspacing="2" width="95%">
+                <xsl:call-template name="class.metrics.header"/>
+                <xsl:apply-templates select="." mode="print.metrics"/>
+            </table-->
+
+            <xsl:variable name="insamepackage" select="/testsuites/testsuite[./@package = $name]"/>
+            <xsl:if test="count($insamepackage) &gt; 0">
+                <h2>Classes</h2>
+                <p>
+                <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+                    <xsl:call-template name="testsuite.test.header"/>
+                    <xsl:apply-templates select="$insamepackage" mode="print.test">
+                        <xsl:sort select="@name"/>
+                    </xsl:apply-templates>
+                </table>
+                </p>
+            </xsl:if>
+        </body>
+    </html>
+</xsl:template>
+
+
+<!--
+    transform string like a.b.c to ../../../
+    @param path the path to transform into a descending directory path
+-->
+<xsl:template name="path">
+    <xsl:param name="path"/>
+    <xsl:if test="contains($path,'.')">
+        <xsl:text>../</xsl:text>
+        <xsl:call-template name="path">
+            <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'.')"/></xsl:with-param>
+        </xsl:call-template>
+    </xsl:if>
+    <xsl:if test="not(contains($path,'.')) and not($path = '')">
+        <xsl:text>../</xsl:text>
+    </xsl:if>
+</xsl:template>
+
+
+<!-- create the link to the stylesheet based on the package name -->
+<xsl:template name="create.stylesheet.link">
+    <xsl:param name="package.name"/>
+    <link rel="stylesheet" type="text/css" title="Style"><xsl:attribute name="href"><xsl:if test="not($package.name = 'unnamed package')"><xsl:call-template name="path"><xsl:with-param name="path" select="$package.name"/></xsl:call-template></xsl:if>stylesheet.css</xsl:attribute></link>
+</xsl:template>
+
+
+<!-- Page HEADER -->
+<xsl:template name="pageHeader">
+    <h1>Unit Test Results</h1>
+    <table width="100%">
+    <tr>
+        <td align="left"></td>
+        <td align="right">Designed for use with <a href="http://www.junit.org/">JUnit</a> and <a href="http://jakarta.apache.org/">Ant</a>.</td>
+    </tr>
+    </table>
+    <hr size="1"/>
+</xsl:template>
+
+<!-- class header -->
+<xsl:template name="testsuite.test.header">
+    <tr valign="top">
+        <th width="80%">Name</th>
+        <th>Tests</th>
+        <th>Errors</th>
+        <th>Failures</th>
+        <th nowrap="nowrap">Time(s)</th>
+        <th nowrap="nowrap">Time Stamp</th>
+        <th>Host</th>
+    </tr>
+</xsl:template>
+
+<!-- method header -->
+<xsl:template name="testcase.test.header">
+    <tr valign="top">
+        <th>Name</th>
+        <th>Status</th>
+        <th width="80%">Type</th>
+        <th nowrap="nowrap">Time(s)</th>
+    </tr>
+</xsl:template>
+
+
+<!-- class information -->
+<xsl:template match="testsuite" mode="print.test">
+    <tr valign="top">
+        <xsl:attribute name="class">
+            <xsl:choose>
+                <xsl:when test="@errors[.&gt; 0]">Error</xsl:when>
+                <xsl:when test="@failures[.&gt; 0]">Failure</xsl:when>
+                <xsl:otherwise>Pass</xsl:otherwise>
+            </xsl:choose>
+        </xsl:attribute>
+        <td><a href="{@name}.html"><xsl:value-of select="@name"/></a></td>
+        <td><xsl:apply-templates select="@tests"/></td>
+        <td><xsl:apply-templates select="@errors"/></td>
+        <td><xsl:apply-templates select="@failures"/></td>
+        <td><xsl:call-template name="display-time">
+                <xsl:with-param name="value" select="@time"/>
+            </xsl:call-template>
+        </td>
+        <td><xsl:apply-templates select="@timestamp"/></td>
+        <td><xsl:apply-templates select="@hostname"/></td>
+    </tr>
+</xsl:template>
+
+<xsl:template match="testcase" mode="print.test">
+    <tr valign="top">
+        <xsl:attribute name="class">
+            <xsl:choose>
+                <xsl:when test="error">Error</xsl:when>
+                <xsl:when test="failure">Failure</xsl:when>
+                <xsl:otherwise>TableRowColor</xsl:otherwise>
+            </xsl:choose>
+        </xsl:attribute>
+        <td><xsl:value-of select="@name"/></td>
+        <xsl:choose>
+            <xsl:when test="failure">
+                <td>Failure</td>
+                <td><xsl:apply-templates select="failure"/></td>
+            </xsl:when>
+            <xsl:when test="error">
+                <td>Error</td>
+                <td><xsl:apply-templates select="error"/></td>
+            </xsl:when>
+            <xsl:otherwise>
+                <td>Success</td>
+                <td></td>
+            </xsl:otherwise>
+        </xsl:choose>
+        <td>
+            <xsl:call-template name="display-time">
+                <xsl:with-param name="value" select="@time"/>
+            </xsl:call-template>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<!-- Note : the below template error and failure are the same style
+            so just call the same style store in the toolkit template -->
+<xsl:template match="failure">
+    <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<xsl:template match="error">
+    <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<!-- Style for the error and failure in the testcase template -->
+<xsl:template name="display-failures">
+    <xsl:choose>
+        <xsl:when test="not(@message)">N/A</xsl:when>
+        <xsl:otherwise>
+            <xsl:value-of select="@message"/>
+        </xsl:otherwise>
+    </xsl:choose>
+    <!-- display the stacktrace -->
+    <br/><br/>
+    <code>
+        <xsl:call-template name="br-replace">
+            <xsl:with-param name="word" select="."/>
+        </xsl:call-template>
+    </code>
+    <!-- the latter is better but might be problematic for non-21" monitors... -->
+    <!--pre><xsl:value-of select="."/></pre-->
+</xsl:template>
+
+<xsl:template name="JS-escape">
+    <xsl:param name="string"/>
+    <xsl:param name="tmp1" select="stringutils:replace(string($string),'\','\\')"/>
+    <xsl:param name="tmp2" select="stringutils:replace(string($tmp1),&quot;'&quot;,&quot;\&apos;&quot;)"/>
+    <xsl:value-of select="$tmp2"/>
+</xsl:template>
+
+
+<!--
+    template that will convert a carriage return into a br tag
+    @param word the text from which to convert CR to BR tag
+-->
+<xsl:template name="br-replace">
+    <xsl:param name="word"/>
+    <xsl:param name="br"><br/></xsl:param>
+    <xsl:value-of select='stringutils:replace(string($word),"&#xA;",$br)'/>
+</xsl:template>
+
+<xsl:template name="display-time">
+    <xsl:param name="value"/>
+    <xsl:value-of select="format-number($value,'0.000')"/>
+</xsl:template>
+
+<xsl:template name="display-percent">
+    <xsl:param name="value"/>
+    <xsl:value-of select="format-number($value,'0.00%')"/>
+</xsl:template>
+</xsl:stylesheet>
Index: /release-kits/shared/ant/etc/junit-frames.xsl
===================================================================
--- /release-kits/shared/ant/etc/junit-frames.xsl	(revision 16139)
+++ /release-kits/shared/ant/etc/junit-frames.xsl	(revision 16139)
@@ -0,0 +1,712 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt"
+    xmlns:redirect="http://xml.apache.org/xalan/redirect"
+    xmlns:stringutils="xalan://org.apache.tools.ant.util.StringUtils"
+    extension-element-prefixes="redirect">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+<xsl:decimal-format decimal-separator="." grouping-separator=","/>
+<!--
+   Copyright 2001-2005 The Apache Software Foundation
+
+   Licensed 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.
+ -->
+
+<!--
+
+ Sample stylesheet to be used with Ant JUnitReport output.
+
+ It creates a set of HTML files a la javadoc where you can browse easily
+ through all packages and classes.
+
+-->
+<xsl:param name="output.dir" select="'.'"/>
+
+
+<xsl:template match="testsuites">
+    <!-- create the index.html -->
+    <redirect:write file="{$output.dir}/index.html">
+        <xsl:call-template name="index.html"/>
+    </redirect:write>
+
+    <!-- create the stylesheet.css -->
+    <redirect:write file="{$output.dir}/stylesheet.css">
+        <xsl:call-template name="stylesheet.css"/>
+    </redirect:write>
+
+    <!-- create the overview-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-summary.html">
+        <xsl:apply-templates select="." mode="overview.packages"/>
+    </redirect:write>
+
+    <!-- create the all-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-frame.html">
+        <xsl:apply-templates select="." mode="all.packages"/>
+    </redirect:write>
+
+    <!-- create the all-classes.html at the root -->
+    <redirect:write file="{$output.dir}/allclasses-frame.html">
+        <xsl:apply-templates select="." mode="all.classes"/>
+    </redirect:write>
+
+    <!-- process all packages -->
+    <xsl:for-each select="./testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+        <xsl:call-template name="package">
+            <xsl:with-param name="name" select="@package"/>
+        </xsl:call-template>
+    </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template name="package">
+    <xsl:param name="name"/>
+    <xsl:variable name="package.dir">
+        <xsl:if test="not($name = '')"><xsl:value-of select="translate($name,'.','/')"/></xsl:if>
+        <xsl:if test="$name = ''">.</xsl:if>
+    </xsl:variable>
+    <!--Processing package <xsl:value-of select="@name"/> in <xsl:value-of select="$output.dir"/> -->
+    <!-- create a classes-list.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-frame.html">
+        <xsl:call-template name="classes.list">
+            <xsl:with-param name="name" select="$name"/>
+        </xsl:call-template>
+    </redirect:write>
+
+    <!-- create a package-summary.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-summary.html">
+        <xsl:call-template name="package.summary">
+            <xsl:with-param name="name" select="$name"/>
+        </xsl:call-template>
+    </redirect:write>
+
+    <!-- for each class, creates a @name.html -->
+    <!-- @bug there will be a problem with inner classes having the same name, it will be overwritten -->
+    <xsl:for-each select="/testsuites/testsuite[@package = $name]">
+        <redirect:write file="{$output.dir}/{$package.dir}/{@id}_{@name}.html">
+            <xsl:apply-templates select="." mode="class.details"/>
+        </redirect:write>
+        <xsl:if test="string-length(./system-out)!=0">
+            <redirect:write file="{$output.dir}/{$package.dir}/{@id}_{@name}-out.txt">
+                <xsl:value-of select="./system-out" />
+            </redirect:write>
+        </xsl:if>
+        <xsl:if test="string-length(./system-err)!=0">
+            <redirect:write file="{$output.dir}/{$package.dir}/{@id}_{@name}-err.txt">
+                <xsl:value-of select="./system-err" />
+            </redirect:write>
+        </xsl:if>
+    </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="index.html">
+<html>
+    <head>
+        <title>Unit Test Results.</title>
+    </head>
+    <frameset cols="20%,80%">
+        <frameset rows="30%,70%">
+            <frame src="overview-frame.html" name="packageListFrame"/>
+            <frame src="allclasses-frame.html" name="classListFrame"/>
+        </frameset>
+        <frame src="overview-summary.html" name="classFrame"/>
+        <noframes>
+            <h2>Frame Alert</h2>
+            <p>
+                This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+            </p>
+        </noframes>
+    </frameset>
+</html>
+</xsl:template>
+
+<!-- this is the stylesheet css to use for nearly everything -->
+<xsl:template name="stylesheet.css">
+body {
+    font:normal 68% verdana,arial,helvetica;
+    color:#000000;
+}
+table tr td, table tr th {
+    font-size: 68%;
+}
+table.details tr th{
+    font-weight: bold;
+    text-align:left;
+    background:#a6caf0;
+}
+table.details tr td{
+    background:#eeeee0;
+}
+
+p {
+    line-height:1.5em;
+    margin-top:0.5em; margin-bottom:1.0em;
+}
+h1 {
+    margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
+}
+h2 {
+    margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
+}
+h3 {
+    margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
+}
+h4 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+h5 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+h6 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+.Error {
+    font-weight:bold; color:red;
+}
+.Failure {
+    font-weight:bold; color:purple;
+}
+.Properties {
+  text-align:right;
+}
+</xsl:template>
+
+
+<!-- ======================================================================
+    This page is created for every testsuite class.
+    It prints a summary of the testsuite and detailed information about
+    testcase methods.
+     ====================================================================== -->
+<xsl:template match="testsuite" mode="class.details">
+    <xsl:variable name="package.name" select="@package"/>
+    <xsl:variable name="class.name"><xsl:if test="not($package.name = '')"><xsl:value-of select="$package.name"/>.</xsl:if><xsl:value-of select="@name"/></xsl:variable>
+    <html>
+        <head>
+          <title>Unit Test Results: <xsl:value-of select="$class.name"/></title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$package.name"/>
+            </xsl:call-template>
+       <script type="text/javascript" language="JavaScript">
+        var TestCases = new Array();
+        var cur;
+        <xsl:apply-templates select="properties"/>
+       </script>
+       <script type="text/javascript" language="JavaScript"><![CDATA[
+        function displayProperties (name) {
+          var win = window.open('','JUnitSystemProperties','scrollbars=1,resizable=1');
+          var doc = win.document.open();
+          doc.write("<html><head><title>Properties of " + name + "</title>");
+          doc.write("<style type=\"text/css\">");
+          doc.write("body {font:normal 68% verdana,arial,helvetica; color:#000000; }");
+          doc.write("table tr td, table tr th { font-size: 68%; }");
+          doc.write("table.properties { border-collapse:collapse; border-left:solid 1 #cccccc; border-top:solid 1 #cccccc; padding:5px; }");
+          doc.write("table.properties th { text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#eeeeee; }");
+          doc.write("table.properties td { font:normal; text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#fffffff; }");
+          doc.write("h3 { margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica }");
+          doc.write("</style>");
+          doc.write("</head><body>");
+          doc.write("<h3>Properties of " + name + "</h3>");
+          doc.write("<div align=\"right\"><a href=\"javascript:window.close();\">Close</a></div>");
+          doc.write("<table class='properties'>");
+          doc.write("<tr><th>Name</th><th>Value</th></tr>");
+          for (prop in TestCases[name]) {
+            doc.write("<tr><th>" + prop + "</th><td>" + TestCases[name][prop] + "</td></tr>");
+          }
+          doc.write("</table>");
+          doc.write("</body></html>");
+          doc.close();
+          win.focus();
+        }
+      ]]>
+      </script>
+        </head>
+        <body>
+            <xsl:call-template name="pageHeader"/>
+            <h3>Class <xsl:value-of select="$class.name"/></h3>
+
+
+            <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+                <xsl:call-template name="testsuite.test.header"/>
+                <xsl:apply-templates select="." mode="print.test"/>
+            </table>
+
+            <h2>Tests</h2>
+            <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+        <xsl:call-template name="testcase.test.header"/>
+              <!--
+              test can even not be started at all (failure to load the class)
+              so report the error directly
+              -->
+                <xsl:if test="./error">
+                    <tr class="Error">
+                        <td colspan="4"><xsl:apply-templates select="./error"/></td>
+                    </tr>
+                </xsl:if>
+                <xsl:apply-templates select="./testcase" mode="print.test"/>
+            </table>
+            <div class="Properties">
+                <a>
+                    <xsl:attribute name="href">javascript:displayProperties('<xsl:value-of select="@package"/>.<xsl:value-of select="@name"/>');</xsl:attribute>
+                    Properties &#187;
+                </a>
+            </div>
+            <xsl:if test="string-length(./system-out)!=0">
+                <div class="Properties">
+                    <a>
+                        <xsl:attribute name="href">./<xsl:value-of select="@id"/>_<xsl:value-of select="@name"/>-out.txt</xsl:attribute>
+                        System.out &#187;
+                    </a>
+                </div>
+            </xsl:if>
+            <xsl:if test="string-length(./system-err)!=0">
+                <div class="Properties">
+                    <a>
+                        <xsl:attribute name="href">./<xsl:value-of select="@id"/>_<xsl:value-of select="@name"/>-err.txt</xsl:attribute>
+                        System.err &#187;
+                    </a>
+                </div>
+            </xsl:if>
+        </body>
+    </html>
+</xsl:template>
+
+  <!--
+   Write properties into a JavaScript data structure.
+   This is based on the original idea by Erik Hatcher (ehatcher@apache.org)
+   -->
+  <xsl:template match="properties">
+    cur = TestCases['<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>'] = new Array();
+    <xsl:for-each select="property">
+    <xsl:sort select="@name"/>
+        cur['<xsl:value-of select="@name"/>'] = '<xsl:call-template name="JS-escape"><xsl:with-param name="string" select="@value"/></xsl:call-template>';
+    </xsl:for-each>
+  </xsl:template>
+
+
+<!-- ======================================================================
+    This page is created for every package.
+    It prints the name of all classes that belongs to this package.
+    @param name the package name to print classes.
+     ====================================================================== -->
+<!-- list of classes in a package -->
+<xsl:template name="classes.list">
+    <xsl:param name="name"/>
+    <html>
+        <head>
+            <title>Unit Test Classes: <xsl:value-of select="$name"/></title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <table width="100%">
+                <tr>
+                    <td nowrap="nowrap">
+                        <h2><a href="package-summary.html" target="classFrame">
+                            <xsl:value-of select="$name"/>
+                            <xsl:if test="$name = ''">&lt;none&gt;</xsl:if>
+                        </a></h2>
+                    </td>
+                </tr>
+            </table>
+
+            <h2>Classes</h2>
+            <table width="100%">
+                <xsl:for-each select="/testsuites/testsuite[./@package = $name]">
+                    <xsl:sort select="@name"/>
+                    <tr>
+                        <td nowrap="nowrap">
+                            <a href="{@id}_{@name}.html" target="classFrame"><xsl:value-of select="@name"/></a>
+                        </td>
+                    </tr>
+                </xsl:for-each>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+
+<!--
+    Creates an all-classes.html file that contains a link to all package-summary.html
+    on each class.
+-->
+<xsl:template match="testsuites" mode="all.classes">
+    <html>
+        <head>
+            <title>All Unit Test Classes</title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <h2>Classes</h2>
+            <table width="100%">
+                <xsl:apply-templates select="testsuite" mode="all.classes">
+                    <xsl:sort select="@name"/>
+                </xsl:apply-templates>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+<xsl:template match="testsuite" mode="all.classes">
+    <xsl:variable name="package.name" select="@package"/>
+    <tr>
+        <td nowrap="nowrap">
+            <a target="classFrame">
+                <xsl:attribute name="href">
+                    <xsl:if test="not($package.name='')">
+                        <xsl:value-of select="translate($package.name,'.','/')"/><xsl:text>/</xsl:text>
+                    </xsl:if><xsl:value-of select="@id"/>_<xsl:value-of select="@name"/><xsl:text>.html</xsl:text>
+                </xsl:attribute>
+                <xsl:value-of select="@name"/>
+            </a>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<!--
+    Creates an html file that contains a link to all package-summary.html files on
+    each package existing on testsuites.
+    @bug there will be a problem here, I don't know yet how to handle unnamed package :(
+-->
+<xsl:template match="testsuites" mode="all.packages">
+    <html>
+        <head>
+            <title>All Unit Test Packages</title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <h2><a href="overview-summary.html" target="classFrame">Home</a></h2>
+            <h2>Packages</h2>
+            <table width="100%">
+                <xsl:apply-templates select="testsuite[not(./@package = preceding-sibling::testsuite/@package)]" mode="all.packages">
+                    <xsl:sort select="@package"/>
+                </xsl:apply-templates>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+<xsl:template match="testsuite" mode="all.packages">
+    <tr>
+        <td nowrap="nowrap">
+            <a href="./{translate(@package,'.','/')}/package-summary.html" target="classFrame">
+                <xsl:value-of select="@package"/>
+                <xsl:if test="@package = ''">&lt;none&gt;</xsl:if>
+            </a>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<xsl:template match="testsuites" mode="overview.packages">
+    <html>
+        <head>
+            <title>Unit Test Results: Summary</title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+        <xsl:attribute name="onload">open('allclasses-frame.html','classListFrame')</xsl:attribute>
+        <xsl:call-template name="pageHeader"/>
+        <h2>Summary</h2>
+        <xsl:variable name="testCount" select="sum(testsuite/@tests)"/>
+        <xsl:variable name="errorCount" select="sum(testsuite/@errors)"/>
+        <xsl:variable name="failureCount" select="sum(testsuite/@failures)"/>
+        <xsl:variable name="timeCount" select="sum(testsuite/@time)"/>
+        <xsl:variable name="successRate" select="($testCount - $failureCount - $errorCount) div $testCount"/>
+        <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+        <tr valign="top">
+            <th>Tests</th>
+            <th>Failures</th>
+            <th>Errors</th>
+            <th>Success rate</th>
+            <th>Time</th>
+        </tr>
+        <tr valign="top">
+            <xsl:attribute name="class">
+                <xsl:choose>
+                    <xsl:when test="$errorCount &gt; 0">Error</xsl:when>
+                    <xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
+                    <xsl:otherwise>Pass</xsl:otherwise>
+                </xsl:choose>
+            </xsl:attribute>
+            <td><xsl:value-of select="$testCount"/></td>
+            <td><xsl:value-of select="$failureCount"/></td>
+            <td><xsl:value-of select="$errorCount"/></td>
+            <td>
+                <xsl:call-template name="display-percent">
+                    <xsl:with-param name="value" select="$successRate"/>
+                </xsl:call-template>
+            </td>
+            <td>
+                <xsl:call-template name="display-time">
+                    <xsl:with-param name="value" select="$timeCount"/>
+                </xsl:call-template>
+            </td>
+
+        </tr>
+        </table>
+        <table border="0" width="95%">
+        <tr>
+        <td style="text-align: justify;">
+        Note: <em>failures</em> are anticipated and checked for with assertions while <em>errors</em> are unanticipated.
+        </td>
+        </tr>
+        </table>
+
+        <h2>Packages</h2>
+        <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+            <xsl:call-template name="testsuite.test.header"/>
+            <xsl:for-each select="testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+                <xsl:sort select="@package" order="ascending"/>
+                <!-- get the node set containing all testsuites that have the same package -->
+                <xsl:variable name="insamepackage" select="/testsuites/testsuite[./@package = current()/@package]"/>
+                <tr valign="top">
+                    <!-- display a failure if there is any failure/error in the package -->
+                    <xsl:attribute name="class">
+                        <xsl:choose>
+                            <xsl:when test="sum($insamepackage/@errors) &gt; 0">Error</xsl:when>
+                            <xsl:when test="sum($insamepackage/@failures) &gt; 0">Failure</xsl:when>
+                            <xsl:otherwise>Pass</xsl:otherwise>
+                        </xsl:choose>
+                    </xsl:attribute>
+                    <td><a href="./{translate(@package,'.','/')}/package-summary.html">
+                        <xsl:value-of select="@package"/>
+                        <xsl:if test="@package = ''">&lt;none&gt;</xsl:if>
+                    </a></td>
+                    <td><xsl:value-of select="sum($insamepackage/@tests)"/></td>
+                    <td><xsl:value-of select="sum($insamepackage/@errors)"/></td>
+                    <td><xsl:value-of select="sum($insamepackage/@failures)"/></td>
+                    <td>
+                    <xsl:call-template name="display-time">
+                        <xsl:with-param name="value" select="sum($insamepackage/@time)"/>
+                    </xsl:call-template>
+                    </td>
+                </tr>
+            </xsl:for-each>
+        </table>
+        </body>
+        </html>
+</xsl:template>
+
+
+<xsl:template name="package.summary">
+    <xsl:param name="name"/>
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <xsl:attribute name="onload">open('package-frame.html','classListFrame')</xsl:attribute>
+            <xsl:call-template name="pageHeader"/>
+            <h3>Package <xsl:value-of select="$name"/></h3>
+
+            <!--table border="0" cellpadding="5" cellspacing="2" width="95%">
+                <xsl:call-template name="class.metrics.header"/>
+                <xsl:apply-templates select="." mode="print.metrics"/>
+            </table-->
+
+            <xsl:variable name="insamepackage" select="/testsuites/testsuite[./@package = $name]"/>
+            <xsl:if test="count($insamepackage) &gt; 0">
+                <h2>Classes</h2>
+                <p>
+                <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+                    <xsl:call-template name="testsuite.test.header"/>
+                    <xsl:apply-templates select="$insamepackage" mode="print.test">
+                        <xsl:sort select="@name"/>
+                    </xsl:apply-templates>
+                </table>
+                </p>
+            </xsl:if>
+        </body>
+    </html>
+</xsl:template>
+
+
+<!--
+    transform string like a.b.c to ../../../
+    @param path the path to transform into a descending directory path
+-->
+<xsl:template name="path">
+    <xsl:param name="path"/>
+    <xsl:if test="contains($path,'.')">
+        <xsl:text>../</xsl:text>
+        <xsl:call-template name="path">
+            <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'.')"/></xsl:with-param>
+        </xsl:call-template>
+    </xsl:if>
+    <xsl:if test="not(contains($path,'.')) and not($path = '')">
+        <xsl:text>../</xsl:text>
+    </xsl:if>
+</xsl:template>
+
+
+<!-- create the link to the stylesheet based on the package name -->
+<xsl:template name="create.stylesheet.link">
+    <xsl:param name="package.name"/>
+    <link rel="stylesheet" type="text/css" title="Style"><xsl:attribute name="href"><xsl:if test="not($package.name = 'unnamed package')"><xsl:call-template name="path"><xsl:with-param name="path" select="$package.name"/></xsl:call-template></xsl:if>stylesheet.css</xsl:attribute></link>
+</xsl:template>
+
+
+<!-- Page HEADER -->
+<xsl:template name="pageHeader">
+    <h1>Unit Test Results</h1>
+    <table width="100%">
+    <tr>
+        <td align="left"></td>
+        <td align="right">Designed for use with <a href="http://www.junit.org/">JUnit</a> and <a href="http://jakarta.apache.org/">Ant</a>.</td>
+    </tr>
+    </table>
+    <hr size="1"/>
+</xsl:template>
+
+<!-- class header -->
+<xsl:template name="testsuite.test.header">
+    <tr valign="top">
+        <th width="80%">Name</th>
+        <th>Tests</th>
+        <th>Errors</th>
+        <th>Failures</th>
+        <th nowrap="nowrap">Time(s)</th>
+    </tr>
+</xsl:template>
+
+<!-- method header -->
+<xsl:template name="testcase.test.header">
+    <tr valign="top">
+        <th>Name</th>
+        <th>Status</th>
+        <th width="80%">Type</th>
+        <th nowrap="nowrap">Time(s)</th>
+    </tr>
+</xsl:template>
+
+
+<!-- class information -->
+<xsl:template match="testsuite" mode="print.test">
+    <tr valign="top">
+        <xsl:attribute name="class">
+            <xsl:choose>
+                <xsl:when test="@errors[.&gt; 0]">Error</xsl:when>
+                <xsl:when test="@failures[.&gt; 0]">Failure</xsl:when>
+                <xsl:otherwise>Pass</xsl:otherwise>
+            </xsl:choose>
+        </xsl:attribute>
+        <td><a href="{@id}_{@name}.html"><xsl:value-of select="@name"/></a></td>
+        <td><xsl:apply-templates select="@tests"/></td>
+        <td><xsl:apply-templates select="@errors"/></td>
+        <td><xsl:apply-templates select="@failures"/></td>
+        <td><xsl:call-template name="display-time">
+                <xsl:with-param name="value" select="@time"/>
+            </xsl:call-template>
+        </td>
+    </tr>
+</xsl:template>
+
+<xsl:template match="testcase" mode="print.test">
+    <tr valign="top">
+        <xsl:attribute name="class">
+            <xsl:choose>
+                <xsl:when test="error">Error</xsl:when>
+                <xsl:when test="failure">Failure</xsl:when>
+                <xsl:otherwise>TableRowColor</xsl:otherwise>
+            </xsl:choose>
+        </xsl:attribute>
+        <td><xsl:value-of select="@name"/></td>
+        <xsl:choose>
+            <xsl:when test="failure">
+                <td>Failure</td>
+                <td><xsl:apply-templates select="failure"/></td>
+            </xsl:when>
+            <xsl:when test="error">
+                <td>Error</td>
+                <td><xsl:apply-templates select="error"/></td>
+            </xsl:when>
+            <xsl:otherwise>
+                <td>Success</td>
+                <td></td>
+            </xsl:otherwise>
+        </xsl:choose>
+        <td>
+            <xsl:call-template name="display-time">
+                <xsl:with-param name="value" select="@time"/>
+            </xsl:call-template>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<!-- Note : the below template error and failure are the same style
+            so just call the same style store in the toolkit template -->
+<xsl:template match="failure">
+    <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<xsl:template match="error">
+    <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<!-- Style for the error and failure in the testcase template -->
+<xsl:template name="display-failures">
+    <xsl:choose>
+        <xsl:when test="not(@message)">N/A</xsl:when>
+        <xsl:otherwise>
+            <xsl:value-of select="@message"/>
+        </xsl:otherwise>
+    </xsl:choose>
+    <!-- display the stacktrace -->
+    <br/><br/>
+    <code>
+        <xsl:call-template name="br-replace">
+            <xsl:with-param name="word" select="."/>
+        </xsl:call-template>
+    </code>
+    <!-- the latter is better but might be problematic for non-21" monitors... -->
+    <!--pre><xsl:value-of select="."/></pre-->
+</xsl:template>
+
+<xsl:template name="JS-escape">
+    <xsl:param name="string"/>
+    <xsl:param name="tmp1" select="stringutils:replace(string($string),'\','\\')"/>
+    <xsl:param name="tmp2" select="stringutils:replace(string($tmp1),&quot;'&quot;,&quot;\&apos;&quot;)"/>
+    <xsl:value-of select="$tmp2"/>
+</xsl:template>
+
+
+<!--
+    template that will convert a carriage return into a br tag
+    @param word the text from which to convert CR to BR tag
+-->
+<xsl:template name="br-replace">
+    <xsl:param name="word"/>
+    <xsl:param name="br"><br/></xsl:param>
+    <xsl:value-of select='stringutils:replace(string($word),"&#xA;",$br)'/>
+</xsl:template>
+
+<xsl:template name="display-time">
+    <xsl:param name="value"/>
+    <xsl:value-of select="format-number($value,'0.000')"/>
+</xsl:template>
+
+<xsl:template name="display-percent">
+    <xsl:param name="value"/>
+    <xsl:value-of select="format-number($value,'0.00%')"/>
+</xsl:template>
+</xsl:stylesheet>
+
Index: /release-kits/shared/ant/etc/junit-noframes.xsl
===================================================================
--- /release-kits/shared/ant/etc/junit-noframes.xsl	(revision 16139)
+++ /release-kits/shared/ant/etc/junit-noframes.xsl	(revision 16139)
@@ -0,0 +1,461 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+	xmlns:lxslt="http://xml.apache.org/xslt"
+	xmlns:stringutils="xalan://org.apache.tools.ant.util.StringUtils">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"
+  doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" />
+<xsl:decimal-format decimal-separator="." grouping-separator="," />
+<!--
+   Copyright 2001-2004 The Apache Software Foundation
+
+   Licensed 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.
+ -->
+ 
+<!--
+ 
+ Sample stylesheet to be used with Ant JUnitReport output.
+ 
+ It creates a non-framed report that can be useful to send via
+ e-mail or such.
+ 
+ @author Stephane Bailliez <a href="mailto:sbailliez@apache.org"/>
+ @author Erik Hatcher <a href="mailto:ehatcher@apache.org"/>
+ 
+-->
+<xsl:template match="testsuites">
+    <html>
+        <head>
+            <title>Unit Test Results</title>
+    <style type="text/css">
+      body {
+        font:normal 68% verdana,arial,helvetica;
+        color:#000000;
+      }
+      table tr td, table tr th {
+          font-size: 68%;
+      }
+      table.details tr th{
+        font-weight: bold;
+        text-align:left;
+        background:#a6caf0;
+      }
+      table.details tr td{
+        background:#eeeee0;
+      }
+      
+      p {
+        line-height:1.5em;
+        margin-top:0.5em; margin-bottom:1.0em;
+      }
+      h1 {
+        margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
+      }
+      h2 {
+        margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
+      }
+      h3 {
+        margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
+      }
+      h4 {
+        margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+      }
+      h5 {
+        margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+      }
+      h6 {
+        margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+      }
+      .Error {
+        font-weight:bold; color:red;
+      }
+      .Failure {
+        font-weight:bold; color:purple;
+      }
+      .Properties {
+        text-align:right;
+      }
+      </style>
+      <script type="text/javascript" language="JavaScript">
+        var TestCases = new Array();
+        var cur;
+        <xsl:for-each select="./testsuite">      
+            <xsl:apply-templates select="properties"/>
+        </xsl:for-each>
+
+       </script>
+       <script type="text/javascript" language="JavaScript"><![CDATA[
+        function displayProperties (name) {
+          var win = window.open('','JUnitSystemProperties','scrollbars=1,resizable=1');
+          var doc = win.document.open();
+          doc.write("<html><head><title>Properties of " + name + "</title>");
+          doc.write("<style>")
+          doc.write("body {font:normal 68% verdana,arial,helvetica; color:#000000; }");
+          doc.write("table tr td, table tr th { font-size: 68%; }");
+          doc.write("table.properties { border-collapse:collapse; border-left:solid 1 #cccccc; border-top:solid 1 #cccccc; padding:5px; }");
+          doc.write("table.properties th { text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#eeeeee; }");
+          doc.write("table.properties td { font:normal; text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#fffffff; }");
+          doc.write("h3 { margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica }");
+          doc.write("</style>");
+          doc.write("</head><body>");
+          doc.write("<h3>Properties of " + name + "</h3>");
+          doc.write("<div align=\"right\"><a href=\"javascript:window.close();\">Close</a></div>");
+          doc.write("<table class='properties'>");
+          doc.write("<tr><th>Name</th><th>Value</th></tr>");
+          for (prop in TestCases[name]) {
+            doc.write("<tr><th>" + prop + "</th><td>" + TestCases[name][prop] + "</td></tr>");
+          }
+          doc.write("</table>");
+          doc.write("</body></html>");
+          doc.close();
+          win.focus();
+        }
+      ]]>  
+      </script>
+        </head>
+        <body>
+            <a name="top"></a>
+            <xsl:call-template name="pageHeader"/>  
+            
+            <!-- Summary part -->
+            <xsl:call-template name="summary"/>
+            <hr size="1" width="95%" align="left"/>
+            
+            <!-- Package List part -->
+            <xsl:call-template name="packagelist"/>
+            <hr size="1" width="95%" align="left"/>
+            
+            <!-- For each package create its part -->
+            <xsl:call-template name="packages"/>
+            <hr size="1" width="95%" align="left"/>
+            
+            <!-- For each class create the  part -->
+            <xsl:call-template name="classes"/>
+            
+        </body>
+    </html>
+</xsl:template>
+    
+    
+    
+    <!-- ================================================================== -->
+    <!-- Write a list of all packages with an hyperlink to the anchor of    -->
+    <!-- of the package name.                                               -->
+    <!-- ================================================================== -->
+    <xsl:template name="packagelist">   
+        <h2>Packages</h2>
+        Note: package statistics are not computed recursively, they only sum up all of its testsuites numbers.
+        <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+            <xsl:call-template name="testsuite.test.header"/>
+            <!-- list all packages recursively -->
+            <xsl:for-each select="./testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+                <xsl:sort select="@package"/>
+                <xsl:variable name="testsuites-in-package" select="/testsuites/testsuite[./@package = current()/@package]"/>
+                <xsl:variable name="testCount" select="sum($testsuites-in-package/@tests)"/>
+                <xsl:variable name="errorCount" select="sum($testsuites-in-package/@errors)"/>
+                <xsl:variable name="failureCount" select="sum($testsuites-in-package/@failures)"/>
+                <xsl:variable name="timeCount" select="sum($testsuites-in-package/@time)"/>
+                
+                <!-- write a summary for the package -->
+                <tr valign="top">
+                    <!-- set a nice color depending if there is an error/failure -->
+                    <xsl:attribute name="class">
+                        <xsl:choose>
+                            <xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
+                            <xsl:when test="$errorCount &gt; 0">Error</xsl:when>
+                        </xsl:choose>
+                    </xsl:attribute>
+                    <td><a href="#{@package}"><xsl:value-of select="@package"/></a></td>
+                    <td><xsl:value-of select="$testCount"/></td>
+                    <td><xsl:value-of select="$errorCount"/></td>
+                    <td><xsl:value-of select="$failureCount"/></td>
+                    <td>
+                    <xsl:call-template name="display-time">
+                        <xsl:with-param name="value" select="$timeCount"/>
+                    </xsl:call-template>
+                    </td>
+                </tr>
+            </xsl:for-each>
+        </table>        
+    </xsl:template>
+    
+    
+    <!-- ================================================================== -->
+    <!-- Write a package level report                                       -->
+    <!-- It creates a table with values from the document:                  -->
+    <!-- Name | Tests | Errors | Failures | Time                            -->
+    <!-- ================================================================== -->
+    <xsl:template name="packages">
+        <!-- create an anchor to this package name -->
+        <xsl:for-each select="/testsuites/testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+            <xsl:sort select="@package"/>
+                <a name="{@package}"></a>
+                <h3>Package <xsl:value-of select="@package"/></h3>
+                
+                <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+                    <xsl:call-template name="testsuite.test.header"/>
+            
+                    <!-- match the testsuites of this package -->
+                    <xsl:apply-templates select="/testsuites/testsuite[./@package = current()/@package]" mode="print.test"/>
+                </table>
+                <a href="#top">Back to top</a>
+                <p/>
+                <p/>
+        </xsl:for-each>
+    </xsl:template>
+    
+    <xsl:template name="classes">
+        <xsl:for-each select="testsuite">
+            <xsl:sort select="@name"/>
+            <!-- create an anchor to this class name -->
+            <a name="{@name}"></a>
+            <h3>TestCase <xsl:value-of select="@name"/></h3>
+            
+            <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+              <xsl:call-template name="testcase.test.header"/>
+              <!--
+              test can even not be started at all (failure to load the class)
+              so report the error directly
+              -->
+                <xsl:if test="./error">
+                    <tr class="Error">
+                        <td colspan="4"><xsl:apply-templates select="./error"/></td>
+                    </tr>
+                </xsl:if>
+                <xsl:apply-templates select="./testcase" mode="print.test"/>
+            </table>
+            <div class="Properties">
+                <a>
+                    <xsl:attribute name="href">javascript:displayProperties('<xsl:value-of select="@package"/>.<xsl:value-of select="@name"/>');</xsl:attribute>
+                    Properties &#187;
+                </a>
+            </div>
+            <p/>
+            
+            <a href="#top">Back to top</a>
+        </xsl:for-each>
+    </xsl:template>
+    
+    <xsl:template name="summary">
+        <h2>Summary</h2>
+        <xsl:variable name="testCount" select="sum(testsuite/@tests)"/>
+        <xsl:variable name="errorCount" select="sum(testsuite/@errors)"/>
+        <xsl:variable name="failureCount" select="sum(testsuite/@failures)"/>
+        <xsl:variable name="timeCount" select="sum(testsuite/@time)"/>
+        <xsl:variable name="successRate" select="($testCount - $failureCount - $errorCount) div $testCount"/>
+        <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+        <tr valign="top">
+            <th>Tests</th>
+            <th>Failures</th>
+            <th>Errors</th>
+            <th>Success rate</th>
+            <th>Time</th>
+        </tr>
+        <tr valign="top">
+            <xsl:attribute name="class">
+                <xsl:choose>
+                    <xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
+                    <xsl:when test="$errorCount &gt; 0">Error</xsl:when>
+                </xsl:choose>
+            </xsl:attribute>
+            <td><xsl:value-of select="$testCount"/></td>
+            <td><xsl:value-of select="$failureCount"/></td>
+            <td><xsl:value-of select="$errorCount"/></td>
+            <td>
+                <xsl:call-template name="display-percent">
+                    <xsl:with-param name="value" select="$successRate"/>
+                </xsl:call-template>
+            </td>
+            <td>
+                <xsl:call-template name="display-time">
+                    <xsl:with-param name="value" select="$timeCount"/>
+                </xsl:call-template>
+            </td>
+
+        </tr>
+        </table>
+        <table border="0" width="95%">
+        <tr>
+        <td style="text-align: justify;">
+        Note: <i>failures</i> are anticipated and checked for with assertions while <i>errors</i> are unanticipated.
+        </td>
+        </tr>
+        </table>
+    </xsl:template>
+    
+  <!--
+   Write properties into a JavaScript data structure.
+   This is based on the original idea by Erik Hatcher (ehatcher@apache.org)
+   -->
+  <xsl:template match="properties">
+    cur = TestCases['<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>'] = new Array();
+    <xsl:for-each select="property">
+    <xsl:sort select="@name"/>
+        cur['<xsl:value-of select="@name"/>'] = '<xsl:call-template name="JS-escape"><xsl:with-param name="string" select="@value"/></xsl:call-template>';
+    </xsl:for-each>
+  </xsl:template>
+    
+<!-- Page HEADER -->
+<xsl:template name="pageHeader">
+    <h1>Unit Test Results</h1>
+    <table width="100%">
+    <tr>
+        <td align="left"></td>
+        <td align="right">Designed for use with <a href='http://www.junit.org'>JUnit</a> and <a href='http://jakarta.apache.org/ant'>Ant</a>.</td>
+    </tr>
+    </table>
+    <hr size="1"/>
+</xsl:template>
+
+<xsl:template match="testsuite" mode="header">
+    <tr valign="top">
+        <th width="80%">Name</th>
+        <th>Tests</th>
+        <th>Errors</th>
+        <th>Failures</th>
+        <th nowrap="nowrap">Time(s)</th>
+    </tr>
+</xsl:template>
+
+<!-- class header -->
+<xsl:template name="testsuite.test.header">
+    <tr valign="top">
+        <th width="80%">Name</th>
+        <th>Tests</th>
+        <th>Errors</th>
+        <th>Failures</th>
+        <th nowrap="nowrap">Time(s)</th>
+    </tr>
+</xsl:template>
+
+<!-- method header -->
+<xsl:template name="testcase.test.header">
+    <tr valign="top">
+        <th>Name</th>
+        <th>Status</th>
+        <th width="80%">Type</th>
+        <th nowrap="nowrap">Time(s)</th>
+    </tr>
+</xsl:template>
+
+
+<!-- class information -->
+<xsl:template match="testsuite" mode="print.test">
+    <tr valign="top">
+        <!-- set a nice color depending if there is an error/failure -->
+        <xsl:attribute name="class">
+            <xsl:choose>
+                <xsl:when test="@failures[.&gt; 0]">Failure</xsl:when>
+                <xsl:when test="@errors[.&gt; 0]">Error</xsl:when>
+            </xsl:choose>
+        </xsl:attribute>
+    
+        <!-- print testsuite information -->
+        <td><a href="#{@name}"><xsl:value-of select="@name"/></a></td>
+        <td><xsl:value-of select="@tests"/></td>
+        <td><xsl:value-of select="@errors"/></td>
+        <td><xsl:value-of select="@failures"/></td>
+        <td>
+            <xsl:call-template name="display-time">
+                <xsl:with-param name="value" select="@time"/>
+            </xsl:call-template>
+        </td>
+    </tr>
+</xsl:template>
+
+<xsl:template match="testcase" mode="print.test">
+    <tr valign="top">
+        <xsl:attribute name="class">
+            <xsl:choose>
+                <xsl:when test="failure | error">Error</xsl:when>
+            </xsl:choose>
+        </xsl:attribute>
+        <td><xsl:value-of select="@name"/></td>
+        <xsl:choose>
+            <xsl:when test="failure">
+                <td>Failure</td>
+                <td><xsl:apply-templates select="failure"/></td>
+            </xsl:when>
+            <xsl:when test="error">
+                <td>Error</td>
+                <td><xsl:apply-templates select="error"/></td>
+            </xsl:when>
+            <xsl:otherwise>
+                <td>Success</td>
+                <td></td>
+            </xsl:otherwise>
+        </xsl:choose>
+        <td>
+            <xsl:call-template name="display-time">
+                <xsl:with-param name="value" select="@time"/>
+            </xsl:call-template>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<xsl:template match="failure">
+    <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<xsl:template match="error">
+    <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<!-- Style for the error and failure in the tescase template -->
+<xsl:template name="display-failures">
+    <xsl:choose>
+        <xsl:when test="not(@message)">N/A</xsl:when>
+        <xsl:otherwise>
+            <xsl:value-of select="@message"/>
+        </xsl:otherwise>
+    </xsl:choose>
+    <!-- display the stacktrace -->
+    <code>
+        <br/><br/>
+        <xsl:call-template name="br-replace">
+            <xsl:with-param name="word" select="."/>
+        </xsl:call-template>
+    </code>
+    <!-- the later is better but might be problematic for non-21" monitors... -->
+    <!--pre><xsl:value-of select="."/></pre-->
+</xsl:template>
+
+<xsl:template name="JS-escape">
+    <xsl:param name="string"/>
+    <xsl:param name="tmp1" select="stringutils:replace(string($string),'\','\\')"/>
+    <xsl:param name="tmp2" select="stringutils:replace(string($tmp1),&quot;'&quot;,&quot;\&apos;&quot;)"/>
+    <xsl:value-of select="$tmp2"/>
+</xsl:template>
+
+
+<!--
+    template that will convert a carriage return into a br tag
+    @param word the text from which to convert CR to BR tag
+-->
+<xsl:template name="br-replace">
+    <xsl:param name="word"/>
+    <xsl:param name="br"><br/></xsl:param>
+    <xsl:value-of select='stringutils:replace(string($word),"&#xA;",$br)'/>
+</xsl:template>
+
+<xsl:template name="display-time">
+    <xsl:param name="value"/>
+    <xsl:value-of select="format-number($value,'0.000')"/>
+</xsl:template>
+
+<xsl:template name="display-percent">
+    <xsl:param name="value"/>
+    <xsl:value-of select="format-number($value,'0.00%')"/>
+</xsl:template>
+
+</xsl:stylesheet>
+
Index: /release-kits/shared/ant/etc/log.xsl
===================================================================
--- /release-kits/shared/ant/etc/log.xsl	(revision 16139)
+++ /release-kits/shared/ant/etc/log.xsl	(revision 16139)
@@ -0,0 +1,210 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+<!--
+    Copyright  2000-2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+ 
+<!--
+
+  The purpose have this XSL is to provide a nice way to look at the output
+  from the Ant XmlLogger (ie: ant -listener org.apache.tools.ant.XmlLogger )
+  
+  @author <a href="mailto:sbailliez@apache.org">Stephane Bailliez</a>
+  
+-->
+<xsl:decimal-format decimal-separator="." grouping-separator="," />
+
+<xsl:template match="/">
+<html>
+  <head>
+    <style type="text/css">
+    .bannercell {
+      border: 0px;
+      padding: 0px;
+    }
+    body {
+      margin: 0;
+      font:normal 100% arial,helvetica,sanserif;
+      background-color:#FFFFFF;
+      color:#000000;
+    }
+    table.status {
+      font:bold 80% arial,helvetica,sanserif;
+      background-color:#525D76;
+      color:#ffffff;
+    }
+    table.log tr td, tr th {
+      font-size: 80%;
+    }
+    .error {
+      color:red;
+    }
+    .warn {
+      color:brown;
+    }
+    .info {
+      color:gray;
+    }
+    .debug{
+      color:gray;
+    }
+    .failed {
+      font-size:80%;
+      background-color: red;
+      color:#FFFFFF;
+      font-weight: bold
+    }
+    .complete {
+      font-size:80%;
+      background-color: #525D76;
+      color:#FFFFFF;
+      font-weight: bold
+    }
+    .a td { 
+      background: #efefef;
+    }
+    .b td { 
+      background: #fff;
+    }
+    th, td {
+      text-align: left;
+      vertical-align: top;
+    }
+    th {
+      background: #ccc;
+      color: black;
+    }
+    table, th, td {
+      border: none
+    }
+    h3 {
+      font:bold 80% arial,helvetica,sanserif;
+      background: #525D76;
+      color: white;
+      text-decoration: none;
+      padding: 5px;
+      margin-right: 2px;
+      margin-left: 2px;
+      margin-bottom: 0;
+    }
+    </style>
+  </head>
+  <body>
+    <!-- jakarta logo -->
+    <table border="0" cellpadding="0" cellspacing="0" width="100%">
+    <tr>
+      <td valign="top" class="bannercell">
+        <a href="http://jakarta.apache.org/">
+        <img src="http://jakarta.apache.org/images/jakarta-logo.gif" alt="http://jakarta.apache.org" align="left" border="0"/>
+        </a>
+      </td>
+      <td style="text-align:right;vertical-align:bottom">
+        <a href="http://jakarta.apache.org/ant">Apache Ant</a>
+      </td>
+    </tr>
+    </table>
+      
+    <table border="0" width="100%">
+    <tr><td><hr noshade="yes" size="1"/></td></tr>
+    </table>
+
+    <xsl:apply-templates select="build"/>
+
+    <!-- FOOTER -->
+    <table width="100%">
+      <tr><td><hr noshade="yes" size="1"/></td></tr>
+      <tr><td>
+      <div align="center"><font color="#525D76" size="-1"><em>
+      Copyright &#169; 2000-2002, Apache Software Foundation
+      </em></font></div>
+      </td></tr>
+    </table>
+  </body>
+</html>
+</xsl:template>
+
+<xsl:template match="build">
+  <!-- build status -->
+  <table width="100%">
+    <xsl:attribute name="class">
+      <xsl:if test="@error">failed</xsl:if>
+      <xsl:if test="not(@error)">complete</xsl:if>
+    </xsl:attribute>
+    <tr>
+      <xsl:if test="@error">
+        <td nowrap="yes">Build Failed</td> 
+      </xsl:if>
+      <xsl:if test="not(@error)">
+        <td nowrap="yes">Build Complete</td>
+      </xsl:if>
+        <td style="text-align:right" nowrap="yes">Total Time: <xsl:value-of select="@time"/></td>
+    </tr>
+    <tr>
+      <td colspan="2">
+        <xsl:if test="@error">
+          <tt><xsl:value-of select="@error"/></tt><br/>
+          <i style="font-size:80%">See the <a href="#stacktrace" alt="Click for details">stacktrace</a>.</i>
+        </xsl:if>
+      </td>
+    </tr>
+  </table>
+  <table border="1" cellspacing="2" cellpadding="3" width="100%" style="font-size:80%">
+    <tr class="a"><td width="1">ant.file</td><td><xsl:value-of select="substring-after(//message[contains(text(),'ant.file')], '->')"/></td></tr>
+    <tr class="b"><td width="1">ant.version</td><td><xsl:value-of select="substring-after(//message[contains(text(),'ant.version')], '->')"/></td></tr>
+    <tr class="a"><td width="1">java.version</td><td><xsl:value-of select="substring-after(//message[contains(text(),'java.vm.version')], '->')"/></td></tr>
+    <tr class="b"><td width="1">os.name</td><td><xsl:value-of select="substring-after(//message[contains(text(),'os.name')], '->')"/></td></tr>
+  </table>
+  <!-- build information -->
+  <h3>Build events</h3>
+  <table class="log" border="1" cellspacing="2" cellpadding="3" width="100%">
+  <tr>
+    <th nowrap="yes" align="left" width="1%">target</th>
+    <th nowrap="yes" align="left" width="1%">task</th>
+    <th nowrap="yes" align="left">message</th>
+  </tr>
+  <xsl:apply-templates select=".//message[@priority != 'debug']"/>
+  </table>
+  <p>
+  <!-- stacktrace -->
+  <xsl:if test="stacktrace">
+  <a name="stacktrace"/>
+  <h3>Error details</h3>
+  <table width="100%">
+    <tr><td>
+      <pre><xsl:value-of select="stacktrace"/></pre>
+    </td></tr>
+  </table>
+  </xsl:if>
+  </p>
+</xsl:template>
+
+<!-- report every message but those with debug priority -->
+<xsl:template match="message[@priority!='debug']">
+  <tr valign="top">
+    <!-- alternated row style -->
+    <xsl:attribute name="class">
+      <xsl:if test="position() mod 2 = 1">a</xsl:if>
+      <xsl:if test="position() mod 2 = 0">b</xsl:if>
+    </xsl:attribute>
+    <td nowrap="yes" width="1%"><xsl:value-of select="../../@name"/></td>
+    <td nowrap="yes" style="text-align:right" width="1%">[ <xsl:value-of select="../@name"/> ]</td>
+    <td class="{@priority}" nowrap="yes">
+            <xsl:value-of select="text()"/>
+    </td>
+  </tr>
+</xsl:template>
+
+</xsl:stylesheet>
Index: /release-kits/shared/ant/etc/maudit-frames.xsl
===================================================================
--- /release-kits/shared/ant/etc/maudit-frames.xsl	(revision 16139)
+++ /release-kits/shared/ant/etc/maudit-frames.xsl	(revision 16139)
@@ -0,0 +1,510 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt"
+    xmlns:redirect="org.apache.xalan.lib.Redirect"
+    extension-element-prefixes="redirect">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+<xsl:decimal-format decimal-separator="." grouping-separator="," />
+<!--
+    Copyright  2001-2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+<!--
+
+    Stylesheet to transform an XML file generated by the Ant MAudit task into
+    a set of JavaDoc-like HTML page to make pages more convenient to be browsed.
+
+    It use the Xalan redirect extension to write to multiple output files.
+
+    @author Stephane Bailliez <a href="mailto:sbailliez@apache.org"/>
+-->
+
+<xsl:param name="output.dir" select="'.'"/>
+
+
+<xsl:template match="classes">
+    <!-- create the index.html -->
+    <redirect:write file="{$output.dir}/index.html">
+        <xsl:call-template name="index.html"/>
+    </redirect:write>
+
+    <!-- create the stylesheet.css -->
+    <redirect:write file="{$output.dir}/stylesheet.css">
+        <xsl:call-template name="stylesheet.css"/>
+    </redirect:write>
+
+    <!-- create the overview-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-summary.html">
+        <xsl:apply-templates select="." mode="overview.packages"/>
+    </redirect:write>
+
+    <!-- create the all-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-frame.html">
+        <xsl:apply-templates select="." mode="all.packages"/>
+    </redirect:write>
+
+    <!-- create the all-classes.html at the root -->
+    <redirect:write file="{$output.dir}/allclasses-frame.html">
+        <xsl:apply-templates select="." mode="all.classes"/>
+    </redirect:write>
+
+    <!-- process all packages -->
+    <xsl:for-each select="./class[not(./@package = preceding-sibling::class/@package)]">
+        <xsl:call-template name="package">
+            <xsl:with-param name="name" select="@package"/>
+        </xsl:call-template>
+    </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template name="package">
+    <xsl:param name="name"/>
+    <xsl:variable name="package.dir">
+        <xsl:if test="not($name = '')"><xsl:value-of select="translate($name,'.','/')"/></xsl:if>
+        <xsl:if test="$name = ''">.</xsl:if>
+    </xsl:variable>
+    <!--Processing package <xsl:value-of select="@name"/> in <xsl:value-of select="$output.dir"/> -->
+    <!-- create a classes-list.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-frame.html">
+        <xsl:call-template name="classes.list">
+            <xsl:with-param name="name" select="$name"/>
+        </xsl:call-template>
+    </redirect:write>
+
+    <!-- create a package-summary.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-summary.html">
+        <xsl:call-template name="package.summary">
+            <xsl:with-param name="name" select="$name"/>
+        </xsl:call-template>
+    </redirect:write>
+
+    <!-- for each class, creates a @name.html -->
+    <!-- @bug there will be a problem with inner classes having the same name, it will be overwritten -->
+    <xsl:for-each select="/classes/class[@package = $name]">
+        <redirect:write file="{$output.dir}/{$package.dir}/{@name}.html">
+            <xsl:apply-templates select="." mode="class.details"/>
+        </redirect:write>
+    </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="index.html">
+<HTML>
+    <HEAD><TITLE>Audit Results.</TITLE></HEAD>
+    <FRAMESET cols="20%,80%">
+        <FRAMESET rows="30%,70%">
+            <FRAME src="overview-frame.html" name="packageListFrame"/>
+            <FRAME src="allclasses-frame.html" name="classListFrame"/>
+        </FRAMESET>
+        <FRAME src="overview-summary.html" name="classFrame"/>
+    </FRAMESET>
+    <noframes>
+        <H2>Frame Alert</H2>
+        <P>
+        This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+        </P>
+    </noframes>
+</HTML>
+</xsl:template>
+
+
+<!-- this is the stylesheet css to use for nearly everything -->
+<xsl:template name="stylesheet.css">
+    .bannercell {
+      border: 0px;
+      padding: 0px;
+    }
+    body {
+      margin-left: 10;
+      margin-right: 10;
+      font:normal 80% arial,helvetica,sanserif;
+      background-color:#FFFFFF;
+      color:#000000;
+    }
+    .a td {
+      background: #efefef;
+    }
+    .b td {
+      background: #fff;
+    }
+    th, td {
+      text-align: left;
+      vertical-align: top;
+    }
+    th {
+      font-weight:bold;
+      background: #ccc;
+      color: black;
+    }
+    table, th, td {
+      font-size:100%;
+      border: none
+    }
+    table.log tr td, tr th {
+
+    }
+    h2 {
+      font-weight:bold;
+      font-size:140%;
+      margin-bottom: 5;
+    }
+    h3 {
+      font-size:100%;
+      font-weight:bold;
+      background: #525D76;
+      color: white;
+      text-decoration: none;
+      padding: 5px;
+      margin-right: 2px;
+      margin-left: 2px;
+      margin-bottom: 0;
+    }
+</xsl:template>
+
+
+<!-- print the violations of the class -->
+<xsl:template match="class" mode="class.details">
+    <xsl:variable name="package.name" select="@package"/>
+    <HTML>
+        <HEAD>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$package.name"/>
+            </xsl:call-template>
+        </HEAD>
+        <BODY>
+            <xsl:call-template name="pageHeader"/>
+            <H3>Class <xsl:if test="not($package.name = '')"><xsl:value-of select="$package.name"/>.</xsl:if><xsl:value-of select="@name"/></H3>
+
+            <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+                <xsl:call-template name="class.audit.header"/>
+                <xsl:apply-templates select="." mode="print.audit"/>
+            </table>
+
+            <H3>Violations</H3>
+            <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+                <xsl:call-template name="violation.audit.header"/>
+                <xsl:apply-templates select="./violation" mode="print.audit">
+                    <xsl:sort data-type="number" select="@line"/>
+                </xsl:apply-templates>
+            </table>
+            <xsl:call-template name="pageFooter"/>
+        </BODY>
+    </HTML>
+</xsl:template>
+
+
+<!-- list of classes in a package -->
+<xsl:template name="classes.list">
+    <xsl:param name="name"/>
+    <HTML>
+        <HEAD>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$name"/>
+            </xsl:call-template>
+        </HEAD>
+        <BODY>
+            <table width="100%">
+                <tr>
+                    <td nowrap="nowrap">
+                        <H2><a href="package-summary.html" target="classFrame"><xsl:value-of select="$name"/></a></H2>
+                    </td>
+                </tr>
+            </table>
+
+            <h2>Classes</h2>
+            <TABLE WIDTH="100%">
+                <xsl:apply-templates select="/classes/class[./@package = $name]" mode="classes.list">
+                    <xsl:sort select="@name"/>
+                </xsl:apply-templates>
+            </TABLE>
+        </BODY>
+    </HTML>
+</xsl:template>
+<!-- the class to list -->
+<xsl:template match="class" mode="classes.list">
+    <tr>
+        <td nowrap="nowrap">
+            <!-- @bug naming to fix for inner classes -->
+            <a href="{@name}.html" target="classFrame"><xsl:value-of select="@name"/></a>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<!--
+    Creates an all-classes.html file that contains a link to all package-summary.html
+    on each class.
+-->
+<xsl:template match="classes" mode="all.classes">
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <h2>Classes</h2>
+            <table width="100%">
+                <xsl:apply-templates select=".//class" mode="all.classes">
+                    <xsl:sort select="@name"/>
+                </xsl:apply-templates>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+<xsl:template match="class" mode="all.classes">
+    <!-- (ancestor::package)[last()] is buggy in MSXML3 ? -->
+    <xsl:variable name="package.name" select="@package"/>
+    <tr>
+        <td nowrap="nowrap">
+            <a target="classFrame">
+                <xsl:attribute name="href">
+                    <xsl:if test="not($package.name='')">
+                        <xsl:value-of select="translate($package.name,'.','/')"/><xsl:text>/</xsl:text>
+                    </xsl:if><xsl:value-of select="@name"/><xsl:text>.html</xsl:text>
+                </xsl:attribute>
+                <xsl:value-of select="@name"/>
+            </a>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<!--
+    Creates an html file that contains a link to all package-summary.html files on
+    each package existing on testsuites.
+    @bug there will be a problem here, I don't know yet how to handle unnamed package :(
+-->
+<xsl:template match="classes" mode="all.packages">
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <h2><a href="overview-summary.html" target="classFrame">Home</a></h2>
+            <h2>Packages</h2>
+                <table width="100%">
+                    <xsl:apply-templates select="class[not(./@package = preceding-sibling::class/@package)]" mode="all.packages">
+                        <xsl:sort select="@package" order="ascending"/>
+                    </xsl:apply-templates>
+                </table>
+        </body>
+    </html>
+</xsl:template>
+
+<xsl:template match="class" mode="all.packages">
+    <tr>
+        <td nowrap="nowrap">
+            <a href="{translate(@package,'.','/')}/package-summary.html" target="classFrame">
+                <xsl:value-of select="@package"/>
+            </a>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<xsl:template match="classes" mode="overview.packages">
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body onload="open('allclasses-frame.html','classListFrame')">
+        <xsl:call-template name="pageHeader"/>
+        <h3>Summary</h3>
+        <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+        <tr>
+            <th>Audited classes</th>
+            <th>Reported classes</th>
+            <th>Violations</th>
+        </tr>
+        <tr class="a">
+            <td><xsl:value-of select="@audited"/></td>
+            <td><xsl:value-of select="@reported"/></td>
+            <td><xsl:value-of select="@violations"/></td>
+        </tr>
+        </table>
+        <table border="0" width="100%">
+        <tr>
+        <td style="text-align: justify;">
+        Note: Rules checked have originated from style guidelines suggested by the language designers,
+        experience from the Java development community and insite experience. Violations are generally
+        reported with a reference to the <a href="http://java.sun.com/docs/books/jls/second_edition/html/jTOC.doc.html">Java Language Specifications</a> (JLS x.x.x)
+        and Metamata Audit rules (x.x).
+        Please consult these documents for additional information about violations.
+        <p/>
+        Rules checked also enforce adherence to <a href="http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html">Sun Java coding guidelines</a> in use at Jakarta.
+        <p/>
+        One should note that these violations do not necessary underline errors but should be used
+        as an indication for <i>possible</i> errors. As always, use your best judgment and review
+        them carefully, it might save you hours of debugging.
+        </td>
+        </tr>
+        </table>
+
+        <h3>Packages</h3>
+        <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+            <xsl:call-template name="class.audit.header"/>
+            <xsl:for-each select="class[not(./@package = preceding-sibling::class/@package)]">
+                <xsl:sort select="@package" order="ascending"/>
+                <tr>
+          <xsl:call-template name="alternate-row"/>
+                    <td><a href="{translate(@package,'.','/')}/package-summary.html"><xsl:value-of select="@package"/></a></td>
+                    <td><xsl:value-of select="sum(/classes/class[./@package = current()/@package]/@violations)"/></td>
+                </tr>
+            </xsl:for-each>
+        </table>
+        <xsl:call-template name="pageFooter"/>
+        </body>
+        </html>
+</xsl:template>
+
+
+<xsl:template name="package.summary">
+    <xsl:param name="name"/>
+    <HTML>
+        <HEAD>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$name"/>
+            </xsl:call-template>
+        </HEAD>
+        <BODY>
+            <xsl:attribute name="onload">open('package-frame.html','classListFrame')</xsl:attribute>
+            <xsl:call-template name="pageHeader"/>
+            <h3>Package <xsl:value-of select="$name"/></h3>
+
+            <!--table border="0" cellpadding="5" cellspacing="2" width="100%">
+                <xsl:call-template name="class.metrics.header"/>
+                <xsl:apply-templates select="." mode="print.metrics"/>
+            </table-->
+
+            <xsl:if test="count(/classes/class[./@package = $name]) &gt; 0">
+                <H3>Classes</H3>
+                <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+                    <xsl:call-template name="class.audit.header"/>
+                    <xsl:apply-templates select="/classes/class[./@package = $name]" mode="print.audit">
+                        <xsl:sort select="@name"/>
+                    </xsl:apply-templates>
+                </table>
+            </xsl:if>
+            <xsl:call-template name="pageFooter"/>
+        </BODY>
+    </HTML>
+</xsl:template>
+
+
+<!--
+    transform string like a.b.c to ../../../
+    @param path the path to transform into a descending directory path
+-->
+<xsl:template name="path">
+    <xsl:param name="path"/>
+    <xsl:if test="contains($path,'.')">
+        <xsl:text>../</xsl:text>
+        <xsl:call-template name="path">
+            <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'.')"/></xsl:with-param>
+        </xsl:call-template>
+    </xsl:if>
+    <xsl:if test="not(contains($path,'.')) and not($path = '')">
+        <xsl:text>../</xsl:text>
+    </xsl:if>
+</xsl:template>
+
+
+<!-- create the link to the stylesheet based on the package name -->
+<xsl:template name="create.stylesheet.link">
+    <xsl:param name="package.name"/>
+    <LINK REL ="stylesheet" TYPE="text/css" TITLE="Style"><xsl:attribute name="href"><xsl:if test="not($package.name = 'unnamed package')"><xsl:call-template name="path"><xsl:with-param name="path" select="$package.name"/></xsl:call-template></xsl:if>stylesheet.css</xsl:attribute></LINK>
+</xsl:template>
+
+<!-- Page HEADER -->
+<xsl:template name="pageHeader">
+
+  <!-- jakarta logo -->
+  <table border="0" cellpadding="0" cellspacing="0" width="100%">
+  <tr>
+    <td class="bannercell" rowspan="2">
+      <a href="http://jakarta.apache.org/">
+      <img src="http://jakarta.apache.org/images/jakarta-logo.gif" alt="http://jakarta.apache.org" align="left" border="0"/>
+      </a>
+    </td>
+        <td style="text-align:right"><h2>Source Code Audit</h2></td>
+        </tr>
+        <tr>
+        <td style="text-align:right">Designed for use with <a href='http://www.webgain.com/products/quality_analyzer/'>Webgain QA/Metamata Audit</a> and <a href='http://jakarta.apache.org'>Ant</a>.</td>
+        </tr>
+  </table>
+    <hr size="1"/>
+</xsl:template>
+
+<!-- Page HEADER -->
+<xsl:template name="pageFooter">
+    <table width="100%">
+      <tr><td><hr noshade="yes" size="1"/></td></tr>
+      <tr><td>
+      <div align="center"><font color="#525D76" size="-1"><em>
+      Copyright &#169; 1999-2001, Apache Software Foundation
+      </em></font></div>
+      </td></tr>
+    </table>
+</xsl:template>
+
+
+<!-- class header -->
+<xsl:template name="class.audit.header">
+    <tr>
+        <th width="80%">Name</th>
+        <th>Violations</th>
+    </tr>
+</xsl:template>
+
+<!-- method header -->
+<xsl:template name="violation.audit.header">
+    <tr>
+        <th>Line</th>
+        <th>Message</th>
+    </tr>
+</xsl:template>
+
+
+<!-- class information -->
+<xsl:template match="class" mode="print.audit">
+    <tr>
+    <xsl:call-template name="alternate-row"/>
+        <td><a href="{@name}.html"><xsl:value-of select="@name"/></a></td>
+        <td><xsl:apply-templates select="@violations"/></td>
+    </tr>
+</xsl:template>
+
+<xsl:template match="violation" mode="print.audit">
+    <tr>
+    <xsl:call-template name="alternate-row"/>
+        <td><xsl:value-of select="@line"/></td>
+        <td><xsl:apply-templates select="@message"/></td>
+    </tr>
+</xsl:template>
+
+<!-- alternated row style -->
+<xsl:template name="alternate-row">
+<xsl:attribute name="class">
+  <xsl:if test="position() mod 2 = 1">a</xsl:if>
+  <xsl:if test="position() mod 2 = 0">b</xsl:if>
+</xsl:attribute>
+</xsl:template>
+
+</xsl:stylesheet>
+
Index: /release-kits/shared/ant/etc/mmetrics-frames.xsl
===================================================================
--- /release-kits/shared/ant/etc/mmetrics-frames.xsl	(revision 16139)
+++ /release-kits/shared/ant/etc/mmetrics-frames.xsl	(revision 16139)
@@ -0,0 +1,1033 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+  xmlns:lxslt="http://xml.apache.org/xslt"
+  xmlns:xalan="http://xml.apache.org/xalan"
+  xmlns:redirect="org.apache.xalan.lib.Redirect"
+  exclude-result-prefixes="xalan"
+  extension-element-prefixes="redirect">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+<xsl:decimal-format decimal-separator="." grouping-separator="," />
+<!--
+    Copyright  2001-2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+<!--
+  @author Stephane Bailliez <a href="mailto:sbailliez@apache.org"/>
+  -->
+<xsl:param name="output.dir" select="'.'"/>
+
+<!-- default max value for the metrics -->
+<xsl:param name="vg.max" select="10"/>
+<xsl:param name="loc.max" select="1000"/>
+<xsl:param name="dit.max" select="10"/>
+<xsl:param name="noa.max" select="250"/>
+<xsl:param name="nrm.max" select="50"/>
+<xsl:param name="nlm.max" select="250"/>
+<xsl:param name="wmc.max" select="250"/>
+<xsl:param name="rfc.max" select="50"/>
+<xsl:param name="dac.max" select="10"/>
+<xsl:param name="fanout.max" select="10"/>
+<xsl:param name="cbo.max" select="15"/>
+<xsl:param name="lcom.max" select="10"/>
+<xsl:param name="nocl.max" select="10"/>
+
+
+<!-- create a tree fragment to speed up processing -->
+<xsl:variable name="doctree.var">
+  <xsl:element name="classes">
+    <xsl:for-each select=".//class">
+      <xsl:element name="class">
+        <xsl:attribute name="package">
+          <xsl:value-of select="(ancestor::package)[last()]/@name"/>
+        </xsl:attribute>
+        <xsl:copy-of select="@*"/>
+        <xsl:attribute name="name">
+          <xsl:apply-templates select="." mode="class.name"/>
+        </xsl:attribute>
+        <xsl:copy-of select="method"/>
+      </xsl:element>
+    </xsl:for-each>
+  </xsl:element>
+</xsl:variable>
+
+<xsl:variable name="doctree" select="xalan:nodeset($doctree.var)"/>
+
+<xsl:template match="metrics">
+
+  <!-- create the index.html -->
+  <redirect:write file="{$output.dir}/index.html">
+    <xsl:call-template name="index.html"/>
+  </redirect:write>
+
+  <!-- create the stylesheet.css -->
+  <redirect:write file="{$output.dir}/stylesheet.css">
+    <xsl:call-template name="stylesheet.css"/>
+  </redirect:write>
+
+  <redirect:write file="{$output.dir}/metrics-reference.html">
+    <xsl:call-template name="metrics-reference.html"/>
+  </redirect:write>
+
+  <!-- create the overview-packages.html at the root -->
+  <redirect:write file="{$output.dir}/overview-summary.html">
+    <xsl:apply-templates select="." mode="overview.packages"/>
+  </redirect:write>
+
+  <!-- create the all-packages.html at the root -->
+  <redirect:write file="{$output.dir}/overview-frame.html">
+    <xsl:apply-templates select="." mode="all.packages"/>
+  </redirect:write>
+
+  <!-- create the all-classes.html at the root -->
+  <redirect:write file="{$output.dir}/allclasses-frame.html">
+    <xsl:apply-templates select="." mode="all.classes"/>
+  </redirect:write>
+
+  <!-- process all packages -->
+  <xsl:apply-templates select=".//package"/>
+</xsl:template>
+
+
+<xsl:template match="package">
+  <xsl:variable name="package.name" select="@name"/>
+  <xsl:variable name="package.dir">
+    <xsl:if test="not($package.name = 'unnamed package')"><xsl:value-of select="translate($package.name,'.','/')"/></xsl:if>
+    <xsl:if test="$package.name = 'unnamed package'">.</xsl:if>
+  </xsl:variable>
+  <!-- create a classes-list.html in the package directory -->
+  <redirect:write file="{$output.dir}/{$package.dir}/package-frame.html">
+    <xsl:apply-templates select="." mode="classes.list"/>
+  </redirect:write>
+
+  <!-- create a package-summary.html in the package directory -->
+  <redirect:write file="{$output.dir}/{$package.dir}/package-summary.html">
+    <xsl:apply-templates select="." mode="package.summary"/>
+  </redirect:write>
+
+  <!-- for each class, creates a @name.html -->
+  <!-- @bug there will be a problem with inner classes having the same name, it will be overwritten -->
+  <xsl:for-each select="$doctree/classes/class[@package = current()/@name]">
+      <!--Processing <xsl:value-of select="$class.name"/><xsl:text>&#10;</xsl:text> -->
+    <redirect:write file="{$output.dir}/{$package.dir}/{@name}.html">
+      <xsl:apply-templates select="." mode="class.details"/>
+    </redirect:write>
+  </xsl:for-each>
+</xsl:template>
+
+<!-- little trick to compute the classname for inner and non inner classes -->
+<!-- this is all in one line to avoid CRLF in the name -->
+<xsl:template match="class" mode="class.name">
+    <xsl:if test="parent::class"><xsl:apply-templates select="parent::class" mode="class.name"/>.<xsl:value-of select="@name"/></xsl:if><xsl:if test="not(parent::class)"><xsl:value-of select="@name"/></xsl:if>
+</xsl:template>
+
+
+<xsl:template name="index.html">
+<HTML>
+  <HEAD><TITLE>Metrics Results.</TITLE></HEAD>
+  <FRAMESET cols="20%,80%">
+    <FRAMESET rows="30%,70%">
+      <FRAME src="overview-frame.html" name="packageListFrame"/>
+      <FRAME src="allclasses-frame.html" name="classListFrame"/>
+    </FRAMESET>
+    <FRAME src="overview-summary.html" name="classFrame"/>
+  </FRAMESET>
+  <noframes>
+    <H2>Frame Alert</H2>
+    <P>
+    This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+    </P>
+  </noframes>
+</HTML>
+</xsl:template>
+
+<!-- this is the stylesheet css to use for nearly everything -->
+<xsl:template name="metrics-reference.html">
+<html>
+<head>
+<link title="Style" type="text/css" rel="stylesheet" href="stylesheet.css"/>
+</head>
+<body style="text-align:justify;">
+<h2>Metrics Reference</h2>
+<a href="#V(G)">V(G)</a> |
+<a href="#LOC">LOC</a> |
+<a href="#DIT">DIT</a> |
+<a href="#NOA">NOA</a> |
+<a href="#NRM">NRM</a> |
+<a href="#NLM">NLM</a> |
+<a href="#WMC">WMC</a> |
+<a href="#RFC">RFC</a> |
+<a href="#DAC">DAC</a> |
+<a href="#FANOUT">FANOUT</a> |
+<a href="#CBO">CBO</a> |
+<a href="#LCOM">LCOM</a> |
+<a href="#NOC">NOC</a>
+
+<a name="V(G)"/>
+<h3>Cyclomatic Complexity - V(G)</h3>
+This metric was introduced in the 1970s to measure the amount of control
+flow complexity or branching complexity in a module such as a
+subroutine. It gives the number of paths that may be taken through the
+code, and was initially developed to give some measure of the cost of
+producing a test case for the module by executing each path.
+<p/>
+Methods with a high cyclomatic complexity tend to be more difficult to
+understand and maintain. In general the more complex the methods of an
+application, the more difficult it will be to test it, and this will adversely
+affect its reliability.
+<p/>
+V(G) is a measure of the control flow complexity of a method or
+constructor.  It counts the number of branches in the body of the method,
+defined as:
+<ul>
+<li>while statements;</li>
+<li>if statements;</li>
+<li>for statements.</li>
+</ul>
+
+The metric can also be configured to count each case of a switch
+statement as well.
+
+<a name="LOC"/>
+<h3>Lines of Code - LOC</h3>
+
+This is perhaps the simplest of all the metrics to define and compute.
+Counting lines has a long history as a software metric dating from before
+the rise of structured programming, and it is still in widespread use today.
+The size of a method affects the ease with which it can be understood, its
+reusability and its maintainability. There are a variety of ways that the size
+can be calculated. These include counting all the lines of code, the number
+of statements, the blank lines of code, the lines of commentary, and the
+lines consisting only of syntax such as block delimiters.
+<p/>
+This metric can also be used for sizing other constructs as well, for
+example, the overall size of a Java class or package can be measured by
+counting the number of source lines it consists of.
+<p/>
+LOC can be used to determine the size of a compilation unit (source file),
+class or interface, method, constructor, or field.  It can be configured to
+ignore:
+<ul>
+<li>blank lines;</li>
+<li>lines consisting only of comments;</li>
+<li>lines consisting only of opening and closing braces.</li>
+</ul>
+
+<a name="DIT"/>
+<h3>Depth of Inheritance Hierarchy - DIT</h3>
+
+This metric calculates how far down the inheritance hierarchy a class is
+declared. In Java all classes have java.lang.Object as their ultimate
+superclass, which is defined to have a depth of 1. So a class that
+immediately extends java.lang.Object has a metric value of 2; any of its
+subclasses will have a value of 3, and so on.
+<p/>
+A class that is deep within the tree inherits more methods and state
+variables, thereby increasing its complexity and making it difficult to
+predict its behavior. It can be harder to understand a system with many
+inheritance layers.
+<p/>
+DIT is defined for classes and interfaces:
+<ul>
+<li>all interface types have a depth of 1;</li>
+<li>the class java.lang.Object has a depth of 1;</li>
+<li>all other classes have a depth of 1 + the depth of their super class.</li>
+</ul>
+
+<a name="NOA"/>
+<h3>Number of Attributes - NOA</h3>
+
+The number of distinct state variables in a class serves as one measure of
+its complexity. The more state a class represents the more difficult it is to
+maintain invariants for it. It also hinders comprehensibility and reuse.
+<p/>
+In Java, state can be exposed to subclasses through protected fields, which
+entails that the subclass also be aware of and maintain any invariants. This
+interference with the class's data encapsulation can be a source of defects
+and hidden dependencies between the state variables.
+<p/>
+NOA is defined for classes and interfaces.  It counts the number of fields
+declared in the class or interface.
+
+<a name="NRM"/>
+<h3>Number of Remote Methods - NRM</h3>
+
+NRM is defined for classes.  A remote method call is defined as an
+invocation of a method that is not declared in any of:
+<ul>
+<li>the class itself;</li>
+<li>a class or interface that the class extends or implements;</li>
+<li>a class or method that extends the class.</li>
+</ul>
+
+The value is the count of all the remote method calls in all of the methods
+and constructors of the class.
+
+<a name="NLM"/>
+<h3>Number of Local Methods - NLM</h3>
+
+NLM is defined for classes and interfaces.  A local method is defined as a
+method that is declared in the class or interface. NLM can be configured to
+include the local methods of all of the class's superclasses.  Methods with
+public, protected, package and private visibility can be independently
+counted by setting configuration parameters.
+
+<a name="WMC"/>
+<h3>Weighted Methods per Class - WMC</h3>
+
+If the number of methods in a class can be determined during the design
+and modeling phase of a project, it can be used as a predictor of how
+much time and effort is needed to develop, debug and maintain it. This
+metric can be further refined by incorporating a weighting for the
+complexity of each method. The usual weighting is given by the cyclomatic
+complexity of the method.
+<p/>
+The subclasses of a class inherit all of its public and protected methods,
+and possibly its package methods as well, so the number of methods a
+class has directly impacts the complexity of its subclasses. Classes with
+large numbers of methods are often specific to a particular application,
+reducing the ability to reuse them.
+<p/>
+The definition of WMC is based upon NLM, and it provides the same
+configuration parameters for counting inherited methods and of varying
+visibility. The main difference is that NLM always counts each method as 1,
+whereas WMC will weight each method. There are two weighting schemes:
+<ul>
+<li>V(G) the cyclomatic complexity of the method is used as its weight.
+   Methods from class files are given a V(G) of 1.</li>
+<li>the arity, or the number of parameters of the method are used to
+   determine the weight.</li>
+</ul>
+
+<a name="RFC"/>
+<h3>Response For Class - RFC</h3>
+
+The response set of a class is the set of all methods that can be invoked as
+a result of a message sent to an object of the class. This includes methods
+in the class's inheritance hierarchy and methods that can be invoked on
+other objects. The Response For Class metric is defined to be size of the
+response set for the class. A class which provides a larger response set is
+considered to be more complex than one with a smaller response set.
+<p/>
+One reason for this is that if a method call on a class can result in a large
+number of different method calls on the target and other classes, then it
+can be harder to test the behavior of the class and debug problems. It will
+typically require a deeper understanding of the potential interactions that
+objects of the class can have with the rest of the system.
+<p/>
+RFC is defined as the sum of NLM and NRM for the class.  The local methods
+include all of the public, protected, package and private methods, but not
+methods declared only in a superclass.
+
+<a name="DAC"/>
+<h3>Data Abstraction Coupling - DAC</h3>
+
+DAC is defined for classes and interfaces.  It counts the number of reference
+types that are used in the field declarations of the class or interface.  The
+component types of arrays are also counted.  Any field with a type that is
+either a supertype or a subtype of the class is not counted.
+
+<a name="FANOUT"/>
+<h3>Fan Out - FANOUT</h3>
+
+FANOUT is defined for classes and interfaces, constructors and methods. It
+counts the number of reference types that are used in:
+<ul>
+<li>field declarations;</li>
+<li>formal parameters and return types;</li>
+<li>throws declarations;</li>
+<li>local variables.</li>
+</ul>
+
+The component types of arrays are also counted. Any type that is either a
+supertype or a subtype of the class is not counted.
+
+<a name="CBO"/>
+<h3>Coupling Between Objects - CBO</h3>
+
+When one object or class uses another object or class they are said to be
+coupled. One major source of coupling is that between a superclass and a
+subclass. A coupling is also introduced when a method or field in another
+class is accessed, or when an object of another class is passed into or out
+of a method invocation. Coupling Between Objects is a measure of the
+non-inheritance coupling between two objects.
+<p/>
+A high value of coupling reduces the modularity of the class and makes
+reuse more difficult. The more independent a class is the more likely it is
+that it will be possible to reuse it in another part of the system. When a
+class is coupled to another class it becomes sensitive to changes in that
+class, thereby making maintenance for difficult. In addition, a class that is
+overly dependent on other classes can be difficult to understand and test in
+isolation.
+<p/>
+CBO is defined for classes and interfaces, constructors and methods. It
+counts the number of reference types that are used in:
+<ul>
+<li>field declarations</li>
+<li>formal parameters and return types</li>
+<li>throws declarations</li>
+<li>local variables</li>
+</ul>
+
+It also counts:
+<ul>
+<li>types from which field and method selections are made</li>
+</ul>
+
+The component types of arrays are also counted. Any type that is either a
+supertype or a subtype of the class is not counted.
+
+<a name="LCOM"/>
+<h3>Lack of Cohesion Of Methods - LCOM</h3>
+
+The cohesion of a class is the degree to which its methods are related to
+each other. It is determined by examining the pattern of state variable
+accesses within the set of methods. If all the methods access the same state
+variables then they have high cohesion; if they access disjoint sets of
+variables then the cohesion is low. An extreme example of low cohesion
+would be if none of the methods accessed any of the state variables.
+
+If a class exhibits low method cohesion it indicates that the design of the
+class has probably been partitioned incorrectly, and could benefit by being
+split into more classes with individually higher cohesion. On the other
+hand, a high value of cohesion (a low lack of cohesion) implies that the
+class is well designed. A cohesive class will tend to provide a high degree
+of encapsulation, whereas a lack of cohesion decreases encapsulation and
+increases complexity.
+<p/>
+Another form of cohesion that is useful for Java programs is cohesion
+between nested and enclosing classes. A nested class that has very low
+cohesion with its enclosing class would probably better designed as a peer
+class rather than a nested class.
+<p/>
+LCOM is defined for classes. Operationally, LCOM takes each pair of
+methods in the class and determines the set of fields they each access. If
+they have disjoint sets of field accesses increase the count P by one. If they
+share at least one field access then increase Q by one. After considering
+each pair of methods,
+LCOM = (P > Q) ? (P - Q) : 0
+<p/>
+Indirect access to fields via local methods can be considered by setting a
+metric configuration parameter.
+
+<a name="NOC"/>
+<h3>Number Of Classes - NOC</h3>
+
+The overall size of the system can be estimated by calculating the number
+of classes it contains. A large system with more classes is more complex
+than a smaller one because the number of potential interactions between
+objects is higher. This reduces the comprehensibility of the system which
+in turn makes it harder to test, debug and maintain.
+<p/>
+If the number of classes in the system can be projected during the initial
+design phase of the project it can serve as a base for estimating the total
+effort and cost of developing, debugging and maintaining the system.
+<p/>
+The NOC metric can also usefully be applied at the package and class level
+as well as the total system.
+<p/>
+NOCL is defined for class and interfaces. It counts the number of classes or
+interfaces that are declared. This is usually 1, but nested class declarations
+will increase this number.
+</body>
+</html>
+</xsl:template>
+
+<!-- this is the stylesheet css to use for nearly everything -->
+<xsl:template name="stylesheet.css">
+    .bannercell {
+      border: 0px;
+      padding: 0px;
+    }
+    body {
+      margin-left: 10;
+      margin-right: 10;
+      font:normal 80% arial,helvetica,sanserif;
+      background-color:#FFFFFF;
+      color:#000000;
+    }
+    .a td {
+      background: #efefef;
+    }
+    .b td {
+      background: #fff;
+    }
+    th, td {
+      text-align: left;
+      vertical-align: top;
+    }
+    th {
+      font-weight:bold;
+      background: #ccc;
+      color: black;
+    }
+    table, th, td {
+      font-size:100%;
+      border: none
+    }
+    table.log tr td, tr th {
+
+    }
+    h2 {
+      font-weight:bold;
+      font-size:140%;
+      margin-bottom: 5;
+    }
+    h3 {
+      font-size:100%;
+      font-weight:bold;
+      background: #525D76;
+      color: white;
+      text-decoration: none;
+      padding: 5px;
+      margin-right: 2px;
+      margin-left: 2px;
+      margin-bottom: 0;
+    }
+    .Error {
+      font-weight:bold; color:red;
+    }
+
+</xsl:template>
+
+<!-- print the metrics of the class -->
+<xsl:template match="class" mode="class.details">
+  <!--xsl:variable name="package.name" select="(ancestor::package)[last()]/@name"/-->
+  <xsl:variable name="package.name" select="@package"/>
+  <HTML>
+    <HEAD>
+      <xsl:call-template name="create.stylesheet.link">
+        <xsl:with-param name="package.name" select="$package.name"/>
+      </xsl:call-template>
+    </HEAD>
+    <BODY>
+      <xsl:call-template name="pageHeader"/>
+
+      <H3>Class <xsl:if test="not($package.name = 'unnamed package')"><xsl:value-of select="$package.name"/>.</xsl:if><xsl:value-of select="@name"/></H3>
+      <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+        <xsl:call-template name="all.metrics.header"/>
+        <xsl:apply-templates select="." mode="print.metrics"/>
+      </table>
+
+      <H3>Methods</H3>
+      <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+        <xsl:call-template name="method.metrics.header"/>
+        <xsl:apply-templates select="method" mode="print.metrics"/>
+      </table>
+
+      <xsl:call-template name="pageFooter"/>
+    </BODY>
+  </HTML>
+</xsl:template>
+
+
+<!-- list of classes in a package -->
+<xsl:template match="package" mode="classes.list">
+  <HTML>
+    <HEAD>
+      <xsl:call-template name="create.stylesheet.link">
+        <xsl:with-param name="package.name" select="@name"/>
+      </xsl:call-template>
+    </HEAD>
+    <BODY>
+      <table width="100%">
+        <tr>
+          <td nowrap="nowrap">
+            <H2><a href="package-summary.html" target="classFrame"><xsl:value-of select="@name"/></a></H2>
+          </td>
+        </tr>
+      </table>
+
+      <H2>Classes</H2>
+      <TABLE WIDTH="100%">
+        <!-- xalan-nodeset:nodeset for Xalan 1.2.2 -->
+            <xsl:for-each select="$doctree/classes/class[@package = current()/@name]">
+                <xsl:sort select="@name"/>
+          <tr>
+            <td nowrap="nowrap">
+              <a href="{@name}.html" target="classFrame"><xsl:value-of select="@name"/></a>
+            </td>
+          </tr>
+            </xsl:for-each>
+      </TABLE>
+    </BODY>
+  </HTML>
+</xsl:template>
+
+
+<!--
+  Creates an all-classes.html file that contains a link to all package-summary.html
+  on each class.
+-->
+<xsl:template match="metrics" mode="all.classes">
+  <html>
+    <head>
+      <xsl:call-template name="create.stylesheet.link">
+        <xsl:with-param name="package.name" select="''"/>
+      </xsl:call-template>
+    </head>
+    <body>
+      <h2>Classes</h2>
+      <table width="100%">
+          <xsl:for-each select="$doctree/classes/class">
+              <xsl:sort select="@name"/>
+              <xsl:apply-templates select="." mode="all.classes"/>
+          </xsl:for-each>
+      </table>
+    </body>
+  </html>
+</xsl:template>
+
+<xsl:template match="class" mode="all.classes">
+    <xsl:variable name="package.name" select="@package"/>
+    <xsl:variable name="class.name" select="@name"/>
+  <tr>
+    <td nowrap="nowrap">
+      <a target="classFrame">
+        <xsl:attribute name="href">
+          <xsl:if test="not($package.name='unnamed package')">
+            <xsl:value-of select="translate($package.name,'.','/')"/><xsl:text>/</xsl:text>
+          </xsl:if>
+          <xsl:value-of select="$class.name"/><xsl:text>.html</xsl:text>
+        </xsl:attribute>
+        <xsl:value-of select="$class.name"/>
+      </a>
+    </td>
+  </tr>
+</xsl:template>
+
+<!--
+  Creates an html file that contains a link to all package-summary.html files on
+  each package existing on testsuites.
+  @bug there will be a problem here, I don't know yet how to handle unnamed package :(
+-->
+<xsl:template match="metrics" mode="all.packages">
+  <html>
+    <head>
+      <xsl:call-template name="create.stylesheet.link">
+        <xsl:with-param name="package.name" select="./package/@name"/>
+      </xsl:call-template>
+    </head>
+    <body>
+      <h2><a href="overview-summary.html" target="classFrame">Home</a></h2>
+      <h2>Packages</h2>
+        <table width="100%">
+          <xsl:apply-templates select=".//package[not(./@name = 'unnamed package')]" mode="all.packages">
+            <xsl:sort select="@name"/>
+          </xsl:apply-templates>
+        </table>
+    </body>
+  </html>
+</xsl:template>
+
+<xsl:template match="package" mode="all.packages">
+  <tr>
+    <td nowrap="nowrap">
+      <a href="{translate(@name,'.','/')}/package-summary.html" target="classFrame">
+        <xsl:value-of select="@name"/>
+      </a>
+    </td>
+  </tr>
+</xsl:template>
+
+
+<xsl:template match="metrics" mode="overview.packages">
+  <html>
+    <head>
+      <xsl:call-template name="create.stylesheet.link">
+        <xsl:with-param name="package.name" select="''"/>
+      </xsl:call-template>
+    </head>
+    <body onload="open('allclasses-frame.html','classListFrame')">
+    <xsl:call-template name="pageHeader"/>
+    <h3>Summary</h3>
+    <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+    <tr>
+      <th><a href="metrics-reference.html#V(G)">V(G)</a></th>
+      <th><a href="metrics-reference.html#LOC">LOC</a></th>
+      <th><a href="metrics-reference.html#DIT">DIT</a></th>
+      <th><a href="metrics-reference.html#NOA">NOA</a></th>
+      <th><a href="metrics-reference.html#NRM">NRM</a></th>
+      <th><a href="metrics-reference.html#NLM">NLM</a></th>
+      <th><a href="metrics-reference.html#WMC">WMC</a></th>
+      <th><a href="metrics-reference.html#RFC">RFC</a></th>
+      <th><a href="metrics-reference.html#DAC">DAC</a></th>
+      <th><a href="metrics-reference.html#FANOUT">FANOUT</a></th>
+      <th><a href="metrics-reference.html#CBO">CBO</a></th>
+      <th><a href="metrics-reference.html#LCOM">LCOM</a></th>
+      <th><a href="metrics-reference.html#NOCL">NOCL</a></th>
+    </tr>
+    <xsl:apply-templates select="." mode="print.metrics"/>
+    </table>
+    <table border="0" width="100%">
+    <tr>
+    <td style="text-align: justify;">
+    Note: Metrics evaluate the quality of software by analyzing the program source and quantifying
+    various kind of complexity. Complexity is a common source of problems and defects in software.
+    High complexity makes it more difficult to develop, understand, maintain, extend, test and debug
+    a program.
+    <p/>
+    The primary use of metrics is to focus your attention on those parts of code that potentially are
+    complexity hot spots. Once the complex areas your program have been uncovered, you can take remedial
+    actions.
+    For additional information about metrics and their meaning, please consult
+    Metamata Metrics manual.
+    </td>
+    </tr>
+    </table>
+
+    <h3>Packages</h3>
+    <table border="0" cellpadding="5" cellspacing="2" width="100%">
+      <xsl:call-template name="all.metrics.header"/>
+      <xsl:for-each select=".//package[not(@name = 'unnamed package')]">
+        <xsl:sort select="@name" order="ascending"/>
+        <xsl:apply-templates select="." mode="print.metrics"/>
+      </xsl:for-each>
+    </table>
+    <!-- @bug there could some classes at this level (classes in unnamed package) -->
+    <xsl:call-template name="pageFooter"/>
+    </body>
+    </html>
+</xsl:template>
+
+<xsl:template match="package" mode="package.summary">
+  <HTML>
+    <HEAD>
+      <xsl:call-template name="create.stylesheet.link">
+        <xsl:with-param name="package.name" select="@name"/>
+      </xsl:call-template>
+    </HEAD>
+    <body onload="open('package-frame.html','classListFrame')">
+      <xsl:call-template name="pageHeader"/>
+      <!-- create an anchor to this package name -->
+      <h3>Package <xsl:value-of select="@name"/></h3>
+
+      <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+        <xsl:call-template name="all.metrics.header"/>
+        <xsl:apply-templates select="." mode="print.metrics"/>
+      </table>
+
+      <table border="0" width="100%">
+      <tr>
+      <td style="text-align: justify;">
+      Note: Metrics evaluate the quality of software by analyzing the program source and quantifying
+      various kind of complexity. Complexity is a common source of problems and defects in software.
+      High complexity makes it more difficult to develop, understand, maintain, extend, test and debug
+      a program.
+      <p/>
+      The primary use of metrics is to focus your attention on those parts of code that potentially are
+      complexity hot spots. Once the complex areas your program have been uncovered, you can take remedial
+      actions.
+      For additional information about metrics and their meaning, please consult
+      Metamata Metrics manual.
+      </td>
+      </tr>
+      </table>
+
+      <xsl:variable name="classes-in-package" select="$doctree/classes/class[@package = current()/@name]"/>
+      <xsl:if test="count($classes-in-package) &gt; 0">
+        <H3>Classes</H3>
+        <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+          <xsl:call-template name="all.metrics.header"/>
+          <xsl:for-each select="$classes-in-package">
+                <xsl:sort select="@name"/>
+                <xsl:apply-templates select="." mode="print.metrics"/>
+          </xsl:for-each>
+        </table>
+      </xsl:if>
+
+      <xsl:call-template name="pageFooter"/>
+    </body>
+  </HTML>
+</xsl:template>
+
+
+<!--
+    transform string like a.b.c to ../../../
+    @param path the path to transform into a descending directory path
+-->
+<xsl:template name="path">
+  <xsl:param name="path"/>
+  <xsl:if test="contains($path,'.')">
+    <xsl:text>../</xsl:text>
+    <xsl:call-template name="path">
+      <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'.')"/></xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:if test="not(contains($path,'.')) and not($path = '')">
+    <xsl:text>../</xsl:text>
+  </xsl:if>
+</xsl:template>
+
+
+<!-- create the link to the stylesheet based on the package name -->
+<xsl:template name="create.stylesheet.link">
+  <xsl:param name="package.name"/>
+  <LINK REL ="stylesheet" TYPE="text/css" TITLE="Style"><xsl:attribute name="href"><xsl:if test="not($package.name = 'unnamed package')"><xsl:call-template name="path"><xsl:with-param name="path" select="$package.name"/></xsl:call-template></xsl:if>stylesheet.css</xsl:attribute></LINK>
+</xsl:template>
+
+
+<!-- Page Header -->
+<xsl:template name="pageHeader">
+
+  <!-- jakarta logo -->
+  <table border="0" cellpadding="0" cellspacing="0" width="100%">
+  <tr>
+    <td class="bannercell" rowspan="2">
+      <a href="http://jakarta.apache.org/">
+      <img src="http://jakarta.apache.org/images/jakarta-logo.gif" alt="http://jakarta.apache.org" align="left" border="0"/>
+      </a>
+    </td>
+    <td style="text-align:right"><h2>Source Code Metrics</h2></td>
+    </tr>
+    <tr>
+    <td style="text-align:right">Designed for use with <a href='http://www.webgain.com/products/quality_analyzer/'>Webgain QA/Metamata Metrics</a> and <a href='http://jakarta.apache.org'>Ant</a>.</td>
+    </tr>
+  </table>
+  <hr size="1"/>
+</xsl:template>
+
+<!-- Page Footer -->
+<xsl:template name="pageFooter">
+    <table width="100%">
+      <tr><td><hr noshade="yes" size="1"/></td></tr>
+      <tr><td>
+      <div align="center"><font color="#525D76" size="-1"><em>
+      Copyright &#169; 1999-2001, Apache Software Foundation
+      </em></font></div>
+      </td></tr>
+    </table>
+</xsl:template>
+
+<!-- class header -->
+<xsl:template name="all.metrics.header">
+  <tr>
+    <th width="80%">Name</th>
+    <th nowrap="nowrap">V(G)</th>
+    <th>LOC</th>
+    <th>DIT</th>
+    <th>NOA</th>
+    <th>NRM</th>
+    <th>NLM</th>
+    <th>WMC</th>
+    <th>RFC</th>
+    <th>DAC</th>
+    <th>FANOUT</th>
+    <th>CBO</th>
+    <th>LCOM</th>
+    <th>NOCL</th>
+  </tr>
+</xsl:template>
+
+<!-- method header -->
+<xsl:template name="method.metrics.header">
+  <tr>
+    <th width="80%">Name</th>
+    <th nowrap="nowrap">V(G)</th>
+    <th>LOC</th>
+    <th>FANOUT</th>
+    <th>CBO</th>
+  </tr>
+</xsl:template>
+
+<!-- method information -->
+<xsl:template match="method" mode="print.metrics">
+  <tr>
+    <xsl:call-template name="alternate-row"/>
+    <td><xsl:apply-templates select="@name"/></td>
+    <td><xsl:apply-templates select="@vg"/></td>
+    <td><xsl:apply-templates select="@loc"/></td>
+    <td><xsl:apply-templates select="@fanout"/></td>
+    <td><xsl:apply-templates select="@cbo"/></td>
+  </tr>
+</xsl:template>
+
+<!-- class information -->
+<xsl:template match="class" mode="print.metrics">
+  <tr>
+    <xsl:call-template name="alternate-row"/>
+    <td><a href="{@name}.html"><xsl:value-of select="@name"/></a></td>
+    <td><xsl:apply-templates select="@vg"/></td>
+    <td><xsl:apply-templates select="@loc"/></td>
+    <td><xsl:apply-templates select="@dit"/></td>
+    <td><xsl:apply-templates select="@noa"/></td>
+    <td><xsl:apply-templates select="@nrm"/></td>
+    <td><xsl:apply-templates select="@nlm"/></td>
+    <td><xsl:apply-templates select="@wmc"/></td>
+    <td><xsl:apply-templates select="@rfc"/></td>
+    <td><xsl:apply-templates select="@dac"/></td>
+    <td><xsl:apply-templates select="@fanout"/></td>
+    <td><xsl:apply-templates select="@cbo"/></td>
+    <td><xsl:apply-templates select="@lcom"/></td>
+    <td><xsl:apply-templates select="@nocl"/></td>
+  </tr>
+</xsl:template>
+
+<xsl:template match="file|package" mode="print.metrics">
+  <tr>
+    <xsl:call-template name="alternate-row"/>
+    <td>
+    <a href="{translate(@name,'.','/')}/package-summary.html" target="classFrame">
+    <xsl:value-of select="@name"/>
+    </a>
+    </td>
+    <td><xsl:apply-templates select="@vg"/></td>
+    <td><xsl:apply-templates select="@loc"/></td>
+    <td><xsl:apply-templates select="@dit"/></td>
+    <td><xsl:apply-templates select="@noa"/></td>
+    <td><xsl:apply-templates select="@nrm"/></td>
+    <td><xsl:apply-templates select="@nlm"/></td>
+    <td><xsl:apply-templates select="@wmc"/></td>
+    <td><xsl:apply-templates select="@rfc"/></td>
+    <td><xsl:apply-templates select="@dac"/></td>
+    <td><xsl:apply-templates select="@fanout"/></td>
+    <td><xsl:apply-templates select="@cbo"/></td>
+    <td><xsl:apply-templates select="@lcom"/></td>
+    <td><xsl:apply-templates select="@nocl"/></td>
+  </tr>
+</xsl:template>
+
+<xsl:template match="metrics" mode="print.metrics">
+  <tr>
+    <xsl:call-template name="alternate-row"/>
+      <!-- the global metrics is the top package metrics -->
+    <td><xsl:apply-templates select="./package/@vg"/></td>
+    <td><xsl:apply-templates select="./package/@loc"/></td>
+    <td><xsl:apply-templates select="./package/@dit"/></td>
+    <td><xsl:apply-templates select="./package/@noa"/></td>
+    <td><xsl:apply-templates select="./package/@nrm"/></td>
+    <td><xsl:apply-templates select="./package/@nlm"/></td>
+    <td><xsl:apply-templates select="./package/@wmc"/></td>
+    <td><xsl:apply-templates select="./package/@rfc"/></td>
+    <td><xsl:apply-templates select="./package/@dac"/></td>
+    <td><xsl:apply-templates select="./package/@fanout"/></td>
+    <td><xsl:apply-templates select="./package/@cbo"/></td>
+    <td><xsl:apply-templates select="./package/@lcom"/></td>
+    <td><xsl:apply-templates select="./package/@nocl"/></td>
+  </tr>
+</xsl:template>
+
+<!-- alternated row style -->
+<xsl:template name="alternate-row">
+<xsl:attribute name="class">
+  <xsl:if test="position() mod 2 = 1">a</xsl:if>
+  <xsl:if test="position() mod 2 = 0">b</xsl:if>
+</xsl:attribute>
+</xsl:template>
+
+
+<!-- how to display the metrics with their max value -->
+<!-- @todo the max values must be external to the xsl -->
+
+  <xsl:template match="@vg">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$vg.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@loc">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$loc.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@dit">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$dit.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@noa">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$noa.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@nrm">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$nrm.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@nlm">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$nlm.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@wmc">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$wmc.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@rfc">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$rfc.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@dac">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$dac.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@fanout">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$fanout.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@cbo">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$cbo.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@lcom">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$lcom.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@nocl">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$nocl.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template name="display-value">
+    <xsl:param name="value"/>
+    <xsl:param name="max"/>
+    <xsl:if test="$value > $max">
+      <xsl:attribute name="class">Error</xsl:attribute>
+    </xsl:if>
+    <xsl:value-of select="$value"/>
+  </xsl:template>
+
+</xsl:stylesheet>
+
Index: /release-kits/shared/ant/etc/tagdiff.xsl
===================================================================
--- /release-kits/shared/ant/etc/tagdiff.xsl	(revision 16139)
+++ /release-kits/shared/ant/etc/tagdiff.xsl	(revision 16139)
@@ -0,0 +1,177 @@
+<!--
+    Copyright  2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+<!-- a stylesheet to display changelogs ala netbeans -->
+<xsl:stylesheet
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    version="1.0">
+  <xsl:param name="title"/>
+  <xsl:param name="module"/>
+  <xsl:param name="cvsweb"/>
+
+  <xsl:output method="html" indent="yes"/>
+
+  <!-- Copy standard document elements.  Elements that
+       should be ignored must be filtered by apply-templates
+       tags. -->
+  <xsl:template match="*">
+    <xsl:copy>
+      <xsl:copy-of select="attribute::*[. != '']"/>
+      <xsl:apply-templates/>
+    </xsl:copy>
+  </xsl:template>
+
+  <xsl:template match="tagdiff">
+    <HTML>
+      <HEAD>
+        <TITLE><xsl:value-of select="$title"/></TITLE>
+      </HEAD>
+      <BODY link="#000000" alink="#000000" vlink="#000000" text="#000000">
+        <style type="text/css">
+          body, p {
+          font-family: verdana,arial,helvetica;
+          font-size: 80%;
+          color:#000000;
+          }
+	  .dateAndAuthor {
+          font-family: verdana,arial,helvetica;
+          font-size: 80%;
+          font-weight: bold;
+          text-align:left;
+          background:#a6caf0;
+	  }
+          tr, td{
+          font-family: verdana,arial,helvetica;
+          font-size: 80%;
+          background:#eeeee0;
+          }	  
+	  </style>        
+          <h1>
+            <a name="top"><xsl:value-of select="$title"/></a>
+          </h1>
+          Tagdiff between <xsl:value-of select="@startTag"/> <xsl:value-of select="@startDate"/> and
+			<xsl:value-of select="@endTag"/> <xsl:value-of select="@endDate"/>
+          <p align="right">Designed for use with <a href="http://ant.apache.org/">Ant</a>.</p>
+          <hr size="2"/>
+	<a name="TOP"/>
+	<table width="100%">
+		<tr>
+			<td align="right">
+				<a href="#New">New Files</a> |
+				<a href="#Modified">Modified Files</a> |
+				<a href="#Removed">Removed Files</a>
+			</td>
+		</tr>
+	</table>
+        <TABLE BORDER="0" WIDTH="100%" CELLPADDING="3" CELLSPACING="1">
+		<xsl:call-template name="show-entries">
+			<xsl:with-param name="title">New Files</xsl:with-param>
+			<xsl:with-param name="anchor">New</xsl:with-param>
+			<xsl:with-param name="entries" select=".//entry[file/revision][not(file/prevrevision)]"/>
+		</xsl:call-template>
+
+		<xsl:call-template name="show-entries">
+			<xsl:with-param name="title">Modified Files</xsl:with-param>
+			<xsl:with-param name="anchor">Modified</xsl:with-param>
+			<xsl:with-param name="entries" select=".//entry[file/revision][file/prevrevision]"/>
+		</xsl:call-template>
+
+		<xsl:call-template name="show-entries">
+			<xsl:with-param name="title">Removed Files</xsl:with-param>
+			<xsl:with-param name="anchor">Removed</xsl:with-param>
+			<xsl:with-param name="entries" select=".//entry[not(file/revision)][not(file/prevrevision)]"/>
+		</xsl:call-template>
+        </TABLE>
+        
+      </BODY>
+    </HTML>
+  </xsl:template>
+
+  <xsl:template name="show-entries">
+	<xsl:param name="title"/>
+	<xsl:param name="anchor"/>
+	<xsl:param name="entries"/>
+	<TR>
+		<TD colspan="2" class="dateAndAuthor">
+			<a>
+				<xsl:attribute name="name"><xsl:value-of select="$anchor"/></xsl:attribute>
+				<xsl:value-of select="$title"/> - <xsl:value-of select="count($entries)"/> entries
+			</a>
+			<a href="#TOP">(back to top)</a>
+		</TD>
+	</TR>
+	<TR>
+		<TD width="20">
+			<xsl:text>    </xsl:text>
+		</TD>
+		<TD>
+		        <ul>
+				<xsl:apply-templates select="$entries"/>
+			</ul>
+		</TD>
+	</TR>
+  </xsl:template>  
+
+  <xsl:template match="entry">
+	<xsl:apply-templates select="file"/>
+  </xsl:template>
+
+  <xsl:template match="date">
+    <i><xsl:value-of select="."/></i>
+  </xsl:template>
+
+  <xsl:template match="time">
+    <i><xsl:value-of select="."/></i>
+  </xsl:template>
+
+  <xsl:template match="author">
+    <i>
+      <a>
+        <xsl:attribute name="href">mailto:<xsl:value-of select="."/></xsl:attribute>
+        <xsl:value-of select="."/>
+      </a>
+    </i>
+  </xsl:template>
+
+  <xsl:template match="file">
+    <li>
+      <a target="_new">
+        <xsl:attribute name="href"><xsl:value-of select="$cvsweb"/><xsl:value-of select="$module" />/<xsl:value-of select="name" /></xsl:attribute>
+        <xsl:value-of select="name" />
+      </a>
+      <xsl:if test="string-length(prevrevision) > 0 or string-length(revision) > 0">
+      <xsl:text> </xsl:text>
+      <a target="_new">
+        <xsl:choose>
+          <xsl:when test="string-length(prevrevision) = 0 ">
+            <xsl:attribute name="href"><xsl:value-of select="$cvsweb"/><xsl:value-of select="$module" />/<xsl:value-of select="name" />?rev=<xsl:value-of select="revision" />&amp;content-type=text/x-cvsweb-markup</xsl:attribute>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:attribute name="href"><xsl:value-of select="$cvsweb"/><xsl:value-of select="$module" />/<xsl:value-of select="name" />?r1=<xsl:value-of select="revision" />&amp;r2=<xsl:value-of select="prevrevision"/>&amp;diff_format=h</xsl:attribute>
+          </xsl:otherwise>
+        </xsl:choose> (<xsl:value-of select="revision"/>)
+      </a>
+      </xsl:if>
+    </li>
+  </xsl:template>
+
+  <!-- Any elements within a msg are processed,
+       so that we can preserve HTML tags. -->
+  <xsl:template match="msg">
+    <b><xsl:apply-templates/></b>
+  </xsl:template>
+  
+</xsl:stylesheet>
Index: /release-kits/shared/ant/lib/README
===================================================================
--- /release-kits/shared/ant/lib/README	(revision 16139)
+++ /release-kits/shared/ant/lib/README	(revision 16139)
@@ -0,0 +1,14 @@
+Please refer to the Ant manual under Installing Ant / Library
+Dependencies for a list of the jar requirements for various optional
+tasks and features.
+
+This directory contains xercesImpl.jar from the 2.6.2 release of
+Apache Xerces.  For more information or newer releases see
+<http://xml.apache.org/xerces2-j/>.  See the file LICENSE.xerces for
+the terms of distribution.
+
+It also contains xml-apis.jar from the 2.6.2 release of Apache Xerces.
+For more information or newer releases see
+<http://xml.apache.org/commons/>.  See the files LICENSE.dom and
+LICENSE.sax for the terms of distribution.
+
Index: /release-kits/shared/ant/src/antidote/WHAT-IS-THIS-P
===================================================================
--- /release-kits/shared/ant/src/antidote/WHAT-IS-THIS-P	(revision 16139)
+++ /release-kits/shared/ant/src/antidote/WHAT-IS-THIS-P	(revision 16139)
@@ -0,0 +1,6 @@
+This /used/ to be the home of Antidote, the GUI for Ant. It has moved
+(or rather, graduate) to the cvs module ant-antidote, and must
+be retrieved separately from Ant, but can be found in the same place you found
+source version of Ant.
+
+-- The Antidote Team --
Index: /release-kits/shared/ant/src/etc/ant-update.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/ant-update.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/ant-update.xsl	(revision 16139)
@@ -0,0 +1,118 @@
+<xsl:stylesheet	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+<xsl:output method="xml" indent="yes"/>
+<!--
+    Copyright  2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+
+<!--
+
+  The purpose have this XSL is to provide a fast way to update a buildfile
+  from deprecated tasks.
+  
+  It should particulary be useful when there is a lot of build files to migrate.
+  If you do not want to migrate to a particular task and want to keep it for
+  various reason, just comment the appropriate template.
+  
+  !!!! Use at your own risk. !!!!
+  
+  @author <a href="sbailliez@apache.org">Stephane Bailliez</a>
+  
+-->
+ 
+ 
+  <!-- (zip|jar|war|ear)file attributes are replaced by destfile in their respective task -->
+  <xsl:template match="zip">
+    <zip destfile="{@zipfile}">
+      <xsl:apply-templates select="@*[not(name()='zipfile')]|node()"/>
+    </zip>
+  </xsl:template>
+  <xsl:template match="jar">
+    <jar destfile="{@jarfile}">
+      <xsl:apply-templates select="@*[not(name()='jarfile')]|node()"/>
+    </jar>
+  </xsl:template>
+  <xsl:template match="war">
+    <war destfile="{@warfile}">
+      <xsl:apply-templates select="@*[not(name()='warfile')]|node()"/>
+    </war>
+  </xsl:template>
+  <xsl:template match="ear">
+    <ear destfile="{@earfile}">
+      <xsl:apply-templates select="@*[not(name()='earfile')]|node()"/>
+    </ear>
+  </xsl:template>
+   
+ 
+  <!-- copydir is replaced by copy -->
+  <xsl:template match="copydir">
+    <copy todir="{@dest}">
+      <xsl:apply-templates select="@flatten|@filtering"/>
+      <xsl:if test="@forceoverwrite">
+        <xsl:attribute name="overwrite"><xsl:value-of select="@forceoverwrite"/></xsl:attribute>
+      </xsl:if>
+      <fileset dir="{@src}">
+          <xsl:apply-templates select="@includes|@includesfile|@excludes|@excludesfile|node()"/>
+      </fileset>
+    </copy>
+  </xsl:template>
+
+  <!-- copyfile is replaced by copy -->
+  <xsl:template match="copyfile">
+    <copy file="{@src}" tofile="{@dest}">
+      <xsl:apply-templates select="@filtering"/>
+      <xsl:if test="@forceoverwrite">
+        <xsl:attribute name="overwrite"><xsl:value-of select="@forceoverwrite"/></xsl:attribute>
+      </xsl:if>
+    </copy>
+  </xsl:template>
+
+  <!-- deltree is replaced by delete -->
+  <xsl:template match="deltree">
+    <delete dir="{@dir}"/>
+  </xsl:template>
+
+  <!-- execon is replaced by apply -->
+  <xsl:template match="execon">
+    <apply>
+      <xsl:apply-templates select="@*|node()"/>
+    </apply>
+  </xsl:template>
+
+  <!-- rename is replaced by move -->
+  <xsl:template match="rename">
+    <move file="{@src}" tofile="{@dest}">
+      <xsl:if test="@replace">
+        <xsl:attribute name="overwrite"><xsl:value-of select="@replace"/></xsl:attribute>
+      </xsl:if>
+    </move>
+  </xsl:template>
+
+  <!-- javadoc2 is replaced by javadoc -->
+  <xsl:template match="javadoc2">
+    <javadoc>
+      <xsl:apply-templates select="@*|node()"/>
+    </javadoc>
+  </xsl:template>
+
+
+  <!-- Copy every node and attributes recursively -->
+  <xsl:template match="node()|@*">
+    <xsl:copy>
+      <xsl:apply-templates select="@*|node()"/>
+    </xsl:copy>
+  </xsl:template>
+  
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/changelog.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/changelog.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/changelog.xsl	(revision 16139)
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<xsl:stylesheet
+    xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
+    version='1.0'>
+
+<!--
+    Copyright  2002,2004-2005 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+  <xsl:param name="title"/>
+  <xsl:param name="module"/>
+  <xsl:param name="cvsweb"/>
+
+  <xsl:output method="html" indent="yes" encoding="US-ASCII"
+              doctype-public="-//W3C//DTD HTML 4.01//EN"
+              doctype-system="http://www.w3.org/TR/html401/strict.dtd"/>
+
+  <!-- Copy standard document elements.  Elements that
+       should be ignored must be filtered by apply-templates
+       tags. -->
+  <xsl:template match="*">
+    <xsl:copy>
+      <xsl:copy-of select="attribute::*[. != '']"/>
+      <xsl:apply-templates/>
+    </xsl:copy>
+  </xsl:template>
+
+  <xsl:template match="changelog">
+    <html>
+      <head>
+        <title><xsl:value-of select="$title"/></title>
+        <style type="text/css">
+          body, p {
+            font-family: Verdana, Arial, Helvetica, sans-serif;
+            font-size: 80%;
+            color: #000000;
+            background-color: #ffffff;
+          }
+          tr, td {
+            font-family: Verdana, Arial, Helvetica, sans-serif;
+            background: #eeeee0;
+          }
+          td {
+            padding-left: 20px;
+          }
+      .dateAndAuthor {
+            font-family: Verdana, Arial, Helvetica, sans-serif;
+            font-weight: bold;
+            text-align: left;
+            background: #a6caf0;
+            padding-left: 3px;
+      }
+          a {
+            color: #000000;
+          }
+          pre {
+            font-weight: bold;
+          }
+        </style>
+      </head>
+      <body>
+        <h1>
+          <a name="top"><xsl:value-of select="$title"/></a>
+        </h1>
+        <p style="text-align: right">Designed for use with <a href="http://ant.apache.org/">Apache Ant</a>.</p>
+        <hr/>
+        <table border="0" width="100%" cellspacing="1">
+          
+          <xsl:apply-templates select=".//entry">
+            <xsl:sort select="date" data-type="text" order="descending"/>
+            <xsl:sort select="time" data-type="text" order="descending"/>
+          </xsl:apply-templates>
+          
+        </table>
+        
+      </body>
+    </html>
+  </xsl:template>
+  
+  <xsl:template match="entry">
+    <tr>
+      <td class="dateAndAuthor">
+        <xsl:value-of select="date"/><xsl:text> </xsl:text><xsl:value-of select="time"/><xsl:text> </xsl:text><xsl:value-of select="author"/>
+      </td>
+    </tr>
+    <tr>
+      <td>
+        <pre>
+<xsl:apply-templates select="msg"/></pre>
+        <ul>
+          <xsl:apply-templates select="file"/>
+        </ul>
+      </td>
+    </tr>
+  </xsl:template>
+
+  <xsl:template match="date">
+    <i><xsl:value-of select="."/></i>
+  </xsl:template>
+
+  <xsl:template match="time">
+    <i><xsl:value-of select="."/></i>
+  </xsl:template>
+
+  <xsl:template match="author">
+    <i>
+      <a>
+        <xsl:attribute name="href">mailto:<xsl:value-of select="."/></xsl:attribute>
+        <xsl:value-of select="."/></a>
+    </i>
+  </xsl:template>
+
+  <xsl:template match="file">
+    <li>
+      <a>
+        <xsl:choose>
+          <xsl:when test="string-length(prevrevision) = 0 ">
+            <xsl:attribute name="href"><xsl:value-of select="$cvsweb"/><xsl:value-of select="$module" />/<xsl:value-of select="name" />?rev=<xsl:value-of select="revision" />&amp;content-type=text/x-cvsweb-markup</xsl:attribute>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:attribute name="href"><xsl:value-of select="$cvsweb"/><xsl:value-of select="$module" />/<xsl:value-of select="name" />?r1=<xsl:value-of select="revision" />&amp;r2=<xsl:value-of select="prevrevision"/></xsl:attribute>
+          </xsl:otherwise>
+        </xsl:choose>
+        <xsl:value-of select="name" /> (<xsl:value-of select="revision"/>)</a>
+    </li>
+  </xsl:template>
+
+  <!-- Any elements within a msg are processed,
+       so that we can preserve HTML tags. -->
+  <xsl:template match="msg">
+    <xsl:apply-templates/>
+  </xsl:template>
+  
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/checkstyle/RequiredHeader.txt
===================================================================
--- /release-kits/shared/ant/src/etc/checkstyle/RequiredHeader.txt	(revision 16139)
+++ /release-kits/shared/ant/src/etc/checkstyle/RequiredHeader.txt	(revision 16139)
@@ -0,0 +1,16 @@
+/*
+ * Copyright Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
Index: /release-kits/shared/ant/src/etc/checkstyle/checkstyle-config
===================================================================
--- /release-kits/shared/ant/src/etc/checkstyle/checkstyle-config	(revision 16139)
+++ /release-kits/shared/ant/src/etc/checkstyle/checkstyle-config	(revision 16139)
@@ -0,0 +1,110 @@
+<?xml version="1.0"?>
+<!DOCTYPE module PUBLIC "-//Puppy Crawl//DTD Check Configuration 1.1//EN" "http://www.puppycrawl.com/dtds/configuration_1_1.dtd">
+
+<module name="Checker">
+  <module name="TreeWalker">
+    <!-- Javadoc requirements -->
+    <module name="JavadocType">
+      <property name="scope" value="protected"/>
+      <property name="authorFormat" value="\S"/>
+    </module>
+    <module name="JavadocMethod">
+      <property name="scope" value="protected"/>
+      <property name="allowUndeclaredRTE" value="true"/>
+    </module>
+    <module name="JavadocVariable">
+       <property name="scope" value="public"/>
+    </module>
+
+    <!-- element naming -->
+    <module name="PackageName"/>
+    <module name="TypeName"/>
+    <module name="ConstantName"/>
+    <module name="LocalFinalVariableName"/>
+    <module name="LocalVariableName"/>
+    <module name="MemberName"/>
+    <module name="MethodName"/>
+    <module name="ParameterName"/>
+    <module name="StaticVariableName"/>
+
+    <!-- required licence file -->
+    <module name="Header">
+        <property name="headerFile" value="${config.dir}/RequiredHeader.txt"/>
+        <property name="ignoreLines" value="2"/>
+    </module>
+
+    <!-- Import conventions -->
+    <module name="AvoidStarImport"/>
+    <!-- <module name="IllegalImport"/> -->
+    <module name="RedundantImport"/>
+    <module name="UnusedImports"/>
+
+    <!-- size limits -->
+    <module name="FileLength"/>
+    <module name="LineLength">
+      <property name="max" value="100"/>
+      <property name="ignorePattern" value="^ *\* *[^ ]+$"/>
+    </module>
+    <module name="MethodLength"/>
+    <module name="ParameterNumber"/>
+
+    <!-- whitespace checks -->
+    <module name="EmptyForIteratorPad"/>
+    <module name="NoWhitespaceAfter"/>
+    <module name="NoWhitespaceBefore"/>
+    <module name="OperatorWrap"/>
+    <module name="ParenPad"/>
+    <module name="TabCharacter"/>
+    <module name="WhitespaceAfter"/>
+    <module name="WhitespaceAround"/>
+
+    <!-- Modifier Checks -->
+    <module name="ModifierOrder"/>
+    <module name="RedundantModifier"/>
+
+
+    <!-- Checks for blocks -->
+    <module name="AvoidNestedBlocks"/>
+    <module name="EmptyBlock">
+      <property name="option" value="text"/>
+    </module>
+    <module name="LeftCurly"/>
+    <module name="NeedBraces"/>
+    <module name="RightCurly"/>
+
+
+    <!-- Checks for common coding problems -->
+    <!--<module name="AvoidInlineConditionals"/> -->
+    <module name="DoubleCheckedLocking"/>
+    <module name="EmptyStatement"/>
+    <module name="EqualsHashCode"/>
+    <module name="IllegalInstantiation">
+      <property name="classes" value="java.lang.Boolean"/>
+    </module>
+    <module name="InnerAssignment"/>
+    <module name="MagicNumber"/>
+    <module name="MissingSwitchDefault"/>
+    <module name="RedundantThrows">
+      <property name="allowUnchecked" value="true"/>
+    </module>
+    <module name="SimplifyBooleanExpression"/>
+    <module name="SimplifyBooleanReturn"/>
+
+    <!-- Checks for class design -->
+    <!-- <module name="DesignForExtension"/> -->
+    <module name="FinalClass"/>
+    <module name="HideUtilityClassConstructor"/>
+    <module name="InterfaceIsType"/>
+    <module name="VisibilityModifier"/>
+
+    <!-- Miscellaneous other checks. -->
+    <module name="ArrayTypeStyle"/>
+    <module name="GenericIllegalRegexp">
+      <property name="format" value="\s+$"/>
+      <property name="message" value="Line has trailing spaces."/>
+    </module>
+    <module name="TodoComment"/>
+    <module name="UpperEll"/>
+  </module>
+  <!-- <module name="au.com.redhillconsulting.simian.SimianCheck"/> -->
+</module>
Index: /release-kits/shared/ant/src/etc/checkstyle/checkstyle-frames.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/checkstyle/checkstyle-frames.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/checkstyle/checkstyle-frames.xsl	(revision 16139)
@@ -0,0 +1,293 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt"
+    xmlns:redirect="org.apache.xalan.lib.Redirect"
+    extension-element-prefixes="redirect">
+
+<!--
+    Copyright  2002-2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+
+    <xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+    <xsl:decimal-format decimal-separator="." grouping-separator="," />
+
+    <xsl:param name="output.dir" select="'.'"/>
+    <xsl:param name="basedir" select="'.'"/>
+
+    <xsl:template match="checkstyle">
+        <!-- create the index.html -->
+        <redirect:write file="{$output.dir}/index.html">
+            <xsl:call-template name="index.html"/>
+        </redirect:write>
+
+        <!-- create the stylesheet.css -->
+        <redirect:write file="{$output.dir}/stylesheet.css">
+            <xsl:call-template name="stylesheet.css"/>
+        </redirect:write>
+
+        <!-- create the overview-summary.html at the root -->
+        <redirect:write file="{$output.dir}/overview-frame.html">
+            <xsl:apply-templates select="." mode="overview"/>
+        </redirect:write>
+
+        <!-- create the all-classes.html at the root -->
+        <redirect:write file="{$output.dir}/allclasses-frame.html">
+            <xsl:apply-templates select="." mode="all.classes"/>
+        </redirect:write>
+
+        <!-- process all files -->
+        <xsl:apply-templates select="file[count(error) != 0]"/>
+    </xsl:template>
+
+    <xsl:template name="index.html">
+        <html>
+            <head>
+                <title>CheckStyle Audit</title>
+            </head>
+            <frameset cols="20%,80%">
+                <frame src="allclasses-frame.html" name="fileListFrame"/>
+                <frame src="overview-frame.html" name="fileFrame"/>
+            </frameset>
+            <noframes>
+                <h2>Frame Alert</h2>
+                <p>
+                    This document is designed to be viewed using the frames feature.
+                    If you see this message, you are using a non-frame-capable web client.
+                </p>
+            </noframes>
+        </html>
+    </xsl:template>
+
+    <xsl:template name="pageHeader">
+        <table border="0" cellpadding="0" cellspacing="0" width="100%">
+            <tr>
+                <td class="text-align:right"><h2>CheckStyle Audit</h2></td>
+            </tr>
+            <tr>
+                <td class="text-align:right">Designed for use with
+                  <a href='http://checkstyle.sourceforge.net/'>CheckStyle</a> and
+                  <a href='http://ant.apache.org/'>Ant</a>.</td>
+            </tr>
+        </table>
+        <hr size="1"/>
+    </xsl:template>
+
+    <xsl:template match="checkstyle" mode="overview">
+        <html>
+            <head>
+                <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+            </head>
+            <body>
+                <!-- page header -->
+                <xsl:call-template name="pageHeader"/>
+
+                <!-- Summary part -->
+                <xsl:apply-templates select="." mode="summary"/>
+                <hr size="1" width="100%" align="left"/>
+
+                <!-- File list part -->
+                <xsl:apply-templates select="." mode="filelist"/>
+            </body>
+        </html>
+    </xsl:template>
+
+    <xsl:template name="stylesheet.css">
+        .bannercell {
+        border: 0px;
+        padding: 0px;
+        }
+        body {
+        margin-left: 10;
+        margin-right: 10;
+        font:normal 80% arial,helvetica,sanserif;
+        background-color:#FFFFFF;
+        color:#000000;
+        }
+        .oddrow td {
+        background: #efefef;
+        }
+        .evenrow td {
+        background: #fff;
+        }
+        th, td {
+        text-align: left;
+        vertical-align: top;
+        }
+        th {
+        font-weight:bold;
+        background: #ccc;
+        color: black;
+        }
+        table, th, td {
+        font-size:100%;
+        border: none
+        }
+        table.log tr td, tr th {
+
+        }
+        h2 {
+        font-weight:bold;
+        font-size:140%;
+        margin-bottom: 5;
+        }
+        h3 {
+        font-size:100%;
+        font-weight:bold;
+        background: #525D76;
+        color: white;
+        text-decoration: none;
+        padding: 5px;
+        margin-right: 2px;
+        margin-left: 2px;
+        margin-bottom: 0;
+        }
+    </xsl:template>
+
+    <!--
+    Creates an all-classes.html file that contains a link to all files.
+    -->
+    <xsl:template match="checkstyle" mode="all.classes">
+        <html>
+            <head>
+                <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+            </head>
+            <body>
+                <h2>Files</h2>
+                <p>
+                    <table width="100%">
+                        <!-- For each file create its part -->
+                        <xsl:apply-templates select="file[count(error) != 0]" mode="all.classes">
+                            <xsl:sort select="substring-after(@name, $basedir)"/>
+                        </xsl:apply-templates>
+                    </table>
+                </p>
+            </body>
+        </html>
+    </xsl:template>
+
+    <xsl:template match="checkstyle" mode="filelist">
+        <h3>Files</h3>
+        <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+            <tr>
+                <th>Name</th>
+                <th>Errors</th>
+            </tr>
+            <xsl:apply-templates select="file[count(error) != 0]" mode="filelist">
+                <xsl:sort select="count(error)" order="descending" data-type="number"/>
+            </xsl:apply-templates>
+        </table>
+    </xsl:template>
+
+    <xsl:template match="file" mode="filelist">
+        <tr>
+            <xsl:call-template name="alternated-row"/>
+            <td nowrap="nowrap">
+                <a>
+                    <xsl:attribute name="href">
+                        <xsl:text>files/</xsl:text><xsl:value-of select="substring-after(@name, $basedir)"/><xsl:text>.html</xsl:text>
+                    </xsl:attribute>
+                    <xsl:value-of select="substring-after(@name, $basedir)"/>
+                </a>
+            </td>
+            <td><xsl:value-of select="count(error)"/></td>
+        </tr>
+    </xsl:template>
+
+    <xsl:template match="file" mode="all.classes">
+        <tr>
+            <td nowrap="nowrap">
+                <a target="fileFrame">
+                    <xsl:attribute name="href">
+                        <xsl:text>files/</xsl:text><xsl:value-of select="substring-after(@name, $basedir)"/><xsl:text>.html</xsl:text>
+                    </xsl:attribute>
+                    <xsl:value-of select="substring-after(@name, $basedir)"/>
+                </a>
+            </td>
+        </tr>
+    </xsl:template>
+
+    <!--
+    transform string like a/b/c to ../../../
+    @param path the path to transform into a descending directory path
+    -->
+    <xsl:template name="path">
+        <xsl:param name="path"/>
+        <xsl:if test="contains($path,'/')">
+            <xsl:text>../</xsl:text>
+            <xsl:call-template name="path">
+                <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'/')"/></xsl:with-param>
+            </xsl:call-template>
+        </xsl:if>
+        <xsl:if test="not(contains($path,'/')) and not($path = '')">
+            <xsl:text>../</xsl:text>
+        </xsl:if>
+    </xsl:template>
+
+    <xsl:template match="file">
+        <redirect:write file="{$output.dir}/files/{substring-after(@name, $basedir)}.html">
+            <html>
+                <head>
+                    <link rel="stylesheet" type="text/css">
+                        <xsl:attribute name="href"><xsl:call-template name="path"><xsl:with-param name="path" select="substring-after(@name, $basedir)"/></xsl:call-template><xsl:text>stylesheet.css</xsl:text></xsl:attribute>
+                    </link>
+                </head>
+                <body>
+                    <xsl:call-template name="pageHeader"/>
+                    <h3>File <xsl:value-of select="substring-after(@name, $basedir)"/></h3>
+                    <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+                        <tr>
+                            <th>Error Description</th>
+                            <th>Line:Column</th>
+                        </tr>
+                        <xsl:for-each select="error">
+                            <tr>
+                                <xsl:call-template name="alternated-row"/>
+                                <td><a title="{@source}"><xsl:value-of select="@message"/></a></td>
+                                <td align="center"><xsl:value-of select="@line"/><xsl:if test="@column">:<xsl:value-of select="@column"/></xsl:if></td>
+                            </tr>
+                        </xsl:for-each>
+                    </table>
+                </body>
+            </html>
+        </redirect:write>
+    </xsl:template>
+
+    <xsl:template match="checkstyle" mode="summary">
+        <h3>Summary</h3>
+        <xsl:variable name="fileCount" select="count(file)"/>
+        <xsl:variable name="errorCount" select="count(file/error)"/>
+        <xsl:variable name="fileErrorCount" select="count(file[count(error) != 0])"/>
+        <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+            <tr>
+                <th>Total Files</th>
+                <th>Files With Errors</th>
+                <th>Errors</th>
+            </tr>
+            <tr>
+                <xsl:call-template name="alternated-row"/>
+                <td><xsl:value-of select="$fileCount"/></td>
+                <td><xsl:value-of select="$fileErrorCount"/></td>
+                <td><xsl:value-of select="$errorCount"/></td>
+            </tr>
+        </table>
+    </xsl:template>
+
+    <xsl:template name="alternated-row">
+        <xsl:attribute name="class">
+            <xsl:if test="position() mod 2 = 1">oddrow</xsl:if>
+            <xsl:if test="position() mod 2 = 0">evenrow</xsl:if>
+        </xsl:attribute>
+    </xsl:template>
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/checkstyle/checkstyle-text.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/checkstyle/checkstyle-text.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/checkstyle/checkstyle-text.xsl	(revision 16139)
@@ -0,0 +1,33 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+<!--
+    Copyright  2003-2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+
+    <xsl:strip-space elements="checkstyle"/>
+    <xsl:preserve-space elements="file"/>
+    <xsl:output method="text"/>
+    <xsl:template match="checkstyle/file/error">
+        <xsl:value-of select="../@name"/>
+        <xsl:text>:</xsl:text>
+        <xsl:value-of select="@line"/>
+        <xsl:text>:</xsl:text>
+        <xsl:value-of select="@column"/>
+        <xsl:text> </xsl:text>
+        <xsl:value-of select="@message"/>
+    </xsl:template>
+</xsl:stylesheet>
+
Index: /release-kits/shared/ant/src/etc/checkstyle/checkstyle-xdoc.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/checkstyle/checkstyle-xdoc.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/checkstyle/checkstyle-xdoc.xsl	(revision 16139)
@@ -0,0 +1,129 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt"
+    xmlns:redirect="org.apache.xalan.lib.Redirect"
+    extension-element-prefixes="redirect">
+
+<!--
+    Copyright  2003-2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+
+    <xsl:output method="xml" indent="yes"/>
+    <xsl:decimal-format decimal-separator="." grouping-separator="," />
+
+    <xsl:param name="output.dir" select="'.'"/>
+    <xsl:param name="basedir" select="'.'"/>
+
+    <xsl:template match="checkstyle">
+      <document>
+        <properties>
+          <title>Checkstyle Audit</title>
+        </properties>
+
+        <body>
+          <xsl:apply-templates select="." mode="summary"/>
+          <!-- File list part -->
+          <xsl:apply-templates select="." mode="filelist"/>
+          <xsl:apply-templates select="file[count(error) != 0]"/>
+        </body>
+      </document>
+    </xsl:template>
+
+    <xsl:template match="checkstyle" mode="filelist">
+      <section name="Files">
+        <table>
+            <tr>
+                <th>Name</th>
+                <th>Errors</th>
+            </tr>
+            <xsl:apply-templates select="file[count(error) != 0]" mode="filelist">
+                <xsl:sort select="count(error)" order="descending" data-type="number"/>
+            </xsl:apply-templates>
+        </table>
+      </section>
+    </xsl:template>
+
+    <xsl:template match="file" mode="filelist">
+        <tr>
+            <xsl:call-template name="alternated-row"/>
+            <td nowrap="nowrap">
+                <a>
+                    <xsl:attribute name="href">
+                        <xsl:text>files</xsl:text><xsl:value-of select="substring-after(@name, $basedir)"/><xsl:text>.html</xsl:text>
+                    </xsl:attribute>
+                    <xsl:value-of select="substring-after(@name, $basedir)"/>
+                </a>
+            </td>
+            <td><xsl:value-of select="count(error)"/></td>
+        </tr>
+    </xsl:template>
+
+    <xsl:template match="file">
+      <redirect:write file="{$output.dir}/files{substring-after(@name, $basedir)}.xml">
+        <document>
+          <properties>
+            <title>Checkstyle Audit</title>
+          </properties>
+
+          <body>
+            <section name="Details for {substring-after(@name, $basedir)}">
+              <table>
+                  <tr>
+                      <th>Error Description</th>
+                      <th>Line</th>
+                  </tr>
+                  <xsl:for-each select="error">
+                      <tr>
+                          <xsl:call-template name="alternated-row"/>
+                          <td><a title="{@source}"><xsl:value-of select="@message"/></a></td>
+                          <td><xsl:value-of select="@line"/></td>
+                      </tr>
+                  </xsl:for-each>
+              </table>
+            </section>
+          </body>
+        </document>
+      </redirect:write>
+    </xsl:template>
+
+    <xsl:template match="checkstyle" mode="summary">
+      <section name="Summary">
+        <xsl:variable name="fileCount" select="count(file)"/>
+        <xsl:variable name="errorCount" select="count(file/error)"/>
+        <xsl:variable name="fileErrorCount" select="count(file[count(error) != 0])"/>
+        <table>
+            <tr>
+                <th>Files</th>
+                <th>Files With Errors</th>
+                <th>Errors</th>
+            </tr>
+            <tr>
+                <xsl:call-template name="alternated-row"/>
+                <td><xsl:value-of select="$fileCount"/></td>
+                <td><xsl:value-of select="$fileErrorCount"/></td>
+                <td><xsl:value-of select="$errorCount"/></td>
+            </tr>
+        </table>
+      </section>
+    </xsl:template>
+
+    <xsl:template name="alternated-row">
+        <xsl:attribute name="class">
+            <xsl:if test="position() mod 2 = 1">oddrow</xsl:if>
+            <xsl:if test="position() mod 2 = 0">evenrow</xsl:if>
+        </xsl:attribute>
+    </xsl:template>
+</xsl:stylesheet>
+
Index: /release-kits/shared/ant/src/etc/coverage-frames.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/coverage-frames.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/coverage-frames.xsl	(revision 16139)
@@ -0,0 +1,496 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt"
+    xmlns:redirect="org.apache.xalan.lib.Redirect"
+    extension-element-prefixes="redirect">
+<xsl:output method="html" indent="yes"/>
+<xsl:decimal-format decimal-separator="." grouping-separator="," />
+<!--
+    Copyright  2001-2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+
+<!--
+
+ Sample stylesheet to be used with JProbe 3.0 XML output.
+
+ It creates a set of HTML files a la javadoc where you can browse easily
+ through all packages and classes.
+
+ It is best used with JProbe Coverage Ant task that gives you the benefit
+ of a reference classpath so that you have the list of classes/methods
+ that are not used at all in a given classpath.
+
+ @author Stephane Bailliez <a href="mailto:sbailliez@apache.org"/>
+
+-->
+
+<!-- default output directory is current directory -->
+<xsl:param name="output.dir" select="'.'"/>
+
+<!-- ======================================================================
+    Root element
+    ======================================================================= -->
+<xsl:template match="/snapshot">
+    <!-- create the index.html -->
+    <redirect:write file="{$output.dir}/index.html">
+        <xsl:call-template name="index.html"/>
+    </redirect:write>
+
+    <!-- create the stylesheet.css -->
+    <redirect:write file="{$output.dir}/stylesheet.css">
+        <xsl:call-template name="stylesheet.css"/>
+    </redirect:write>
+
+    <!-- create the overview-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-summary.html">
+        <xsl:apply-templates select="." mode="overview.packages"/>
+    </redirect:write>
+
+    <!-- create the all-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-frame.html">
+        <xsl:apply-templates select="." mode="all.packages"/>
+    </redirect:write>
+
+    <!-- create the all-classes.html at the root -->
+    <redirect:write file="{$output.dir}/allclasses-frame.html">
+        <xsl:apply-templates select="." mode="all.classes"/>
+    </redirect:write>
+
+    <!-- process all packages -->
+    <xsl:apply-templates select="./package" mode="write"/>
+</xsl:template>
+
+<!-- =======================================================================
+    Frameset definition. Entry point for the report.
+    3 frames: packageListFrame, classListFrame, classFrame
+    ======================================================================= -->
+<xsl:template name="index.html">
+<html>
+    <head><title>Coverage Results.</title></head>
+    <frameset cols="20%,80%">
+        <frameset rows="30%,70%">
+            <frame src="overview-frame.html" name="packageListFrame"/>
+            <frame src="allclasses-frame.html" name="classListFrame"/>
+        </frameset>
+        <frame src="overview-summary.html" name="classFrame"/>
+    </frameset>
+    <noframes>
+        <h2>Frame Alert</h2>
+        <p>
+        This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+        </p>
+    </noframes>
+</html>
+</xsl:template>
+
+<!-- =======================================================================
+    Stylesheet CSS used
+    ======================================================================= -->
+<!-- this is the stylesheet css to use for nearly everything -->
+<xsl:template name="stylesheet.css">
+    .bannercell {
+      border: 0px;
+      padding: 0px;
+    }
+    body {
+      margin-left: 10;
+      margin-right: 10;
+      font:normal 80% arial,helvetica,sanserif;
+      background-color:#FFFFFF;
+      color:#000000;
+    }
+    .a td {
+      background: #efefef;
+    }
+    .b td {
+      background: #fff;
+    }
+    th, td {
+      text-align: left;
+      vertical-align: top;
+    }
+    th {
+      font-weight:bold;
+      background: #ccc;
+      color: black;
+    }
+    table, th, td {
+      font-size:100%;
+      border: none
+    }
+    table.log tr td, tr th {
+
+    }
+    h2 {
+      font-weight:bold;
+      font-size:140%;
+      margin-bottom: 5;
+    }
+    h3 {
+      font-size:100%;
+      font-weight:bold;
+      background: #525D76;
+      color: white;
+      text-decoration: none;
+      padding: 5px;
+      margin-right: 2px;
+      margin-left: 2px;
+      margin-bottom: 0;
+    }
+</xsl:template>
+
+<!-- =======================================================================
+    List of all classes in all packages
+    This will be the first page in the classListFrame
+    ======================================================================= -->
+<xsl:template match="snapshot" mode="all.classes">
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link"/>
+        </head>
+        <body>
+            <h2>Classes</h2>
+            <table width="100%">
+                <xsl:for-each select="package/class">
+                    <xsl:sort select="@name"/>
+                    <xsl:variable name="package.name" select="(ancestor::package)[last()]/@name"/>
+                    <xsl:variable name="link">
+                        <xsl:if test="not($package.name='')">
+                            <xsl:value-of select="translate($package.name,'.','/')"/><xsl:text>/</xsl:text>
+                        </xsl:if><xsl:value-of select="@name"/><xsl:text>.html</xsl:text>
+                    </xsl:variable>
+                    <tr>
+                        <td nowrap="nowrap">
+                            <a target="classFrame" href="{$link}"><xsl:value-of select="@name"/></a>
+                        </td>
+                    </tr>
+                </xsl:for-each>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+<!-- list of all packages -->
+<xsl:template match="snapshot" mode="all.packages">
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link"/>
+        </head>
+        <body>
+            <h2><a href="overview-summary.html" target="classFrame">Home</a></h2>
+            <h2>Packages</h2>
+            <table width="100%">
+                <xsl:for-each select="package">
+                    <xsl:sort select="@name" order="ascending"/>
+                    <tr>
+                        <td nowrap="nowrap">
+                            <a href="{translate(@name,'.','/')}/package-summary.html" target="classFrame">
+                                <xsl:value-of select="@name"/>
+                            </a>
+                        </td>
+                    </tr>
+                </xsl:for-each>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+<!-- overview of statistics in packages -->
+<xsl:template match="snapshot" mode="overview.packages">
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link"/>
+        </head>
+        <body onload="open('allclasses-frame.html','classListFrame')">
+        <xsl:call-template name="pageHeader"/>
+        <h3>Summary</h3>
+        <table class="log" cellpadding="5" cellspacing="2" width="100%">
+            <tr>
+                <!--th width="10%" nowrap="nowrap">Date</th>
+                <th width="10%" nowrap="nowrap">Elapsed time</th-->
+                <th width="10%" nowrap="nowrap">Reported Classes</th>
+                <th width="10%" nowrap="nowrap">Methods Hit</th>
+                <th width="10%" nowrap="nowrap">Lines Hit</th>
+            </tr>
+            <tr class="a">
+                <!--td nowrap="nowrap"><xsl:value-of select="execution_log/@program_start"/></td>
+                <td><xsl:value-of select="format-number(execution_log/@elapsed_time div 1000,'0.0')"/>secs</td-->
+                <td><xsl:value-of select="count(package/class)"/></td>
+                <td><xsl:value-of select="format-number(cov.data/@hit_methods div cov.data/@total_methods,'0.0%')"/></td>
+                <td><xsl:value-of select="format-number(cov.data/@hit_lines div cov.data/@total_lines,'0.0%')"/></td>
+            </tr>
+        </table>
+        <table border="0" width="100%">
+        <tr>
+        <td style="text-align: justify;">
+        To ensure accurate test runs on Java applications, developers need to know how much of
+        the code has been tested, and where to find any untested code. Coverage helps you
+        locate untested code, and measure precisely how much code has been exercised.
+        The result is a higher quality application in a shorter period of time.
+        <p/>
+        </td>
+        </tr>
+        </table>
+
+        <h3>Packages</h3>
+        <table class="log" cellpadding="5" cellspacing="2" width="100%">
+            <xsl:apply-templates select="package[1]" mode="stats.header"/>
+            <!-- display packages and sort them via their coverage rate -->
+            <xsl:for-each select="package">
+                <xsl:sort data-type="number" select="cov.data/@hit_lines div cov.data/@total_lines"/>
+                <tr>
+                  <xsl:call-template name="alternate-row"/>
+                    <td><a href="{translate(@name,'.','/')}/package-summary.html"><xsl:value-of select="@name"/></a></td>
+                    <td><xsl:value-of select="format-number(cov.data/@hit_methods div cov.data/@total_methods,'0.0%')"/></td>
+                    <td><xsl:value-of select="format-number(cov.data/@hit_lines div cov.data/@total_lines,'0.0%')"/></td>
+                </tr>
+            </xsl:for-each>
+        </table>
+        <xsl:call-template name="pageFooter"/>
+        </body>
+        </html>
+</xsl:template>
+
+<!--
+ detailed info for a package. It will output the list of classes
+, the summary page, and the info for each class
+-->
+<xsl:template match="package" mode="write">
+    <xsl:variable name="package.dir">
+        <xsl:if test="not(@name = '')"><xsl:value-of select="translate(@name,'.','/')"/></xsl:if>
+        <xsl:if test="@name = ''">.</xsl:if>
+    </xsl:variable>
+
+    <!-- create a classes-list.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-frame.html">
+        <xsl:apply-templates select="." mode="classes.list"/>
+    </redirect:write>
+
+    <!-- create a package-summary.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-summary.html">
+        <xsl:apply-templates select="." mode="package.summary"/>
+    </redirect:write>
+
+    <!-- for each class, creates a @name.html -->
+    <xsl:for-each select="class">
+        <redirect:write file="{$output.dir}/{$package.dir}/{@name}.html">
+            <xsl:apply-templates select="." mode="class.details"/>
+        </redirect:write>
+    </xsl:for-each>
+</xsl:template>
+
+<!-- list of classes in a package -->
+<xsl:template match="package" mode="classes.list">
+    <html>
+        <HEAD>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="@name"/>
+            </xsl:call-template>
+        </HEAD>
+        <BODY>
+            <table width="100%">
+                <tr>
+                    <td nowrap="nowrap">
+                        <H2><a href="package-summary.html" target="classFrame"><xsl:value-of select="@name"/></a></H2>
+                    </td>
+                </tr>
+            </table>
+
+            <H2>Classes</H2>
+            <TABLE WIDTH="100%">
+                <xsl:for-each select="class">
+                    <xsl:sort select="@name"/>
+                    <tr>
+                        <td nowrap="nowrap">
+                            <a href="{@name}.html" target="classFrame"><xsl:value-of select="@name"/></a>
+                        </td>
+                    </tr>
+                </xsl:for-each>
+            </TABLE>
+        </BODY>
+    </html>
+</xsl:template>
+
+<!-- summary of a package -->
+<xsl:template match="package" mode="package.summary">
+    <HTML>
+        <HEAD>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="@name"/>
+            </xsl:call-template>
+        </HEAD>
+        <!-- when loading this package, it will open the classes into the frame -->
+        <BODY onload="open('package-frame.html','classListFrame')">
+            <xsl:call-template name="pageHeader"/>
+            <h3>Package <xsl:value-of select="@name"/></h3>
+            <table class="log" cellpadding="5" cellspacing="2" width="100%">
+                <xsl:apply-templates select="." mode="stats.header"/>
+                <xsl:apply-templates select="." mode="stats"/>
+            </table>
+
+            <xsl:if test="count(class) &gt; 0">
+                <H3>Classes</H3>
+                <table class="log" cellpadding="5" cellspacing="2" width="100%">
+                    <xsl:apply-templates select="." mode="stats.header"/>
+                    <xsl:apply-templates select="class" mode="stats">
+                        <xsl:sort data-type="number" select="cov.data/@hit_lines div cov.data/@total_lines"/>
+                    </xsl:apply-templates>
+                </table>
+            </xsl:if>
+            <xsl:call-template name="pageFooter"/>
+        </BODY>
+    </HTML>
+</xsl:template>
+
+<!-- details of a class -->
+<xsl:template match="class" mode="class.details">
+    <xsl:variable name="package.name" select="(ancestor::package)[last()]/@name"/>
+    <HTML>
+        <HEAD>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$package.name"/>
+            </xsl:call-template>
+        </HEAD>
+        <BODY>
+            <xsl:call-template name="pageHeader"/>
+            <H3>Class <xsl:if test="not($package.name = '')"><xsl:value-of select="$package.name"/>.</xsl:if><xsl:value-of select="@name"/></H3>
+
+            <!-- class summary -->
+            <table class="log" cellpadding="5" cellspacing="2" width="100%">
+                <xsl:apply-templates select="." mode="stats.header"/>
+                <xsl:apply-templates select="." mode="stats"/>
+            </table>
+
+            <!-- details of methods -->
+            <H3>Methods</H3>
+            <table class="log" cellpadding="5" cellspacing="2" width="100%">
+                <xsl:apply-templates select="method[1]" mode="stats.header"/>
+                <xsl:apply-templates select="method" mode="stats">
+                    <xsl:sort data-type="number" select="cov.data/@hit_lines div cov.data/@total_lines"/>
+                </xsl:apply-templates>
+            </table>
+            <xsl:call-template name="pageFooter"/>
+        </BODY>
+    </HTML>
+
+</xsl:template>
+
+<!-- Page Header -->
+<xsl:template name="pageHeader">
+  <!-- jakarta logo -->
+  <table border="0" cellpadding="0" cellspacing="0" width="100%">
+  <tr>
+    <td class="bannercell" rowspan="2">
+      <a href="http://jakarta.apache.org/">
+      <img src="http://jakarta.apache.org/images/jakarta-logo.gif" alt="http://jakarta.apache.org" align="left" border="0"/>
+      </a>
+    </td>
+        <td style="text-align:right"><h2>Source Code Coverage</h2></td>
+        </tr>
+        <tr>
+        <td style="text-align:right">Designed for use with <a href='http://www.sitraka.com/jprobe'>Sitraka JProbe</a> and <a href='http://jakarta.apache.org'>Ant</a>.</td>
+        </tr>
+  </table>
+    <hr size="1"/>
+</xsl:template>
+
+<!-- Page Footer -->
+<xsl:template name="pageFooter">
+    <table width="100%">
+      <tr><td><hr noshade="yes" size="1"/></td></tr>
+      <tr><td>
+      <div align="center"><font color="#525D76" size="-1"><em>
+      Copyright &#169; 1999-2001, Apache Software Foundation
+      </em></font></div>
+      </td></tr>
+    </table>
+</xsl:template>
+
+
+<xsl:template name="table.header">
+    <tr>
+        <th width="80%">Name</th>
+        <th width="10%" nowrap="nowrap">Methods Hit</th>
+        <th width="10%" nowrap="nowrap">Lines Hit</th>
+    </tr>
+</xsl:template>
+
+<xsl:template match="method" mode="stats.header">
+    <tr>
+        <th width="90%">Name</th>
+        <th width="10%" nowrap="nowrap">Lines Hit</th>
+    </tr>
+</xsl:template>
+<xsl:template match="method" mode="stats">
+    <tr>
+      <xsl:call-template name="alternate-row"/>
+        <td><xsl:value-of select="@name"/></td>
+        <td>
+        <xsl:value-of select="format-number(cov.data/@hit_lines div cov.data/@total_lines,'0.0%')"/>
+        </td>
+    </tr>
+</xsl:template>
+
+<xsl:template match="package|class" mode="stats.header">
+    <tr>
+        <th width="80%">Name</th>
+        <th width="10%" nowrap="nowrap">Methods Hit</th>
+        <th width="10%" nowrap="nowrap">Lines Hit</th>
+    </tr>
+</xsl:template>
+<xsl:template match="package|class" mode="stats">
+    <tr>
+      <xsl:call-template name="alternate-row"/>
+        <td><xsl:value-of select="@name"/></td>
+        <td><xsl:value-of select="format-number(cov.data/@hit_methods div cov.data/@total_methods,'0.0%')"/></td>
+        <td><xsl:value-of select="format-number(cov.data/@hit_lines div cov.data/@total_lines,'0.0%')"/></td>
+    </tr>
+</xsl:template>
+
+<!--
+    transform string like a.b.c to ../../../
+    @param path the path to transform into a descending directory path
+-->
+<xsl:template name="path">
+    <xsl:param name="path"/>
+    <xsl:if test="contains($path,'.')">
+        <xsl:text>../</xsl:text>
+        <xsl:call-template name="path">
+            <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'.')"/></xsl:with-param>
+        </xsl:call-template>
+    </xsl:if>
+    <xsl:if test="not(contains($path,'.')) and not($path = '')">
+        <xsl:text>../</xsl:text>
+    </xsl:if>
+</xsl:template>
+
+
+<!-- create the link to the stylesheet based on the package name -->
+<xsl:template name="create.stylesheet.link">
+    <xsl:param name="package.name"/>
+    <LINK REL ="stylesheet" TYPE="text/css" TITLE="Style"><xsl:attribute name="href"><xsl:if test="not($package.name = 'unnamed package')"><xsl:call-template name="path"><xsl:with-param name="path" select="$package.name"/></xsl:call-template></xsl:if>stylesheet.css</xsl:attribute></LINK>
+</xsl:template>
+
+<!-- alternated row style -->
+<xsl:template name="alternate-row">
+<xsl:attribute name="class">
+  <xsl:if test="position() mod 2 = 1">a</xsl:if>
+  <xsl:if test="position() mod 2 = 0">b</xsl:if>
+</xsl:attribute>
+</xsl:template>
+
+</xsl:stylesheet>
+
+
Index: /release-kits/shared/ant/src/etc/jdepend-frames.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/jdepend-frames.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/jdepend-frames.xsl	(revision 16139)
@@ -0,0 +1,485 @@
+<xsl:stylesheet  xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+  xmlns:lxslt="http://xml.apache.org/xslt"
+  xmlns:redirect="org.apache.xalan.lib.Redirect"
+  extension-element-prefixes="redirect">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+<!--
+    Copyright  2002-2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+<!--
+
+ Sample stylesheet to be used with JDepend XML output.
+
+ It creates a set of HTML files a la javadoc where you can browse easily
+ through all packages and classes.
+
+ @author <a href="mailto:jtulley@novell.com">Jeff Tulley</a>
+
+  -->
+<xsl:param name="output.dir" select="'.'"/>
+
+<xsl:template match="JDepend">
+   <!-- create the index.html -->
+   <redirect:write file="{$output.dir}/index.html">
+      <xsl:call-template name="index.html"/>
+   </redirect:write>
+
+  <!-- create the stylesheet.css -->
+  <redirect:write file="{$output.dir}/stylesheet.css">
+    <xsl:call-template name="stylesheet.css"/>
+   </redirect:write>
+
+   <!-- create the overview-packages.html at the root -->
+  <redirect:write file="{$output.dir}/overview-summary.html">
+    <xsl:apply-templates select="." mode="overview.packages"/>
+  </redirect:write>
+
+   <!-- create the overview-packages.html at the root -->
+   <redirect:write file="{$output.dir}/overview-packages.html">
+    <xsl:apply-templates select="." mode="packages.details"/>
+  </redirect:write>
+
+   <!-- create the overview-cycles.html at the root -->
+   <redirect:write file="{$output.dir}/overview-cycles.html">
+    <xsl:apply-templates select="." mode="cycles.details"/>
+  </redirect:write>
+
+   <!-- create the overview-cycles.html at the root -->
+   <redirect:write file="{$output.dir}/overview-explanations.html">
+    <xsl:apply-templates select="." mode="explanations"/>
+  </redirect:write>
+
+  <!-- create the all-packages.html at the root -->
+   <redirect:write file="{$output.dir}/all-packages.html">
+    <xsl:apply-templates select="Packages" mode="all.packages"/>
+  </redirect:write>
+
+  <!-- create the all-cycles.html at the root -->
+  <redirect:write file="{$output.dir}/all-cycles.html">
+    <xsl:apply-templates select="Cycles" mode="all.cycles"/>
+  </redirect:write>
+</xsl:template>
+
+
+<xsl:template name="index.html">
+<html>
+   <head>
+      <title>JDepend Analysis</title>
+   </head>
+      <frameset cols="20%,80%">
+         <frameset rows="30%,70%">
+            <frame src="all-packages.html" name="packageListFrame"/>
+            <frame src="all-cycles.html" name="classListFrame"/>
+         </frameset>
+         <frame src="overview-summary.html" name="classFrame"/>
+      </frameset>
+      <noframes>
+         <h2>Frame Alert</h2>
+         <p>
+            This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+         </p>
+      </noframes>
+</html>
+</xsl:template>
+
+<!-- this is the stylesheet css to use for nearly everything -->
+<xsl:template name="stylesheet.css">
+   <style type="text/css">
+    body {
+    font:normal 68% verdana,arial,helvetica;
+    color:#000000;
+    }
+    table tr td, tr th {
+      font-size: 68%;
+    }
+    table.details tr th{
+    font-weight: bold;
+    text-align:left;
+    background:#a6caf0;
+    }
+    table.details tr td{
+    background:#eeeee0;
+    }
+
+    p {
+    line-height:1.5em;
+    margin-top:0.5em; margin-bottom:1.0em;
+    margin-left:2em;
+    margin-right:2em;
+    }
+    h1 {
+    margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
+    }
+    h2 {
+    margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
+    }
+    h3 {
+    margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
+    }
+    h4 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+    }
+    h5 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+    }
+    h6 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+    }
+    .Error {
+    font-weight:bold; color:red;
+    }
+    .Failure {
+    font-weight:bold; color:purple;
+    }
+    .Properties {
+    text-align:right;
+    }
+  </style>
+</xsl:template>
+
+<xsl:template match="JDepend" mode="overview.packages">
+   <html>
+      <head>
+         <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+      </head>
+      <body>
+         <xsl:call-template name="pageHeader"/>
+  <table width="100%"><tr align="left"><h2>Summary</h2><td>
+  </td><td align="right">
+  [summary]
+  [<a href="overview-packages.html">packages</a>]
+  [<a href="overview-cycles.html">cycles</a>]
+  [<a href="overview-explanations.html">explanations</a>]
+   </td></tr></table>
+         <table width="100%" class="details">
+            <tr>
+               <th>Package</th>
+               <th>Total Classes</th>
+               <th><a href="overview-explanations.html#EXnumber">Abstract Classes</a></th>
+               <th><a href="overview-explanations.html#EXnumber">Concrete Classes</a></th>
+               <th><a href="overview-explanations.html#EXafferent">Afferent Couplings</a></th>
+               <th><a href="overview-explanations.html#EXefferent">Efferent Couplings</a></th>
+               <th><a href="overview-explanations.html#EXabstractness">Abstractness</a></th>
+               <th><a href="overview-explanations.html#EXinstability">Instability</a></th>
+               <th><a href="overview-explanations.html#EXdistance">Distance</a></th>
+
+            </tr>
+            <xsl:for-each select="./Packages/Package">
+               <xsl:if test="count(error) = 0">
+                  <tr>
+                     <td align="left">
+                        <a>
+                           <xsl:attribute name="href">overview-packages.html#PK<xsl:value-of select="@name"/>
+                           </xsl:attribute>
+                           <xsl:value-of select="@name"/>
+                        </a>
+                     </td>
+                     <td align="right"><xsl:value-of select="Stats/TotalClasses"/></td>
+                     <td align="right"><xsl:value-of select="Stats/AbstractClasses"/></td>
+                     <td align="right"><xsl:value-of select="Stats/ConcreteClasses"/></td>
+                     <td align="right"><xsl:value-of select="Stats/Ca"/></td>
+                     <td align="right"><xsl:value-of select="Stats/Ce"/></td>
+                     <td align="right"><xsl:value-of select="Stats/A"/></td>
+                     <td align="right"><xsl:value-of select="Stats/I"/></td>
+                     <td align="right"><xsl:value-of select="Stats/D"/></td>
+                  </tr>
+               </xsl:if>
+            </xsl:for-each>
+            <xsl:for-each select="./Packages/Package">
+               <xsl:if test="count(error) &gt; 0">
+                  <tr>
+                     <td align="left">
+                        <xsl:value-of select="@name"/>
+                     </td>
+                     <td align="left" colspan="8"><xsl:value-of select="error"/></td>
+                  </tr>
+               </xsl:if>
+            </xsl:for-each>
+         </table>
+      </body>
+   </html>
+</xsl:template>
+
+<xsl:template match="JDepend" mode="packages.details">
+   <html>
+      <head>
+          <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+      </head>
+      <body>
+         <xsl:call-template name="pageHeader"/>
+  <table width="100%"><tr align="left"><h2>Packages</h2><td>
+  </td><td align="right">
+  [<a href="overview-summary.html">summary</a>]
+  [packages]
+  [<a href="overview-cycles.html">cycles</a>]
+  [<a href="overview-explanations.html">explanations</a>]
+   </td></tr></table>
+
+  <xsl:for-each select="./Packages/Package">
+    <xsl:if test="count(error) = 0">
+      <h3><a><xsl:attribute name="name">PK<xsl:value-of select="@name"/></xsl:attribute>
+      <xsl:value-of select="@name"/></a></h3>
+
+      <table width="100%"><tr>
+        <td><a href="overview-explanations.html#EXafferent">Afferent Couplings</a>: <xsl:value-of select="Stats/Ca"/></td>
+        <td><a href="overview-explanations.html#EXefferent">Efferent Couplings</a>: <xsl:value-of select="Stats/Ce"/></td>
+        <td><a href="overview-explanations.html#EXabstractness">Abstractness</a>: <xsl:value-of select="Stats/A"/></td>
+        <td><a href="overview-explanations.html#EXinstability">Instability</a>: <xsl:value-of select="Stats/I"/></td>
+        <td><a href="overview-explanations.html#EXdistance">Distance</a>: <xsl:value-of select="Stats/D"/></td>
+      </tr></table>
+
+      <table width="100%" class="details">
+        <tr>
+          <th>Abstract Classes</th>
+          <th>Concrete Classes</th>
+          <th>Used by Packages</th>
+          <th>Uses Packages</th>
+        </tr>
+        <tr>
+          <td valign="top" width="25%">
+          <xsl:if test="count(AbstractClasses/Class)=0">
+              <i>None</i>
+            </xsl:if>
+            <xsl:for-each select="AbstractClasses/Class">
+              <xsl:value-of select="node()"/><br/>
+            </xsl:for-each>
+          </td>
+          <td valign="top" width="25%">
+            <xsl:if test="count(ConcreteClasses/Class)=0">
+              <i>None</i>
+            </xsl:if>
+            <xsl:for-each select="ConcreteClasses/Class">
+              <xsl:value-of select="node()"/><br/>
+            </xsl:for-each>
+          </td>
+          <td valign="top" width="25%">
+            <xsl:if test="count(UsedBy/Package)=0">
+              <i>None</i>
+            </xsl:if>
+            <xsl:for-each select="UsedBy/Package">
+              <a>
+                        <xsl:attribute name="href">overview-packages.html#PK<xsl:value-of select="node()"/></xsl:attribute>
+                <xsl:value-of select="node()"/>
+              </a><br/>
+            </xsl:for-each>
+          </td>
+          <td valign="top" width="25%">
+            <xsl:if test="count(DependsUpon/Package)=0">
+              <i>None</i>
+            </xsl:if>
+            <xsl:for-each select="DependsUpon/Package">
+              <a>
+                        <xsl:attribute name="href">overview-packages.html#PK<xsl:value-of select="node()"/></xsl:attribute>
+                <xsl:value-of select="node()"/>
+              </a><br/>
+            </xsl:for-each>
+          </td>
+        </tr>
+      </table>
+    </xsl:if>
+  </xsl:for-each>
+  <!-- this is often a long listing; provide a lower navigation table also -->
+  <table width="100%"><tr align="left"><td></td><td align="right">
+  [<a href="overview-summary.html">summary</a>]
+  [packages]
+  [<a href="overview-cycles.html">cycles</a>]
+  [<a href="overview-explanations.html">explanations</a>]
+   </td></tr></table>
+      </body>
+   </html>
+</xsl:template>
+
+<xsl:template match="JDepend" mode="cycles.details">
+   <html>
+      <head>
+         <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+      </head>
+      <body>
+         <xsl:call-template name="pageHeader"/>
+  <table width="100%"><tr align="left"><h2>Cycles</h2><td>
+  </td><td align="right">
+  [<a href="overview-summary.html">summary</a>]
+  [<a href="overview-packages.html">packages</a>]
+  [cycles]
+  [<a href="overview-explanations.html">explanations</a>]
+   </td></tr></table>
+  <!--<table width="100%"><tr><td>
+  </td><td align="right">
+    [<a href="#NVsummary">summary</a>]
+  [<a href="#NVpackages">packages</a>]
+  [<a href="#NVcycles">cycles</a>]
+   [<a href="#NVexplanations">explanations</a>]
+  </td></tr></table> -->
+
+  <xsl:if test="count(Cycles/Package) = 0">
+    <p>There are no cyclic dependancies.</p>
+  </xsl:if>
+  <xsl:for-each select="Cycles/Package">
+     <h3><a><xsl:attribute name="name">#CY<xsl:value-of select="@Name"/></xsl:attribute><xsl:value-of select="@Name"/></a></h3><p>
+    <xsl:for-each select="Package">
+      <xsl:value-of select="."/><br/>
+    </xsl:for-each></p>
+  </xsl:for-each>
+  <!-- this is often a long listing; provide a lower navigation table also -->
+  <table width="100%"><tr align="left"><td></td><td align="right">
+  [<a href="overview-summary.html">summary</a>]
+  [<a href="overview-packages.html">packages</a>]
+  [cycles]
+  [<a href="overview-explanations.html">explanations</a>]
+   </td></tr></table>
+  </body>
+  </html>
+</xsl:template>
+
+<xsl:template match="JDepend" mode="explanations">
+   <html>
+      <head>
+         <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+      </head>
+      <body>
+         <xsl:call-template name="pageHeader"/>
+
+  <table width="100%"><tr align="left"><h2>Explanations</h2><td>
+  </td><td align="right">
+  [<a href="overview-summary.html">summary</a>]
+  [<a href="overview-packages.html">packages</a>]
+  [<a href="overview-cycles.html">cycles</a>]
+  [explanations]
+   </td></tr></table>
+
+  <p>The following explanations are for quick reference and are lifted directly from the original <a href="http://www.clarkware.com/software/JDepend.html">JDepend documentation</a>.</p>
+
+  <h3><a name="EXnumber">Number of Classes</a></h3>
+    <p>The number of concrete and abstract classes (and interfaces) in the package is an indicator of the extensibility of the package.</p>
+  <h3><a name="EXafferent">Afferent Couplings</a></h3>
+    <p>The number of other packages that depend upon classes within the package is an indicator of the package's responsibility. </p>
+  <h3><a name="EXefferent">Efferent Couplings</a></h3>
+    <p>The number of other packages that the classes in the package depend upon is an indicator of the package's independence. </p>
+  <h3><a name="EXabstractness">Abstractness</a></h3>
+    <p>The ratio of the number of abstract classes (and interfaces) in the analyzed package to the total number of classes in the analyzed package. </p>
+    <p>The range for this metric is 0 to 1, with A=0 indicating a completely concrete package and A=1 indicating a completely abstract package. </p>
+  <h3><a name="EXinstability">Instability</a></h3>
+    <p>The ratio of efferent coupling (Ce) to total coupling (Ce / (Ce + Ca)). This metric is an indicator of the package's resilience to change. </p>
+    <p>The range for this metric is 0 to 1, with I=0 indicating a completely stable package and I=1 indicating a completely instable package. </p>
+  <h3><a name="EXdistance">Distance</a></h3>
+    <p>The perpendicular distance of a package from the idealized line A + I = 1. This metric is an indicator of the package's balance between abstractness and stability. </p>
+    <p>A package squarely on the main sequence is optimally balanced with respect to its abstractness and stability. Ideal packages are either completely abstract and stable (x=0, y=1) or completely concrete and instable (x=1, y=0). </p>
+    <p>The range for this metric is 0 to 1, with D=0 indicating a package that is coincident with the main sequence and D=1 indicating a package that is as far from the main sequence as possible. </p>
+
+      </body>
+   </html>
+</xsl:template>
+
+
+<!--
+Creates an html file that contains a link to all package links in overview-packages.html.
+  @bug there will be a problem here, I don't know yet how to handle unnamed package :(
+-->
+<xsl:template match="JDepend/Packages" mode="all.packages">
+  <html>
+    <head>
+      <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+    </head>
+    <body>
+  <table width="100%"><tr align="left"><td></td><td nowrap="nowrap" align="right">
+  [<a href="overview-summary.html" target="classFrame">summary</a>]
+  [<a href="overview-packages.html" target="classFrame">packages</a>]
+  [<a href="overview-cycles.html" target="classFrame">cycles</a>]
+  [<a href="overview-explanations.html" target="classFrame">explanations</a>]
+   </td></tr></table>
+      <h2>Packages</h2>
+        <table width="100%">
+          <xsl:apply-templates select="Package[count(error)=0]" mode="all.packages.link">
+            <xsl:sort select="@name"/>
+          </xsl:apply-templates>
+          <xsl:apply-templates select="Package[count(error) &gt; 0]" mode="all.packages.nolink">
+            <xsl:sort select="@name"/>
+          </xsl:apply-templates>
+        </table>
+    </body>
+  </html>
+</xsl:template>
+
+<xsl:template match="JDepend/Packages/Package" mode="all.packages.link">
+  <tr>
+    <td nowrap="nowrap">
+         <a href="overview-packages.html#PK{@name}" target="classFrame">
+        <xsl:value-of select="@name"/>
+      </a>
+    </td>
+  </tr>
+</xsl:template>
+
+<!--
+I do not know JDepend enough to know if every error results in a non-analyzed package,
+but that is how I am presenting it to the viewer.  This may need to change.
+  @bug there will be a problem here, I don't know yet how to handle unnamed package :(
+-->
+<xsl:template match="JDepend/Packages/Package" mode="all.packages.nolink">
+  <tr>
+    <td nowrap="nowrap">
+       Not Analyzed: <xsl:value-of select="@name"/>
+    </td>
+  </tr>
+</xsl:template>
+
+<!--
+Creates an html file that contains a link to all package links in overview-cycles.html.
+  @bug there will be a problem here, I don't know yet how to handle unnamed package :(
+-->
+<xsl:template match="JDepend/Cycles" mode="all.cycles">
+  <html>
+    <head>
+      <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
+    </head>
+    <body>
+  <table width="100%"><tr align="left"><td></td><td nowrap="nowrap" align="right">
+  [<a href="overview-summary.html" target="classFrame">summary</a>]
+  [<a href="overview-packages.html" target="classFrame">packages</a>]
+  [<a href="overview-cycles.html" target="classFrame">cycles</a>]
+  [<a href="overview-explanations.html" target="classFrame">explanations</a>]
+   </td></tr></table>
+      <h2>Cycles</h2>
+        <table width="100%">
+           <xsl:apply-templates select="Package" mode="all.cycles">
+            <xsl:sort select="@Name"/>
+          </xsl:apply-templates>
+        </table>
+    </body>
+  </html>
+</xsl:template>
+
+<xsl:template match="JDepend/Cycles/Package" mode="all.cycles">
+  <tr>
+    <td nowrap="nowrap">
+         <a href="overview-cycles.html#CY{@Name}" target="classFrame"><xsl:value-of select="@Name"/></a>
+    </td>
+  </tr>
+</xsl:template>
+
+<!-- Page HEADER -->
+<xsl:template name="pageHeader">
+   <h1>JDepend Analysis</h1>
+  <table width="100%">
+  <tr>
+    <td align="left"></td>
+      <td align="right">Designed for use with <a href="http://www.clarkware.com/software/JDepend.html">JDepend</a> and <a href="http://jakarta.apache.org">Ant</a>.</td>
+  </tr>
+  </table>
+  <hr size="1"/>
+</xsl:template>
+
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/jdepend.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/jdepend.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/jdepend.xsl	(revision 16139)
@@ -0,0 +1,275 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+<!--
+    Copyright  2002,2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+
+<xsl:output method="html" indent="yes"  encoding="US-ASCII"/>
+
+<xsl:template match="JDepend">
+    <html>
+    <head>
+        <title>JDepend Analysis</title>
+        
+    <style type="text/css">
+      body {
+        font:normal 68% verdana,arial,helvetica;
+        color:#000000;
+      }
+      table tr td, tr th {
+          font-size: 68%;
+      }
+      table.details tr th{
+        font-weight: bold;
+        text-align:left;
+        background:#a6caf0;
+      }
+      table.details tr td{
+        background:#eeeee0;
+      }
+      
+      p {
+        line-height:1.5em;
+        margin-top:0.5em; margin-bottom:1.0em;
+        margin-left:2em;
+        margin-right:2em;
+      }
+      h1 {
+        margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
+      }
+      h2 {
+        margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
+      }
+      h3 {
+        margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
+      }
+      h4 {
+        margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+      }
+      h5 {
+        margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+      }
+      h6 {
+        margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+      }
+      .Error {
+        font-weight:bold; color:red;
+      }
+      .Failure {
+        font-weight:bold; color:purple;
+      }
+      .Properties {
+        text-align:right;
+      }
+      </style>
+        
+        
+    </head>
+    <body>
+    <!--h1>JDepend Report</h1>
+    <ul>
+    <xsl:for-each select="./Packages/Package">
+                <xsl:sort select="@name"/>
+        <li><xsl:value-of select="@name"/></li>
+    </xsl:for-each>
+    </ul-->
+    
+    <h1><a name="top">JDepend Analysis</a></h1>
+    <p align="right">Designed for use with <a href="http://www.clarkware.com/software/JDepend.html">JDepend</a> and <a href="http://jakarta.apache.org">Ant</a>.</p>
+    <hr size="2" />
+    
+    <table width="100%"><tr><td>
+    <a name="NVsummary"><h2>Summary</h2></a>
+    </td><td align="right">
+    [<a href="#NVsummary">summary</a>]
+    [<a href="#NVpackages">packages</a>]
+    [<a href="#NVcycles">cycles</a>]
+    [<a href="#NVexplanations">explanations</a>]
+    </td></tr></table>
+    
+    <table width="100%" class="details">
+        <tr>
+            <th>Package</th>
+            <th>Total Classes</th>
+            <th><a href="#EXnumber">Abstract Classes</a></th>
+            <th><a href="#EXnumber">Concrete Classes</a></th>
+            <th><a href="#EXafferent">Afferent Couplings</a></th>
+            <th><a href="#EXefferent">Efferent Couplings</a></th>
+            <th><a href="#EXabstractness">Abstractness</a></th>
+            <th><a href="#EXinstability">Instability</a></th>
+            <th><a href="#EXdistance">Distance</a></th>
+            
+        </tr>
+    <xsl:for-each select="./Packages/Package">
+        <xsl:if test="count(error) = 0">
+            <tr>
+                <td align="left">
+                    <a>
+                    <xsl:attribute name="href">#PK<xsl:value-of select="@name"/>
+                    </xsl:attribute>
+                    <xsl:value-of select="@name"/>
+                    </a>
+                </td>
+                <td align="right"><xsl:value-of select="Stats/TotalClasses"/></td>
+                <td align="right"><xsl:value-of select="Stats/AbstractClasses"/></td>
+                <td align="right"><xsl:value-of select="Stats/ConcreteClasses"/></td>
+                <td align="right"><xsl:value-of select="Stats/Ca"/></td>
+                <td align="right"><xsl:value-of select="Stats/Ce"/></td>
+                <td align="right"><xsl:value-of select="Stats/A"/></td>
+                <td align="right"><xsl:value-of select="Stats/I"/></td>
+                <td align="right"><xsl:value-of select="Stats/D"/></td>
+                
+
+            </tr>
+        </xsl:if>
+    </xsl:for-each>
+    <xsl:for-each select="./Packages/Package">
+        <xsl:if test="count(error) &gt; 0">
+            <tr>
+                <td align="left">
+                    <xsl:value-of select="@name"/>
+                </td>
+                <td align="left" colspan="8"><xsl:value-of select="error"/></td>
+            </tr>
+        </xsl:if>
+    </xsl:for-each>
+    </table>
+    
+    <table width="100%"><tr><td>
+    <a name="NVpackages"><h2>Packages</h2></a>
+    </td><td align="right">
+    [<a href="#NVsummary">summary</a>]
+    [<a href="#NVpackages">packages</a>]
+    [<a href="#NVcycles">cycles</a>]
+    [<a href="#NVexplanations">explanations</a>]
+    </td></tr></table>
+    
+    <xsl:for-each select="./Packages/Package">
+        <xsl:if test="count(error) = 0">
+            <h3><a><xsl:attribute name="name">PK<xsl:value-of select="@name"/></xsl:attribute>
+            <xsl:value-of select="@name"/></a></h3>
+            
+            <table width="100%"><tr>
+                <td><a href="#EXafferent">Afferent Couplings</a>: <xsl:value-of select="Stats/Ca"/></td>
+                <td><a href="#EXefferent">Efferent Couplings</a>: <xsl:value-of select="Stats/Ce"/></td>
+                <td><a href="#EXabstractness">Abstractness</a>: <xsl:value-of select="Stats/A"/></td>
+                <td><a href="#EXinstability">Instability</a>: <xsl:value-of select="Stats/I"/></td>
+                <td><a href="#EXdistance">Distance</a>: <xsl:value-of select="Stats/D"/></td>
+            </tr></table>
+            
+            <table width="100%" class="details">
+                <tr>
+                    <th>Abstract Classes</th>
+                    <th>Concrete Classes</th>
+                    <th>Used by Packages</th>
+                    <th>Uses Packages</th>
+                </tr>
+                <tr>
+                    <td valign="top" width="25%">
+                    <xsl:if test="count(AbstractClasses/Class)=0">
+                            <i>None</i>
+                        </xsl:if>
+                        <xsl:for-each select="AbstractClasses/Class">
+                            <xsl:value-of select="node()"/><br/>
+                        </xsl:for-each>
+                    </td>
+                    <td valign="top" width="25%">
+                        <xsl:if test="count(ConcreteClasses/Class)=0">
+                            <i>None</i>
+                        </xsl:if>
+                        <xsl:for-each select="ConcreteClasses/Class">
+                            <xsl:value-of select="node()"/><br/>
+                        </xsl:for-each>
+                    </td>
+                    <td valign="top" width="25%">
+                        <xsl:if test="count(UsedBy/Package)=0">
+                            <i>None</i>
+                        </xsl:if>
+                        <xsl:for-each select="UsedBy/Package">
+                            <a>
+                                <xsl:attribute name="href">#PK<xsl:value-of select="node()"/></xsl:attribute>
+                                <xsl:value-of select="node()"/>
+                            </a><br/>
+                        </xsl:for-each>
+                    </td>
+                    <td valign="top" width="25%">
+                        <xsl:if test="count(DependsUpon/Package)=0">
+                            <i>None</i>
+                        </xsl:if>
+                        <xsl:for-each select="DependsUpon/Package">
+                            <a>
+                                <xsl:attribute name="href">#PK<xsl:value-of select="node()"/></xsl:attribute>
+                                <xsl:value-of select="node()"/>
+                            </a><br/>
+                        </xsl:for-each>
+                    </td>
+                </tr>
+            </table>
+        </xsl:if>
+    </xsl:for-each>
+    
+    <table width="100%"><tr><td>
+    <a name="NVcycles"><h2>Cycles</h2></a>
+    </td><td align="right">
+    [<a href="#NVsummary">summary</a>]
+    [<a href="#NVpackages">packages</a>]
+    [<a href="#NVcycles">cycles</a>]
+    [<a href="#NVexplanations">explanations</a>]
+    </td></tr></table>
+    
+    <xsl:if test="count(Cycles/Package) = 0">
+        <p>There are no cyclic dependancies.</p>
+    </xsl:if>
+    <xsl:for-each select="Cycles/Package">
+        <h3><xsl:value-of select="@Name"/></h3><p>
+        <xsl:for-each select="Package">
+            <xsl:value-of select="."/><br/>
+        </xsl:for-each></p>
+    </xsl:for-each>
+    
+    <table width="100%"><tr><td>
+    <a name="NVexplanations"><h2>Explanations</h2></a>
+    </td><td align="right">
+    [<a href="#NVsummary">summary</a>]
+    [<a href="#NVpackages">packages</a>]
+    [<a href="#NVcycles">cycles</a>]
+    [<a href="#NVexplanations">explanations</a>]
+    </td></tr></table>
+    
+    <p>The following explanations are for quick reference and are lifted directly from the original <a href="http://www.clarkware.com/software/JDepend.html">JDepend documentation</a>.</p>
+    
+    <h3><a name="EXnumber">Number of Classes</a></h3>
+        <p>The number of concrete and abstract classes (and interfaces) in the package is an indicator of the extensibility of the package.</p>
+    <h3><a name="EXafferent">Afferent Couplings</a></h3>
+        <p>The number of other packages that depend upon classes within the package is an indicator of the package's responsibility. </p>
+    <h3><a name="EXefferent">Efferent Couplings</a></h3>
+        <p>The number of other packages that the classes in the package depend upon is an indicator of the package's independence. </p>
+    <h3><a name="EXabstractness">Abstractness</a></h3> 
+        <p>The ratio of the number of abstract classes (and interfaces) in the analyzed package to the total number of classes in the analyzed package. </p>
+        <p>The range for this metric is 0 to 1, with A=0 indicating a completely concrete package and A=1 indicating a completely abstract package. </p>
+    <h3><a name="EXinstability">Instability</a></h3>
+        <p>The ratio of efferent coupling (Ce) to total coupling (Ce / (Ce + Ca)). This metric is an indicator of the package's resilience to change. </p>
+        <p>The range for this metric is 0 to 1, with I=0 indicating a completely stable package and I=1 indicating a completely instable package. </p>
+    <h3><a name="EXdistance">Distance</a></h3>
+        <p>The perpendicular distance of a package from the idealized line A + I = 1. This metric is an indicator of the package's balance between abstractness and stability. </p>
+        <p>A package squarely on the main sequence is optimally balanced with respect to its abstractness and stability. Ideal packages are either completely abstract and stable (x=0, y=1) or completely concrete and instable (x=1, y=0). </p>
+        <p>The range for this metric is 0 to 1, with D=0 indicating a package that is coincident with the main sequence and D=1 indicating a package that is as far from the main sequence as possible. </p>
+    
+    </body>
+    </html>
+</xsl:template>
+
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/junit-frames-xalan1.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/junit-frames-xalan1.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/junit-frames-xalan1.xsl	(revision 16139)
@@ -0,0 +1,716 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt"
+    xmlns:redirect="org.apache.xalan.lib.Redirect"
+    xmlns:stringutils="xalan://org.apache.tools.ant.util.StringUtils"
+    extension-element-prefixes="redirect">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+<xsl:decimal-format decimal-separator="." grouping-separator=","/>
+<!--
+   Copyright 2001-2004 The Apache Software Foundation
+
+   Licensed 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.
+ -->
+
+<!--
+
+ Sample stylesheet to be used with Ant JUnitReport output.
+
+ It creates a set of HTML files a la javadoc where you can browse easily
+ through all packages and classes.
+
+-->
+<xsl:param name="output.dir" select="'.'"/>
+
+
+<xsl:template match="testsuites">
+    <!-- create the index.html -->
+    <redirect:write file="{$output.dir}/index.html">
+        <xsl:call-template name="index.html"/>
+    </redirect:write>
+
+    <!-- create the stylesheet.css -->
+    <redirect:write file="{$output.dir}/stylesheet.css">
+        <xsl:call-template name="stylesheet.css"/>
+    </redirect:write>
+
+    <!-- create the overview-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-summary.html">
+        <xsl:apply-templates select="." mode="overview.packages"/>
+    </redirect:write>
+
+    <!-- create the all-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-frame.html">
+        <xsl:apply-templates select="." mode="all.packages"/>
+    </redirect:write>
+
+    <!-- create the all-classes.html at the root -->
+    <redirect:write file="{$output.dir}/allclasses-frame.html">
+        <xsl:apply-templates select="." mode="all.classes"/>
+    </redirect:write>
+
+    <!-- process all packages -->
+    <xsl:for-each select="./testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+        <xsl:call-template name="package">
+            <xsl:with-param name="name" select="@package"/>
+        </xsl:call-template>
+    </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template name="package">
+    <xsl:param name="name"/>
+    <xsl:variable name="package.dir">
+        <xsl:if test="not($name = '')"><xsl:value-of select="translate($name,'.','/')"/></xsl:if>
+        <xsl:if test="$name = ''">.</xsl:if>
+    </xsl:variable>
+    <!--Processing package <xsl:value-of select="@name"/> in <xsl:value-of select="$output.dir"/> -->
+    <!-- create a classes-list.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-frame.html">
+        <xsl:call-template name="classes.list">
+            <xsl:with-param name="name" select="$name"/>
+        </xsl:call-template>
+    </redirect:write>
+
+    <!-- create a package-summary.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-summary.html">
+        <xsl:call-template name="package.summary">
+            <xsl:with-param name="name" select="$name"/>
+        </xsl:call-template>
+    </redirect:write>
+
+    <!-- for each class, creates a @name.html -->
+    <!-- @bug there will be a problem with inner classes having the same name, it will be overwritten -->
+    <xsl:for-each select="/testsuites/testsuite[@package = $name]">
+        <redirect:write file="{$output.dir}/{$package.dir}/{@name}.html">
+            <xsl:apply-templates select="." mode="class.details"/>
+        </redirect:write>
+        <xsl:if test="string-length(./system-out)!=0">
+            <redirect:write file="{$output.dir}/{$package.dir}/{@name}-out.txt">
+                <xsl:value-of select="./system-out" />
+            </redirect:write>
+        </xsl:if>
+        <xsl:if test="string-length(./system-err)!=0">
+            <redirect:write file="{$output.dir}/{$package.dir}/{@name}-err.txt">
+                <xsl:value-of select="./system-err" />
+            </redirect:write>
+        </xsl:if>
+    </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="index.html">
+<html>
+    <head>
+        <title>Unit Test Results.</title>
+    </head>
+    <frameset cols="20%,80%">
+        <frameset rows="30%,70%">
+            <frame src="overview-frame.html" name="packageListFrame"/>
+            <frame src="allclasses-frame.html" name="classListFrame"/>
+        </frameset>
+        <frame src="overview-summary.html" name="classFrame"/>
+        <noframes>
+            <h2>Frame Alert</h2>
+            <p>
+                This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+            </p>
+        </noframes>
+    </frameset>
+</html>
+</xsl:template>
+
+<!-- this is the stylesheet css to use for nearly everything -->
+<xsl:template name="stylesheet.css">
+body {
+    font:normal 68% verdana,arial,helvetica;
+    color:#000000;
+}
+table tr td, table tr th {
+    font-size: 68%;
+}
+table.details tr th{
+    font-weight: bold;
+    text-align:left;
+    background:#a6caf0;
+}
+table.details tr td{
+    background:#eeeee0;
+}
+
+p {
+    line-height:1.5em;
+    margin-top:0.5em; margin-bottom:1.0em;
+}
+h1 {
+    margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
+}
+h2 {
+    margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
+}
+h3 {
+    margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
+}
+h4 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+h5 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+h6 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+.Error {
+    font-weight:bold; color:red;
+}
+.Failure {
+    font-weight:bold; color:purple;
+}
+.Properties {
+  text-align:right;
+}
+</xsl:template>
+
+
+<!-- ======================================================================
+    This page is created for every testsuite class.
+    It prints a summary of the testsuite and detailed information about
+    testcase methods.
+     ====================================================================== -->
+<xsl:template match="testsuite" mode="class.details">
+    <xsl:variable name="package.name" select="@package"/>
+    <xsl:variable name="class.name"><xsl:if test="not($package.name = '')"><xsl:value-of select="$package.name"/>.</xsl:if><xsl:value-of select="@name"/></xsl:variable>
+    <html>
+        <head>
+          <title>Unit Test Results: <xsl:value-of select="$class.name"/></title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$package.name"/>
+            </xsl:call-template>
+       <script type="text/javascript" language="JavaScript">
+        var TestCases = new Array();
+        var cur;
+        <xsl:apply-templates select="properties"/>
+       </script>
+       <script type="text/javascript" language="JavaScript"><![CDATA[
+        function displayProperties (name) {
+          var win = window.open('','JUnitSystemProperties','scrollbars=1,resizable=1');
+          var doc = win.document.open();
+          doc.write("<html><head><title>Properties of " + name + "</title>");
+          doc.write("<style type=\"text/css\">");
+          doc.write("body {font:normal 68% verdana,arial,helvetica; color:#000000; }");
+          doc.write("table tr td, table tr th { font-size: 68%; }");
+          doc.write("table.properties { border-collapse:collapse; border-left:solid 1 #cccccc; border-top:solid 1 #cccccc; padding:5px; }");
+          doc.write("table.properties th { text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#eeeeee; }");
+          doc.write("table.properties td { font:normal; text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#fffffff; }");
+          doc.write("h3 { margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica }");
+          doc.write("</style>");
+          doc.write("</head><body>");
+          doc.write("<h3>Properties of " + name + "</h3>");
+          doc.write("<div align=\"right\"><a href=\"javascript:window.close();\">Close</a></div>");
+          doc.write("<table class='properties'>");
+          doc.write("<tr><th>Name</th><th>Value</th></tr>");
+          for (prop in TestCases[name]) {
+            doc.write("<tr><th>" + prop + "</th><td>" + TestCases[name][prop] + "</td></tr>");
+          }
+          doc.write("</table>");
+          doc.write("</body></html>");
+          doc.close();
+          win.focus();
+        }
+      ]]>
+      </script>
+        </head>
+        <body>
+            <xsl:call-template name="pageHeader"/>
+            <h3>Class <xsl:value-of select="$class.name"/></h3>
+
+
+            <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+                <xsl:call-template name="testsuite.test.header"/>
+                <xsl:apply-templates select="." mode="print.test"/>
+            </table>
+
+            <h2>Tests</h2>
+            <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+        <xsl:call-template name="testcase.test.header"/>
+              <!--
+              test can even not be started at all (failure to load the class)
+              so report the error directly
+              -->
+                <xsl:if test="./error">
+                    <tr class="Error">
+                        <td colspan="4"><xsl:apply-templates select="./error"/></td>
+                    </tr>
+                </xsl:if>
+                <xsl:apply-templates select="./testcase" mode="print.test"/>
+            </table>
+            <div class="Properties">
+                <a>
+                    <xsl:attribute name="href">javascript:displayProperties('<xsl:value-of select="@package"/>.<xsl:value-of select="@name"/>');</xsl:attribute>
+                    Properties &#187;
+                </a>
+            </div>
+            <xsl:if test="string-length(./system-out)!=0">
+                <div class="Properties">
+                    <a>
+                        <xsl:attribute name="href">./<xsl:value-of select="@name"/>-out.txt</xsl:attribute>
+                        System.out &#187;
+                    </a>
+                </div>
+            </xsl:if>
+            <xsl:if test="string-length(./system-err)!=0">
+                <div class="Properties">
+                    <a>
+                        <xsl:attribute name="href">./<xsl:value-of select="@name"/>-err.txt</xsl:attribute>
+                        System.err &#187;
+                    </a>
+                </div>
+            </xsl:if>
+        </body>
+    </html>
+</xsl:template>
+
+  <!--
+   Write properties into a JavaScript data structure.
+   This is based on the original idea by Erik Hatcher (ehatcher@apache.org)
+   -->
+  <xsl:template match="properties">
+    cur = TestCases['<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>'] = new Array();
+    <xsl:for-each select="property">
+    <xsl:sort select="@name"/>
+        cur['<xsl:value-of select="@name"/>'] = '<xsl:call-template name="JS-escape"><xsl:with-param name="string" select="@value"/></xsl:call-template>';
+    </xsl:for-each>
+  </xsl:template>
+
+
+<!-- ======================================================================
+    This page is created for every package.
+    It prints the name of all classes that belongs to this package.
+    @param name the package name to print classes.
+     ====================================================================== -->
+<!-- list of classes in a package -->
+<xsl:template name="classes.list">
+    <xsl:param name="name"/>
+    <html>
+        <head>
+            <title>Unit Test Classes: <xsl:value-of select="$name"/></title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <table width="100%">
+                <tr>
+                    <td nowrap="nowrap">
+                        <h2><a href="package-summary.html" target="classFrame">
+                            <xsl:value-of select="$name"/>
+                            <xsl:if test="$name = ''">&lt;none&gt;</xsl:if>
+                        </a></h2>
+                    </td>
+                </tr>
+            </table>
+
+            <h2>Classes</h2>
+            <table width="100%">
+                <xsl:for-each select="/testsuites/testsuite[./@package = $name]">
+                    <xsl:sort select="@name"/>
+                    <tr>
+                        <td nowrap="nowrap">
+                            <a href="{@name}.html" target="classFrame"><xsl:value-of select="@name"/></a>
+                        </td>
+                    </tr>
+                </xsl:for-each>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+
+<!--
+    Creates an all-classes.html file that contains a link to all package-summary.html
+    on each class.
+-->
+<xsl:template match="testsuites" mode="all.classes">
+    <html>
+        <head>
+            <title>All Unit Test Classes</title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <h2>Classes</h2>
+            <table width="100%">
+                <xsl:apply-templates select="testsuite" mode="all.classes">
+                    <xsl:sort select="@name"/>
+                </xsl:apply-templates>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+<xsl:template match="testsuite" mode="all.classes">
+    <xsl:variable name="package.name" select="@package"/>
+    <tr>
+        <td nowrap="nowrap">
+            <a target="classFrame">
+                <xsl:attribute name="href">
+                    <xsl:if test="not($package.name='')">
+                        <xsl:value-of select="translate($package.name,'.','/')"/><xsl:text>/</xsl:text>
+                    </xsl:if><xsl:value-of select="@name"/><xsl:text>.html</xsl:text>
+                </xsl:attribute>
+                <xsl:value-of select="@name"/>
+            </a>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<!--
+    Creates an html file that contains a link to all package-summary.html files on
+    each package existing on testsuites.
+    @bug there will be a problem here, I don't know yet how to handle unnamed package :(
+-->
+<xsl:template match="testsuites" mode="all.packages">
+    <html>
+        <head>
+            <title>All Unit Test Packages</title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <h2><a href="overview-summary.html" target="classFrame">Home</a></h2>
+            <h2>Packages</h2>
+            <table width="100%">
+                <xsl:apply-templates select="testsuite[not(./@package = preceding-sibling::testsuite/@package)]" mode="all.packages">
+                    <xsl:sort select="@package"/>
+                </xsl:apply-templates>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+<xsl:template match="testsuite" mode="all.packages">
+    <tr>
+        <td nowrap="nowrap">
+            <a href="./{translate(@package,'.','/')}/package-summary.html" target="classFrame">
+                <xsl:value-of select="@package"/>
+                <xsl:if test="@package = ''">&lt;none&gt;</xsl:if>
+            </a>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<xsl:template match="testsuites" mode="overview.packages">
+    <html>
+        <head>
+            <title>Unit Test Results: Summary</title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+        <xsl:attribute name="onload">open('allclasses-frame.html','classListFrame')</xsl:attribute>
+        <xsl:call-template name="pageHeader"/>
+        <h2>Summary</h2>
+        <xsl:variable name="testCount" select="sum(testsuite/@tests)"/>
+        <xsl:variable name="errorCount" select="sum(testsuite/@errors)"/>
+        <xsl:variable name="failureCount" select="sum(testsuite/@failures)"/>
+        <xsl:variable name="timeCount" select="sum(testsuite/@time)"/>
+        <xsl:variable name="successRate" select="($testCount - $failureCount - $errorCount) div $testCount"/>
+        <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+        <tr valign="top">
+            <th>Tests</th>
+            <th>Failures</th>
+            <th>Errors</th>
+            <th>Success rate</th>
+            <th>Time</th>
+        </tr>
+        <tr valign="top">
+            <xsl:attribute name="class">
+                <xsl:choose>
+                    <xsl:when test="$errorCount &gt; 0">Error</xsl:when>
+                    <xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
+                    <xsl:otherwise>Pass</xsl:otherwise>
+                </xsl:choose>
+            </xsl:attribute>
+            <td><xsl:value-of select="$testCount"/></td>
+            <td><xsl:value-of select="$failureCount"/></td>
+            <td><xsl:value-of select="$errorCount"/></td>
+            <td>
+                <xsl:call-template name="display-percent">
+                    <xsl:with-param name="value" select="$successRate"/>
+                </xsl:call-template>
+            </td>
+            <td>
+                <xsl:call-template name="display-time">
+                    <xsl:with-param name="value" select="$timeCount"/>
+                </xsl:call-template>
+            </td>
+        </tr>
+        </table>
+        <table border="0" width="95%">
+        <tr>
+        <td style="text-align: justify;">
+        Note: <em>failures</em> are anticipated and checked for with assertions while <em>errors</em> are unanticipated.
+        </td>
+        </tr>
+        </table>
+
+        <h2>Packages</h2>
+        <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+            <xsl:call-template name="testsuite.test.header"/>
+            <xsl:for-each select="testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+                <xsl:sort select="@package" order="ascending"/>
+                <!-- get the node set containing all testsuites that have the same package -->
+                <xsl:variable name="insamepackage" select="/testsuites/testsuite[./@package = current()/@package]"/>
+                <tr valign="top">
+                    <!-- display a failure if there is any failure/error in the package -->
+                    <xsl:attribute name="class">
+                        <xsl:choose>
+                            <xsl:when test="sum($insamepackage/@errors) &gt; 0">Error</xsl:when>
+                            <xsl:when test="sum($insamepackage/@failures) &gt; 0">Failure</xsl:when>
+                            <xsl:otherwise>Pass</xsl:otherwise>
+                        </xsl:choose>
+                    </xsl:attribute>
+                    <td><a href="./{translate(@package,'.','/')}/package-summary.html">
+                        <xsl:value-of select="@package"/>
+                        <xsl:if test="@package = ''">&lt;none&gt;</xsl:if>
+                    </a></td>
+                    <td><xsl:value-of select="sum($insamepackage/@tests)"/></td>
+                    <td><xsl:value-of select="sum($insamepackage/@errors)"/></td>
+                    <td><xsl:value-of select="sum($insamepackage/@failures)"/></td>
+                    <td>
+                    <xsl:call-template name="display-time">
+                        <xsl:with-param name="value" select="sum($insamepackage/@time)"/>
+                    </xsl:call-template>
+                    </td>
+                    <td><xsl:value-of select="$insamepackage/@timestamp"/></td>
+                    <td><xsl:value-of select="$insamepackage/@hostname"/></td>
+                </tr>
+            </xsl:for-each>
+        </table>
+        </body>
+        </html>
+</xsl:template>
+
+
+<xsl:template name="package.summary">
+    <xsl:param name="name"/>
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <xsl:attribute name="onload">open('package-frame.html','classListFrame')</xsl:attribute>
+            <xsl:call-template name="pageHeader"/>
+            <h3>Package <xsl:value-of select="$name"/></h3>
+
+            <!--table border="0" cellpadding="5" cellspacing="2" width="95%">
+                <xsl:call-template name="class.metrics.header"/>
+                <xsl:apply-templates select="." mode="print.metrics"/>
+            </table-->
+
+            <xsl:variable name="insamepackage" select="/testsuites/testsuite[./@package = $name]"/>
+            <xsl:if test="count($insamepackage) &gt; 0">
+                <h2>Classes</h2>
+                <p>
+                <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+                    <xsl:call-template name="testsuite.test.header"/>
+                    <xsl:apply-templates select="$insamepackage" mode="print.test">
+                        <xsl:sort select="@name"/>
+                    </xsl:apply-templates>
+                </table>
+                </p>
+            </xsl:if>
+        </body>
+    </html>
+</xsl:template>
+
+
+<!--
+    transform string like a.b.c to ../../../
+    @param path the path to transform into a descending directory path
+-->
+<xsl:template name="path">
+    <xsl:param name="path"/>
+    <xsl:if test="contains($path,'.')">
+        <xsl:text>../</xsl:text>
+        <xsl:call-template name="path">
+            <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'.')"/></xsl:with-param>
+        </xsl:call-template>
+    </xsl:if>
+    <xsl:if test="not(contains($path,'.')) and not($path = '')">
+        <xsl:text>../</xsl:text>
+    </xsl:if>
+</xsl:template>
+
+
+<!-- create the link to the stylesheet based on the package name -->
+<xsl:template name="create.stylesheet.link">
+    <xsl:param name="package.name"/>
+    <link rel="stylesheet" type="text/css" title="Style"><xsl:attribute name="href"><xsl:if test="not($package.name = 'unnamed package')"><xsl:call-template name="path"><xsl:with-param name="path" select="$package.name"/></xsl:call-template></xsl:if>stylesheet.css</xsl:attribute></link>
+</xsl:template>
+
+
+<!-- Page HEADER -->
+<xsl:template name="pageHeader">
+    <h1>Unit Test Results</h1>
+    <table width="100%">
+    <tr>
+        <td align="left"></td>
+        <td align="right">Designed for use with <a href="http://www.junit.org/">JUnit</a> and <a href="http://jakarta.apache.org/">Ant</a>.</td>
+    </tr>
+    </table>
+    <hr size="1"/>
+</xsl:template>
+
+<!-- class header -->
+<xsl:template name="testsuite.test.header">
+    <tr valign="top">
+        <th width="80%">Name</th>
+        <th>Tests</th>
+        <th>Errors</th>
+        <th>Failures</th>
+        <th nowrap="nowrap">Time(s)</th>
+        <th nowrap="nowrap">Time Stamp</th>
+        <th>Host</th>
+    </tr>
+</xsl:template>
+
+<!-- method header -->
+<xsl:template name="testcase.test.header">
+    <tr valign="top">
+        <th>Name</th>
+        <th>Status</th>
+        <th width="80%">Type</th>
+        <th nowrap="nowrap">Time(s)</th>
+    </tr>
+</xsl:template>
+
+
+<!-- class information -->
+<xsl:template match="testsuite" mode="print.test">
+    <tr valign="top">
+        <xsl:attribute name="class">
+            <xsl:choose>
+                <xsl:when test="@errors[.&gt; 0]">Error</xsl:when>
+                <xsl:when test="@failures[.&gt; 0]">Failure</xsl:when>
+                <xsl:otherwise>Pass</xsl:otherwise>
+            </xsl:choose>
+        </xsl:attribute>
+        <td><a href="{@name}.html"><xsl:value-of select="@name"/></a></td>
+        <td><xsl:apply-templates select="@tests"/></td>
+        <td><xsl:apply-templates select="@errors"/></td>
+        <td><xsl:apply-templates select="@failures"/></td>
+        <td><xsl:call-template name="display-time">
+                <xsl:with-param name="value" select="@time"/>
+            </xsl:call-template>
+        </td>
+        <td><xsl:apply-templates select="@timestamp"/></td>
+        <td><xsl:apply-templates select="@hostname"/></td>
+    </tr>
+</xsl:template>
+
+<xsl:template match="testcase" mode="print.test">
+    <tr valign="top">
+        <xsl:attribute name="class">
+            <xsl:choose>
+                <xsl:when test="error">Error</xsl:when>
+                <xsl:when test="failure">Failure</xsl:when>
+                <xsl:otherwise>TableRowColor</xsl:otherwise>
+            </xsl:choose>
+        </xsl:attribute>
+        <td><xsl:value-of select="@name"/></td>
+        <xsl:choose>
+            <xsl:when test="failure">
+                <td>Failure</td>
+                <td><xsl:apply-templates select="failure"/></td>
+            </xsl:when>
+            <xsl:when test="error">
+                <td>Error</td>
+                <td><xsl:apply-templates select="error"/></td>
+            </xsl:when>
+            <xsl:otherwise>
+                <td>Success</td>
+                <td></td>
+            </xsl:otherwise>
+        </xsl:choose>
+        <td>
+            <xsl:call-template name="display-time">
+                <xsl:with-param name="value" select="@time"/>
+            </xsl:call-template>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<!-- Note : the below template error and failure are the same style
+            so just call the same style store in the toolkit template -->
+<xsl:template match="failure">
+    <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<xsl:template match="error">
+    <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<!-- Style for the error and failure in the testcase template -->
+<xsl:template name="display-failures">
+    <xsl:choose>
+        <xsl:when test="not(@message)">N/A</xsl:when>
+        <xsl:otherwise>
+            <xsl:value-of select="@message"/>
+        </xsl:otherwise>
+    </xsl:choose>
+    <!-- display the stacktrace -->
+    <br/><br/>
+    <code>
+        <xsl:call-template name="br-replace">
+            <xsl:with-param name="word" select="."/>
+        </xsl:call-template>
+    </code>
+    <!-- the latter is better but might be problematic for non-21" monitors... -->
+    <!--pre><xsl:value-of select="."/></pre-->
+</xsl:template>
+
+<xsl:template name="JS-escape">
+    <xsl:param name="string"/>
+    <xsl:param name="tmp1" select="stringutils:replace(string($string),'\','\\')"/>
+    <xsl:param name="tmp2" select="stringutils:replace(string($tmp1),&quot;'&quot;,&quot;\&apos;&quot;)"/>
+    <xsl:value-of select="$tmp2"/>
+</xsl:template>
+
+
+<!--
+    template that will convert a carriage return into a br tag
+    @param word the text from which to convert CR to BR tag
+-->
+<xsl:template name="br-replace">
+    <xsl:param name="word"/>
+    <xsl:param name="br"><br/></xsl:param>
+    <xsl:value-of select='stringutils:replace(string($word),"&#xA;",$br)'/>
+</xsl:template>
+
+<xsl:template name="display-time">
+    <xsl:param name="value"/>
+    <xsl:value-of select="format-number($value,'0.000')"/>
+</xsl:template>
+
+<xsl:template name="display-percent">
+    <xsl:param name="value"/>
+    <xsl:value-of select="format-number($value,'0.00%')"/>
+</xsl:template>
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/junit-frames.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/junit-frames.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/junit-frames.xsl	(revision 16139)
@@ -0,0 +1,712 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt"
+    xmlns:redirect="http://xml.apache.org/xalan/redirect"
+    xmlns:stringutils="xalan://org.apache.tools.ant.util.StringUtils"
+    extension-element-prefixes="redirect">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+<xsl:decimal-format decimal-separator="." grouping-separator=","/>
+<!--
+   Copyright 2001-2005 The Apache Software Foundation
+
+   Licensed 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.
+ -->
+
+<!--
+
+ Sample stylesheet to be used with Ant JUnitReport output.
+
+ It creates a set of HTML files a la javadoc where you can browse easily
+ through all packages and classes.
+
+-->
+<xsl:param name="output.dir" select="'.'"/>
+
+
+<xsl:template match="testsuites">
+    <!-- create the index.html -->
+    <redirect:write file="{$output.dir}/index.html">
+        <xsl:call-template name="index.html"/>
+    </redirect:write>
+
+    <!-- create the stylesheet.css -->
+    <redirect:write file="{$output.dir}/stylesheet.css">
+        <xsl:call-template name="stylesheet.css"/>
+    </redirect:write>
+
+    <!-- create the overview-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-summary.html">
+        <xsl:apply-templates select="." mode="overview.packages"/>
+    </redirect:write>
+
+    <!-- create the all-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-frame.html">
+        <xsl:apply-templates select="." mode="all.packages"/>
+    </redirect:write>
+
+    <!-- create the all-classes.html at the root -->
+    <redirect:write file="{$output.dir}/allclasses-frame.html">
+        <xsl:apply-templates select="." mode="all.classes"/>
+    </redirect:write>
+
+    <!-- process all packages -->
+    <xsl:for-each select="./testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+        <xsl:call-template name="package">
+            <xsl:with-param name="name" select="@package"/>
+        </xsl:call-template>
+    </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template name="package">
+    <xsl:param name="name"/>
+    <xsl:variable name="package.dir">
+        <xsl:if test="not($name = '')"><xsl:value-of select="translate($name,'.','/')"/></xsl:if>
+        <xsl:if test="$name = ''">.</xsl:if>
+    </xsl:variable>
+    <!--Processing package <xsl:value-of select="@name"/> in <xsl:value-of select="$output.dir"/> -->
+    <!-- create a classes-list.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-frame.html">
+        <xsl:call-template name="classes.list">
+            <xsl:with-param name="name" select="$name"/>
+        </xsl:call-template>
+    </redirect:write>
+
+    <!-- create a package-summary.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-summary.html">
+        <xsl:call-template name="package.summary">
+            <xsl:with-param name="name" select="$name"/>
+        </xsl:call-template>
+    </redirect:write>
+
+    <!-- for each class, creates a @name.html -->
+    <!-- @bug there will be a problem with inner classes having the same name, it will be overwritten -->
+    <xsl:for-each select="/testsuites/testsuite[@package = $name]">
+        <redirect:write file="{$output.dir}/{$package.dir}/{@id}_{@name}.html">
+            <xsl:apply-templates select="." mode="class.details"/>
+        </redirect:write>
+        <xsl:if test="string-length(./system-out)!=0">
+            <redirect:write file="{$output.dir}/{$package.dir}/{@id}_{@name}-out.txt">
+                <xsl:value-of select="./system-out" />
+            </redirect:write>
+        </xsl:if>
+        <xsl:if test="string-length(./system-err)!=0">
+            <redirect:write file="{$output.dir}/{$package.dir}/{@id}_{@name}-err.txt">
+                <xsl:value-of select="./system-err" />
+            </redirect:write>
+        </xsl:if>
+    </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="index.html">
+<html>
+    <head>
+        <title>Unit Test Results.</title>
+    </head>
+    <frameset cols="20%,80%">
+        <frameset rows="30%,70%">
+            <frame src="overview-frame.html" name="packageListFrame"/>
+            <frame src="allclasses-frame.html" name="classListFrame"/>
+        </frameset>
+        <frame src="overview-summary.html" name="classFrame"/>
+        <noframes>
+            <h2>Frame Alert</h2>
+            <p>
+                This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+            </p>
+        </noframes>
+    </frameset>
+</html>
+</xsl:template>
+
+<!-- this is the stylesheet css to use for nearly everything -->
+<xsl:template name="stylesheet.css">
+body {
+    font:normal 68% verdana,arial,helvetica;
+    color:#000000;
+}
+table tr td, table tr th {
+    font-size: 68%;
+}
+table.details tr th{
+    font-weight: bold;
+    text-align:left;
+    background:#a6caf0;
+}
+table.details tr td{
+    background:#eeeee0;
+}
+
+p {
+    line-height:1.5em;
+    margin-top:0.5em; margin-bottom:1.0em;
+}
+h1 {
+    margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
+}
+h2 {
+    margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
+}
+h3 {
+    margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
+}
+h4 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+h5 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+h6 {
+    margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+}
+.Error {
+    font-weight:bold; color:red;
+}
+.Failure {
+    font-weight:bold; color:purple;
+}
+.Properties {
+  text-align:right;
+}
+</xsl:template>
+
+
+<!-- ======================================================================
+    This page is created for every testsuite class.
+    It prints a summary of the testsuite and detailed information about
+    testcase methods.
+     ====================================================================== -->
+<xsl:template match="testsuite" mode="class.details">
+    <xsl:variable name="package.name" select="@package"/>
+    <xsl:variable name="class.name"><xsl:if test="not($package.name = '')"><xsl:value-of select="$package.name"/>.</xsl:if><xsl:value-of select="@name"/></xsl:variable>
+    <html>
+        <head>
+          <title>Unit Test Results: <xsl:value-of select="$class.name"/></title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$package.name"/>
+            </xsl:call-template>
+       <script type="text/javascript" language="JavaScript">
+        var TestCases = new Array();
+        var cur;
+        <xsl:apply-templates select="properties"/>
+       </script>
+       <script type="text/javascript" language="JavaScript"><![CDATA[
+        function displayProperties (name) {
+          var win = window.open('','JUnitSystemProperties','scrollbars=1,resizable=1');
+          var doc = win.document.open();
+          doc.write("<html><head><title>Properties of " + name + "</title>");
+          doc.write("<style type=\"text/css\">");
+          doc.write("body {font:normal 68% verdana,arial,helvetica; color:#000000; }");
+          doc.write("table tr td, table tr th { font-size: 68%; }");
+          doc.write("table.properties { border-collapse:collapse; border-left:solid 1 #cccccc; border-top:solid 1 #cccccc; padding:5px; }");
+          doc.write("table.properties th { text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#eeeeee; }");
+          doc.write("table.properties td { font:normal; text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#fffffff; }");
+          doc.write("h3 { margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica }");
+          doc.write("</style>");
+          doc.write("</head><body>");
+          doc.write("<h3>Properties of " + name + "</h3>");
+          doc.write("<div align=\"right\"><a href=\"javascript:window.close();\">Close</a></div>");
+          doc.write("<table class='properties'>");
+          doc.write("<tr><th>Name</th><th>Value</th></tr>");
+          for (prop in TestCases[name]) {
+            doc.write("<tr><th>" + prop + "</th><td>" + TestCases[name][prop] + "</td></tr>");
+          }
+          doc.write("</table>");
+          doc.write("</body></html>");
+          doc.close();
+          win.focus();
+        }
+      ]]>
+      </script>
+        </head>
+        <body>
+            <xsl:call-template name="pageHeader"/>
+            <h3>Class <xsl:value-of select="$class.name"/></h3>
+
+
+            <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+                <xsl:call-template name="testsuite.test.header"/>
+                <xsl:apply-templates select="." mode="print.test"/>
+            </table>
+
+            <h2>Tests</h2>
+            <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+        <xsl:call-template name="testcase.test.header"/>
+              <!--
+              test can even not be started at all (failure to load the class)
+              so report the error directly
+              -->
+                <xsl:if test="./error">
+                    <tr class="Error">
+                        <td colspan="4"><xsl:apply-templates select="./error"/></td>
+                    </tr>
+                </xsl:if>
+                <xsl:apply-templates select="./testcase" mode="print.test"/>
+            </table>
+            <div class="Properties">
+                <a>
+                    <xsl:attribute name="href">javascript:displayProperties('<xsl:value-of select="@package"/>.<xsl:value-of select="@name"/>');</xsl:attribute>
+                    Properties &#187;
+                </a>
+            </div>
+            <xsl:if test="string-length(./system-out)!=0">
+                <div class="Properties">
+                    <a>
+                        <xsl:attribute name="href">./<xsl:value-of select="@id"/>_<xsl:value-of select="@name"/>-out.txt</xsl:attribute>
+                        System.out &#187;
+                    </a>
+                </div>
+            </xsl:if>
+            <xsl:if test="string-length(./system-err)!=0">
+                <div class="Properties">
+                    <a>
+                        <xsl:attribute name="href">./<xsl:value-of select="@id"/>_<xsl:value-of select="@name"/>-err.txt</xsl:attribute>
+                        System.err &#187;
+                    </a>
+                </div>
+            </xsl:if>
+        </body>
+    </html>
+</xsl:template>
+
+  <!--
+   Write properties into a JavaScript data structure.
+   This is based on the original idea by Erik Hatcher (ehatcher@apache.org)
+   -->
+  <xsl:template match="properties">
+    cur = TestCases['<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>'] = new Array();
+    <xsl:for-each select="property">
+    <xsl:sort select="@name"/>
+        cur['<xsl:value-of select="@name"/>'] = '<xsl:call-template name="JS-escape"><xsl:with-param name="string" select="@value"/></xsl:call-template>';
+    </xsl:for-each>
+  </xsl:template>
+
+
+<!-- ======================================================================
+    This page is created for every package.
+    It prints the name of all classes that belongs to this package.
+    @param name the package name to print classes.
+     ====================================================================== -->
+<!-- list of classes in a package -->
+<xsl:template name="classes.list">
+    <xsl:param name="name"/>
+    <html>
+        <head>
+            <title>Unit Test Classes: <xsl:value-of select="$name"/></title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <table width="100%">
+                <tr>
+                    <td nowrap="nowrap">
+                        <h2><a href="package-summary.html" target="classFrame">
+                            <xsl:value-of select="$name"/>
+                            <xsl:if test="$name = ''">&lt;none&gt;</xsl:if>
+                        </a></h2>
+                    </td>
+                </tr>
+            </table>
+
+            <h2>Classes</h2>
+            <table width="100%">
+                <xsl:for-each select="/testsuites/testsuite[./@package = $name]">
+                    <xsl:sort select="@name"/>
+                    <tr>
+                        <td nowrap="nowrap">
+                            <a href="{@id}_{@name}.html" target="classFrame"><xsl:value-of select="@name"/></a>
+                        </td>
+                    </tr>
+                </xsl:for-each>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+
+<!--
+    Creates an all-classes.html file that contains a link to all package-summary.html
+    on each class.
+-->
+<xsl:template match="testsuites" mode="all.classes">
+    <html>
+        <head>
+            <title>All Unit Test Classes</title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <h2>Classes</h2>
+            <table width="100%">
+                <xsl:apply-templates select="testsuite" mode="all.classes">
+                    <xsl:sort select="@name"/>
+                </xsl:apply-templates>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+<xsl:template match="testsuite" mode="all.classes">
+    <xsl:variable name="package.name" select="@package"/>
+    <tr>
+        <td nowrap="nowrap">
+            <a target="classFrame">
+                <xsl:attribute name="href">
+                    <xsl:if test="not($package.name='')">
+                        <xsl:value-of select="translate($package.name,'.','/')"/><xsl:text>/</xsl:text>
+                    </xsl:if><xsl:value-of select="@id"/>_<xsl:value-of select="@name"/><xsl:text>.html</xsl:text>
+                </xsl:attribute>
+                <xsl:value-of select="@name"/>
+            </a>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<!--
+    Creates an html file that contains a link to all package-summary.html files on
+    each package existing on testsuites.
+    @bug there will be a problem here, I don't know yet how to handle unnamed package :(
+-->
+<xsl:template match="testsuites" mode="all.packages">
+    <html>
+        <head>
+            <title>All Unit Test Packages</title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <h2><a href="overview-summary.html" target="classFrame">Home</a></h2>
+            <h2>Packages</h2>
+            <table width="100%">
+                <xsl:apply-templates select="testsuite[not(./@package = preceding-sibling::testsuite/@package)]" mode="all.packages">
+                    <xsl:sort select="@package"/>
+                </xsl:apply-templates>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+<xsl:template match="testsuite" mode="all.packages">
+    <tr>
+        <td nowrap="nowrap">
+            <a href="./{translate(@package,'.','/')}/package-summary.html" target="classFrame">
+                <xsl:value-of select="@package"/>
+                <xsl:if test="@package = ''">&lt;none&gt;</xsl:if>
+            </a>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<xsl:template match="testsuites" mode="overview.packages">
+    <html>
+        <head>
+            <title>Unit Test Results: Summary</title>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+        <xsl:attribute name="onload">open('allclasses-frame.html','classListFrame')</xsl:attribute>
+        <xsl:call-template name="pageHeader"/>
+        <h2>Summary</h2>
+        <xsl:variable name="testCount" select="sum(testsuite/@tests)"/>
+        <xsl:variable name="errorCount" select="sum(testsuite/@errors)"/>
+        <xsl:variable name="failureCount" select="sum(testsuite/@failures)"/>
+        <xsl:variable name="timeCount" select="sum(testsuite/@time)"/>
+        <xsl:variable name="successRate" select="($testCount - $failureCount - $errorCount) div $testCount"/>
+        <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+        <tr valign="top">
+            <th>Tests</th>
+            <th>Failures</th>
+            <th>Errors</th>
+            <th>Success rate</th>
+            <th>Time</th>
+        </tr>
+        <tr valign="top">
+            <xsl:attribute name="class">
+                <xsl:choose>
+                    <xsl:when test="$errorCount &gt; 0">Error</xsl:when>
+                    <xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
+                    <xsl:otherwise>Pass</xsl:otherwise>
+                </xsl:choose>
+            </xsl:attribute>
+            <td><xsl:value-of select="$testCount"/></td>
+            <td><xsl:value-of select="$failureCount"/></td>
+            <td><xsl:value-of select="$errorCount"/></td>
+            <td>
+                <xsl:call-template name="display-percent">
+                    <xsl:with-param name="value" select="$successRate"/>
+                </xsl:call-template>
+            </td>
+            <td>
+                <xsl:call-template name="display-time">
+                    <xsl:with-param name="value" select="$timeCount"/>
+                </xsl:call-template>
+            </td>
+
+        </tr>
+        </table>
+        <table border="0" width="95%">
+        <tr>
+        <td style="text-align: justify;">
+        Note: <em>failures</em> are anticipated and checked for with assertions while <em>errors</em> are unanticipated.
+        </td>
+        </tr>
+        </table>
+
+        <h2>Packages</h2>
+        <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+            <xsl:call-template name="testsuite.test.header"/>
+            <xsl:for-each select="testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+                <xsl:sort select="@package" order="ascending"/>
+                <!-- get the node set containing all testsuites that have the same package -->
+                <xsl:variable name="insamepackage" select="/testsuites/testsuite[./@package = current()/@package]"/>
+                <tr valign="top">
+                    <!-- display a failure if there is any failure/error in the package -->
+                    <xsl:attribute name="class">
+                        <xsl:choose>
+                            <xsl:when test="sum($insamepackage/@errors) &gt; 0">Error</xsl:when>
+                            <xsl:when test="sum($insamepackage/@failures) &gt; 0">Failure</xsl:when>
+                            <xsl:otherwise>Pass</xsl:otherwise>
+                        </xsl:choose>
+                    </xsl:attribute>
+                    <td><a href="./{translate(@package,'.','/')}/package-summary.html">
+                        <xsl:value-of select="@package"/>
+                        <xsl:if test="@package = ''">&lt;none&gt;</xsl:if>
+                    </a></td>
+                    <td><xsl:value-of select="sum($insamepackage/@tests)"/></td>
+                    <td><xsl:value-of select="sum($insamepackage/@errors)"/></td>
+                    <td><xsl:value-of select="sum($insamepackage/@failures)"/></td>
+                    <td>
+                    <xsl:call-template name="display-time">
+                        <xsl:with-param name="value" select="sum($insamepackage/@time)"/>
+                    </xsl:call-template>
+                    </td>
+                </tr>
+            </xsl:for-each>
+        </table>
+        </body>
+        </html>
+</xsl:template>
+
+
+<xsl:template name="package.summary">
+    <xsl:param name="name"/>
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <xsl:attribute name="onload">open('package-frame.html','classListFrame')</xsl:attribute>
+            <xsl:call-template name="pageHeader"/>
+            <h3>Package <xsl:value-of select="$name"/></h3>
+
+            <!--table border="0" cellpadding="5" cellspacing="2" width="95%">
+                <xsl:call-template name="class.metrics.header"/>
+                <xsl:apply-templates select="." mode="print.metrics"/>
+            </table-->
+
+            <xsl:variable name="insamepackage" select="/testsuites/testsuite[./@package = $name]"/>
+            <xsl:if test="count($insamepackage) &gt; 0">
+                <h2>Classes</h2>
+                <p>
+                <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+                    <xsl:call-template name="testsuite.test.header"/>
+                    <xsl:apply-templates select="$insamepackage" mode="print.test">
+                        <xsl:sort select="@name"/>
+                    </xsl:apply-templates>
+                </table>
+                </p>
+            </xsl:if>
+        </body>
+    </html>
+</xsl:template>
+
+
+<!--
+    transform string like a.b.c to ../../../
+    @param path the path to transform into a descending directory path
+-->
+<xsl:template name="path">
+    <xsl:param name="path"/>
+    <xsl:if test="contains($path,'.')">
+        <xsl:text>../</xsl:text>
+        <xsl:call-template name="path">
+            <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'.')"/></xsl:with-param>
+        </xsl:call-template>
+    </xsl:if>
+    <xsl:if test="not(contains($path,'.')) and not($path = '')">
+        <xsl:text>../</xsl:text>
+    </xsl:if>
+</xsl:template>
+
+
+<!-- create the link to the stylesheet based on the package name -->
+<xsl:template name="create.stylesheet.link">
+    <xsl:param name="package.name"/>
+    <link rel="stylesheet" type="text/css" title="Style"><xsl:attribute name="href"><xsl:if test="not($package.name = 'unnamed package')"><xsl:call-template name="path"><xsl:with-param name="path" select="$package.name"/></xsl:call-template></xsl:if>stylesheet.css</xsl:attribute></link>
+</xsl:template>
+
+
+<!-- Page HEADER -->
+<xsl:template name="pageHeader">
+    <h1>Unit Test Results</h1>
+    <table width="100%">
+    <tr>
+        <td align="left"></td>
+        <td align="right">Designed for use with <a href="http://www.junit.org/">JUnit</a> and <a href="http://jakarta.apache.org/">Ant</a>.</td>
+    </tr>
+    </table>
+    <hr size="1"/>
+</xsl:template>
+
+<!-- class header -->
+<xsl:template name="testsuite.test.header">
+    <tr valign="top">
+        <th width="80%">Name</th>
+        <th>Tests</th>
+        <th>Errors</th>
+        <th>Failures</th>
+        <th nowrap="nowrap">Time(s)</th>
+    </tr>
+</xsl:template>
+
+<!-- method header -->
+<xsl:template name="testcase.test.header">
+    <tr valign="top">
+        <th>Name</th>
+        <th>Status</th>
+        <th width="80%">Type</th>
+        <th nowrap="nowrap">Time(s)</th>
+    </tr>
+</xsl:template>
+
+
+<!-- class information -->
+<xsl:template match="testsuite" mode="print.test">
+    <tr valign="top">
+        <xsl:attribute name="class">
+            <xsl:choose>
+                <xsl:when test="@errors[.&gt; 0]">Error</xsl:when>
+                <xsl:when test="@failures[.&gt; 0]">Failure</xsl:when>
+                <xsl:otherwise>Pass</xsl:otherwise>
+            </xsl:choose>
+        </xsl:attribute>
+        <td><a href="{@id}_{@name}.html"><xsl:value-of select="@name"/></a></td>
+        <td><xsl:apply-templates select="@tests"/></td>
+        <td><xsl:apply-templates select="@errors"/></td>
+        <td><xsl:apply-templates select="@failures"/></td>
+        <td><xsl:call-template name="display-time">
+                <xsl:with-param name="value" select="@time"/>
+            </xsl:call-template>
+        </td>
+    </tr>
+</xsl:template>
+
+<xsl:template match="testcase" mode="print.test">
+    <tr valign="top">
+        <xsl:attribute name="class">
+            <xsl:choose>
+                <xsl:when test="error">Error</xsl:when>
+                <xsl:when test="failure">Failure</xsl:when>
+                <xsl:otherwise>TableRowColor</xsl:otherwise>
+            </xsl:choose>
+        </xsl:attribute>
+        <td><xsl:value-of select="@name"/></td>
+        <xsl:choose>
+            <xsl:when test="failure">
+                <td>Failure</td>
+                <td><xsl:apply-templates select="failure"/></td>
+            </xsl:when>
+            <xsl:when test="error">
+                <td>Error</td>
+                <td><xsl:apply-templates select="error"/></td>
+            </xsl:when>
+            <xsl:otherwise>
+                <td>Success</td>
+                <td></td>
+            </xsl:otherwise>
+        </xsl:choose>
+        <td>
+            <xsl:call-template name="display-time">
+                <xsl:with-param name="value" select="@time"/>
+            </xsl:call-template>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<!-- Note : the below template error and failure are the same style
+            so just call the same style store in the toolkit template -->
+<xsl:template match="failure">
+    <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<xsl:template match="error">
+    <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<!-- Style for the error and failure in the testcase template -->
+<xsl:template name="display-failures">
+    <xsl:choose>
+        <xsl:when test="not(@message)">N/A</xsl:when>
+        <xsl:otherwise>
+            <xsl:value-of select="@message"/>
+        </xsl:otherwise>
+    </xsl:choose>
+    <!-- display the stacktrace -->
+    <br/><br/>
+    <code>
+        <xsl:call-template name="br-replace">
+            <xsl:with-param name="word" select="."/>
+        </xsl:call-template>
+    </code>
+    <!-- the latter is better but might be problematic for non-21" monitors... -->
+    <!--pre><xsl:value-of select="."/></pre-->
+</xsl:template>
+
+<xsl:template name="JS-escape">
+    <xsl:param name="string"/>
+    <xsl:param name="tmp1" select="stringutils:replace(string($string),'\','\\')"/>
+    <xsl:param name="tmp2" select="stringutils:replace(string($tmp1),&quot;'&quot;,&quot;\&apos;&quot;)"/>
+    <xsl:value-of select="$tmp2"/>
+</xsl:template>
+
+
+<!--
+    template that will convert a carriage return into a br tag
+    @param word the text from which to convert CR to BR tag
+-->
+<xsl:template name="br-replace">
+    <xsl:param name="word"/>
+    <xsl:param name="br"><br/></xsl:param>
+    <xsl:value-of select='stringutils:replace(string($word),"&#xA;",$br)'/>
+</xsl:template>
+
+<xsl:template name="display-time">
+    <xsl:param name="value"/>
+    <xsl:value-of select="format-number($value,'0.000')"/>
+</xsl:template>
+
+<xsl:template name="display-percent">
+    <xsl:param name="value"/>
+    <xsl:value-of select="format-number($value,'0.00%')"/>
+</xsl:template>
+</xsl:stylesheet>
+
Index: /release-kits/shared/ant/src/etc/junit-noframes.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/junit-noframes.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/junit-noframes.xsl	(revision 16139)
@@ -0,0 +1,461 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+	xmlns:lxslt="http://xml.apache.org/xslt"
+	xmlns:stringutils="xalan://org.apache.tools.ant.util.StringUtils">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"
+  doctype-public="-//W3C//DTD HTML 4.01 Transitional//EN" />
+<xsl:decimal-format decimal-separator="." grouping-separator="," />
+<!--
+   Copyright 2001-2004 The Apache Software Foundation
+
+   Licensed 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.
+ -->
+ 
+<!--
+ 
+ Sample stylesheet to be used with Ant JUnitReport output.
+ 
+ It creates a non-framed report that can be useful to send via
+ e-mail or such.
+ 
+ @author Stephane Bailliez <a href="mailto:sbailliez@apache.org"/>
+ @author Erik Hatcher <a href="mailto:ehatcher@apache.org"/>
+ 
+-->
+<xsl:template match="testsuites">
+    <html>
+        <head>
+            <title>Unit Test Results</title>
+    <style type="text/css">
+      body {
+        font:normal 68% verdana,arial,helvetica;
+        color:#000000;
+      }
+      table tr td, table tr th {
+          font-size: 68%;
+      }
+      table.details tr th{
+        font-weight: bold;
+        text-align:left;
+        background:#a6caf0;
+      }
+      table.details tr td{
+        background:#eeeee0;
+      }
+      
+      p {
+        line-height:1.5em;
+        margin-top:0.5em; margin-bottom:1.0em;
+      }
+      h1 {
+        margin: 0px 0px 5px; font: 165% verdana,arial,helvetica
+      }
+      h2 {
+        margin-top: 1em; margin-bottom: 0.5em; font: bold 125% verdana,arial,helvetica
+      }
+      h3 {
+        margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica
+      }
+      h4 {
+        margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+      }
+      h5 {
+        margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+      }
+      h6 {
+        margin-bottom: 0.5em; font: bold 100% verdana,arial,helvetica
+      }
+      .Error {
+        font-weight:bold; color:red;
+      }
+      .Failure {
+        font-weight:bold; color:purple;
+      }
+      .Properties {
+        text-align:right;
+      }
+      </style>
+      <script type="text/javascript" language="JavaScript">
+        var TestCases = new Array();
+        var cur;
+        <xsl:for-each select="./testsuite">      
+            <xsl:apply-templates select="properties"/>
+        </xsl:for-each>
+
+       </script>
+       <script type="text/javascript" language="JavaScript"><![CDATA[
+        function displayProperties (name) {
+          var win = window.open('','JUnitSystemProperties','scrollbars=1,resizable=1');
+          var doc = win.document.open();
+          doc.write("<html><head><title>Properties of " + name + "</title>");
+          doc.write("<style>")
+          doc.write("body {font:normal 68% verdana,arial,helvetica; color:#000000; }");
+          doc.write("table tr td, table tr th { font-size: 68%; }");
+          doc.write("table.properties { border-collapse:collapse; border-left:solid 1 #cccccc; border-top:solid 1 #cccccc; padding:5px; }");
+          doc.write("table.properties th { text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#eeeeee; }");
+          doc.write("table.properties td { font:normal; text-align:left; border-right:solid 1 #cccccc; border-bottom:solid 1 #cccccc; background-color:#fffffff; }");
+          doc.write("h3 { margin-bottom: 0.5em; font: bold 115% verdana,arial,helvetica }");
+          doc.write("</style>");
+          doc.write("</head><body>");
+          doc.write("<h3>Properties of " + name + "</h3>");
+          doc.write("<div align=\"right\"><a href=\"javascript:window.close();\">Close</a></div>");
+          doc.write("<table class='properties'>");
+          doc.write("<tr><th>Name</th><th>Value</th></tr>");
+          for (prop in TestCases[name]) {
+            doc.write("<tr><th>" + prop + "</th><td>" + TestCases[name][prop] + "</td></tr>");
+          }
+          doc.write("</table>");
+          doc.write("</body></html>");
+          doc.close();
+          win.focus();
+        }
+      ]]>  
+      </script>
+        </head>
+        <body>
+            <a name="top"></a>
+            <xsl:call-template name="pageHeader"/>  
+            
+            <!-- Summary part -->
+            <xsl:call-template name="summary"/>
+            <hr size="1" width="95%" align="left"/>
+            
+            <!-- Package List part -->
+            <xsl:call-template name="packagelist"/>
+            <hr size="1" width="95%" align="left"/>
+            
+            <!-- For each package create its part -->
+            <xsl:call-template name="packages"/>
+            <hr size="1" width="95%" align="left"/>
+            
+            <!-- For each class create the  part -->
+            <xsl:call-template name="classes"/>
+            
+        </body>
+    </html>
+</xsl:template>
+    
+    
+    
+    <!-- ================================================================== -->
+    <!-- Write a list of all packages with an hyperlink to the anchor of    -->
+    <!-- of the package name.                                               -->
+    <!-- ================================================================== -->
+    <xsl:template name="packagelist">   
+        <h2>Packages</h2>
+        Note: package statistics are not computed recursively, they only sum up all of its testsuites numbers.
+        <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+            <xsl:call-template name="testsuite.test.header"/>
+            <!-- list all packages recursively -->
+            <xsl:for-each select="./testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+                <xsl:sort select="@package"/>
+                <xsl:variable name="testsuites-in-package" select="/testsuites/testsuite[./@package = current()/@package]"/>
+                <xsl:variable name="testCount" select="sum($testsuites-in-package/@tests)"/>
+                <xsl:variable name="errorCount" select="sum($testsuites-in-package/@errors)"/>
+                <xsl:variable name="failureCount" select="sum($testsuites-in-package/@failures)"/>
+                <xsl:variable name="timeCount" select="sum($testsuites-in-package/@time)"/>
+                
+                <!-- write a summary for the package -->
+                <tr valign="top">
+                    <!-- set a nice color depending if there is an error/failure -->
+                    <xsl:attribute name="class">
+                        <xsl:choose>
+                            <xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
+                            <xsl:when test="$errorCount &gt; 0">Error</xsl:when>
+                        </xsl:choose>
+                    </xsl:attribute>
+                    <td><a href="#{@package}"><xsl:value-of select="@package"/></a></td>
+                    <td><xsl:value-of select="$testCount"/></td>
+                    <td><xsl:value-of select="$errorCount"/></td>
+                    <td><xsl:value-of select="$failureCount"/></td>
+                    <td>
+                    <xsl:call-template name="display-time">
+                        <xsl:with-param name="value" select="$timeCount"/>
+                    </xsl:call-template>
+                    </td>
+                </tr>
+            </xsl:for-each>
+        </table>        
+    </xsl:template>
+    
+    
+    <!-- ================================================================== -->
+    <!-- Write a package level report                                       -->
+    <!-- It creates a table with values from the document:                  -->
+    <!-- Name | Tests | Errors | Failures | Time                            -->
+    <!-- ================================================================== -->
+    <xsl:template name="packages">
+        <!-- create an anchor to this package name -->
+        <xsl:for-each select="/testsuites/testsuite[not(./@package = preceding-sibling::testsuite/@package)]">
+            <xsl:sort select="@package"/>
+                <a name="{@package}"></a>
+                <h3>Package <xsl:value-of select="@package"/></h3>
+                
+                <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+                    <xsl:call-template name="testsuite.test.header"/>
+            
+                    <!-- match the testsuites of this package -->
+                    <xsl:apply-templates select="/testsuites/testsuite[./@package = current()/@package]" mode="print.test"/>
+                </table>
+                <a href="#top">Back to top</a>
+                <p/>
+                <p/>
+        </xsl:for-each>
+    </xsl:template>
+    
+    <xsl:template name="classes">
+        <xsl:for-each select="testsuite">
+            <xsl:sort select="@name"/>
+            <!-- create an anchor to this class name -->
+            <a name="{@name}"></a>
+            <h3>TestCase <xsl:value-of select="@name"/></h3>
+            
+            <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+              <xsl:call-template name="testcase.test.header"/>
+              <!--
+              test can even not be started at all (failure to load the class)
+              so report the error directly
+              -->
+                <xsl:if test="./error">
+                    <tr class="Error">
+                        <td colspan="4"><xsl:apply-templates select="./error"/></td>
+                    </tr>
+                </xsl:if>
+                <xsl:apply-templates select="./testcase" mode="print.test"/>
+            </table>
+            <div class="Properties">
+                <a>
+                    <xsl:attribute name="href">javascript:displayProperties('<xsl:value-of select="@package"/>.<xsl:value-of select="@name"/>');</xsl:attribute>
+                    Properties &#187;
+                </a>
+            </div>
+            <p/>
+            
+            <a href="#top">Back to top</a>
+        </xsl:for-each>
+    </xsl:template>
+    
+    <xsl:template name="summary">
+        <h2>Summary</h2>
+        <xsl:variable name="testCount" select="sum(testsuite/@tests)"/>
+        <xsl:variable name="errorCount" select="sum(testsuite/@errors)"/>
+        <xsl:variable name="failureCount" select="sum(testsuite/@failures)"/>
+        <xsl:variable name="timeCount" select="sum(testsuite/@time)"/>
+        <xsl:variable name="successRate" select="($testCount - $failureCount - $errorCount) div $testCount"/>
+        <table class="details" border="0" cellpadding="5" cellspacing="2" width="95%">
+        <tr valign="top">
+            <th>Tests</th>
+            <th>Failures</th>
+            <th>Errors</th>
+            <th>Success rate</th>
+            <th>Time</th>
+        </tr>
+        <tr valign="top">
+            <xsl:attribute name="class">
+                <xsl:choose>
+                    <xsl:when test="$failureCount &gt; 0">Failure</xsl:when>
+                    <xsl:when test="$errorCount &gt; 0">Error</xsl:when>
+                </xsl:choose>
+            </xsl:attribute>
+            <td><xsl:value-of select="$testCount"/></td>
+            <td><xsl:value-of select="$failureCount"/></td>
+            <td><xsl:value-of select="$errorCount"/></td>
+            <td>
+                <xsl:call-template name="display-percent">
+                    <xsl:with-param name="value" select="$successRate"/>
+                </xsl:call-template>
+            </td>
+            <td>
+                <xsl:call-template name="display-time">
+                    <xsl:with-param name="value" select="$timeCount"/>
+                </xsl:call-template>
+            </td>
+
+        </tr>
+        </table>
+        <table border="0" width="95%">
+        <tr>
+        <td style="text-align: justify;">
+        Note: <i>failures</i> are anticipated and checked for with assertions while <i>errors</i> are unanticipated.
+        </td>
+        </tr>
+        </table>
+    </xsl:template>
+    
+  <!--
+   Write properties into a JavaScript data structure.
+   This is based on the original idea by Erik Hatcher (ehatcher@apache.org)
+   -->
+  <xsl:template match="properties">
+    cur = TestCases['<xsl:value-of select="../@package"/>.<xsl:value-of select="../@name"/>'] = new Array();
+    <xsl:for-each select="property">
+    <xsl:sort select="@name"/>
+        cur['<xsl:value-of select="@name"/>'] = '<xsl:call-template name="JS-escape"><xsl:with-param name="string" select="@value"/></xsl:call-template>';
+    </xsl:for-each>
+  </xsl:template>
+    
+<!-- Page HEADER -->
+<xsl:template name="pageHeader">
+    <h1>Unit Test Results</h1>
+    <table width="100%">
+    <tr>
+        <td align="left"></td>
+        <td align="right">Designed for use with <a href='http://www.junit.org'>JUnit</a> and <a href='http://jakarta.apache.org/ant'>Ant</a>.</td>
+    </tr>
+    </table>
+    <hr size="1"/>
+</xsl:template>
+
+<xsl:template match="testsuite" mode="header">
+    <tr valign="top">
+        <th width="80%">Name</th>
+        <th>Tests</th>
+        <th>Errors</th>
+        <th>Failures</th>
+        <th nowrap="nowrap">Time(s)</th>
+    </tr>
+</xsl:template>
+
+<!-- class header -->
+<xsl:template name="testsuite.test.header">
+    <tr valign="top">
+        <th width="80%">Name</th>
+        <th>Tests</th>
+        <th>Errors</th>
+        <th>Failures</th>
+        <th nowrap="nowrap">Time(s)</th>
+    </tr>
+</xsl:template>
+
+<!-- method header -->
+<xsl:template name="testcase.test.header">
+    <tr valign="top">
+        <th>Name</th>
+        <th>Status</th>
+        <th width="80%">Type</th>
+        <th nowrap="nowrap">Time(s)</th>
+    </tr>
+</xsl:template>
+
+
+<!-- class information -->
+<xsl:template match="testsuite" mode="print.test">
+    <tr valign="top">
+        <!-- set a nice color depending if there is an error/failure -->
+        <xsl:attribute name="class">
+            <xsl:choose>
+                <xsl:when test="@failures[.&gt; 0]">Failure</xsl:when>
+                <xsl:when test="@errors[.&gt; 0]">Error</xsl:when>
+            </xsl:choose>
+        </xsl:attribute>
+    
+        <!-- print testsuite information -->
+        <td><a href="#{@name}"><xsl:value-of select="@name"/></a></td>
+        <td><xsl:value-of select="@tests"/></td>
+        <td><xsl:value-of select="@errors"/></td>
+        <td><xsl:value-of select="@failures"/></td>
+        <td>
+            <xsl:call-template name="display-time">
+                <xsl:with-param name="value" select="@time"/>
+            </xsl:call-template>
+        </td>
+    </tr>
+</xsl:template>
+
+<xsl:template match="testcase" mode="print.test">
+    <tr valign="top">
+        <xsl:attribute name="class">
+            <xsl:choose>
+                <xsl:when test="failure | error">Error</xsl:when>
+            </xsl:choose>
+        </xsl:attribute>
+        <td><xsl:value-of select="@name"/></td>
+        <xsl:choose>
+            <xsl:when test="failure">
+                <td>Failure</td>
+                <td><xsl:apply-templates select="failure"/></td>
+            </xsl:when>
+            <xsl:when test="error">
+                <td>Error</td>
+                <td><xsl:apply-templates select="error"/></td>
+            </xsl:when>
+            <xsl:otherwise>
+                <td>Success</td>
+                <td></td>
+            </xsl:otherwise>
+        </xsl:choose>
+        <td>
+            <xsl:call-template name="display-time">
+                <xsl:with-param name="value" select="@time"/>
+            </xsl:call-template>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<xsl:template match="failure">
+    <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<xsl:template match="error">
+    <xsl:call-template name="display-failures"/>
+</xsl:template>
+
+<!-- Style for the error and failure in the tescase template -->
+<xsl:template name="display-failures">
+    <xsl:choose>
+        <xsl:when test="not(@message)">N/A</xsl:when>
+        <xsl:otherwise>
+            <xsl:value-of select="@message"/>
+        </xsl:otherwise>
+    </xsl:choose>
+    <!-- display the stacktrace -->
+    <code>
+        <br/><br/>
+        <xsl:call-template name="br-replace">
+            <xsl:with-param name="word" select="."/>
+        </xsl:call-template>
+    </code>
+    <!-- the later is better but might be problematic for non-21" monitors... -->
+    <!--pre><xsl:value-of select="."/></pre-->
+</xsl:template>
+
+<xsl:template name="JS-escape">
+    <xsl:param name="string"/>
+    <xsl:param name="tmp1" select="stringutils:replace(string($string),'\','\\')"/>
+    <xsl:param name="tmp2" select="stringutils:replace(string($tmp1),&quot;'&quot;,&quot;\&apos;&quot;)"/>
+    <xsl:value-of select="$tmp2"/>
+</xsl:template>
+
+
+<!--
+    template that will convert a carriage return into a br tag
+    @param word the text from which to convert CR to BR tag
+-->
+<xsl:template name="br-replace">
+    <xsl:param name="word"/>
+    <xsl:param name="br"><br/></xsl:param>
+    <xsl:value-of select='stringutils:replace(string($word),"&#xA;",$br)'/>
+</xsl:template>
+
+<xsl:template name="display-time">
+    <xsl:param name="value"/>
+    <xsl:value-of select="format-number($value,'0.000')"/>
+</xsl:template>
+
+<xsl:template name="display-percent">
+    <xsl:param name="value"/>
+    <xsl:value-of select="format-number($value,'0.00%')"/>
+</xsl:template>
+
+</xsl:stylesheet>
+
Index: /release-kits/shared/ant/src/etc/log.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/log.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/log.xsl	(revision 16139)
@@ -0,0 +1,210 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+<!--
+    Copyright  2000-2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+ 
+<!--
+
+  The purpose have this XSL is to provide a nice way to look at the output
+  from the Ant XmlLogger (ie: ant -listener org.apache.tools.ant.XmlLogger )
+  
+  @author <a href="mailto:sbailliez@apache.org">Stephane Bailliez</a>
+  
+-->
+<xsl:decimal-format decimal-separator="." grouping-separator="," />
+
+<xsl:template match="/">
+<html>
+  <head>
+    <style type="text/css">
+    .bannercell {
+      border: 0px;
+      padding: 0px;
+    }
+    body {
+      margin: 0;
+      font:normal 100% arial,helvetica,sanserif;
+      background-color:#FFFFFF;
+      color:#000000;
+    }
+    table.status {
+      font:bold 80% arial,helvetica,sanserif;
+      background-color:#525D76;
+      color:#ffffff;
+    }
+    table.log tr td, tr th {
+      font-size: 80%;
+    }
+    .error {
+      color:red;
+    }
+    .warn {
+      color:brown;
+    }
+    .info {
+      color:gray;
+    }
+    .debug{
+      color:gray;
+    }
+    .failed {
+      font-size:80%;
+      background-color: red;
+      color:#FFFFFF;
+      font-weight: bold
+    }
+    .complete {
+      font-size:80%;
+      background-color: #525D76;
+      color:#FFFFFF;
+      font-weight: bold
+    }
+    .a td { 
+      background: #efefef;
+    }
+    .b td { 
+      background: #fff;
+    }
+    th, td {
+      text-align: left;
+      vertical-align: top;
+    }
+    th {
+      background: #ccc;
+      color: black;
+    }
+    table, th, td {
+      border: none
+    }
+    h3 {
+      font:bold 80% arial,helvetica,sanserif;
+      background: #525D76;
+      color: white;
+      text-decoration: none;
+      padding: 5px;
+      margin-right: 2px;
+      margin-left: 2px;
+      margin-bottom: 0;
+    }
+    </style>
+  </head>
+  <body>
+    <!-- jakarta logo -->
+    <table border="0" cellpadding="0" cellspacing="0" width="100%">
+    <tr>
+      <td valign="top" class="bannercell">
+        <a href="http://jakarta.apache.org/">
+        <img src="http://jakarta.apache.org/images/jakarta-logo.gif" alt="http://jakarta.apache.org" align="left" border="0"/>
+        </a>
+      </td>
+      <td style="text-align:right;vertical-align:bottom">
+        <a href="http://jakarta.apache.org/ant">Apache Ant</a>
+      </td>
+    </tr>
+    </table>
+      
+    <table border="0" width="100%">
+    <tr><td><hr noshade="yes" size="1"/></td></tr>
+    </table>
+
+    <xsl:apply-templates select="build"/>
+
+    <!-- FOOTER -->
+    <table width="100%">
+      <tr><td><hr noshade="yes" size="1"/></td></tr>
+      <tr><td>
+      <div align="center"><font color="#525D76" size="-1"><em>
+      Copyright &#169; 2000-2002, Apache Software Foundation
+      </em></font></div>
+      </td></tr>
+    </table>
+  </body>
+</html>
+</xsl:template>
+
+<xsl:template match="build">
+  <!-- build status -->
+  <table width="100%">
+    <xsl:attribute name="class">
+      <xsl:if test="@error">failed</xsl:if>
+      <xsl:if test="not(@error)">complete</xsl:if>
+    </xsl:attribute>
+    <tr>
+      <xsl:if test="@error">
+        <td nowrap="yes">Build Failed</td> 
+      </xsl:if>
+      <xsl:if test="not(@error)">
+        <td nowrap="yes">Build Complete</td>
+      </xsl:if>
+        <td style="text-align:right" nowrap="yes">Total Time: <xsl:value-of select="@time"/></td>
+    </tr>
+    <tr>
+      <td colspan="2">
+        <xsl:if test="@error">
+          <tt><xsl:value-of select="@error"/></tt><br/>
+          <i style="font-size:80%">See the <a href="#stacktrace" alt="Click for details">stacktrace</a>.</i>
+        </xsl:if>
+      </td>
+    </tr>
+  </table>
+  <table border="1" cellspacing="2" cellpadding="3" width="100%" style="font-size:80%">
+    <tr class="a"><td width="1">ant.file</td><td><xsl:value-of select="substring-after(//message[contains(text(),'ant.file')], '->')"/></td></tr>
+    <tr class="b"><td width="1">ant.version</td><td><xsl:value-of select="substring-after(//message[contains(text(),'ant.version')], '->')"/></td></tr>
+    <tr class="a"><td width="1">java.version</td><td><xsl:value-of select="substring-after(//message[contains(text(),'java.vm.version')], '->')"/></td></tr>
+    <tr class="b"><td width="1">os.name</td><td><xsl:value-of select="substring-after(//message[contains(text(),'os.name')], '->')"/></td></tr>
+  </table>
+  <!-- build information -->
+  <h3>Build events</h3>
+  <table class="log" border="1" cellspacing="2" cellpadding="3" width="100%">
+  <tr>
+    <th nowrap="yes" align="left" width="1%">target</th>
+    <th nowrap="yes" align="left" width="1%">task</th>
+    <th nowrap="yes" align="left">message</th>
+  </tr>
+  <xsl:apply-templates select=".//message[@priority != 'debug']"/>
+  </table>
+  <p>
+  <!-- stacktrace -->
+  <xsl:if test="stacktrace">
+  <a name="stacktrace"/>
+  <h3>Error details</h3>
+  <table width="100%">
+    <tr><td>
+      <pre><xsl:value-of select="stacktrace"/></pre>
+    </td></tr>
+  </table>
+  </xsl:if>
+  </p>
+</xsl:template>
+
+<!-- report every message but those with debug priority -->
+<xsl:template match="message[@priority!='debug']">
+  <tr valign="top">
+    <!-- alternated row style -->
+    <xsl:attribute name="class">
+      <xsl:if test="position() mod 2 = 1">a</xsl:if>
+      <xsl:if test="position() mod 2 = 0">b</xsl:if>
+    </xsl:attribute>
+    <td nowrap="yes" width="1%"><xsl:value-of select="../../@name"/></td>
+    <td nowrap="yes" style="text-align:right" width="1%">[ <xsl:value-of select="../@name"/> ]</td>
+    <td class="{@priority}" nowrap="yes">
+            <xsl:value-of select="text()"/>
+    </td>
+  </tr>
+</xsl:template>
+
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/manifest
===================================================================
--- /release-kits/shared/ant/src/etc/manifest	(revision 16139)
+++ /release-kits/shared/ant/src/etc/manifest	(revision 16139)
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Main-Class: org.apache.tools.ant.Main
+
+
Index: /release-kits/shared/ant/src/etc/maudit-frames.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/maudit-frames.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/maudit-frames.xsl	(revision 16139)
@@ -0,0 +1,510 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+    xmlns:lxslt="http://xml.apache.org/xslt"
+    xmlns:redirect="org.apache.xalan.lib.Redirect"
+    extension-element-prefixes="redirect">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+<xsl:decimal-format decimal-separator="." grouping-separator="," />
+<!--
+    Copyright  2001-2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+<!--
+
+    Stylesheet to transform an XML file generated by the Ant MAudit task into
+    a set of JavaDoc-like HTML page to make pages more convenient to be browsed.
+
+    It use the Xalan redirect extension to write to multiple output files.
+
+    @author Stephane Bailliez <a href="mailto:sbailliez@apache.org"/>
+-->
+
+<xsl:param name="output.dir" select="'.'"/>
+
+
+<xsl:template match="classes">
+    <!-- create the index.html -->
+    <redirect:write file="{$output.dir}/index.html">
+        <xsl:call-template name="index.html"/>
+    </redirect:write>
+
+    <!-- create the stylesheet.css -->
+    <redirect:write file="{$output.dir}/stylesheet.css">
+        <xsl:call-template name="stylesheet.css"/>
+    </redirect:write>
+
+    <!-- create the overview-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-summary.html">
+        <xsl:apply-templates select="." mode="overview.packages"/>
+    </redirect:write>
+
+    <!-- create the all-packages.html at the root -->
+    <redirect:write file="{$output.dir}/overview-frame.html">
+        <xsl:apply-templates select="." mode="all.packages"/>
+    </redirect:write>
+
+    <!-- create the all-classes.html at the root -->
+    <redirect:write file="{$output.dir}/allclasses-frame.html">
+        <xsl:apply-templates select="." mode="all.classes"/>
+    </redirect:write>
+
+    <!-- process all packages -->
+    <xsl:for-each select="./class[not(./@package = preceding-sibling::class/@package)]">
+        <xsl:call-template name="package">
+            <xsl:with-param name="name" select="@package"/>
+        </xsl:call-template>
+    </xsl:for-each>
+</xsl:template>
+
+
+<xsl:template name="package">
+    <xsl:param name="name"/>
+    <xsl:variable name="package.dir">
+        <xsl:if test="not($name = '')"><xsl:value-of select="translate($name,'.','/')"/></xsl:if>
+        <xsl:if test="$name = ''">.</xsl:if>
+    </xsl:variable>
+    <!--Processing package <xsl:value-of select="@name"/> in <xsl:value-of select="$output.dir"/> -->
+    <!-- create a classes-list.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-frame.html">
+        <xsl:call-template name="classes.list">
+            <xsl:with-param name="name" select="$name"/>
+        </xsl:call-template>
+    </redirect:write>
+
+    <!-- create a package-summary.html in the package directory -->
+    <redirect:write file="{$output.dir}/{$package.dir}/package-summary.html">
+        <xsl:call-template name="package.summary">
+            <xsl:with-param name="name" select="$name"/>
+        </xsl:call-template>
+    </redirect:write>
+
+    <!-- for each class, creates a @name.html -->
+    <!-- @bug there will be a problem with inner classes having the same name, it will be overwritten -->
+    <xsl:for-each select="/classes/class[@package = $name]">
+        <redirect:write file="{$output.dir}/{$package.dir}/{@name}.html">
+            <xsl:apply-templates select="." mode="class.details"/>
+        </redirect:write>
+    </xsl:for-each>
+</xsl:template>
+
+<xsl:template name="index.html">
+<HTML>
+    <HEAD><TITLE>Audit Results.</TITLE></HEAD>
+    <FRAMESET cols="20%,80%">
+        <FRAMESET rows="30%,70%">
+            <FRAME src="overview-frame.html" name="packageListFrame"/>
+            <FRAME src="allclasses-frame.html" name="classListFrame"/>
+        </FRAMESET>
+        <FRAME src="overview-summary.html" name="classFrame"/>
+    </FRAMESET>
+    <noframes>
+        <H2>Frame Alert</H2>
+        <P>
+        This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+        </P>
+    </noframes>
+</HTML>
+</xsl:template>
+
+
+<!-- this is the stylesheet css to use for nearly everything -->
+<xsl:template name="stylesheet.css">
+    .bannercell {
+      border: 0px;
+      padding: 0px;
+    }
+    body {
+      margin-left: 10;
+      margin-right: 10;
+      font:normal 80% arial,helvetica,sanserif;
+      background-color:#FFFFFF;
+      color:#000000;
+    }
+    .a td {
+      background: #efefef;
+    }
+    .b td {
+      background: #fff;
+    }
+    th, td {
+      text-align: left;
+      vertical-align: top;
+    }
+    th {
+      font-weight:bold;
+      background: #ccc;
+      color: black;
+    }
+    table, th, td {
+      font-size:100%;
+      border: none
+    }
+    table.log tr td, tr th {
+
+    }
+    h2 {
+      font-weight:bold;
+      font-size:140%;
+      margin-bottom: 5;
+    }
+    h3 {
+      font-size:100%;
+      font-weight:bold;
+      background: #525D76;
+      color: white;
+      text-decoration: none;
+      padding: 5px;
+      margin-right: 2px;
+      margin-left: 2px;
+      margin-bottom: 0;
+    }
+</xsl:template>
+
+
+<!-- print the violations of the class -->
+<xsl:template match="class" mode="class.details">
+    <xsl:variable name="package.name" select="@package"/>
+    <HTML>
+        <HEAD>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$package.name"/>
+            </xsl:call-template>
+        </HEAD>
+        <BODY>
+            <xsl:call-template name="pageHeader"/>
+            <H3>Class <xsl:if test="not($package.name = '')"><xsl:value-of select="$package.name"/>.</xsl:if><xsl:value-of select="@name"/></H3>
+
+            <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+                <xsl:call-template name="class.audit.header"/>
+                <xsl:apply-templates select="." mode="print.audit"/>
+            </table>
+
+            <H3>Violations</H3>
+            <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+                <xsl:call-template name="violation.audit.header"/>
+                <xsl:apply-templates select="./violation" mode="print.audit">
+                    <xsl:sort data-type="number" select="@line"/>
+                </xsl:apply-templates>
+            </table>
+            <xsl:call-template name="pageFooter"/>
+        </BODY>
+    </HTML>
+</xsl:template>
+
+
+<!-- list of classes in a package -->
+<xsl:template name="classes.list">
+    <xsl:param name="name"/>
+    <HTML>
+        <HEAD>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$name"/>
+            </xsl:call-template>
+        </HEAD>
+        <BODY>
+            <table width="100%">
+                <tr>
+                    <td nowrap="nowrap">
+                        <H2><a href="package-summary.html" target="classFrame"><xsl:value-of select="$name"/></a></H2>
+                    </td>
+                </tr>
+            </table>
+
+            <h2>Classes</h2>
+            <TABLE WIDTH="100%">
+                <xsl:apply-templates select="/classes/class[./@package = $name]" mode="classes.list">
+                    <xsl:sort select="@name"/>
+                </xsl:apply-templates>
+            </TABLE>
+        </BODY>
+    </HTML>
+</xsl:template>
+<!-- the class to list -->
+<xsl:template match="class" mode="classes.list">
+    <tr>
+        <td nowrap="nowrap">
+            <!-- @bug naming to fix for inner classes -->
+            <a href="{@name}.html" target="classFrame"><xsl:value-of select="@name"/></a>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<!--
+    Creates an all-classes.html file that contains a link to all package-summary.html
+    on each class.
+-->
+<xsl:template match="classes" mode="all.classes">
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <h2>Classes</h2>
+            <table width="100%">
+                <xsl:apply-templates select=".//class" mode="all.classes">
+                    <xsl:sort select="@name"/>
+                </xsl:apply-templates>
+            </table>
+        </body>
+    </html>
+</xsl:template>
+
+<xsl:template match="class" mode="all.classes">
+    <!-- (ancestor::package)[last()] is buggy in MSXML3 ? -->
+    <xsl:variable name="package.name" select="@package"/>
+    <tr>
+        <td nowrap="nowrap">
+            <a target="classFrame">
+                <xsl:attribute name="href">
+                    <xsl:if test="not($package.name='')">
+                        <xsl:value-of select="translate($package.name,'.','/')"/><xsl:text>/</xsl:text>
+                    </xsl:if><xsl:value-of select="@name"/><xsl:text>.html</xsl:text>
+                </xsl:attribute>
+                <xsl:value-of select="@name"/>
+            </a>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<!--
+    Creates an html file that contains a link to all package-summary.html files on
+    each package existing on testsuites.
+    @bug there will be a problem here, I don't know yet how to handle unnamed package :(
+-->
+<xsl:template match="classes" mode="all.packages">
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body>
+            <h2><a href="overview-summary.html" target="classFrame">Home</a></h2>
+            <h2>Packages</h2>
+                <table width="100%">
+                    <xsl:apply-templates select="class[not(./@package = preceding-sibling::class/@package)]" mode="all.packages">
+                        <xsl:sort select="@package" order="ascending"/>
+                    </xsl:apply-templates>
+                </table>
+        </body>
+    </html>
+</xsl:template>
+
+<xsl:template match="class" mode="all.packages">
+    <tr>
+        <td nowrap="nowrap">
+            <a href="{translate(@package,'.','/')}/package-summary.html" target="classFrame">
+                <xsl:value-of select="@package"/>
+            </a>
+        </td>
+    </tr>
+</xsl:template>
+
+
+<xsl:template match="classes" mode="overview.packages">
+    <html>
+        <head>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name"/>
+            </xsl:call-template>
+        </head>
+        <body onload="open('allclasses-frame.html','classListFrame')">
+        <xsl:call-template name="pageHeader"/>
+        <h3>Summary</h3>
+        <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+        <tr>
+            <th>Audited classes</th>
+            <th>Reported classes</th>
+            <th>Violations</th>
+        </tr>
+        <tr class="a">
+            <td><xsl:value-of select="@audited"/></td>
+            <td><xsl:value-of select="@reported"/></td>
+            <td><xsl:value-of select="@violations"/></td>
+        </tr>
+        </table>
+        <table border="0" width="100%">
+        <tr>
+        <td style="text-align: justify;">
+        Note: Rules checked have originated from style guidelines suggested by the language designers,
+        experience from the Java development community and insite experience. Violations are generally
+        reported with a reference to the <a href="http://java.sun.com/docs/books/jls/second_edition/html/jTOC.doc.html">Java Language Specifications</a> (JLS x.x.x)
+        and Metamata Audit rules (x.x).
+        Please consult these documents for additional information about violations.
+        <p/>
+        Rules checked also enforce adherence to <a href="http://java.sun.com/docs/codeconv/html/CodeConvTOC.doc.html">Sun Java coding guidelines</a> in use at Jakarta.
+        <p/>
+        One should note that these violations do not necessary underline errors but should be used
+        as an indication for <i>possible</i> errors. As always, use your best judgment and review
+        them carefully, it might save you hours of debugging.
+        </td>
+        </tr>
+        </table>
+
+        <h3>Packages</h3>
+        <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+            <xsl:call-template name="class.audit.header"/>
+            <xsl:for-each select="class[not(./@package = preceding-sibling::class/@package)]">
+                <xsl:sort select="@package" order="ascending"/>
+                <tr>
+          <xsl:call-template name="alternate-row"/>
+                    <td><a href="{translate(@package,'.','/')}/package-summary.html"><xsl:value-of select="@package"/></a></td>
+                    <td><xsl:value-of select="sum(/classes/class[./@package = current()/@package]/@violations)"/></td>
+                </tr>
+            </xsl:for-each>
+        </table>
+        <xsl:call-template name="pageFooter"/>
+        </body>
+        </html>
+</xsl:template>
+
+
+<xsl:template name="package.summary">
+    <xsl:param name="name"/>
+    <HTML>
+        <HEAD>
+            <xsl:call-template name="create.stylesheet.link">
+                <xsl:with-param name="package.name" select="$name"/>
+            </xsl:call-template>
+        </HEAD>
+        <BODY>
+            <xsl:attribute name="onload">open('package-frame.html','classListFrame')</xsl:attribute>
+            <xsl:call-template name="pageHeader"/>
+            <h3>Package <xsl:value-of select="$name"/></h3>
+
+            <!--table border="0" cellpadding="5" cellspacing="2" width="100%">
+                <xsl:call-template name="class.metrics.header"/>
+                <xsl:apply-templates select="." mode="print.metrics"/>
+            </table-->
+
+            <xsl:if test="count(/classes/class[./@package = $name]) &gt; 0">
+                <H3>Classes</H3>
+                <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+                    <xsl:call-template name="class.audit.header"/>
+                    <xsl:apply-templates select="/classes/class[./@package = $name]" mode="print.audit">
+                        <xsl:sort select="@name"/>
+                    </xsl:apply-templates>
+                </table>
+            </xsl:if>
+            <xsl:call-template name="pageFooter"/>
+        </BODY>
+    </HTML>
+</xsl:template>
+
+
+<!--
+    transform string like a.b.c to ../../../
+    @param path the path to transform into a descending directory path
+-->
+<xsl:template name="path">
+    <xsl:param name="path"/>
+    <xsl:if test="contains($path,'.')">
+        <xsl:text>../</xsl:text>
+        <xsl:call-template name="path">
+            <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'.')"/></xsl:with-param>
+        </xsl:call-template>
+    </xsl:if>
+    <xsl:if test="not(contains($path,'.')) and not($path = '')">
+        <xsl:text>../</xsl:text>
+    </xsl:if>
+</xsl:template>
+
+
+<!-- create the link to the stylesheet based on the package name -->
+<xsl:template name="create.stylesheet.link">
+    <xsl:param name="package.name"/>
+    <LINK REL ="stylesheet" TYPE="text/css" TITLE="Style"><xsl:attribute name="href"><xsl:if test="not($package.name = 'unnamed package')"><xsl:call-template name="path"><xsl:with-param name="path" select="$package.name"/></xsl:call-template></xsl:if>stylesheet.css</xsl:attribute></LINK>
+</xsl:template>
+
+<!-- Page HEADER -->
+<xsl:template name="pageHeader">
+
+  <!-- jakarta logo -->
+  <table border="0" cellpadding="0" cellspacing="0" width="100%">
+  <tr>
+    <td class="bannercell" rowspan="2">
+      <a href="http://jakarta.apache.org/">
+      <img src="http://jakarta.apache.org/images/jakarta-logo.gif" alt="http://jakarta.apache.org" align="left" border="0"/>
+      </a>
+    </td>
+        <td style="text-align:right"><h2>Source Code Audit</h2></td>
+        </tr>
+        <tr>
+        <td style="text-align:right">Designed for use with <a href='http://www.webgain.com/products/quality_analyzer/'>Webgain QA/Metamata Audit</a> and <a href='http://jakarta.apache.org'>Ant</a>.</td>
+        </tr>
+  </table>
+    <hr size="1"/>
+</xsl:template>
+
+<!-- Page HEADER -->
+<xsl:template name="pageFooter">
+    <table width="100%">
+      <tr><td><hr noshade="yes" size="1"/></td></tr>
+      <tr><td>
+      <div align="center"><font color="#525D76" size="-1"><em>
+      Copyright &#169; 1999-2001, Apache Software Foundation
+      </em></font></div>
+      </td></tr>
+    </table>
+</xsl:template>
+
+
+<!-- class header -->
+<xsl:template name="class.audit.header">
+    <tr>
+        <th width="80%">Name</th>
+        <th>Violations</th>
+    </tr>
+</xsl:template>
+
+<!-- method header -->
+<xsl:template name="violation.audit.header">
+    <tr>
+        <th>Line</th>
+        <th>Message</th>
+    </tr>
+</xsl:template>
+
+
+<!-- class information -->
+<xsl:template match="class" mode="print.audit">
+    <tr>
+    <xsl:call-template name="alternate-row"/>
+        <td><a href="{@name}.html"><xsl:value-of select="@name"/></a></td>
+        <td><xsl:apply-templates select="@violations"/></td>
+    </tr>
+</xsl:template>
+
+<xsl:template match="violation" mode="print.audit">
+    <tr>
+    <xsl:call-template name="alternate-row"/>
+        <td><xsl:value-of select="@line"/></td>
+        <td><xsl:apply-templates select="@message"/></td>
+    </tr>
+</xsl:template>
+
+<!-- alternated row style -->
+<xsl:template name="alternate-row">
+<xsl:attribute name="class">
+  <xsl:if test="position() mod 2 = 1">a</xsl:if>
+  <xsl:if test="position() mod 2 = 0">b</xsl:if>
+</xsl:attribute>
+</xsl:template>
+
+</xsl:stylesheet>
+
Index: /release-kits/shared/ant/src/etc/mmetrics-frames.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/mmetrics-frames.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/mmetrics-frames.xsl	(revision 16139)
@@ -0,0 +1,1033 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+  xmlns:lxslt="http://xml.apache.org/xslt"
+  xmlns:xalan="http://xml.apache.org/xalan"
+  xmlns:redirect="org.apache.xalan.lib.Redirect"
+  exclude-result-prefixes="xalan"
+  extension-element-prefixes="redirect">
+<xsl:output method="html" indent="yes" encoding="US-ASCII"/>
+<xsl:decimal-format decimal-separator="." grouping-separator="," />
+<!--
+    Copyright  2001-2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+<!--
+  @author Stephane Bailliez <a href="mailto:sbailliez@apache.org"/>
+  -->
+<xsl:param name="output.dir" select="'.'"/>
+
+<!-- default max value for the metrics -->
+<xsl:param name="vg.max" select="10"/>
+<xsl:param name="loc.max" select="1000"/>
+<xsl:param name="dit.max" select="10"/>
+<xsl:param name="noa.max" select="250"/>
+<xsl:param name="nrm.max" select="50"/>
+<xsl:param name="nlm.max" select="250"/>
+<xsl:param name="wmc.max" select="250"/>
+<xsl:param name="rfc.max" select="50"/>
+<xsl:param name="dac.max" select="10"/>
+<xsl:param name="fanout.max" select="10"/>
+<xsl:param name="cbo.max" select="15"/>
+<xsl:param name="lcom.max" select="10"/>
+<xsl:param name="nocl.max" select="10"/>
+
+
+<!-- create a tree fragment to speed up processing -->
+<xsl:variable name="doctree.var">
+  <xsl:element name="classes">
+    <xsl:for-each select=".//class">
+      <xsl:element name="class">
+        <xsl:attribute name="package">
+          <xsl:value-of select="(ancestor::package)[last()]/@name"/>
+        </xsl:attribute>
+        <xsl:copy-of select="@*"/>
+        <xsl:attribute name="name">
+          <xsl:apply-templates select="." mode="class.name"/>
+        </xsl:attribute>
+        <xsl:copy-of select="method"/>
+      </xsl:element>
+    </xsl:for-each>
+  </xsl:element>
+</xsl:variable>
+
+<xsl:variable name="doctree" select="xalan:nodeset($doctree.var)"/>
+
+<xsl:template match="metrics">
+
+  <!-- create the index.html -->
+  <redirect:write file="{$output.dir}/index.html">
+    <xsl:call-template name="index.html"/>
+  </redirect:write>
+
+  <!-- create the stylesheet.css -->
+  <redirect:write file="{$output.dir}/stylesheet.css">
+    <xsl:call-template name="stylesheet.css"/>
+  </redirect:write>
+
+  <redirect:write file="{$output.dir}/metrics-reference.html">
+    <xsl:call-template name="metrics-reference.html"/>
+  </redirect:write>
+
+  <!-- create the overview-packages.html at the root -->
+  <redirect:write file="{$output.dir}/overview-summary.html">
+    <xsl:apply-templates select="." mode="overview.packages"/>
+  </redirect:write>
+
+  <!-- create the all-packages.html at the root -->
+  <redirect:write file="{$output.dir}/overview-frame.html">
+    <xsl:apply-templates select="." mode="all.packages"/>
+  </redirect:write>
+
+  <!-- create the all-classes.html at the root -->
+  <redirect:write file="{$output.dir}/allclasses-frame.html">
+    <xsl:apply-templates select="." mode="all.classes"/>
+  </redirect:write>
+
+  <!-- process all packages -->
+  <xsl:apply-templates select=".//package"/>
+</xsl:template>
+
+
+<xsl:template match="package">
+  <xsl:variable name="package.name" select="@name"/>
+  <xsl:variable name="package.dir">
+    <xsl:if test="not($package.name = 'unnamed package')"><xsl:value-of select="translate($package.name,'.','/')"/></xsl:if>
+    <xsl:if test="$package.name = 'unnamed package'">.</xsl:if>
+  </xsl:variable>
+  <!-- create a classes-list.html in the package directory -->
+  <redirect:write file="{$output.dir}/{$package.dir}/package-frame.html">
+    <xsl:apply-templates select="." mode="classes.list"/>
+  </redirect:write>
+
+  <!-- create a package-summary.html in the package directory -->
+  <redirect:write file="{$output.dir}/{$package.dir}/package-summary.html">
+    <xsl:apply-templates select="." mode="package.summary"/>
+  </redirect:write>
+
+  <!-- for each class, creates a @name.html -->
+  <!-- @bug there will be a problem with inner classes having the same name, it will be overwritten -->
+  <xsl:for-each select="$doctree/classes/class[@package = current()/@name]">
+      <!--Processing <xsl:value-of select="$class.name"/><xsl:text>&#10;</xsl:text> -->
+    <redirect:write file="{$output.dir}/{$package.dir}/{@name}.html">
+      <xsl:apply-templates select="." mode="class.details"/>
+    </redirect:write>
+  </xsl:for-each>
+</xsl:template>
+
+<!-- little trick to compute the classname for inner and non inner classes -->
+<!-- this is all in one line to avoid CRLF in the name -->
+<xsl:template match="class" mode="class.name">
+    <xsl:if test="parent::class"><xsl:apply-templates select="parent::class" mode="class.name"/>.<xsl:value-of select="@name"/></xsl:if><xsl:if test="not(parent::class)"><xsl:value-of select="@name"/></xsl:if>
+</xsl:template>
+
+
+<xsl:template name="index.html">
+<HTML>
+  <HEAD><TITLE>Metrics Results.</TITLE></HEAD>
+  <FRAMESET cols="20%,80%">
+    <FRAMESET rows="30%,70%">
+      <FRAME src="overview-frame.html" name="packageListFrame"/>
+      <FRAME src="allclasses-frame.html" name="classListFrame"/>
+    </FRAMESET>
+    <FRAME src="overview-summary.html" name="classFrame"/>
+  </FRAMESET>
+  <noframes>
+    <H2>Frame Alert</H2>
+    <P>
+    This document is designed to be viewed using the frames feature. If you see this message, you are using a non-frame-capable web client.
+    </P>
+  </noframes>
+</HTML>
+</xsl:template>
+
+<!-- this is the stylesheet css to use for nearly everything -->
+<xsl:template name="metrics-reference.html">
+<html>
+<head>
+<link title="Style" type="text/css" rel="stylesheet" href="stylesheet.css"/>
+</head>
+<body style="text-align:justify;">
+<h2>Metrics Reference</h2>
+<a href="#V(G)">V(G)</a> |
+<a href="#LOC">LOC</a> |
+<a href="#DIT">DIT</a> |
+<a href="#NOA">NOA</a> |
+<a href="#NRM">NRM</a> |
+<a href="#NLM">NLM</a> |
+<a href="#WMC">WMC</a> |
+<a href="#RFC">RFC</a> |
+<a href="#DAC">DAC</a> |
+<a href="#FANOUT">FANOUT</a> |
+<a href="#CBO">CBO</a> |
+<a href="#LCOM">LCOM</a> |
+<a href="#NOC">NOC</a>
+
+<a name="V(G)"/>
+<h3>Cyclomatic Complexity - V(G)</h3>
+This metric was introduced in the 1970s to measure the amount of control
+flow complexity or branching complexity in a module such as a
+subroutine. It gives the number of paths that may be taken through the
+code, and was initially developed to give some measure of the cost of
+producing a test case for the module by executing each path.
+<p/>
+Methods with a high cyclomatic complexity tend to be more difficult to
+understand and maintain. In general the more complex the methods of an
+application, the more difficult it will be to test it, and this will adversely
+affect its reliability.
+<p/>
+V(G) is a measure of the control flow complexity of a method or
+constructor.  It counts the number of branches in the body of the method,
+defined as:
+<ul>
+<li>while statements;</li>
+<li>if statements;</li>
+<li>for statements.</li>
+</ul>
+
+The metric can also be configured to count each case of a switch
+statement as well.
+
+<a name="LOC"/>
+<h3>Lines of Code - LOC</h3>
+
+This is perhaps the simplest of all the metrics to define and compute.
+Counting lines has a long history as a software metric dating from before
+the rise of structured programming, and it is still in widespread use today.
+The size of a method affects the ease with which it can be understood, its
+reusability and its maintainability. There are a variety of ways that the size
+can be calculated. These include counting all the lines of code, the number
+of statements, the blank lines of code, the lines of commentary, and the
+lines consisting only of syntax such as block delimiters.
+<p/>
+This metric can also be used for sizing other constructs as well, for
+example, the overall size of a Java class or package can be measured by
+counting the number of source lines it consists of.
+<p/>
+LOC can be used to determine the size of a compilation unit (source file),
+class or interface, method, constructor, or field.  It can be configured to
+ignore:
+<ul>
+<li>blank lines;</li>
+<li>lines consisting only of comments;</li>
+<li>lines consisting only of opening and closing braces.</li>
+</ul>
+
+<a name="DIT"/>
+<h3>Depth of Inheritance Hierarchy - DIT</h3>
+
+This metric calculates how far down the inheritance hierarchy a class is
+declared. In Java all classes have java.lang.Object as their ultimate
+superclass, which is defined to have a depth of 1. So a class that
+immediately extends java.lang.Object has a metric value of 2; any of its
+subclasses will have a value of 3, and so on.
+<p/>
+A class that is deep within the tree inherits more methods and state
+variables, thereby increasing its complexity and making it difficult to
+predict its behavior. It can be harder to understand a system with many
+inheritance layers.
+<p/>
+DIT is defined for classes and interfaces:
+<ul>
+<li>all interface types have a depth of 1;</li>
+<li>the class java.lang.Object has a depth of 1;</li>
+<li>all other classes have a depth of 1 + the depth of their super class.</li>
+</ul>
+
+<a name="NOA"/>
+<h3>Number of Attributes - NOA</h3>
+
+The number of distinct state variables in a class serves as one measure of
+its complexity. The more state a class represents the more difficult it is to
+maintain invariants for it. It also hinders comprehensibility and reuse.
+<p/>
+In Java, state can be exposed to subclasses through protected fields, which
+entails that the subclass also be aware of and maintain any invariants. This
+interference with the class's data encapsulation can be a source of defects
+and hidden dependencies between the state variables.
+<p/>
+NOA is defined for classes and interfaces.  It counts the number of fields
+declared in the class or interface.
+
+<a name="NRM"/>
+<h3>Number of Remote Methods - NRM</h3>
+
+NRM is defined for classes.  A remote method call is defined as an
+invocation of a method that is not declared in any of:
+<ul>
+<li>the class itself;</li>
+<li>a class or interface that the class extends or implements;</li>
+<li>a class or method that extends the class.</li>
+</ul>
+
+The value is the count of all the remote method calls in all of the methods
+and constructors of the class.
+
+<a name="NLM"/>
+<h3>Number of Local Methods - NLM</h3>
+
+NLM is defined for classes and interfaces.  A local method is defined as a
+method that is declared in the class or interface. NLM can be configured to
+include the local methods of all of the class's superclasses.  Methods with
+public, protected, package and private visibility can be independently
+counted by setting configuration parameters.
+
+<a name="WMC"/>
+<h3>Weighted Methods per Class - WMC</h3>
+
+If the number of methods in a class can be determined during the design
+and modeling phase of a project, it can be used as a predictor of how
+much time and effort is needed to develop, debug and maintain it. This
+metric can be further refined by incorporating a weighting for the
+complexity of each method. The usual weighting is given by the cyclomatic
+complexity of the method.
+<p/>
+The subclasses of a class inherit all of its public and protected methods,
+and possibly its package methods as well, so the number of methods a
+class has directly impacts the complexity of its subclasses. Classes with
+large numbers of methods are often specific to a particular application,
+reducing the ability to reuse them.
+<p/>
+The definition of WMC is based upon NLM, and it provides the same
+configuration parameters for counting inherited methods and of varying
+visibility. The main difference is that NLM always counts each method as 1,
+whereas WMC will weight each method. There are two weighting schemes:
+<ul>
+<li>V(G) the cyclomatic complexity of the method is used as its weight.
+   Methods from class files are given a V(G) of 1.</li>
+<li>the arity, or the number of parameters of the method are used to
+   determine the weight.</li>
+</ul>
+
+<a name="RFC"/>
+<h3>Response For Class - RFC</h3>
+
+The response set of a class is the set of all methods that can be invoked as
+a result of a message sent to an object of the class. This includes methods
+in the class's inheritance hierarchy and methods that can be invoked on
+other objects. The Response For Class metric is defined to be size of the
+response set for the class. A class which provides a larger response set is
+considered to be more complex than one with a smaller response set.
+<p/>
+One reason for this is that if a method call on a class can result in a large
+number of different method calls on the target and other classes, then it
+can be harder to test the behavior of the class and debug problems. It will
+typically require a deeper understanding of the potential interactions that
+objects of the class can have with the rest of the system.
+<p/>
+RFC is defined as the sum of NLM and NRM for the class.  The local methods
+include all of the public, protected, package and private methods, but not
+methods declared only in a superclass.
+
+<a name="DAC"/>
+<h3>Data Abstraction Coupling - DAC</h3>
+
+DAC is defined for classes and interfaces.  It counts the number of reference
+types that are used in the field declarations of the class or interface.  The
+component types of arrays are also counted.  Any field with a type that is
+either a supertype or a subtype of the class is not counted.
+
+<a name="FANOUT"/>
+<h3>Fan Out - FANOUT</h3>
+
+FANOUT is defined for classes and interfaces, constructors and methods. It
+counts the number of reference types that are used in:
+<ul>
+<li>field declarations;</li>
+<li>formal parameters and return types;</li>
+<li>throws declarations;</li>
+<li>local variables.</li>
+</ul>
+
+The component types of arrays are also counted. Any type that is either a
+supertype or a subtype of the class is not counted.
+
+<a name="CBO"/>
+<h3>Coupling Between Objects - CBO</h3>
+
+When one object or class uses another object or class they are said to be
+coupled. One major source of coupling is that between a superclass and a
+subclass. A coupling is also introduced when a method or field in another
+class is accessed, or when an object of another class is passed into or out
+of a method invocation. Coupling Between Objects is a measure of the
+non-inheritance coupling between two objects.
+<p/>
+A high value of coupling reduces the modularity of the class and makes
+reuse more difficult. The more independent a class is the more likely it is
+that it will be possible to reuse it in another part of the system. When a
+class is coupled to another class it becomes sensitive to changes in that
+class, thereby making maintenance for difficult. In addition, a class that is
+overly dependent on other classes can be difficult to understand and test in
+isolation.
+<p/>
+CBO is defined for classes and interfaces, constructors and methods. It
+counts the number of reference types that are used in:
+<ul>
+<li>field declarations</li>
+<li>formal parameters and return types</li>
+<li>throws declarations</li>
+<li>local variables</li>
+</ul>
+
+It also counts:
+<ul>
+<li>types from which field and method selections are made</li>
+</ul>
+
+The component types of arrays are also counted. Any type that is either a
+supertype or a subtype of the class is not counted.
+
+<a name="LCOM"/>
+<h3>Lack of Cohesion Of Methods - LCOM</h3>
+
+The cohesion of a class is the degree to which its methods are related to
+each other. It is determined by examining the pattern of state variable
+accesses within the set of methods. If all the methods access the same state
+variables then they have high cohesion; if they access disjoint sets of
+variables then the cohesion is low. An extreme example of low cohesion
+would be if none of the methods accessed any of the state variables.
+
+If a class exhibits low method cohesion it indicates that the design of the
+class has probably been partitioned incorrectly, and could benefit by being
+split into more classes with individually higher cohesion. On the other
+hand, a high value of cohesion (a low lack of cohesion) implies that the
+class is well designed. A cohesive class will tend to provide a high degree
+of encapsulation, whereas a lack of cohesion decreases encapsulation and
+increases complexity.
+<p/>
+Another form of cohesion that is useful for Java programs is cohesion
+between nested and enclosing classes. A nested class that has very low
+cohesion with its enclosing class would probably better designed as a peer
+class rather than a nested class.
+<p/>
+LCOM is defined for classes. Operationally, LCOM takes each pair of
+methods in the class and determines the set of fields they each access. If
+they have disjoint sets of field accesses increase the count P by one. If they
+share at least one field access then increase Q by one. After considering
+each pair of methods,
+LCOM = (P > Q) ? (P - Q) : 0
+<p/>
+Indirect access to fields via local methods can be considered by setting a
+metric configuration parameter.
+
+<a name="NOC"/>
+<h3>Number Of Classes - NOC</h3>
+
+The overall size of the system can be estimated by calculating the number
+of classes it contains. A large system with more classes is more complex
+than a smaller one because the number of potential interactions between
+objects is higher. This reduces the comprehensibility of the system which
+in turn makes it harder to test, debug and maintain.
+<p/>
+If the number of classes in the system can be projected during the initial
+design phase of the project it can serve as a base for estimating the total
+effort and cost of developing, debugging and maintaining the system.
+<p/>
+The NOC metric can also usefully be applied at the package and class level
+as well as the total system.
+<p/>
+NOCL is defined for class and interfaces. It counts the number of classes or
+interfaces that are declared. This is usually 1, but nested class declarations
+will increase this number.
+</body>
+</html>
+</xsl:template>
+
+<!-- this is the stylesheet css to use for nearly everything -->
+<xsl:template name="stylesheet.css">
+    .bannercell {
+      border: 0px;
+      padding: 0px;
+    }
+    body {
+      margin-left: 10;
+      margin-right: 10;
+      font:normal 80% arial,helvetica,sanserif;
+      background-color:#FFFFFF;
+      color:#000000;
+    }
+    .a td {
+      background: #efefef;
+    }
+    .b td {
+      background: #fff;
+    }
+    th, td {
+      text-align: left;
+      vertical-align: top;
+    }
+    th {
+      font-weight:bold;
+      background: #ccc;
+      color: black;
+    }
+    table, th, td {
+      font-size:100%;
+      border: none
+    }
+    table.log tr td, tr th {
+
+    }
+    h2 {
+      font-weight:bold;
+      font-size:140%;
+      margin-bottom: 5;
+    }
+    h3 {
+      font-size:100%;
+      font-weight:bold;
+      background: #525D76;
+      color: white;
+      text-decoration: none;
+      padding: 5px;
+      margin-right: 2px;
+      margin-left: 2px;
+      margin-bottom: 0;
+    }
+    .Error {
+      font-weight:bold; color:red;
+    }
+
+</xsl:template>
+
+<!-- print the metrics of the class -->
+<xsl:template match="class" mode="class.details">
+  <!--xsl:variable name="package.name" select="(ancestor::package)[last()]/@name"/-->
+  <xsl:variable name="package.name" select="@package"/>
+  <HTML>
+    <HEAD>
+      <xsl:call-template name="create.stylesheet.link">
+        <xsl:with-param name="package.name" select="$package.name"/>
+      </xsl:call-template>
+    </HEAD>
+    <BODY>
+      <xsl:call-template name="pageHeader"/>
+
+      <H3>Class <xsl:if test="not($package.name = 'unnamed package')"><xsl:value-of select="$package.name"/>.</xsl:if><xsl:value-of select="@name"/></H3>
+      <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+        <xsl:call-template name="all.metrics.header"/>
+        <xsl:apply-templates select="." mode="print.metrics"/>
+      </table>
+
+      <H3>Methods</H3>
+      <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+        <xsl:call-template name="method.metrics.header"/>
+        <xsl:apply-templates select="method" mode="print.metrics"/>
+      </table>
+
+      <xsl:call-template name="pageFooter"/>
+    </BODY>
+  </HTML>
+</xsl:template>
+
+
+<!-- list of classes in a package -->
+<xsl:template match="package" mode="classes.list">
+  <HTML>
+    <HEAD>
+      <xsl:call-template name="create.stylesheet.link">
+        <xsl:with-param name="package.name" select="@name"/>
+      </xsl:call-template>
+    </HEAD>
+    <BODY>
+      <table width="100%">
+        <tr>
+          <td nowrap="nowrap">
+            <H2><a href="package-summary.html" target="classFrame"><xsl:value-of select="@name"/></a></H2>
+          </td>
+        </tr>
+      </table>
+
+      <H2>Classes</H2>
+      <TABLE WIDTH="100%">
+        <!-- xalan-nodeset:nodeset for Xalan 1.2.2 -->
+            <xsl:for-each select="$doctree/classes/class[@package = current()/@name]">
+                <xsl:sort select="@name"/>
+          <tr>
+            <td nowrap="nowrap">
+              <a href="{@name}.html" target="classFrame"><xsl:value-of select="@name"/></a>
+            </td>
+          </tr>
+            </xsl:for-each>
+      </TABLE>
+    </BODY>
+  </HTML>
+</xsl:template>
+
+
+<!--
+  Creates an all-classes.html file that contains a link to all package-summary.html
+  on each class.
+-->
+<xsl:template match="metrics" mode="all.classes">
+  <html>
+    <head>
+      <xsl:call-template name="create.stylesheet.link">
+        <xsl:with-param name="package.name" select="''"/>
+      </xsl:call-template>
+    </head>
+    <body>
+      <h2>Classes</h2>
+      <table width="100%">
+          <xsl:for-each select="$doctree/classes/class">
+              <xsl:sort select="@name"/>
+              <xsl:apply-templates select="." mode="all.classes"/>
+          </xsl:for-each>
+      </table>
+    </body>
+  </html>
+</xsl:template>
+
+<xsl:template match="class" mode="all.classes">
+    <xsl:variable name="package.name" select="@package"/>
+    <xsl:variable name="class.name" select="@name"/>
+  <tr>
+    <td nowrap="nowrap">
+      <a target="classFrame">
+        <xsl:attribute name="href">
+          <xsl:if test="not($package.name='unnamed package')">
+            <xsl:value-of select="translate($package.name,'.','/')"/><xsl:text>/</xsl:text>
+          </xsl:if>
+          <xsl:value-of select="$class.name"/><xsl:text>.html</xsl:text>
+        </xsl:attribute>
+        <xsl:value-of select="$class.name"/>
+      </a>
+    </td>
+  </tr>
+</xsl:template>
+
+<!--
+  Creates an html file that contains a link to all package-summary.html files on
+  each package existing on testsuites.
+  @bug there will be a problem here, I don't know yet how to handle unnamed package :(
+-->
+<xsl:template match="metrics" mode="all.packages">
+  <html>
+    <head>
+      <xsl:call-template name="create.stylesheet.link">
+        <xsl:with-param name="package.name" select="./package/@name"/>
+      </xsl:call-template>
+    </head>
+    <body>
+      <h2><a href="overview-summary.html" target="classFrame">Home</a></h2>
+      <h2>Packages</h2>
+        <table width="100%">
+          <xsl:apply-templates select=".//package[not(./@name = 'unnamed package')]" mode="all.packages">
+            <xsl:sort select="@name"/>
+          </xsl:apply-templates>
+        </table>
+    </body>
+  </html>
+</xsl:template>
+
+<xsl:template match="package" mode="all.packages">
+  <tr>
+    <td nowrap="nowrap">
+      <a href="{translate(@name,'.','/')}/package-summary.html" target="classFrame">
+        <xsl:value-of select="@name"/>
+      </a>
+    </td>
+  </tr>
+</xsl:template>
+
+
+<xsl:template match="metrics" mode="overview.packages">
+  <html>
+    <head>
+      <xsl:call-template name="create.stylesheet.link">
+        <xsl:with-param name="package.name" select="''"/>
+      </xsl:call-template>
+    </head>
+    <body onload="open('allclasses-frame.html','classListFrame')">
+    <xsl:call-template name="pageHeader"/>
+    <h3>Summary</h3>
+    <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+    <tr>
+      <th><a href="metrics-reference.html#V(G)">V(G)</a></th>
+      <th><a href="metrics-reference.html#LOC">LOC</a></th>
+      <th><a href="metrics-reference.html#DIT">DIT</a></th>
+      <th><a href="metrics-reference.html#NOA">NOA</a></th>
+      <th><a href="metrics-reference.html#NRM">NRM</a></th>
+      <th><a href="metrics-reference.html#NLM">NLM</a></th>
+      <th><a href="metrics-reference.html#WMC">WMC</a></th>
+      <th><a href="metrics-reference.html#RFC">RFC</a></th>
+      <th><a href="metrics-reference.html#DAC">DAC</a></th>
+      <th><a href="metrics-reference.html#FANOUT">FANOUT</a></th>
+      <th><a href="metrics-reference.html#CBO">CBO</a></th>
+      <th><a href="metrics-reference.html#LCOM">LCOM</a></th>
+      <th><a href="metrics-reference.html#NOCL">NOCL</a></th>
+    </tr>
+    <xsl:apply-templates select="." mode="print.metrics"/>
+    </table>
+    <table border="0" width="100%">
+    <tr>
+    <td style="text-align: justify;">
+    Note: Metrics evaluate the quality of software by analyzing the program source and quantifying
+    various kind of complexity. Complexity is a common source of problems and defects in software.
+    High complexity makes it more difficult to develop, understand, maintain, extend, test and debug
+    a program.
+    <p/>
+    The primary use of metrics is to focus your attention on those parts of code that potentially are
+    complexity hot spots. Once the complex areas your program have been uncovered, you can take remedial
+    actions.
+    For additional information about metrics and their meaning, please consult
+    Metamata Metrics manual.
+    </td>
+    </tr>
+    </table>
+
+    <h3>Packages</h3>
+    <table border="0" cellpadding="5" cellspacing="2" width="100%">
+      <xsl:call-template name="all.metrics.header"/>
+      <xsl:for-each select=".//package[not(@name = 'unnamed package')]">
+        <xsl:sort select="@name" order="ascending"/>
+        <xsl:apply-templates select="." mode="print.metrics"/>
+      </xsl:for-each>
+    </table>
+    <!-- @bug there could some classes at this level (classes in unnamed package) -->
+    <xsl:call-template name="pageFooter"/>
+    </body>
+    </html>
+</xsl:template>
+
+<xsl:template match="package" mode="package.summary">
+  <HTML>
+    <HEAD>
+      <xsl:call-template name="create.stylesheet.link">
+        <xsl:with-param name="package.name" select="@name"/>
+      </xsl:call-template>
+    </HEAD>
+    <body onload="open('package-frame.html','classListFrame')">
+      <xsl:call-template name="pageHeader"/>
+      <!-- create an anchor to this package name -->
+      <h3>Package <xsl:value-of select="@name"/></h3>
+
+      <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+        <xsl:call-template name="all.metrics.header"/>
+        <xsl:apply-templates select="." mode="print.metrics"/>
+      </table>
+
+      <table border="0" width="100%">
+      <tr>
+      <td style="text-align: justify;">
+      Note: Metrics evaluate the quality of software by analyzing the program source and quantifying
+      various kind of complexity. Complexity is a common source of problems and defects in software.
+      High complexity makes it more difficult to develop, understand, maintain, extend, test and debug
+      a program.
+      <p/>
+      The primary use of metrics is to focus your attention on those parts of code that potentially are
+      complexity hot spots. Once the complex areas your program have been uncovered, you can take remedial
+      actions.
+      For additional information about metrics and their meaning, please consult
+      Metamata Metrics manual.
+      </td>
+      </tr>
+      </table>
+
+      <xsl:variable name="classes-in-package" select="$doctree/classes/class[@package = current()/@name]"/>
+      <xsl:if test="count($classes-in-package) &gt; 0">
+        <H3>Classes</H3>
+        <table class="log" border="0" cellpadding="5" cellspacing="2" width="100%">
+          <xsl:call-template name="all.metrics.header"/>
+          <xsl:for-each select="$classes-in-package">
+                <xsl:sort select="@name"/>
+                <xsl:apply-templates select="." mode="print.metrics"/>
+          </xsl:for-each>
+        </table>
+      </xsl:if>
+
+      <xsl:call-template name="pageFooter"/>
+    </body>
+  </HTML>
+</xsl:template>
+
+
+<!--
+    transform string like a.b.c to ../../../
+    @param path the path to transform into a descending directory path
+-->
+<xsl:template name="path">
+  <xsl:param name="path"/>
+  <xsl:if test="contains($path,'.')">
+    <xsl:text>../</xsl:text>
+    <xsl:call-template name="path">
+      <xsl:with-param name="path"><xsl:value-of select="substring-after($path,'.')"/></xsl:with-param>
+    </xsl:call-template>
+  </xsl:if>
+  <xsl:if test="not(contains($path,'.')) and not($path = '')">
+    <xsl:text>../</xsl:text>
+  </xsl:if>
+</xsl:template>
+
+
+<!-- create the link to the stylesheet based on the package name -->
+<xsl:template name="create.stylesheet.link">
+  <xsl:param name="package.name"/>
+  <LINK REL ="stylesheet" TYPE="text/css" TITLE="Style"><xsl:attribute name="href"><xsl:if test="not($package.name = 'unnamed package')"><xsl:call-template name="path"><xsl:with-param name="path" select="$package.name"/></xsl:call-template></xsl:if>stylesheet.css</xsl:attribute></LINK>
+</xsl:template>
+
+
+<!-- Page Header -->
+<xsl:template name="pageHeader">
+
+  <!-- jakarta logo -->
+  <table border="0" cellpadding="0" cellspacing="0" width="100%">
+  <tr>
+    <td class="bannercell" rowspan="2">
+      <a href="http://jakarta.apache.org/">
+      <img src="http://jakarta.apache.org/images/jakarta-logo.gif" alt="http://jakarta.apache.org" align="left" border="0"/>
+      </a>
+    </td>
+    <td style="text-align:right"><h2>Source Code Metrics</h2></td>
+    </tr>
+    <tr>
+    <td style="text-align:right">Designed for use with <a href='http://www.webgain.com/products/quality_analyzer/'>Webgain QA/Metamata Metrics</a> and <a href='http://jakarta.apache.org'>Ant</a>.</td>
+    </tr>
+  </table>
+  <hr size="1"/>
+</xsl:template>
+
+<!-- Page Footer -->
+<xsl:template name="pageFooter">
+    <table width="100%">
+      <tr><td><hr noshade="yes" size="1"/></td></tr>
+      <tr><td>
+      <div align="center"><font color="#525D76" size="-1"><em>
+      Copyright &#169; 1999-2001, Apache Software Foundation
+      </em></font></div>
+      </td></tr>
+    </table>
+</xsl:template>
+
+<!-- class header -->
+<xsl:template name="all.metrics.header">
+  <tr>
+    <th width="80%">Name</th>
+    <th nowrap="nowrap">V(G)</th>
+    <th>LOC</th>
+    <th>DIT</th>
+    <th>NOA</th>
+    <th>NRM</th>
+    <th>NLM</th>
+    <th>WMC</th>
+    <th>RFC</th>
+    <th>DAC</th>
+    <th>FANOUT</th>
+    <th>CBO</th>
+    <th>LCOM</th>
+    <th>NOCL</th>
+  </tr>
+</xsl:template>
+
+<!-- method header -->
+<xsl:template name="method.metrics.header">
+  <tr>
+    <th width="80%">Name</th>
+    <th nowrap="nowrap">V(G)</th>
+    <th>LOC</th>
+    <th>FANOUT</th>
+    <th>CBO</th>
+  </tr>
+</xsl:template>
+
+<!-- method information -->
+<xsl:template match="method" mode="print.metrics">
+  <tr>
+    <xsl:call-template name="alternate-row"/>
+    <td><xsl:apply-templates select="@name"/></td>
+    <td><xsl:apply-templates select="@vg"/></td>
+    <td><xsl:apply-templates select="@loc"/></td>
+    <td><xsl:apply-templates select="@fanout"/></td>
+    <td><xsl:apply-templates select="@cbo"/></td>
+  </tr>
+</xsl:template>
+
+<!-- class information -->
+<xsl:template match="class" mode="print.metrics">
+  <tr>
+    <xsl:call-template name="alternate-row"/>
+    <td><a href="{@name}.html"><xsl:value-of select="@name"/></a></td>
+    <td><xsl:apply-templates select="@vg"/></td>
+    <td><xsl:apply-templates select="@loc"/></td>
+    <td><xsl:apply-templates select="@dit"/></td>
+    <td><xsl:apply-templates select="@noa"/></td>
+    <td><xsl:apply-templates select="@nrm"/></td>
+    <td><xsl:apply-templates select="@nlm"/></td>
+    <td><xsl:apply-templates select="@wmc"/></td>
+    <td><xsl:apply-templates select="@rfc"/></td>
+    <td><xsl:apply-templates select="@dac"/></td>
+    <td><xsl:apply-templates select="@fanout"/></td>
+    <td><xsl:apply-templates select="@cbo"/></td>
+    <td><xsl:apply-templates select="@lcom"/></td>
+    <td><xsl:apply-templates select="@nocl"/></td>
+  </tr>
+</xsl:template>
+
+<xsl:template match="file|package" mode="print.metrics">
+  <tr>
+    <xsl:call-template name="alternate-row"/>
+    <td>
+    <a href="{translate(@name,'.','/')}/package-summary.html" target="classFrame">
+    <xsl:value-of select="@name"/>
+    </a>
+    </td>
+    <td><xsl:apply-templates select="@vg"/></td>
+    <td><xsl:apply-templates select="@loc"/></td>
+    <td><xsl:apply-templates select="@dit"/></td>
+    <td><xsl:apply-templates select="@noa"/></td>
+    <td><xsl:apply-templates select="@nrm"/></td>
+    <td><xsl:apply-templates select="@nlm"/></td>
+    <td><xsl:apply-templates select="@wmc"/></td>
+    <td><xsl:apply-templates select="@rfc"/></td>
+    <td><xsl:apply-templates select="@dac"/></td>
+    <td><xsl:apply-templates select="@fanout"/></td>
+    <td><xsl:apply-templates select="@cbo"/></td>
+    <td><xsl:apply-templates select="@lcom"/></td>
+    <td><xsl:apply-templates select="@nocl"/></td>
+  </tr>
+</xsl:template>
+
+<xsl:template match="metrics" mode="print.metrics">
+  <tr>
+    <xsl:call-template name="alternate-row"/>
+      <!-- the global metrics is the top package metrics -->
+    <td><xsl:apply-templates select="./package/@vg"/></td>
+    <td><xsl:apply-templates select="./package/@loc"/></td>
+    <td><xsl:apply-templates select="./package/@dit"/></td>
+    <td><xsl:apply-templates select="./package/@noa"/></td>
+    <td><xsl:apply-templates select="./package/@nrm"/></td>
+    <td><xsl:apply-templates select="./package/@nlm"/></td>
+    <td><xsl:apply-templates select="./package/@wmc"/></td>
+    <td><xsl:apply-templates select="./package/@rfc"/></td>
+    <td><xsl:apply-templates select="./package/@dac"/></td>
+    <td><xsl:apply-templates select="./package/@fanout"/></td>
+    <td><xsl:apply-templates select="./package/@cbo"/></td>
+    <td><xsl:apply-templates select="./package/@lcom"/></td>
+    <td><xsl:apply-templates select="./package/@nocl"/></td>
+  </tr>
+</xsl:template>
+
+<!-- alternated row style -->
+<xsl:template name="alternate-row">
+<xsl:attribute name="class">
+  <xsl:if test="position() mod 2 = 1">a</xsl:if>
+  <xsl:if test="position() mod 2 = 0">b</xsl:if>
+</xsl:attribute>
+</xsl:template>
+
+
+<!-- how to display the metrics with their max value -->
+<!-- @todo the max values must be external to the xsl -->
+
+  <xsl:template match="@vg">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$vg.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@loc">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$loc.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@dit">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$dit.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@noa">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$noa.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@nrm">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$nrm.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@nlm">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$nlm.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@wmc">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$wmc.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@rfc">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$rfc.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@dac">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$dac.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@fanout">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$fanout.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@cbo">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$cbo.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@lcom">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$lcom.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template match="@nocl">
+    <xsl:call-template name="display-value">
+      <xsl:with-param name="value" select="current()"/>
+      <xsl:with-param name="max" select="$nocl.max"/>
+    </xsl:call-template>
+  </xsl:template>
+
+  <xsl:template name="display-value">
+    <xsl:param name="value"/>
+    <xsl:param name="max"/>
+    <xsl:if test="$value > $max">
+      <xsl:attribute name="class">Error</xsl:attribute>
+    </xsl:if>
+    <xsl:value-of select="$value"/>
+  </xsl:template>
+
+</xsl:stylesheet>
+
Index: /release-kits/shared/ant/src/etc/tagdiff.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/tagdiff.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/tagdiff.xsl	(revision 16139)
@@ -0,0 +1,177 @@
+<!--
+    Copyright  2004 The Apache Software Foundation
+   
+     Licensed 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.
+   
+-->
+<!-- a stylesheet to display changelogs ala netbeans -->
+<xsl:stylesheet
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    version="1.0">
+  <xsl:param name="title"/>
+  <xsl:param name="module"/>
+  <xsl:param name="cvsweb"/>
+
+  <xsl:output method="html" indent="yes"/>
+
+  <!-- Copy standard document elements.  Elements that
+       should be ignored must be filtered by apply-templates
+       tags. -->
+  <xsl:template match="*">
+    <xsl:copy>
+      <xsl:copy-of select="attribute::*[. != '']"/>
+      <xsl:apply-templates/>
+    </xsl:copy>
+  </xsl:template>
+
+  <xsl:template match="tagdiff">
+    <HTML>
+      <HEAD>
+        <TITLE><xsl:value-of select="$title"/></TITLE>
+      </HEAD>
+      <BODY link="#000000" alink="#000000" vlink="#000000" text="#000000">
+        <style type="text/css">
+          body, p {
+          font-family: verdana,arial,helvetica;
+          font-size: 80%;
+          color:#000000;
+          }
+	  .dateAndAuthor {
+          font-family: verdana,arial,helvetica;
+          font-size: 80%;
+          font-weight: bold;
+          text-align:left;
+          background:#a6caf0;
+	  }
+          tr, td{
+          font-family: verdana,arial,helvetica;
+          font-size: 80%;
+          background:#eeeee0;
+          }	  
+	  </style>        
+          <h1>
+            <a name="top"><xsl:value-of select="$title"/></a>
+          </h1>
+          Tagdiff between <xsl:value-of select="@startTag"/> <xsl:value-of select="@startDate"/> and
+			<xsl:value-of select="@endTag"/> <xsl:value-of select="@endDate"/>
+          <p align="right">Designed for use with <a href="http://ant.apache.org/">Ant</a>.</p>
+          <hr size="2"/>
+	<a name="TOP"/>
+	<table width="100%">
+		<tr>
+			<td align="right">
+				<a href="#New">New Files</a> |
+				<a href="#Modified">Modified Files</a> |
+				<a href="#Removed">Removed Files</a>
+			</td>
+		</tr>
+	</table>
+        <TABLE BORDER="0" WIDTH="100%" CELLPADDING="3" CELLSPACING="1">
+		<xsl:call-template name="show-entries">
+			<xsl:with-param name="title">New Files</xsl:with-param>
+			<xsl:with-param name="anchor">New</xsl:with-param>
+			<xsl:with-param name="entries" select=".//entry[file/revision][not(file/prevrevision)]"/>
+		</xsl:call-template>
+
+		<xsl:call-template name="show-entries">
+			<xsl:with-param name="title">Modified Files</xsl:with-param>
+			<xsl:with-param name="anchor">Modified</xsl:with-param>
+			<xsl:with-param name="entries" select=".//entry[file/revision][file/prevrevision]"/>
+		</xsl:call-template>
+
+		<xsl:call-template name="show-entries">
+			<xsl:with-param name="title">Removed Files</xsl:with-param>
+			<xsl:with-param name="anchor">Removed</xsl:with-param>
+			<xsl:with-param name="entries" select=".//entry[not(file/revision)][not(file/prevrevision)]"/>
+		</xsl:call-template>
+        </TABLE>
+        
+      </BODY>
+    </HTML>
+  </xsl:template>
+
+  <xsl:template name="show-entries">
+	<xsl:param name="title"/>
+	<xsl:param name="anchor"/>
+	<xsl:param name="entries"/>
+	<TR>
+		<TD colspan="2" class="dateAndAuthor">
+			<a>
+				<xsl:attribute name="name"><xsl:value-of select="$anchor"/></xsl:attribute>
+				<xsl:value-of select="$title"/> - <xsl:value-of select="count($entries)"/> entries
+			</a>
+			<a href="#TOP">(back to top)</a>
+		</TD>
+	</TR>
+	<TR>
+		<TD width="20">
+			<xsl:text>    </xsl:text>
+		</TD>
+		<TD>
+		        <ul>
+				<xsl:apply-templates select="$entries"/>
+			</ul>
+		</TD>
+	</TR>
+  </xsl:template>  
+
+  <xsl:template match="entry">
+	<xsl:apply-templates select="file"/>
+  </xsl:template>
+
+  <xsl:template match="date">
+    <i><xsl:value-of select="."/></i>
+  </xsl:template>
+
+  <xsl:template match="time">
+    <i><xsl:value-of select="."/></i>
+  </xsl:template>
+
+  <xsl:template match="author">
+    <i>
+      <a>
+        <xsl:attribute name="href">mailto:<xsl:value-of select="."/></xsl:attribute>
+        <xsl:value-of select="."/>
+      </a>
+    </i>
+  </xsl:template>
+
+  <xsl:template match="file">
+    <li>
+      <a target="_new">
+        <xsl:attribute name="href"><xsl:value-of select="$cvsweb"/><xsl:value-of select="$module" />/<xsl:value-of select="name" /></xsl:attribute>
+        <xsl:value-of select="name" />
+      </a>
+      <xsl:if test="string-length(prevrevision) > 0 or string-length(revision) > 0">
+      <xsl:text> </xsl:text>
+      <a target="_new">
+        <xsl:choose>
+          <xsl:when test="string-length(prevrevision) = 0 ">
+            <xsl:attribute name="href"><xsl:value-of select="$cvsweb"/><xsl:value-of select="$module" />/<xsl:value-of select="name" />?rev=<xsl:value-of select="revision" />&amp;content-type=text/x-cvsweb-markup</xsl:attribute>
+          </xsl:when>
+          <xsl:otherwise>
+            <xsl:attribute name="href"><xsl:value-of select="$cvsweb"/><xsl:value-of select="$module" />/<xsl:value-of select="name" />?r1=<xsl:value-of select="revision" />&amp;r2=<xsl:value-of select="prevrevision"/>&amp;diff_format=h</xsl:attribute>
+          </xsl:otherwise>
+        </xsl:choose> (<xsl:value-of select="revision"/>)
+      </a>
+      </xsl:if>
+    </li>
+  </xsl:template>
+
+  <!-- Any elements within a msg are processed,
+       so that we can preserve HTML tags. -->
+  <xsl:template match="msg">
+    <b><xsl:apply-templates/></b>
+  </xsl:template>
+  
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/testcases/core/case.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/case.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/case.xml	(revision 16139)
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+
+<project name="parsing-test" basedir="." default="help">
+
+  <target name="help">
+    <echo>
+This build file is intended to be used for testing Ant
+    </echo>
+  </target>
+
+  <target name="case-sensitivity">
+    <concat>
+      <fileSet dir="." includes="parse.xml"/>
+    </concat>
+  </target>
+
+  <target name="taskcase">
+    <ecHO>Should fail</ecHO>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/containersrc/test/SpecialSeq.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/containersrc/test/SpecialSeq.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/containersrc/test/SpecialSeq.java	(revision 16139)
@@ -0,0 +1,66 @@
+/* 
+ * Copyright  2003-2004 Apache Software Foundation
+ *
+ *  Licensed 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.
+ * 
+ */
+package test;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.taskdefs.Echo;
+import java.util.*;
+
+public class SpecialSeq extends Task implements TaskContainer {
+    /** Optional Vector holding the nested tasks */
+    private Vector nestedTasks = new Vector();
+
+    private FileSet fileset;
+    
+    private Echo nestedEcho;
+    
+    /**
+     * Add a nested task.
+     * <p>
+     * @param nestedTask  Nested task to execute
+     * <p>
+     */
+    public void addTask(Task nestedTask) {
+        nestedTasks.addElement(nestedTask);
+    }
+
+    /**
+     * Execute all nestedTasks.
+     */
+    public void execute() throws BuildException {
+        if (fileset == null || fileset.getDir(getProject()) == null) {
+            throw new BuildException("Fileset was not configured");
+        }
+        for (Enumeration e = nestedTasks.elements(); e.hasMoreElements();) {
+            Task nestedTask = (Task) e.nextElement();
+            nestedTask.perform();
+        }
+        nestedEcho.reconfigure();
+        nestedEcho.perform();
+    }
+
+    public void addFileset(FileSet fileset) {
+        this.fileset = fileset;
+    }
+    
+    public void addNested(Echo nestedEcho) {
+        this.nestedEcho = nestedEcho;
+    }
+}
Index: /release-kits/shared/ant/src/etc/testcases/core/directoryscanner.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/directoryscanner.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/directoryscanner.xml	(revision 16139)
@@ -0,0 +1,22 @@
+<project name="directoryscanner-test" basedir=".">
+    <property name="tmp.dir" location="tmp"/>
+    <target name="setup">
+        <mkdir dir="${tmp.dir}/alpha/beta/gamma"/>
+        <touch file="${tmp.dir}/alpha/beta/gamma/gamma.xml"/>
+        <touch file="${tmp.dir}/alpha/beta/beta.xml"/>
+    </target>
+    <target name="children-of-excluded-dir-setup" depends="setup">
+        <mkdir dir="${tmp.dir}/delta"/>
+        <touch file="${tmp.dir}/delta/delta.xml"/>
+    </target>
+    <target name="cleanup">
+        <delete dir="${tmp.dir}" quiet="true"/>
+    </target>
+
+    <target name="symlink-setup" depends="setup">
+        <mkdir dir="${tmp.dir}/epsilon/gamma"/>
+        <delete dir="${tmp.dir}/alpha/beta"/>
+        <symlink link="${tmp.dir}/alpha/beta" resource="${tmp.dir}/epsilon"/>
+        <touch file="${tmp.dir}/alpha/beta/gamma/gamma.xml"/>
+    </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/executor.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/executor.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/executor.xml	(revision 16139)
@@ -0,0 +1,12 @@
+<project>
+  <target name="foo">
+    <echo>foo</echo>
+    <fail if="failfoo" message="failfoo" />
+  </target>
+  <target name="a" depends="foo">
+    <echo>a</echo>
+  </target>
+  <target name="b" depends="foo">
+    <echo>b</echo>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/immutable.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/immutable.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/immutable.xml	(revision 16139)
@@ -0,0 +1,63 @@
+<?xml version="1.0"?>
+
+<project name="immutable-test" basedir="." default="test1">
+
+  <target name="test1">
+    <property name="test" value="original"/>
+    <available file="immutable.xml" property="test" value="override"/>
+  </target>
+
+  <target name="test2">
+    <tstamp/>
+    <tstamp prefix="start"/>
+  </target>
+
+  <target name="test3">
+    <property name="DSTAMP" value="original"/>
+    <tstamp/>
+  </target>
+
+  <target name="test4">
+    <property name="test" value="original"/>
+    <condition property="test" value="override">
+      <equals arg1="1" arg2="1"/>
+    </condition>
+  </target>
+
+  <target name="test5">
+    <property name="test" value="original"/>
+    <checksum file="immutable.xml" verifyProperty="test"/>
+  </target>
+
+  <target name="test6">
+    <property name="test1" value="original"/>
+    <property name="test2" value="original"/>
+    <!--  How to make this cross-platform? -->
+    <exec executable="cmd.exe" os="Windows 2000" outputproperty="test1" resultProperty="test2">
+      <arg line="/c dir"/>
+    </exec>
+  </target>
+
+  <target name="test7">
+    <property name="test" value="original"/>
+    <pathconvert targetos="unix" property="test" >
+      <path>
+        <pathelement location="/lib/weblogicaux.jar" />
+        <pathelement location="/classes" />
+        <pathelement location="/mssqlserver4/classes" />
+        <pathelement location="c:\winnt\System32" />
+      </path>
+    </pathconvert>
+  </target>
+
+  <target name="test8">
+    <antcall inheritAll="false" target="echo-target">
+      <param name="echo.value" value="Meep meep!" />
+    </antcall>
+  </target>
+
+  <target name="echo-target">
+    <echo message="Value of echo=${echo.value}"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/basic/include.inc
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/basic/include.inc	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/basic/include.inc	(revision 16139)
@@ -0,0 +1,3 @@
+<target name="test1">
+    <echo message="from included entity"/>
+</target>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/basic/include.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/basic/include.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/basic/include.xml	(revision 16139)
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE project [
+  <!ENTITY include SYSTEM "file:./include.inc">
+]>
+
+<project name="include-test" basedir="." default="test1">
+    &include;
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/basic/relative.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/basic/relative.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/basic/relative.xml	(revision 16139)
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE project [
+  <!ENTITY include SYSTEM "./include.inc">
+]>
+
+<project name="include-test" basedir="." default="test1">
+    &include;
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/frag#ment/include.inc
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/frag#ment/include.inc	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/frag#ment/include.inc	(revision 16139)
@@ -0,0 +1,3 @@
+<target name="test1">
+    <echo message="from included entity"/>
+</target>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/frag#ment/include.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/frag#ment/include.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/frag#ment/include.xml	(revision 16139)
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE project [
+  <!ENTITY include SYSTEM "file:include.inc">
+]>
+
+<project name="include-test" basedir="." default="test1">
+    &include;
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/frag#ment/relative.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/frag#ment/relative.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/frag#ment/relative.xml	(revision 16139)
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE project [
+  <!ENTITY include SYSTEM "include.inc">
+]>
+
+<project name="include-test" basedir="." default="test1">
+    &include;
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/frag#ment/simple.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/frag#ment/simple.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/frag#ment/simple.xml	(revision 16139)
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<project name="include-test" basedir="." default="test1">
+<target name="test1">
+    <echo message="from simple buildfile"/>
+</target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/included_file_parse_error/build.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/included_file_parse_error/build.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/included_file_parse_error/build.xml	(revision 16139)
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<!DOCTYPE project [
+    <!ENTITY included_file SYSTEM "file:./included_file.xml">
+]>
+
+<project name="test" default="test" basedir=".">
+
+  <target name="setup">
+  </target>
+
+  &included_file;
+
+  <target name="test" depends="included-target">
+    <echo>test target ran.</echo>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/included_file_parse_error/included_file.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/included_file_parse_error/included_file.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/included_file_parse_error/included_file.xml	(revision 16139)
@@ -0,0 +1,4 @@
+<target name="included-target">
+  extraneous_text
+  <echo>included-target ran.</echo>
+</target>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/included_file_task_error/build.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/included_file_task_error/build.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/included_file_task_error/build.xml	(revision 16139)
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+<!DOCTYPE project [
+    <!ENTITY included_file SYSTEM "file:./included_file.xml">
+]>
+
+<project name="test" default="test" basedir=".">
+
+  <target name="setup">
+  </target>
+
+  &included_file;
+
+  <target name="test" depends="included-target">
+    <echo>test target ran.</echo>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/included_file_task_error/included_file.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/included_file_task_error/included_file.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/included_file_task_error/included_file.xml	(revision 16139)
@@ -0,0 +1,4 @@
+<target name="included-target">
+  <copy file="nonexistent-file" todir="/non/existent/dir"/>
+  <echo>included-target ran.</echo>
+</target>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/including_file_parse_error/build.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/including_file_parse_error/build.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/including_file_parse_error/build.xml	(revision 16139)
@@ -0,0 +1,19 @@
+<?xml version="1.0"?>
+<!DOCTYPE project [
+    <!ENTITY included_file SYSTEM "file:./included_file.xml">
+]>
+
+<project name="test" default="test" basedir=".">
+
+  <target name="setup">
+  </target>
+
+  &included_file;
+
+  extraneous_text
+
+  <target name="test" depends="included-target">
+    <echo>test target ran.</echo>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/including_file_parse_error/included_file.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/including_file_parse_error/included_file.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/including_file_parse_error/included_file.xml	(revision 16139)
@@ -0,0 +1,3 @@
+<target name="included-target">
+  <echo>included-target ran.</echo>
+</target>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/including_file_task_error/build.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/including_file_task_error/build.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/including_file_task_error/build.xml	(revision 16139)
@@ -0,0 +1,18 @@
+<?xml version="1.0"?>
+<!DOCTYPE project [
+    <!ENTITY included_file SYSTEM "file:./included_file.xml">
+]>
+
+<project name="test" default="test" basedir=".">
+
+  <target name="setup">
+  </target>
+
+  &included_file;
+
+  <target name="test" depends="included-target">
+    <copy file="nonexistent-file" todir="/non/existent/dir"/>
+    <echo>test target ran.</echo>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/including_file_task_error/included_file.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/including_file_task_error/included_file.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/including_file_task_error/included_file.xml	(revision 16139)
@@ -0,0 +1,3 @@
+<target name="included-target">
+  <echo>included-target ran.</echo>
+</target>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/with space/include.inc
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/with space/include.inc	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/with space/include.inc	(revision 16139)
@@ -0,0 +1,3 @@
+<target name="test1">
+    <echo message="from included entity in &apos;with space&apos;"/>
+</target>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/with space/include.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/with space/include.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/with space/include.xml	(revision 16139)
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE project [
+  <!ENTITY include SYSTEM "file:include.inc">
+]>
+
+<project name="include-test" basedir="." default="test1">
+    &include;
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/with space/relative.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/with space/relative.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/with space/relative.xml	(revision 16139)
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+
+<!DOCTYPE project [
+  <!ENTITY include SYSTEM "include.inc">
+]>
+
+<project name="include-test" basedir="." default="test1">
+    &include;
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/include/with space/simple.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/include/with space/simple.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/include/with space/simple.xml	(revision 16139)
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<project name="include-test" basedir="." default="test1">
+  <target name="test1">
+    <echo message="from simple buildfile in &apos;with space&apos;"/>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/loaderref/loaderref.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/loaderref/loaderref.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/loaderref/loaderref.xml	(revision 16139)
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+
+<project name="loaderref-test" default="help">
+
+  <property name="src.dir" value="src"/>
+  <property name="classes.dir" value="classes"/>
+
+  <target name="help">
+    <echo>
+This build file is intended to be used for testing Ant
+    </echo>
+  </target>
+
+  <target name="clean">
+    <delete dir="${classes.dir}"/>
+  </target>
+
+  <target name="compile">
+    <mkdir dir="${classes.dir}"/>
+    <javac srcdir="${src.dir}" destdir="${classes.dir}"/>
+  </target>
+
+  <target name="testbadref" depends="compile" >
+    <taskdef loaderref="loaderref-test"
+             name="test1"
+             classname="Test1"
+             classpath="${classes.dir}"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/loaderref/src/Task1.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/loaderref/src/Task1.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/loaderref/src/Task1.java	(revision 16139)
@@ -0,0 +1,5 @@
+import org.apache.tools.ant.Task;
+
+public class Task1 extends Task {
+}
+
Index: /release-kits/shared/ant/src/etc/testcases/core/taskcontainer.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/taskcontainer.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/taskcontainer.xml	(revision 16139)
@@ -0,0 +1,48 @@
+<project default="dont-run-this">
+
+  <property name="build.dir" value="build"/>
+  
+  <target name="dont-run-this">
+    <fail>This build file is supposed to be run by a Unit test</fail>
+  </target>
+ 
+
+  <target name="testPropertyExpansion">
+    <sequential>
+      <property name="foo" value="it worked"/>
+      <echo message="As attribute: ${foo}"/>
+      <echo>As nested text: ${foo}</echo>
+    </sequential>
+  </target>
+
+  <target name="testTaskdef">
+    <mkdir dir="${build.dir}"/>
+    <javac srcdir="containersrc" destdir="${build.dir}" debug="on"/>
+
+    <sequential>    
+      <taskdef name="sseq" classpath="${build.dir}" classname="test.SpecialSeq"/>
+      <sseq>
+        <fileset dir="."/>
+        <property name="foo" value="it worked"/>
+        <echo message="As attribute: ${foo}"/>
+        <echo>As nested text: ${foo}</echo>
+        <nested message="As nested task: ${foo}"/>
+      </sseq>
+    </sequential>
+  </target>
+
+  <target name="testCaseInsensitive">
+    <taskdef name="Prattle" classname="org.apache.tools.ant.taskdefs.Echo"/>  
+    <taskdef name="Seq"  
+             classname="org.apache.tools.ant.taskdefs.Sequential"/>  
+    <Prattle>hello</Prattle>  
+    <Seq>  
+      <Prattle> world</Prattle>  
+    </Seq>  
+  </target>  
+
+  <target name="cleanup">
+    <delete dir="${build.dir}"/>
+  </target>
+  
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/topleveltasks/notarget.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/topleveltasks/notarget.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/topleveltasks/notarget.xml	(revision 16139)
@@ -0,0 +1,4 @@
+<project default="foo">
+  <echo message="Called" />
+  <target name="foo" />
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/topleveltasks/targetlevelant.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/topleveltasks/targetlevelant.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/topleveltasks/targetlevelant.xml	(revision 16139)
@@ -0,0 +1,5 @@
+<project basedir="." default="foo">
+  <target name="foo">
+    <ant antfile="notarget.xml" />
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/core/topleveltasks/toplevelant.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/core/topleveltasks/toplevelant.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/core/topleveltasks/toplevelant.xml	(revision 16139)
@@ -0,0 +1,4 @@
+<project basedir="." default="foo">
+  <ant antfile="notarget.xml" />
+  <target name="foo" />
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/filters/build.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/build.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/build.xml	(revision 16139)
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+<project default="cleanup" basedir=".">
+
+  <target name="init">
+    <mkdir dir="result" />
+  </target>
+
+  <target name="cleanup">
+    <delete dir="result"/>
+  </target>
+
+  <target name="testLineContains" depends="init">
+    <copy todir="result">
+      <fileset dir="input">
+        <include name="linecontains.test"/>
+      </fileset>
+      <filterchain>
+        <filterreader classname="org.apache.tools.ant.filters.LineContains">
+          <param type="contains" value="beta"/>
+        </filterreader>
+      </filterchain>
+    </copy>
+    <!--<fixcrlf srcdir="result" eol="lf">
+      <include name="linecontains.test"/>
+    </fixcrlf>-->
+  </target>
+  
+  <target name="testEscapeUnicode" depends="init">
+    <copy todir="result" encoding="UTF-8">
+      <fileset dir="input">
+        <include name="escapeunicode.test"/>
+      </fileset>
+      <filterchain>
+        <escapeunicode/>
+      </filterchain>
+    </copy>
+    <fixcrlf srcdir="result" eol="crlf">
+      <include name="escapeunicode.test"/>
+    </fixcrlf>
+  </target>
+
+  <target name="testStripJavaComments" depends="init">
+    <copy todir="result">
+      <fileset dir="input" includes="stripjavacomments.test" />
+      <filterchain>
+        <filterreader classname="org.apache.tools.ant.filters.StripJavaComments" />
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testReplaceTokens" depends="init">
+    <copy todir="result">
+      <fileset dir="input" includes="replacetokens.test" />
+      <filterchain>
+        <replacetokens>
+          <token key="foo" value=""/>
+        </replacetokens>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testNoAddNewLine" depends="init">
+    <concat destfile="result/nonl">This has no new lines</concat>
+    <copy file="result/nonl" tofile="result/nonl-copyfilter">
+      <filterchain><tokenfilter/></filterchain>
+    </copy>
+    <condition property="filterchain.files.are.same">
+      <filesmatch file1="result/nonl" file2="result/nonl-copyfilter"/>
+    </condition>
+    <fail unless="filterchain.files.are.same">File was modified</fail>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/filters/concat.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/concat.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/concat.xml	(revision 16139)
@@ -0,0 +1,105 @@
+<?xml version="1.0"?>
+<project default="cleanup" basedir=".">
+
+  <target name="init">
+    <mkdir dir="result" />
+    <echo file="result/prepend.txt" message="this-should-be-the-first-line${line.separator}"/>
+    <echo file="result/append.txt" message="this-should-be-the-last-line${line.separator}"/>
+    <copy file="input/head-tail.test" tofile="input/concatfilter.test"/>
+    <fixcrlf srcDir="input" includes="concatfilter.test"/>
+    <!-- to be consistent on MacOS X.  fixcrlf uses CR while line.sep is LF -->
+    <fixcrlf srcDir="result" includes="append.txt,prepend.txt"/>
+  </target>
+
+  <target name="cleanup">
+    <delete dir="result"/>
+    <delete>
+      <fileset dir="input" includes="concatfilter.test"/>
+    </delete>
+  </target>
+
+
+  <target name="testFilterReaderNoArgs" depends="init">
+    <copy file="input/concatfilter.test"
+          tofile="result/concat.FilterReaderNoArgs.test">
+      <filterchain>
+        <filterreader classname="org.apache.tools.ant.filters.ConcatFilter"/>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testFilterReaderPrepend" depends="init">
+    <copy file="input/concatfilter.test"
+          tofile="result/concat.FilterReaderPrepend.test">
+      <filterchain>
+        <filterreader classname="org.apache.tools.ant.filters.ConcatFilter">
+          <param name="prepend" value="result/prepend.txt"/>
+        </filterreader>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testFilterReaderAppend" depends="init">
+    <copy file="input/concatfilter.test"
+          tofile="result/concat.FilterReaderAppend.test">
+      <filterchain>
+        <filterreader classname="org.apache.tools.ant.filters.ConcatFilter">
+          <param name="append" value="result/append.txt"/>
+        </filterreader>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testFilterReaderPrependAppend" depends="init">
+    <copy file="input/concatfilter.test"
+          tofile="result/concat.FilterReaderPrependAppend.test">
+      <filterchain>
+        <filterreader classname="org.apache.tools.ant.filters.ConcatFilter">
+          <param name="prepend" value="result/prepend.txt"/>
+          <param name="append"  value="result/append.txt"/>
+        </filterreader>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testConcatFilter" depends="init">
+    <typedef name="concatfilter" classname="org.apache.tools.ant.filters.ConcatFilter"/>
+    <copy file="input/concatfilter.test"
+          tofile="result/concat.ConcatFilter.test">
+      <filterchain>
+        <concatfilter/>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testConcatFilterPrepend" depends="init">
+    <typedef name="concatfilter" classname="org.apache.tools.ant.filters.ConcatFilter"/>
+    <copy file="input/concatfilter.test"
+          tofile="result/concat.ConcatFilterPrepend.test">
+      <filterchain>
+        <concatfilter prepend="result/prepend.txt"/>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testConcatFilterAppend" depends="init">
+    <typedef name="concatfilter" classname="org.apache.tools.ant.filters.ConcatFilter"/>
+    <copy file="input/concatfilter.test"
+          tofile="result/concat.ConcatFilterAppend.test">
+      <filterchain>
+        <concatfilter append="result/append.txt"/>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testConcatFilterPrependAppend" depends="init">
+    <typedef name="concatfilter" classname="org.apache.tools.ant.filters.ConcatFilter"/>
+    <copy file="input/concatfilter.test"
+          tofile="result/concat.ConcatFilterPrependAppend.test">
+      <filterchain>
+        <concatfilter prepend="result/prepend.txt" append="result/append.txt"/>
+      </filterchain>
+    </copy>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/filters/dynamicfilter.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/dynamicfilter.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/dynamicfilter.xml	(revision 16139)
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<project default="cleanup" basedir=".">
+
+  <target name="init">
+    <mkdir dir="result" />
+  </target>
+
+  <target name="cleanup">
+    <delete dir="result"/>
+  </target>
+
+  <target name="dynamicfilter">
+    <path id="test-classes">
+      <pathelement location="../../../../build/testcases" />
+      <pathelement path="${java.class.path}" />
+    </path>
+    <typedef 
+      name="customfilter" 
+      classname="org.apache.tools.ant.filters.DynamicFilterTest$CustomFilter">
+      <classpath refid="test-classes"/>
+    </typedef>
+    
+    <concat destfile="result/input">
+      hello world
+    </concat>
+
+    <copy file="result/input" tofile="result/dynamicfilter">
+      <filterchain>
+        <customfilter replace="o" with="O"/>
+      </filterchain>
+    </copy>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/filters/expected/escapeunicode.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/expected/escapeunicode.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/expected/escapeunicode.test	(revision 16139)
@@ -0,0 +1,9 @@
+#hebrew shalom olam (hello world)
+text.hebrew=\u05e9\u05dc\u05d5\u05dd \u05e2\u05d5\u05dc\u05dd
+#goethe gingko biloba
+text.german.1=Sp\u00fcrst du nicht an meinen Liedern,
+text.german.2=Da\u00df ich eins und doppelt bin ?
+# Francois Villon Ballade des Pendus
+text.french=Fr\u00e8res humains qui apr\u00e8s nous vivez
+# Usual IT example
+text.basic.latin=Hello World 
Index: /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.head.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.head.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.head.test	(revision 16139)
@@ -0,0 +1,10 @@
+Line  1
+Line  2
+Line  3
+Line  4
+Line  5
+Line  6
+Line  7
+Line  8
+Line  9
+Line 10
Index: /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.headAllSkip.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.headAllSkip.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.headAllSkip.test	(revision 16139)
@@ -0,0 +1,58 @@
+Line  3
+Line  4
+Line  5
+Line  6
+Line  7
+Line  8
+Line  9
+Line 10
+Line 11
+Line 12
+Line 13
+Line 14
+Line 15
+Line 16
+Line 17
+Line 18
+Line 19
+Line 20
+Line 21
+Line 22
+Line 23
+Line 24
+Line 25
+Line 26
+Line 27
+Line 28
+Line 29
+Line 30
+Line 31
+Line 32
+Line 33
+Line 34
+Line 35
+Line 36
+Line 37
+Line 38
+Line 39
+Line 40
+Line 41
+Line 42
+Line 43
+Line 44
+Line 45
+Line 46
+Line 47
+Line 48
+Line 49
+Line 50
+Line 51
+Line 52
+Line 53
+Line 54
+Line 55
+Line 56
+Line 57
+Line 58
+Line 59
+Line 60
Index: /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.headLines.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.headLines.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.headLines.test	(revision 16139)
@@ -0,0 +1,2 @@
+Line  1
+Line  2
Index: /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.headLinesSkip.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.headLinesSkip.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.headLinesSkip.test	(revision 16139)
@@ -0,0 +1,2 @@
+Line  3
+Line  4
Index: /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.headSkip.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.headSkip.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.headSkip.test	(revision 16139)
@@ -0,0 +1,10 @@
+Line  3
+Line  4
+Line  5
+Line  6
+Line  7
+Line  8
+Line  9
+Line 10
+Line 11
+Line 12
Index: /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.headtail.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.headtail.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.headtail.test	(revision 16139)
@@ -0,0 +1,2 @@
+Line  3
+Line  4
Index: /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.tail.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.tail.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.tail.test	(revision 16139)
@@ -0,0 +1,10 @@
+Line 51
+Line 52
+Line 53
+Line 54
+Line 55
+Line 56
+Line 57
+Line 58
+Line 59
+Line 60
Index: /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.tailAllSkip.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.tailAllSkip.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.tailAllSkip.test	(revision 16139)
@@ -0,0 +1,58 @@
+Line  1
+Line  2
+Line  3
+Line  4
+Line  5
+Line  6
+Line  7
+Line  8
+Line  9
+Line 10
+Line 11
+Line 12
+Line 13
+Line 14
+Line 15
+Line 16
+Line 17
+Line 18
+Line 19
+Line 20
+Line 21
+Line 22
+Line 23
+Line 24
+Line 25
+Line 26
+Line 27
+Line 28
+Line 29
+Line 30
+Line 31
+Line 32
+Line 33
+Line 34
+Line 35
+Line 36
+Line 37
+Line 38
+Line 39
+Line 40
+Line 41
+Line 42
+Line 43
+Line 44
+Line 45
+Line 46
+Line 47
+Line 48
+Line 49
+Line 50
+Line 51
+Line 52
+Line 53
+Line 54
+Line 55
+Line 56
+Line 57
+Line 58
Index: /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.tailLines.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.tailLines.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.tailLines.test	(revision 16139)
@@ -0,0 +1,2 @@
+Line 59
+Line 60
Index: /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.tailLinesSkip.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.tailLinesSkip.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.tailLinesSkip.test	(revision 16139)
@@ -0,0 +1,2 @@
+Line 57
+Line 58
Index: /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.tailSkip.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.tailSkip.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/expected/head-tail.tailSkip.test	(revision 16139)
@@ -0,0 +1,10 @@
+Line 49
+Line 50
+Line 51
+Line 52
+Line 53
+Line 54
+Line 55
+Line 56
+Line 57
+Line 58
Index: /release-kits/shared/ant/src/etc/testcases/filters/expected/linecontains.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/expected/linecontains.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/expected/linecontains.test	(revision 16139)
@@ -0,0 +1,4 @@
+This is line 2 with beta.
+This is line 3 with beta.
+This is line 5 with beta.
+This is line 7 with beta.
Index: /release-kits/shared/ant/src/etc/testcases/filters/expected/replacetokens.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/expected/replacetokens.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/expected/replacetokens.test	(revision 16139)
@@ -0,0 +1,2 @@
+12
+3
Index: /release-kits/shared/ant/src/etc/testcases/filters/expected/stripjavacomments.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/expected/stripjavacomments.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/expected/stripjavacomments.test	(revision 16139)
@@ -0,0 +1,19 @@
+
+
+ 
+public class NormalLine {
+
+    private String withComment; 
+
+    
+    public void doNothing() {
+        
+        int i;
+        
+        int j;
+    }
+
+    private String url = "http://ant.apache.org/"; 
+    private String url2 = "\"http://ant.apache.org/\""; 
+
+}
Index: /release-kits/shared/ant/src/etc/testcases/filters/head-tail.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/head-tail.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/head-tail.xml	(revision 16139)
@@ -0,0 +1,131 @@
+<?xml version="1.0"?>
+<project default="cleanup" basedir=".">
+
+  <target name="init">
+    <mkdir dir="result" />
+  </target>
+
+  <target name="cleanup">
+    <delete dir="result"/>
+  </target>
+
+  <!-- Testcases for HeadFilter -->
+
+  <target name="testHead" depends="init">
+    <copy file="input/head-tail.test" tofile="result/head-tail.head.test">
+      <filterchain>
+        <headfilter/>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testHeadLines" depends="init">
+    <copy file="input/head-tail.test" tofile="result/head-tail.headLines.test">
+      <filterchain>
+        <headfilter lines="2"/>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testHeadSkip" depends="init">
+    <copy file="input/head-tail.test" tofile="result/head-tail.headSkip.test">
+      <filterchain>
+        <headfilter skip="2"/>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testHeadLinesSkip" depends="init">
+    <copy file="input/head-tail.test" tofile="result/head-tail.headLinesSkip.test">
+      <filterchain>
+        <headfilter lines="2" skip="2"/>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testFilterReaderHeadLinesSkip" depends="init">
+    <copy file="input/head-tail.test" 
+          tofile="result/head-tail.filterReaderHeadLinesSkip.test">
+      <filterchain>
+        <filterreader classname="org.apache.tools.ant.filters.HeadFilter">
+          <param name="lines" value="2"/>
+          <param name="skip" value="2"/>
+        </filterreader>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testHeadAllSkip" depends="init">
+    <copy file="input/head-tail.test" tofile="result/head-tail.headAllSkip.test">
+      <filterchain>
+        <headfilter lines="-1" skip="2"/>
+      </filterchain>
+    </copy>
+  </target>
+
+  <!-- Testcases for TailFilter -->
+
+  <target name="testTail" depends="init">
+    <copy file="input/head-tail.test" tofile="result/head-tail.tail.test">
+      <filterchain>
+        <tailfilter/>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testTailLines" depends="init">
+    <copy file="input/head-tail.test" tofile="result/head-tail.tailLines.test">
+      <filterchain>
+        <tailfilter lines="2"/>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testTailSkip" depends="init">
+    <copy file="input/head-tail.test" tofile="result/head-tail.tailSkip.test">
+      <filterchain>
+        <tailfilter skip="2"/>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testTailLinesSkip" depends="init">
+    <copy file="input/head-tail.test" tofile="result/head-tail.tailLinesSkip.test">
+      <filterchain>
+        <tailfilter lines="2" skip="2"/>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testFilterReaderTailLinesSkip" depends="init">
+    <copy file="input/head-tail.test" 
+          tofile="result/head-tail.filterReaderTailLinesSkip.test">
+      <filterchain>
+        <filterreader classname="org.apache.tools.ant.filters.TailFilter">
+          <param name="lines" value="2"/>
+          <param name="skip" value="2"/>
+        </filterreader>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testTailAllSkip" depends="init">
+    <copy file="input/head-tail.test" tofile="result/head-tail.tailAllSkip.test">
+      <filterchain>
+        <tailfilter lines="-1" skip="2"/>
+      </filterchain>
+    </copy>
+  </target>
+
+  <!-- Testcases for combined scenarios -->
+
+  <target name="testHeadTail" depends="init">
+    <copy file="input/head-tail.test" tofile="result/head-tail.headtail.test">
+      <filterchain>
+        <headfilter lines="4"/>
+        <tailfilter lines="2"/>
+      </filterchain>
+    </copy>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/filters/input/escapeunicode.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/input/escapeunicode.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/input/escapeunicode.test	(revision 16139)
@@ -0,0 +1,9 @@
+#hebrew shalom olam (hello world)
+text.hebrew=×©××× ×¢×××
+#goethe gingko biloba
+text.german.1=SpÃŒrst du nicht an meinen Liedern,
+text.german.2=DaÃ ich eins und doppelt bin ?
+# Francois Villon Ballade des Pendus
+text.french=FrÃšres humains qui aprÃšs nous vivez
+# Usual IT example
+text.basic.latin=Hello World 
Index: /release-kits/shared/ant/src/etc/testcases/filters/input/head-tail.small.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/input/head-tail.small.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/input/head-tail.small.test	(revision 16139)
@@ -0,0 +1,5 @@
+Line  1
+Line  2
+Line  3
+Line  4
+Line  5
Index: /release-kits/shared/ant/src/etc/testcases/filters/input/head-tail.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/input/head-tail.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/input/head-tail.test	(revision 16139)
@@ -0,0 +1,60 @@
+Line  1
+Line  2
+Line  3
+Line  4
+Line  5
+Line  6
+Line  7
+Line  8
+Line  9
+Line 10
+Line 11
+Line 12
+Line 13
+Line 14
+Line 15
+Line 16
+Line 17
+Line 18
+Line 19
+Line 20
+Line 21
+Line 22
+Line 23
+Line 24
+Line 25
+Line 26
+Line 27
+Line 28
+Line 29
+Line 30
+Line 31
+Line 32
+Line 33
+Line 34
+Line 35
+Line 36
+Line 37
+Line 38
+Line 39
+Line 40
+Line 41
+Line 42
+Line 43
+Line 44
+Line 45
+Line 46
+Line 47
+Line 48
+Line 49
+Line 50
+Line 51
+Line 52
+Line 53
+Line 54
+Line 55
+Line 56
+Line 57
+Line 58
+Line 59
+Line 60
Index: /release-kits/shared/ant/src/etc/testcases/filters/input/linecontains.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/input/linecontains.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/input/linecontains.test	(revision 16139)
@@ -0,0 +1,7 @@
+This is line 1 with alpha.
+This is line 2 with beta.
+This is line 3 with beta.
+This is line 4 with gamma.
+This is line 5 with beta.
+This is line 6 with delta.
+This is line 7 with beta.
Index: /release-kits/shared/ant/src/etc/testcases/filters/input/replacetokens.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/input/replacetokens.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/input/replacetokens.test	(revision 16139)
@@ -0,0 +1,2 @@
+1@foo@2
+3
Index: /release-kits/shared/ant/src/etc/testcases/filters/input/stripjavacomments.test
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/input/stripjavacomments.test	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/input/stripjavacomments.test	(revision 16139)
@@ -0,0 +1,30 @@
+/*
+ * Copyright text
+ * has to be removed
+ */
+
+/**
+ * JavaDoc text about the class.
+ * has to be removed
+ */ 
+public class NormalLine {
+
+    private String withComment; // this comment should be removed
+
+    /* this comment
+     * should be
+     * removed
+     */
+    public void doNothing() {
+        // this comment should be removed
+        int i;
+        /* this comment
+           should be removed
+        */
+        int j;
+    }
+
+    private String url = "http://ant.apache.org/"; // very difficult!
+    private String url2 = "\"http://ant.apache.org/\""; // even worse
+
+}
Index: /release-kits/shared/ant/src/etc/testcases/filters/tokenfilter.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/filters/tokenfilter.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/filters/tokenfilter.xml	(revision 16139)
@@ -0,0 +1,343 @@
+<?xml version="1.0"?>
+<project default="cleanup" basedir=".">
+
+  <target name="init">
+    <mkdir dir="result" />
+  </target>
+
+  <target name="cleanup">
+    <delete dir="result"/>
+  </target>
+
+  <target name="tokenfilter">
+    <copy file="input/linecontains.test" tofile="result/file1">
+      <filterchain>
+        <tokenfilter/>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="trimignore">
+    <concat destfile="result/input">
+      Hello
+      
+      World
+    </concat>
+    <copy file="result/input" tofile="result/output" overwrite="yes">
+      <filterchain>
+        <tokenfilter delimoutput="-">
+          <trim/>
+          <ignoreblank/>
+        </tokenfilter>
+      </filterchain>
+    </copy>
+    <concat>
+      <filelist dir="." files="result/output"/>
+    </concat>
+  </target>
+
+  <target name="trimfile">
+    <concat destfile="result/trimfile">
+      This is the contents of the trimmed file.
+      This is the second line.
+      <filterchain>
+        <trim byline="no"/>
+      </filterchain>
+    </concat>
+  </target>
+
+  <target name="trimfilebyline">
+    <concat destfile="result/trimfilebyline">
+      This is the contents of the trimmed file.
+      This is the second line.
+      <filterchain>
+        <trim/>
+        <tokenfilter delimoutput="\n"/>
+      </filterchain>
+    </concat>
+  </target>
+
+  <target name="filterreplacestring">
+    <concat destfile="result/filterreplacestring">
+      This is foo bar
+      <filterchain>
+        <replacestring from="foo" to="the"/>
+        <replacestring from="bar" to="moon"/>
+      </filterchain>
+    </concat>
+  </target>
+
+  <target name="filterreplacestrings">
+    <concat>
+      foo foo foo
+      <filterchain>
+        <replacestring from="foo" to="bar"/>
+      </filterchain>
+    </concat>
+  </target>
+
+  <target name="stringtokenizer">
+    <concat destfile="result/input">
+      This is a number
+      of words
+    </concat>
+    <copy file="result/input" tofile="result/output" overwrite="yes">
+      <filterchain>
+        <tokenfilter delimoutput="#">
+          <stringtokenizer/>
+        </tokenfilter>
+      </filterchain>
+    </copy>
+    <concat>
+      <filelist dir="." files="result/output"/>
+    </concat>
+  </target>
+
+  <target name="unixlineoutput">
+    <concat destfile="result/unixlineoutput">
+      This is a number
+      of words
+      <filterchain>
+        <tokenfilter delimoutput="\n">
+          <stringtokenizer/>
+        </tokenfilter>
+      </filterchain>
+    </concat>
+  </target>
+
+  <target name="doslineoutput">
+    <concat destfile="result/doslineoutput">
+      This is a number
+      of words
+      <filterchain>
+        <tokenfilter delimoutput="\r\n">
+          <stringtokenizer/>
+        </tokenfilter>
+      </filterchain>
+    </concat>
+  </target>
+
+  <target name="filetokenizer">
+    <concat destfile="result/input">
+      This is a number
+      of words
+    </concat>
+    <copy file="result/input" tofile="result/filetokenizer">
+      <filterchain>
+        <tokenfilter>
+          <filetokenizer/>
+          <trim/>
+        </tokenfilter>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="replacestring">
+    <concat destfile="result/replacestring">
+      this is the sun
+      <filterchain>
+        <tokenfilter>
+            <replacestring from="sun" to="moon"/>
+        </tokenfilter>
+      </filterchain>
+    </concat>
+  </target>
+
+  <target name="replacestrings">
+    <concat>
+      foo foo foo
+      <filterchain>
+        <tokenfilter>
+          <replacestring from="foo" to="bar"/>
+        </tokenfilter>
+      </filterchain>
+    </concat>
+  </target>
+
+  <target name="containsstring">
+    <concat destfile="result/input">
+      this is a line contains foo
+      this line does not
+    </concat>
+    <copy file="result/input" tofile="result/containsstring">
+      <filterchain>
+        <tokenfilter>
+          <containsstring contains="foo"/>
+        </tokenfilter>
+      </filterchain>
+    </copy>
+  </target>
+
+  <!-- need to check for existance of regex -->
+  <target name="replaceregex">
+    <concat destfile="result/input">
+      hello Hello HELLO hello
+      cat Cat cat
+      Sun Sun Sun
+      WhiteSpace 	tab
+      This is a line with digits - 1234 -- there
+    </concat>
+    <copy file="result/input" tofile="result/replaceregex">
+      <filterchain>
+        <tokenfilter>
+          <replaceregex pattern="hello" replace="world" flags="gi"/>
+          <replaceregex pattern="cat" replace="dog" flags="g"/>
+          <replaceregex pattern="sun" replace="moon" flags="i"/>
+          <replaceregex pattern="WhiteSpace[ \t]+tab"
+                        replace="found WhiteSpace"/>
+          <replaceregex pattern="This is a line with dig.* ([0-9]+).*"
+                        replace="Found digits [\1]"/>
+        </tokenfilter>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="filterreplaceregex">
+    <concat destfile="result/filterreplaceregex">
+      hello Hello HELLO hello
+      <filterchain>
+        <replaceregex pattern="hello" replace="world" flags="gi"/>
+      </filterchain>
+    </concat>
+  </target>
+
+  <target name="dollermatch">
+    <concat>
+      @hello@
+      <filterchain>
+        <replaceregex pattern="@([^@]*)@" replace="${\1}"/>
+      </filterchain>
+    </concat>
+  </target>
+
+  <!-- need to check for existance of regex -->
+  <target name="containsregex">
+    <concat destfile="result/input">
+      hello world
+      this is the moon
+      World here
+    </concat>
+    <copy file="result/input" tofile="result/containsregex">
+      <filterchain>
+        <tokenfilter>
+          <containsregex pattern="(hello|world)" flags="i"/>
+        </tokenfilter>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="filtercontainsregex">
+    <concat destfile="result/filtercontainsregex">
+      hello world
+      this is the moon
+      World here
+      <filterchain>
+        <tokenfilter>
+          <containsregex pattern="(hello|world)" flags="i"/>
+        </tokenfilter>
+      </filterchain>
+    </concat>
+  </target>
+
+
+  <!-- need to check for existance of regex -->
+  <target name="containsregex2">
+    <concat destfile="result/input">
+      SUITE(TestSuite, bits);
+      here
+    </concat>
+    <copy file="result/input" tofile="result/containsregex2">
+      <filterchain>
+        <tokenfilter>
+          <containsregex
+            pattern="^ *SUITE\(.*,\s*(.*)\s*\).*"
+            replace="void register_\1();"/>
+        </tokenfilter>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="deletecharacters">
+    <concat destfile="result/deletechars">
+      This is some ### s
+      some ****
+      <filterchain>
+        <tokenfilter>
+          <deletecharacters chars="#"/>
+        </tokenfilter>
+        <deletecharacters chars="*"/>
+      </filterchain>
+    </concat>
+  </target>
+
+  <target name="scriptfilter">
+    <concat destfile="result/input">
+      hello world
+    </concat>
+    <copy file="result/input" tofile="result/scriptfilter">
+      <filterchain>
+        <tokenfilter>
+          <scriptfilter language="javascript">
+            self.setToken(self.getToken().toUpperCase());
+          </scriptfilter>
+       </tokenfilter>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="scriptfilter2">
+    <concat destfile="result/input">
+      hello moon
+    </concat>
+    <copy file="result/input" tofile="result/scriptfilter2">
+      <filterchain>
+        <scriptfilter language="javascript">
+          self.setToken(self.getToken().toUpperCase());
+        </scriptfilter>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="customtokenfilter">
+    <path id="test-classes">
+      <pathelement location="../../../../build/testcases" />
+      <pathelement path="${java.class.path}" />
+    </path>
+
+
+    <typedef 
+      name="capitalize" 
+      classname="org.apache.tools.ant.filters.TokenFilterTest$Capitalize">
+      <classpath refid="test-classes"/>
+    </typedef>
+    
+    <concat destfile="result/input">
+      hello world
+    </concat>
+
+    <copy file="result/input" tofile="result/custom">
+      <filterchain>
+        <tokenfilter>
+          <stringtokenizer/>
+          <capitalize/>
+       </tokenfilter>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="hasscript">
+    <script language="javascript">
+      i = 1;
+    </script>
+  </target>
+  
+  <target name="hasregex">
+    <concat destfile="result/replaceregexp">
+      hello world
+    </concat>
+    <replaceregexp file="result/replaceregexp"
+                  match="hello( )world"
+                  replace="bye\1world"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/abstractcvstask.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/abstractcvstask.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/abstractcvstask.xml	(revision 16139)
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+
+<project name="abstractcvstask-test" basedir="../../../../" 
+         default="all">
+
+  <property name="file" value="ant.properties.sample" />
+  <target name="all">
+    <cvs failonerror="true" command="status ${file}"/>
+    <cvs failonerror="true">
+      <commandline>
+        <argument value="up"/>
+        <argument value="-r"/>
+        <argument value="1.1"/>
+        <argument value="${file}"/>
+      </commandline>
+    </cvs>
+    <cvs failonerror="true" command="status ${file}"/>
+    <cvs failonerror="true">
+      <commandline>
+        <argument line="up -r HEAD ${file}" />
+      </commandline>
+    </cvs>
+    <cvs failonerror="true" command="status ${file}"/>
+  </target>
+
+  <target name="package-attribute">
+    <mkdir dir="tmpdir" />
+    <cvs cvsroot=":pserver:anoncvs@cvs.apache.org:/home/cvspublic"
+         package="ant/build.xml"
+         dest="tmpdir"
+         quiet="true" />
+  </target>
+
+  <target name="tag-attribute">
+    <mkdir dir="tmpdir" />
+    <cvs cvsroot=":pserver:anoncvs@cvs.apache.org:/home/cvspublic"
+         package="ant/build.xml"
+         dest="tmpdir"
+         quiet="true"
+         tag="ANT_141" />
+    <cvs cvsroot=":pserver:anoncvs@cvs.apache.org:/home/cvspublic"
+         package="ant/build.xml"
+         dest="tmpdir"
+         command="status"/>
+  </target>
+
+  <target name="cleanup">
+    <delete dir="tmpdir" />
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/ant.topleveltest.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/ant.topleveltest.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/ant.topleveltest.xml	(revision 16139)
@@ -0,0 +1,3 @@
+<project>
+  <echo>Hello world</echo>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/ant.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/ant.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/ant.xml	(revision 16139)
@@ -0,0 +1,238 @@
+<?xml version="1.0"?>
+
+<project name="ant-test" basedir="." default="test1">
+
+  <path id="inheritable">
+    <pathelement path="${java.class.path}" />
+  </path>
+
+  <target name="cleanup">
+    <delete file="test1.log" />
+    <delete file="test2.log" />
+    <delete file="ant/test3.log" />
+    <delete file="ant/test4.log" />
+  </target>
+
+  <target name="all" depends="test1,test2,test3,test4"/>
+
+  <target name="test1">
+    <ant antfile="ant.xml" dir="." target="test1"/>
+  </target>
+
+  <target name="test2">
+    <antcall/>
+  </target>
+
+  <target name="test3">
+    <antcall target="test3"/>
+  </target>
+
+  <target name="test4">
+    <antcall target=""/>
+  </target>
+
+  <target name="test4b">
+    <antcall target="does-not-exist"/>
+  </target>
+
+  <target name="test5">
+    <antcall target="dummy"/>
+  </target>
+
+  <target name="test6">
+    <ant antfile="ant.xml" dir="." target="dummy"/>
+  </target>
+
+  <target name="dummy">
+  </target>
+
+  <target name="inheritBasedir">
+    <ant antfile="ant/ant.xml" target="dummy" inheritAll="true" />
+  </target>
+
+  <target name="doNotInheritBasedir">
+    <ant antfile="ant/ant.xml" target="dummy" inheritAll="false" />
+  </target>
+
+  <target name="explicitBasedir1">
+    <ant antfile="taskdefs/ant/ant.xml" target="dummy" inheritAll="true" 
+         dir=".." />
+  </target>
+
+  <target name="explicitBasedir2">
+    <ant antfile="taskdefs/ant/ant.xml" target="dummy" inheritAll="false" 
+         dir=".." />
+  </target>
+
+  <target name="tripleCall">
+    <ant antfile="ant/ant.xml" target="callback" inheritAll="false" />
+  </target>
+
+  <target name="testInherit">
+    <ant antfile="ant/references.xml" inheritRefs="true" target="dummy" />
+  </target>
+
+  <target name="testNoInherit">
+    <ant antfile="ant/references.xml" inheritRefs="false" target="dummy" />
+  </target>
+
+  <target name="testRename">
+    <ant antfile="ant/references.xml" inheritRefs="false" target="dummy">
+      <reference refid="path" torefid="newpath" />
+    </ant>
+  </target>
+
+  <target name="testInheritPath" description="try to pass a reference to a path, which refers itself to a second path">
+    <property name="rootdir" location="."/>
+    <path id="project.classpath">
+      <pathelement location="../classes"/>
+    </path>
+    <path id="test.classpath">
+      <pathelement location="${rootdir}/test/testframework.jar"/>
+      <path refid="project.classpath"/>
+    </path>
+    <ant antfile="ant/references.xml"  target="testInheritPath">
+      <reference refid="test.classpath"/>
+    </ant>
+  </target>
+
+  <target name="testLogfilePlacement">
+    <ant antfile="ant.xml" target="dummy" output="test1.log"
+         inheritall="false" />
+    <ant antfile="ant.xml" target="dummy" output="test2.log" />
+    <ant antfile="ant.xml" target="dummy" output="test3.log"
+         inheritall="false" dir="ant" />
+    <ant antfile="ant.xml" target="dummy" output="test4.log" 
+         dir="ant" />
+  </target>
+
+  <target name="testRefid">
+    <ant antfile="ant/references.xml" inheritRefs="false" target="dummy">
+      <property name="testprop" refid="inheritable" />
+    </ant>
+  </target>
+
+  <target name="test-property-override-inheritall-start">
+    <property name="test" value="1" />
+    <ant antfile="ant.xml"
+         target="test-property-override-inheritall-level-2"
+         inheritall="true">
+      <property name="test" value="2" />
+    </ant>
+  </target>
+
+  <target name="test-property-override-inheritall-level-2">
+    <property name="test" value="3" />
+    <ant antfile="ant.xml"
+         target="test-property-override-inheritall-level-3"
+         inheritall="true">
+      <property name="test" value="4" />
+    </ant>
+  </target>
+
+  <target name="test-property-override-inheritall-level-3">
+    <property name="test" value="5" />
+    <echo message="The value of test is ${test}" />
+  </target>
+
+  <target name="test-property-override-no-inheritall-start">
+    <property name="test" value="1" />
+    <ant antfile="ant.xml"
+         target="test-property-override-no-inheritall-level-2"
+         inheritall="false">
+      <property name="test" value="2" />
+    </ant>
+  </target>
+
+  <target name="test-property-override-no-inheritall-level-2">
+    <property name="test" value="3" />
+    <ant antfile="ant.xml"
+         target="test-property-override-no-inheritall-level-3"
+         inheritall="false">
+      <property name="test" value="4" />
+    </ant>
+  </target>
+
+  <target name="test-property-override-no-inheritall-level-3">
+    <property name="test" value="5" />
+    <echo message="The value of test is ${test}" />
+  </target>
+
+  <target name="test-propertyset">
+    <property name="test1" value="1"/>
+    <property name="test2" value="2"/>
+    <propertyset id="set">
+      <propertyref name="test1"/>
+      <mapper type="glob" from="*" to="*.x"/>
+    </propertyset>
+    <ant antfile="ant.xml" target="echo-for-propertyset-test" 
+         inheritall="false">
+      <propertyset refid="set"/>
+    </ant>
+  </target>
+
+  <target name="echo-for-propertyset-test">
+    <echo>test1 is ${test1}</echo>
+    <echo>test2 is ${test2}</echo>
+    <echo>test1.x is ${test1.x}</echo>
+  </target>
+
+  <target name="infinite-loop-via-depends">
+    <antcall target="dependent"/>
+  </target>
+
+  <target name="middleman" depends="infinite-loop-via-depends"/>
+  <target name="dependent" depends="middleman"/>
+  
+  <target name="multi-same-property">
+    <ant antfile="ant.xml" target="echo-for-multi-same">
+      <property name="prop" value="one"/>
+      <property name="prop" value="two"/>
+    </ant>
+  </target>
+
+  <target name="echo-for-multi-same">
+    <echo>prop is ${prop}</echo>
+  </target>
+
+  <target name="topleveltarget">
+    <ant antfile="ant.topleveltest.xml"/>
+  </target>
+
+  <target name="multiple-property-file-children">
+    <ant target="dummy" antfile="ant.xml">
+      <property file="foo.properties"/>
+      <property file="bar.properties"/>
+    </ant>
+  </target>
+
+  <target name="blank-target">
+    <ant antfile="ant.topleveltest.xml">
+      <target name="" />
+    </ant>
+  </target>
+
+  <target name="multiple-targets">
+    <ant antfile="ant.xml">
+      <target name="ta" />
+      <target name="tb" />
+      <target name="tc" />
+    </ant>
+  </target>
+
+  <target name="multiple-targets-2">
+    <ant antfile="ant.xml">
+      <target name="tb" />
+      <target name="da" />
+    </ant>
+  </target>
+
+  <target name="ta"><echo>ta</echo></target>
+  <target name="tb" depends="da,dc"><echo>tb</echo></target>
+  <target name="tc" depends="db,dc"><echo>tc</echo></target>
+
+  <target name="da"><echo>da</echo></target>
+  <target name="db"><echo>db</echo></target>
+  <target name="dc"><echo>dc</echo></target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/ant/ant.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/ant/ant.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/ant/ant.xml	(revision 16139)
@@ -0,0 +1,16 @@
+<project name="test" default="def" basedir=".">
+
+  <target name="def">
+    <fail>This build file should only be run from within the testcase</fail>
+  </target>
+
+  <target name="dummy">
+    <echo message="${basedir}" />
+  </target>
+
+  <target name="callback">
+    <ant antfile="../ant.xml" target="dummy" inheritAll="false" />
+  </target>
+
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/ant/references.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/ant/references.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/ant/references.xml	(revision 16139)
@@ -0,0 +1,16 @@
+<project name="test" default="def" basedir=".">
+
+  <path id="no-override" />
+
+  <target name="def">
+    <fail>This build file should only be run from within the testcase</fail>
+  </target>
+
+  <target name="dummy" />
+
+  <target name="testInheritPath">
+    <pathconvert refid="test.classpath" pathsep="${line.separator}" property="myprop"/>
+    <echo>${myprop}</echo>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/antlib.current-test.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/antlib.current-test.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/antlib.current-test.xml	(revision 16139)
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+<antlib xmlns:c="ant:current">
+  <typedef name="echo2" classname="org.apache.tools.ant.taskdefs.Echo"/>
+  <presetdef name="preset.echo">
+    <typedef classname="org.apache.tools.ant.taskdefs.Echo"/>
+  </presetdef>
+  <c:preset.echo name="p"/>
+  <macrodef name="useecho2">
+    <sequential>
+      <c:echo2>Echo2 inside a macro</c:echo2>
+    </sequential>
+  </macrodef>
+</antlib>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/antlib.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/antlib.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/antlib.xml	(revision 16139)
@@ -0,0 +1,39 @@
+<?xml version="1.0"?>
+<project name="test">
+
+  <property name="testcases.dir" location="../../../../build/testcases"/>
+
+  <path id="testclasses">
+    <pathelement location="${testcases.dir}" />
+    <pathelement path="${java.class.path}" />
+  </path>
+
+  <target name="antlib.file">
+    <typedef file="test.antlib.xml"
+             classpathref="testclasses"/>
+    <mytask/>
+  </target>
+
+  <target name="antlib.resource">
+    <typedef resource="taskdefs/test.antlib.xml">
+        <classpath>
+            <!-- To load the task classes: -->
+            <path refid="testclasses"/>
+            <!-- For test.antlib.xml: -->
+            <pathelement location=".."/>
+            <!-- For test2.antlib.xml: -->
+            <pathelement location="${testcases.dir}/org/apache/tools/ant/taskdefs/test2-antlib.jar"/>
+        </classpath>
+    </typedef>
+    <mytask/>
+    <echo>-and-then-</echo>
+    <mytask2/>
+  </target>
+
+  <target name="ns.current">
+    <typedef file="antlib.current-test.xml" uri="abc"/>
+    <x:useecho2 xmlns:x="abc"/>
+    <x:preset.echo xmlns:x="abc" name="p"/>
+    <x:p xmlns:x="abc">Hello from x:p</x:p>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/antstructure.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/antstructure.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/antstructure.xml	(revision 16139)
@@ -0,0 +1,9 @@
+<?xml version="1.0"?>
+
+<project name="antstructure-test" basedir="." default="test1">
+
+  <target name="test1">
+    <antstructure/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/available.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/available.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/available.xml	(revision 16139)
@@ -0,0 +1,165 @@
+<?xml version="1.0"?>
+
+<project name="available-test" basedir="." default="test1">
+
+  <target name="test1">
+    <available/>
+  </target>
+
+  <target name="test2">
+    <available property="test"/>
+  </target>
+
+  <target name="test3">
+    <available file="test"/>
+  </target>
+
+  <target name="test4">
+    <available property="test" 
+               file="src/etc/testcases/taskdefs/this_file_does_not_exist"/>
+  </target>
+
+  <target name="test5">
+    <available property="test" 
+               file="available.xml"/>
+  </target>
+
+  <target name="test6">
+    <available property="test" 
+               resource="org/apache/tools/ant/taskdefs/this_resource_does_not_exist"/>
+  </target>
+
+  <target name="test7">
+    <available property="test" 
+               resource="org/apache/tools/ant/taskdefs/defaults.properties"/>
+  </target>
+
+  <target name="test8">
+    <available property="test" 
+               classname="org.apache.tools.ant.taskdefs.this_class_does_not_exist"/>
+  </target>
+
+  <target name="test9">
+    <available property="test" 
+               classname="org.apache.tools.ant.taskdefs.Ant"/>
+  </target>
+
+  <target name="test10">
+    <available property="test" 
+               file="available.xml"
+               resource="org/apache/tools/ant/taskdefs/defaults.properties"
+               classname="org.apache.tools.ant.taskdefs.Ant"/>
+  </target>
+  
+  <target name="test11">
+    <available property="test" 
+               file="src/etc/testcases/taskdefs/available.xml"
+               resource="org/apache/tools/ant/taskdefs/defaults.properties"
+               classname="org.apache.tools.ant.taskdefs.this_class_does_not_exist"/>
+  </target>  
+  
+  <target name="test12">
+    <available property="" 
+               file="available.xml"/>
+  </target>
+
+  <target name="test13">
+    <available property="test" 
+               file="" type="file" />
+  </target>
+
+  <target name="test13b">
+    <available property="test" 
+               file=""/>
+  </target>
+
+  <target name="test14">
+    <available property="test" 
+               resource=""/>
+  </target>
+
+  <target name="test15">
+    <available property="test" 
+               classname="org.apache.tools.ant.taskdefs.this_class_does_not_exist"/>
+  </target>
+
+  <target name="test16">
+    <available property="test" 
+               file="" type="dir"/>
+  </target>
+
+  <target name="test17">
+    <available property="test" 
+               file="../taskdefs" type="dir"/>
+  </target>
+
+  <target name="test18">
+    <available property="test" 
+               file="../this_dir_should_never_exist" type="dir"/>
+  </target>
+
+  <target name="test19">
+    <available property="test" 
+               file="available.xml" type="Foo"/>
+  </target>
+
+  <target name="test20">
+    <available property="test" ignoresystemclasses="true"
+               classname="java.awt.Graphics"/>
+  </target>
+
+  <target name="test21">
+    <available property="test" ignoresystemclasses="true"
+               classname="java.awt.Graphics">
+      <classpath>
+        <pathelement location="${java.home}/lib/rt.jar" />
+        <pathelement location="${java.home}/lib/classes.zip" />
+        <pathelement location="${java.home}/lib/graphics.jar" /> <!-- IBM JDK -->
+        <pathelement location="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Classes/classes.jar" />
+        <pathelement location="/System/Library/Frameworks/JavaVM.framework/Versions/CurrentJDK/Classes/ui.jar" />
+      </classpath>
+    </available>
+  </target>
+
+  <target name="test22">
+    <available property="test" ignoresystemclasses="false"
+               classname="java.awt.Graphics"/>
+  </target>
+
+  <target name="test23">
+    <available property="test"
+               classname="java.awt.Graphics"/>
+  </target>
+
+  <target name="test24">
+    <!-- create a dummy file and look for it -->
+    <mkdir dir="${user.dir}/test"/>
+    <echo message="package test;public class test {}" file="${user.dir}/test/test.java"/>
+    <javac srcdir="${user.dir}" includes="test/test.java"/>
+    <jar destfile="${user.dir}/test.jar" basedir="${user.dir}" includes="test/test.class"/>
+    <available property="test"
+               classname="test.test" classpath="${user.dir}/test.jar"/>
+    <delete dir="${user.dir}/test"/>
+    <delete file="${user.dir}/test.jar"/>
+  </target>
+
+  <target name="searchInPathNotThere">
+    <available file="not_there" filepath="..:optional"
+               property="test" />
+  </target>
+
+  <target name="searchInPathIsThere">
+    <available file="pvcs.xml" filepath="..:optional"
+               property="test" />
+  </target>
+
+  <target name="testDoubleBasedir">
+    <echo>testing ${basedir}${file.separator}${ant.file}</echo>
+    <fail>
+      <condition>
+        <available file="${basedir}${file.separator}${ant.file}" />
+      </condition>
+    </fail>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/bar.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/bar.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/bar.properties	(revision 16139)
@@ -0,0 +1,1 @@
+bar=Bar
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/basename.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/basename.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/basename.xml	(revision 16139)
@@ -0,0 +1,41 @@
+<?xml version="1.0"?>
+
+<project name="xxx-test" basedir="." default="test1">
+
+  <target name="test1">
+    <basename/>
+  </target>
+
+  <target name="test2">
+    <basename property="propname"/>
+  </target>
+
+  <target name="test3">
+    <basename file="filename"/>
+  </target>
+
+  <target name="test4">
+    <basename property="file.w.suf" file="${user.dir}/foo.txt"/>
+  </target>
+
+  <target name="test5">
+    <basename property="file.wo.suf" file="foo.txt" suffix="txt"/>
+  </target>
+
+  <target name="testMultipleDots">
+    <basename property="file.wo.suf" file="foo.bar.txt" suffix="txt"/>
+  </target>
+
+  <target name="testNoDots">
+    <basename property="file.wo.suf" file="foo.bartxt" suffix="txt"/>
+  </target>
+  
+  <target name="testValueEqualsSuffixWithDot">
+    <basename property="file.wo.suf" file=".txt" suffix=".txt"/>
+  </target>
+
+  <target name="testValueEqualsSuffixWithoutDot">
+    <basename property="file.wo.suf" file=".txt" suffix="txt"/>
+  </target>
+  
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/bunzip2.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/bunzip2.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/bunzip2.xml	(revision 16139)
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<project basedir="." default="cleanup">
+
+  <target name="realTest">
+    <bunzip2 src="expected/asf-logo-huge.tar.bz2" dest="asf-logo-huge.tar" />
+  </target>
+
+  <target name="cleanup">
+    <delete file="asf-logo-huge.tar" />
+    <delete file="expected/asf-logo-huge.tar" />
+  </target>
+
+  <target name="prepare">
+    <gunzip src="expected/asf-logo-huge.tar.gz"/>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/bzip2.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/bzip2.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/bzip2.xml	(revision 16139)
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<project basedir="." default="cleanup">
+
+  <target name="realTest">
+    <bzip2 src="expected/asf-logo-huge.tar" zipfile="asf-logo-huge.tar.bz2" />
+  </target>
+
+  <target name="testDateCheck">
+    <touch file="asf-logo.gif.bz2"/>
+    <bzip2 src="../asf-logo.gif" zipfile="asf-logo.gif.bz2" />
+  </target>
+
+  <target name="cleanup">
+    <delete file="asf-logo-huge.tar.bz2" />
+    <delete file="asf-logo.gif.bz2" />
+    <delete file="expected/asf-logo-huge.tar"/>
+  </target>
+
+  <target name="prepare">
+    <gunzip src="expected/asf-logo-huge.tar.gz"/>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/calltarget.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/calltarget.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/calltarget.xml	(revision 16139)
@@ -0,0 +1,83 @@
+<?xml version="1.0"?>
+<project name ="calltarget-test" default="testinheritreffileset" basedir=".">
+    <property name="tmp.dir" value="tmp.dir" />
+    <target name="setup">
+        <mkdir dir="${tmp.dir}"/>
+    </target>
+    <target name="cleanup">
+        <delete dir="${tmp.dir}" quiet="true"/>
+    </target>
+    <target name="mytarget">
+      <pathconvert property="myproperty" targetos="unix" refid="myfileset"/>
+      <echo message="myproperty=${myproperty}"/>
+    </target>
+    <target name="testinheritreffileset">
+    <!-- this testcase should show that the fileset defined here
+    can be read in the called target -->
+      <fileset dir="." id="myfileset">
+        <include name="calltarget.xml"/>
+      </fileset>
+      <antcall target="mytarget" inheritrefs="true"/>
+    </target>
+    <target name="copytest2">
+       <copy file="${tmp.dir}/copytest.in" toFile="${tmp.dir}/copytest1.out" overwrite="true">
+          <filterset refid="foo"/>
+       </copy>
+    </target>
+    <target name="testinheritreffilterset" depends="setup">
+       <echo file="${tmp.dir}/copytest.in">@@foo@@</echo>
+       <filterset id="foo" begintoken="@@" endtoken="@@">
+          <filter token="foo" value="bar"/>
+       </filterset>
+       <antcall target="copytest2" inheritrefs="true"/>
+       <copy file="${tmp.dir}/copytest.in" toFile="${tmp.dir}/copytest2.out" overwrite="true">
+          <filterset refid="foo"/>
+       </copy>
+       <loadfile srcFile="${tmp.dir}/copytest2.out" property="copytest2"/>
+       <loadfile srcFile="${tmp.dir}/copytest1.out" property="copytest1"/>
+       <condition property="success">
+           <equals arg1="${copytest1}" arg2="${copytest2}"/>
+       </condition>
+       <fail message="filterset not properly passed across by antcall" unless="success"/>
+    </target>
+
+    <property name="multi" value="DEFAULT"/>
+    <target name="multi">
+        <echo>multi is ${multi}</echo>
+    </target>
+    <target name="call-multi">
+        <antcall target="multi">
+            <param name="multi" value="SET"/>
+        </antcall>
+    </target>
+
+    <target name="blank-target">
+        <antcall>
+            <target name="" />
+        </antcall>
+    </target>
+
+    <target name="multiple-targets">
+        <antcall>
+            <target name="ta" />
+            <target name="tb" />
+            <target name="tc" />
+        </antcall>
+    </target>
+
+    <target name="multiple-targets-2">
+      <ant antfile="ant.xml">
+        <target name="tb" />
+        <target name="da" />
+      </ant>
+    </target>
+
+    <target name="ta"><echo>ta</echo></target>
+    <target name="tb" depends="da,dc"><echo>tb</echo></target>
+    <target name="tc" depends="db,dc"><echo>tc</echo></target>
+
+    <target name="da"><echo>da</echo></target>
+    <target name="db"><echo>db</echo></target>
+    <target name="dc"><echo>dc</echo></target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/checksum.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/checksum.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/checksum.xml	(revision 16139)
@@ -0,0 +1,76 @@
+<?xml version="1.0"?>
+<project default="cleanup" basedir=".">
+
+  <target name="cleanup">
+    <delete file="../asf-logo.gif.MD5" />
+    <delete file="../asf-logo.gif.md5" />
+    <delete>
+      <fileset dir="checksum">
+        <include name="**/*.MD5"/>
+      </fileset>
+    </delete>
+    <delete dir="checksum/checksums" />
+  </target>
+
+  <target name="createMd5">
+    <checksum file="../asf-logo.gif" fileext=".MD5" />
+  </target>
+
+  <target name="setProperty">
+    <checksum file="../asf-logo.gif" property="logo.MD5" />
+  </target>
+
+  <target name="verifyAsTask">
+    <copy file="expected/asf-logo.gif.md5" todir=".." />
+    <checksum file="../asf-logo.gif" fileext=".md5"
+              verifyproperty="logo.MD5" />
+
+    <copy file="checksum.xml" tofile="../asf-logo.gif.MD5"
+          overwrite="true" />
+    <checksum file="../asf-logo.gif" fileext=".MD5"
+              verifyproperty="no.logo.MD5" />
+  </target>
+
+  <target name="verifyAsCondition">
+    <copy file="expected/asf-logo.gif.md5" todir=".." />
+    <condition property="logo.MD5">
+      <checksum file="../asf-logo.gif" fileext=".md5" />
+    </condition>
+
+    <copy file="checksum.xml" tofile="../asf-logo.gif.MD5"
+          overwrite="true" />
+    <condition property="no.logo.MD5">
+      <checksum file="../asf-logo.gif" fileext=".MD5" />
+    </condition>
+  </target>
+
+  <target name="verifyFromProperty">
+    <checksum property="checksum" file="checksum.xml"/>
+    <checksum property="${checksum}" file="checksum.xml"
+              verifyproperty="verify"/>
+  </target>
+
+  <target name="verifyTotal">
+    <checksum totalproperty="total">
+      <fileset dir="${basedir}/checksum">
+        <exclude name="**/*.MD5"/>
+      </fileset>
+    </checksum>
+  </target>
+
+  <target name="verifyChecksumdir">
+    <checksum totalproperty="total" todir="${basedir}/checksum/checksums">
+      <fileset dir="${basedir}/checksum">
+        <exclude name="**/*.MD5"/>
+      </fileset>
+    </checksum>
+  </target>
+    <!-- bug report 25606 -->
+    <target name="verifyChecksumdirNoTotal">
+      <checksum todir="${basedir}/checksum/checksums">
+        <fileset dir="${basedir}/checksum">
+          <exclude name="**/*.MD5"/>
+        </fileset>
+      </checksum>
+    </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/checksum/foo/Bar
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/checksum/foo/Bar	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/checksum/foo/Bar	(revision 16139)
@@ -0,0 +1,1 @@
+Barbapapa
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/checksum/foo/zap/Eenie
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/checksum/foo/zap/Eenie	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/checksum/foo/zap/Eenie	(revision 16139)
@@ -0,0 +1,1 @@
+Meenie Minie Moe
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/classloader.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/classloader.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/classloader.xml	(revision 16139)
@@ -0,0 +1,23 @@
+<project name="classloader-test" default="main" basedir=".">
+
+  <target name="init">
+
+    <path id="myJars" >
+      <!-- both ant-junit.jar and junit.jar must be loaded from the same path -->
+      <pathelement path="${ant.home}/lib/ant-junit.jar" />
+      <pathelement path="${junit.jar}" />
+    </path>
+
+    <classloader classpathRef="myJars" 
+                 reverse="true" >
+      
+    </classloader>
+    <junit />
+  
+  </target>
+
+  <target name="main" depends="init">
+    <echo message="Found JUNIT" />
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/concat-input/A
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/concat-input/A	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/concat-input/A	(revision 16139)
@@ -0,0 +1,1 @@
+a
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/concat-input/B
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/concat-input/B	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/concat-input/B	(revision 16139)
@@ -0,0 +1,1 @@
+b
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/concat.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/concat.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/concat.xml	(revision 16139)
@@ -0,0 +1,188 @@
+<?xml version="1.0"?>
+
+<project name="concat-test" basedir="." default="test1">
+
+  <property name="tmp.file" value="concat.tmp" />
+  <property name="tmp.file.2" value="concat.tmp.2" />
+
+  <property name="world" value="World" />
+
+  <target name="cleanup">
+    <delete file="TESTDEST"/>
+    <delete file="${tmp.file}"/>
+    <delete file="${tmp.file.2}"/>
+    <delete file="concat.line4"/>
+    <delete file="concat.noeol"/>
+    <delete file="concat.linecr"/>
+    <delete file="concat.utf8"/>
+  </target>
+
+  <target name="test1">
+    <concat>
+    </concat>
+  </target>
+
+  <target name="test2">
+    <concat destfile="">Hello, ${world}!</concat>
+  </target>
+
+  <target name="test3">
+    <concat destfile="${tmp.file}">Hello, ${world}!</concat>
+  </target>
+
+  <target name="test4">
+    <concat destfile="${tmp.file.2}">
+      <fileset dir="${basedir}" includes="${tmp.file}" />
+      <filelist dir="${basedir}" files="${tmp.file},${tmp.file}" />
+    </concat>
+  </target>
+
+  <target name="test5">
+    <concat>Hello, ${world}!</concat>
+  </target>
+
+  <target name="test6">
+    <concat destfile="TESTDEST" append="true">
+      <filelist dir="${basedir}" files="thisfiledoesnotexist"/>
+    </concat>
+    <available file="TESTDEST" property="TESTDEST.was.created"/>
+    <fail message="TESTDEST created for nonexistant files"
+          if="TESTDEST.was.created"/>
+  </target>
+
+  <target name="testConcatNoNewline">
+    <concat>
+      <fileset dir="concat-input"/>
+    </concat>
+  </target>
+
+  <target name="testConcatNoNewlineEncoding">
+    <concat encoding="ASCII">
+      <fileset dir="concat-input"/>
+    </concat>
+  </target>
+
+  <target name="testPath">
+    <concat destfile="${tmp.file.2}">
+      <path path="${tmp.file}"/>
+    </concat>
+  </target>
+
+  <target name="testAppend">
+    <concat destfile="${tmp.file.2}">
+      <path path="${tmp.file}"/>
+    </concat>
+    <concat destfile="${tmp.file.2}" append="true">
+      <path path="${tmp.file}"/>
+    </concat>
+  </target>
+
+  <target name="testfilter">
+    <concat destfile="${tmp.file}">@REPLACEME@</concat>
+    <concat>
+      <path path="${tmp.file}"/>
+      <filterchain>
+        <replacetokens>
+          <token key="REPLACEME" value="REPLACED"/>
+        </replacetokens>
+      </filterchain>
+    </concat>
+  </target>
+
+  <target name="testnooverwrite">
+    <touch file="${tmp.file.2}"/>
+    <!-- concat.xml is now older than tmp.file.2
+         so the following should not do anything -->
+    <concat destfile="${tmp.file.2}" force="false">
+      <path path="concat.xml"/>
+    </concat>
+  </target>
+
+  <target name="testheaderfooter">
+    <concat>
+      <header filtering="false" trim="yes">
+        header
+      </header>
+      <path path="${tmp.file}"/>
+      <footer filtering="no">footer</footer>
+    </concat>
+  </target>
+
+  <target name="testfileheader">
+    <concat>
+      <header file="${tmp.file}"/>
+      <path path="${tmp.file}"/>
+    </concat>
+  </target>
+
+  <target name="samefile">
+    <touch file="${tmp.file}"/>
+    <concat destfile="${tmp.file}">
+      <path path="${tmp.file}"/>
+    </concat>
+  </target>
+
+  <target name="testfilterinline">
+    <concat>
+      @REPLACEME@
+      <filterchain>
+        <replacetokens>
+          <token key="REPLACEME" value="REPLACED"/>
+        </replacetokens>
+      </filterchain>
+    </concat>
+  </target>
+  
+  <target name="testmultireader">
+    <concat destfile="${tmp.file}">Hello, World
+    </concat>
+    <concat destfile="${tmp.file.2}">Bye, World
+    </concat>
+    <concat>
+      <path path="${tmp.file}"/>
+      <path path="${tmp.file}"/>
+      <path path="${tmp.file}"/>
+      <path path="${tmp.file}"/>
+      <path path="${tmp.file}"/>
+      <path path="${tmp.file}"/>
+      <path path="${tmp.file}"/>
+      <path path="${tmp.file}"/>
+      <path path="${tmp.file}"/>
+      <!-- tailfilter seems to behave a little stange, place two
+           here in case the implementation changes -->
+      <path path="${tmp.file.2}"/>
+      <path path="${tmp.file.2}"/>
+      <filterchain>
+        <tailfilter lines="2"/>
+      </filterchain>
+    </concat>
+  </target>
+  
+  <target name="create-noel">
+    <concat destfile="concat.noeol">This has no end of line</concat>
+  </target>
+
+  <target name="testfixlastline" depends="create-noel">
+    <concat destfile="concat.line4" fixlastline="yes">
+      <path path="concat.noeol"/>
+      <path path="concat.noeol"/>
+      <path path="concat.noeol"/>
+      <path path="concat.noeol"/>
+    </concat>
+  </target>
+
+  <target name="testfixlastlineeol" depends="create-noel">
+    <concat destfile="concat.linecr" fixlastline="yes" eol="mac">
+      <path path="concat.noeol"/>
+      <path path="concat.noeol"/>
+    </concat>
+  </target>
+
+  <target name="testTranscoding">
+    <concat destfile="concat.utf8"
+            encoding="ISO8859_1" outputencoding="UTF8">
+      <path path="copy/input/iso8859-1"/>
+    </concat>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/condition.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/condition.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/condition.xml	(revision 16139)
@@ -0,0 +1,385 @@
+<?xml version="1.0"?>
+
+<!-- test conditioning -->
+<project name="condition-test" basedir="." default="test1">
+
+  <target name="basic">
+    <condition property="basic">
+      <equals arg1="a" arg2="a" />
+    </condition>
+    <echo>${basic}</echo>
+  </target>
+
+  <target name="condition-incomplete">
+    <condition >
+      <equals arg1="a" arg2="a" />
+    </condition>
+  </target>  
+
+  <target name="condition-empty">
+    <condition property="condition-empty"/>
+    <echo>${condition-empty}</echo>
+  </target>  
+  
+  <target name="shortcut">
+    <property name="shortcut" value="set"/>
+    <condition property="shortcut">
+      <equals arg1="a" arg2="a" />
+    </condition>
+    <echo>${shortcut}</echo>
+  </target>
+
+  <target name="dontset">
+    <condition property="dontset">
+      <equals arg1="a" arg2="b" />
+    </condition>
+    <echo>${dontset}</echo>
+  </target>
+
+  <target name="setvalue">
+    <condition property="setvalue" value="woowoo" >
+      <equals arg1="a" arg2="a" />
+    </condition>
+    <echo>${setvalue}</echo>
+  </target>
+
+  <target name="negation">
+    <condition property="negation">
+      <not>
+        <equals arg1="a" arg2="B" />
+      </not>
+    </condition>
+    <echo>${negation}</echo>
+  </target>
+  
+  <target name="negationfalse">
+    <condition property="negationfalse">
+      <not>
+        <equals arg1="a" arg2="a" />
+      </not>
+    </condition>
+    <echo>${negationfalse}</echo>
+  </target>
+  
+  <target name="negationincomplete">
+    <condition property="negationincomplete">
+      <not />
+    </condition>
+    <echo>${negationincomplete}</echo>
+  </target>
+  
+  <target name="and">
+    <condition property="and">
+      <and>
+        <equals arg1="a" arg2="a" />
+        <equals arg1="B" arg2="B" />
+      </and>
+    </condition>
+    <echo>${and}</echo>
+  </target>
+
+  <target name="andfails">
+    <condition property="andfails">
+      <and>
+        <equals arg1="a" arg2="B" />
+        <equals arg1="B" arg2="a" />
+      </and>
+    </condition>
+    <echo>${andfails}</echo>
+  </target>  
+  
+  <target name="andincomplete">
+    <condition property="andincomplete">
+      <and>
+        <equals arg1="a" arg2="B" />
+      </and>
+    </condition>
+    <echo>${andincomplete}</echo>
+  </target>
+    
+  
+  <target name="andempty">
+    <condition property="andempty">
+      <and/>
+    </condition>
+    <echo>${andempty}</echo>
+  </target>
+    
+    
+  <target name="or">
+    <condition property="or">
+      <or>
+        <equals arg1="a" arg2="B" />
+        <equals arg1="B" arg2="B" />
+      </or>
+    </condition>
+    <echo>${or}</echo>
+  </target>
+    
+  <target name="orincomplete">
+    <condition property="orincomplete">
+      <or>
+        <equals arg1="a" arg2="a" />
+      </or>
+    </condition>
+    <echo>${orincomplete}</echo>
+  </target>
+  
+  <target name="orempty">
+    <condition property="orempty">
+      <or/>
+    </condition>
+    <echo>${orempty}</echo>
+  </target>
+  
+  <target name="orfails">
+    <condition property="orfails">
+      <or>
+        <equals arg1="a" arg2="B" />
+        <equals arg1="B" arg2="a" />
+      </or>
+    </condition>
+    <echo>${orfails}</echo>
+  </target>  
+
+  <target name="orboth">
+    <condition property="orboth">
+      <or>
+        <equals arg1="a" arg2="a" />
+        <equals arg1="B" arg2="B" />
+      </or>
+    </condition>
+    <echo>${orboth}</echo>
+  </target>   
+  
+  <target name="filesmatch-identical" >
+    <condition property="filesmatch-identical">
+        <filesmatch 
+          file1="condition.xml"
+          file2="condition.xml" />
+    </condition>
+    <echo>${filesmatch-identical}</echo>
+  </target>   
+  
+  <target name="filesmatch-incomplete" >
+    <condition property="filesmatch-incomplete">
+        <filesmatch 
+          file1="condition.xml"/>
+    </condition>
+    <echo>${filesmatch-incomplete}</echo>
+  </target> 
+  
+  <target name="filesmatch-oddsizes" >
+    <condition property="filesmatch-oddsizes">
+        <filesmatch 
+          file1="condition.xml"
+          file2="property.xml" />
+    </condition>
+    <echo>${filesmatch-oddsizes}</echo>
+  </target>   
+
+  <target name="filesmatch-existence" >
+    <condition property="filesmatch-existence">
+      <filesmatch 
+        file1="condition.xml" 
+        file2="this-file-doesnt-exist.xml" />
+    </condition>
+    <echo>${filesmatch-existence}</echo>
+  </target>   
+
+  <target name="filesmatch-different">
+    <echo file="match1.txt" message="012345676890" />
+    <echo file="match2.txt" message="012345676889" />
+    <condition property="filesmatch-different">
+      <filesmatch 
+        file1="match1.txt" 
+        file2="match2.txt" />
+    </condition>
+    <echo>${filesmatch-different}</echo>
+  </target>   
+
+  
+  <target name="filesmatch-match" >
+    <echo file="match3.txt" message="012345676890" />
+    <echo file="match4.txt" message="012345676890" />
+    <condition property="filesmatch-match">
+      <filesmatch 
+        file1="match3.txt" 
+        file2="match4.txt" />
+    </condition>
+    <echo>${filesmatch-match}</echo>
+  </target>   
+
+  <target name="filesmatch-different-sizes">
+    <echo file="match5.txt" message="012345676890" />
+    <echo file="match6.txt" message="0123456768" />
+    <condition property="filesmatch-different-sizes">
+      <filesmatch 
+        file1="match5.txt" 
+        file2="match6.txt" />
+    </condition>
+    <echo>${filesmatch-different-sizes}</echo>
+  </target>   
+
+  <target name="filesmatch-different-onemissing">
+    <condition property="filesmatch-different-sizes">
+      <filesmatch 
+        file1="condition.xml" 
+        file2="missing-file.txt" />
+    </condition>
+    <echo>${filesmatch-different-onemissing}</echo>
+  </target>   
+  
+  <target name="contains" >
+    <condition property="contains">
+      <contains 
+        string="abcd" 
+        substring="cd" />
+    </condition>
+    <echo>${contains}</echo>
+  </target> 
+  
+  <target name="contains-doesnt" >
+    <condition property="contains-doesnt">
+      <contains 
+        string="abcd" 
+        substring="CD" />
+    </condition>
+    <echo>${contains-doesnt}</echo>
+  </target> 
+  
+  <target name="contains-anycase" >
+    <condition property="contains-anycase">
+      <contains casesensitive="false"
+        string="abcd" 
+        substring="CD" />
+    </condition>
+    <echo>${contains-anycase}</echo>
+  </target> 
+  
+  <target name="contains-incomplete1" >
+    <condition property="contains-incomplete1">
+      <contains 
+        string="abcd" />
+    </condition>
+    <echo>${contains-incomplete1}</echo>
+  </target> 
+
+  <target name="contains-incomplete2" >
+    <condition property="contains-incomplete2">
+      <contains 
+        substring="CD" />
+    </condition>
+    <echo>${contains-incomplete2}</echo>
+  </target> 
+
+  <target name="istrue" >
+    <property name="t" value="true" />
+    <property name="o" value="o" />
+    <property name="n" value="n" />
+    <condition property="istrue">
+      <and>
+        <istrue value="${t}" />
+        <istrue value="TRUE" />
+        <istrue value="yes" />
+        <istrue value="YeS" />
+        <istrue value="on" />
+        <istrue value="${o}${n}" />
+      </and>
+    </condition>
+    <echo>${istrue}</echo>
+  </target> 
+
+  <target name="istrue-not" >
+    <condition property="istrue-not">
+      <istrue 
+        value="this sentence is true" />
+    </condition>
+    <echo>${istrue-not}</echo>
+  </target> 
+
+  <target name="istrue-false" >
+    <condition property="istrue-false">
+      <or>
+        <istrue value="false" />
+        <istrue value="" />
+      </or>
+    </condition>
+    <echo>${istrue-false}</echo>
+  </target> 
+  
+  <target name="istrue-incomplete" >
+    <condition property="istrue-incomplete">
+      <istrue />
+    </condition>
+    <echo>${istrue-incomplete}</echo>
+  </target>
+  
+  
+  <target name="isfalse-true" >
+    <property name="t" value="true" />
+    <condition property="isfalse-true">
+      <isfalse 
+        value="${t}" />
+    </condition>
+    <echo>${isfalse-true}</echo>
+  </target> 
+
+  <target name="isfalse-not" >
+    <condition property="isfalse-not">
+      <isfalse 
+        value="this sentence is true" />
+    </condition>
+    <echo>${isfalse-not}</echo>
+  </target> 
+
+  <target name="isfalse-false" >
+    <condition property="isfalse-false">
+      <isfalse 
+        value="false" />
+    </condition>
+    <echo>${isfalse-false}</echo>
+  </target> 
+  
+  <target name="isfalse-incomplete" >
+    <condition property="isfalse-incomplete">
+      <isfalse />
+    </condition>
+    <echo>${isfalse-incomplete}</echo>
+  </target>
+
+  <target name="testElse">
+    <condition property="unset" value="foo">
+      <or />
+    </condition>
+    <condition property="value" value="foo" else="bar">
+      <and />
+    </condition>
+    <condition property="else" value="foo" else="bar">
+      <or />
+    </condition>
+    <fail>
+      <condition>
+        <or>
+          <isset property="unset" />
+          <not>
+            <and>
+              <equals arg1="${value}" arg2="foo" />
+              <equals arg1="${else}" arg2="bar" />
+            </and>
+          </not>
+        </or>
+      </condition>
+    </fail>
+  </target>
+  
+  <target name="cleanup" >
+    <delete file="match1.txt" />
+    <delete file="match2.txt" />
+    <delete file="match3.txt" />
+    <delete file="match4.txt" />
+    <delete file="match5.txt" />
+    <delete file="match6.txt" />
+  </target>
+   
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/conditions/isreference.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/conditions/isreference.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/conditions/isreference.xml	(revision 16139)
@@ -0,0 +1,43 @@
+<project default="nope">
+  <path id="global-path-id"/>
+  <echo id="echo-id"/>
+
+  <target name="nope">
+    <fail>This build file should be run by a testcase</fail>
+  </target>
+
+  <target name="define">
+    <path id="target-path-id"/>
+  </target>
+
+  <target name="basic" depends="define">
+    <condition property="global-path">
+      <isreference refid="global-path-id"/>
+    </condition>
+    <condition property="target-path">
+      <isreference refid="target-path-id"/>
+    </condition>
+  </target>
+
+  <target name="isreference-incomplete">
+    <condition property="foo">
+      <isreference/>
+    </condition>
+  </target>
+
+  <target name="type">
+    <condition property="global-path">
+      <isreference refid="global-path-id" type="path"/>
+    </condition>
+    <condition property="global-path-as-fileset">
+      <isreference refid="global-path-id" type="fileset"/>
+    </condition>
+    <condition property="global-path-as-foo">
+      <isreference refid="global-path-id" type="foo"/>
+    </condition>
+    <condition property="global-echo">
+      <isreference refid="echo-id" type="echo"/>
+    </condition>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/copy.filterset
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/copy.filterset	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/copy.filterset	(revision 16139)
@@ -0,0 +1,1 @@
+This is the @TITLE@.
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/copy.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/copy.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/copy.xml	(revision 16139)
@@ -0,0 +1,141 @@
+<?xml version="1.0"?>
+
+<project name="copy-test" basedir="." default="test1">
+
+  <target name="test1">
+    <copy file="copy.xml" tofile="copytest1.tmp" />
+  </target>
+ 
+  <target name="test2">
+    <copy file="copy.xml" todir="copytest1dir" overwrite="true"/>
+  </target>
+
+  <target name="filtertest">
+    <!-- check fix for bugzilla 23154 -->
+    <delete quiet="yes" file="copy.filter.out"/>
+    <delete quiet="yes" file="copy.filter.inp"/>
+    <concat destfile="copy.filter.inp">
+PRODUCT_VERSION=6.2.1.4
+PRODUCT_BUILD=6.5 (BLD_65036)
+PRODUCT_VERSION=6.2.1.4
+PRODUCT_BUILD=6.5 (BLD_65036)
+    </concat>
+    <copy file="copy.filter.inp" tofile="copy.filter.out">
+      <filterset begintoken="6" endtoken="4">
+        <filter token=".2.1." value="2.6.4" />
+      </filterset>
+    </copy>
+    <concat><path path="copy.filter.out"/></concat>
+  </target>
+
+  <target name="infinitetest">
+    <delete quiet="yes" file="copy.filter.out"/>
+    <delete quiet="yes" file="copy.filter.inp"/>
+    <concat destfile="copy.filter.inp">
+a=b=
+    </concat>
+    <copy file="copy.filter.inp" tofile="copy.filter.out">
+      <filterset begintoken="=" endtoken="=">
+        <filter token="b" value="=b="/>
+      </filterset>
+    </copy>
+    <concat><path path="copy.filter.out"/></concat>
+  </target>
+
+  <target name="test3">
+    <!-- create an empty file -->
+    <touch file="copytest3.tmp"/>
+    <!--wait -->
+    <sleep seconds="4"/>
+    <!-- copy a different file to two places --> 
+    <copy file="copy.xml" tofile="copytest3a.tmp" overwrite="true"/>
+    <copy file="copy.xml" tofile="copytest3b.tmp" overwrite="true"/>
+    <!--wait -->
+    <sleep seconds="4"/>
+    <!-- copy an old file onto a newer file (should not work) -->
+    <copy file="copytest3.tmp" tofile="copytest3b.tmp" />
+    <!-- copy an older file onto a new one, should succeed -->
+    <copy file="copytest3.tmp" tofile="copytest3c.tmp"
+      overwrite="true"
+      preservelastmodified="true" />
+    <!-- copy a newer file onto an older one (should work) -->
+    <copy file="copytest3a.tmp" tofile="copytest3.tmp" 
+      preservelastmodified="true" />
+    <!-- expected state :
+      3a.tmp==3.tmp==copy.xml
+      timeof(3a.tmp)==timeof(3.tmp)==now()-4
+      sizeof(3c)==0
+      timeof(3c.tmp)<timeof(3a.tmp);
+      3b.tmp==copy.xml
+    -->
+  </target>
+  
+  <target name="test_single_file_fileset">
+    <copy tofile="copytest_single_file_fileset.tmp">
+      <fileset dir="." includes="copy.xml"/>
+    </copy>
+  </target>
+  
+  <target name="testFilterSet">
+    <copy file="copy.filterset" tofile="copy.filterset.tmp">
+      <filterset>
+        <filter token="TITLE" value="Apache Ant Project"/>
+      </filterset>
+    </copy>
+  </target>
+
+  <target name="testFilterChain">
+    <copy file="copy.filterset" tofile="copy.filterchain.tmp">
+      <filterchain>
+        <replacetokens>
+          <token key="TITLE" value="Apache Ant Project"/>
+        </replacetokens>
+      </filterchain>
+    </copy>
+  </target>
+
+  <target name="testTranscoding">
+    <copy file="copy/input/iso8859-1" tofile="copytest1.tmp"
+          encoding="ISO8859_1" outputencoding="UTF8"/>
+  </target>
+
+  <target name="testMissingFileIgnore">
+    <copy file="not-there" tofile="copytest1.tmp"
+          failonerror="false"/>
+  </target>
+
+  <target name="testMissingFileBail">
+    <copy file="not-there" tofile="copytest1.tmp"
+          failonerror="true"/>
+  </target>
+
+  <target name="testMissingDirIgnore">
+    <copy todir="copytest1dir" failonerror="false">
+      <fileset dir="not-there"/>
+    </copy>
+  </target>
+
+  <target name="testMissingDirBail">
+    <copy todir="copytest1dir" failonerror="true">
+      <fileset dir="not-there"/>
+    </copy>
+  </target>
+
+    
+  
+  <target name="cleanup"> 
+    <delete file="copytest1.tmp"/>
+    <delete file="copytest3.tmp"/>
+    <delete file="copytest3a.tmp"/>
+    <delete file="copytest3b.tmp"/>
+    <delete file="copytest3c.tmp"/>
+    <delete file="copytest_single_file_fileset.tmp"/>
+    <delete file="copy.filterset.tmp"/>
+    <delete file="copy.filterchain.tmp"/>
+    <delete dir="copytest1dir"/>
+    <delete quiet="yes" file="copy.filter.out"/>
+    <delete quiet="yes" file="copy.filter.inp"/>
+  </target>
+
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/copydir.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/copydir.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/copydir.xml	(revision 16139)
@@ -0,0 +1,37 @@
+<?xml version="1.0"?>
+
+<project name="copydir-test" basedir="." default="test1">
+
+  <target name="test1">
+    <copydir/>
+  </target>
+
+  <target name="test2">
+    <copydir src=""/>
+  </target>
+
+  <target name="test3">
+    <copydir dest=""/>
+  </target>
+
+  <target name="test4">
+    <copydir src="." 
+             dest="."/>
+  </target>
+
+  <target name="test5">
+    <mkdir dir="../taskdefs.tmp" />
+    <copydir src="." 
+             dest="../taskdefs.tmp"/>
+  </target>
+
+  <target name="test6">
+    <copydir src="." 
+             dest="template.xml"/>
+  </target>
+
+  <target name="cleanup">
+    <delete dir="../taskdefs.tmp" />
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/copyfile.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/copyfile.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/copyfile.xml	(revision 16139)
@@ -0,0 +1,38 @@
+<?xml version="1.0"?>
+
+<project name="copyfile-test" basedir="." default="test1">
+
+  <target name="test1">
+    <copyfile/>
+  </target>
+
+  <target name="test2">
+    <copyfile src=""/>
+  </target>
+
+  <target name="test3">
+    <copyfile dest=""/>
+  </target>
+
+  <target name="test4">
+    <copyfile src="template.xml" 
+             dest="template.xml"/>
+  </target>
+
+  <target name="test5">
+    <copyfile src="copyfile.xml" 
+             dest="copyfile.tmp"/>
+  </target>
+
+  <target name="test6">
+    <delete file="testdir"/>
+    <mkdir dir="testdir" />
+    <copyfile src="copyfile.xml" 
+              dest="testdir"
+              forceoverwrite="true" />
+  </target>
+
+  <target name="cleanup">
+    <delete dir="testdir" />
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/cvspass.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/cvspass.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/cvspass.xml	(revision 16139)
@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+
+<project name="cvspass-test" basedir="." default="test1">
+
+  <taskdef name="cvspass" classname="org.apache.tools.ant.taskdefs.CVSPass"/>
+
+  <target name="test1">
+    <cvspass />
+  </target>
+ 
+  <target name="test2">
+    <cvspass
+      cvsroot=":pserver:anoncvs@jakarta.apache.org:/home/cvspublic"
+      passfile="testpassfile.tmp"
+    />
+  </target>
+ 
+  <!-- testPassFile -->
+  <target name="test3">
+    <cvspass
+      cvsroot=":pserver:anoncvs@jakarta.apache.org:/home/cvspublic"
+      password="anoncvs"
+      passfile="testpassfile.tmp"
+    />
+  </target>
+
+  <!-- testPassFileDuplicateEntry -->
+  <target name="test4">
+    <cvspass
+      cvsroot=":pserver:anoncvs@jakarta.apache.org:/home/cvspublic"
+      password="anoncvs"
+      passfile="testpassfile.tmp"
+    />
+    <cvspass
+      cvsroot=":pserver:anoncvs@jakarta.apache.org:/home/cvspublic"
+      password="anoncvs"
+      passfile="testpassfile.tmp"
+    />
+    <cvspass
+      cvsroot=":pserver:guest@cvs.tigris.org:/cvs"
+      password="guest"
+      passfile="testpassfile.tmp"
+    />
+  </target>
+
+  <!-- testPassFileMultipleEntry -->
+  <target name="test5">
+    <cvspass
+      cvsroot=":pserver:anoncvs@jakarta.apache.org:/home/cvspublic"
+      password="anoncvs"
+      passfile="testpassfile.tmp"
+    />
+    <cvspass
+      cvsroot=":pserver:anoncvs@xml.apache.org:/home/cvspublic"
+      password="anoncvs"
+      passfile="testpassfile.tmp"
+    />
+    <cvspass
+      cvsroot=":pserver:guest@cvs.tigris.org:/cvs"
+      password="guest"
+      passfile="testpassfile.tmp"
+    />
+  </target>
+
+  <target name="cleanup"> 
+    <delete file="testpassfile.tmp"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/defaultexcludes.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/defaultexcludes.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/defaultexcludes.xml	(revision 16139)
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+
+<project name="echo-test" basedir="." default="test1">
+
+  <target name="cleanup">
+    <defaultexcludes default="true"/>
+  </target>
+
+  <target name="test1">
+    <defaultexcludes echo="true"/>
+  </target>
+
+  <target name="test2">
+    <defaultexcludes default="true" add="foo" echo="true"/>
+  </target>
+
+  <target name="test3">
+    <defaultexcludes default="true" remove="**/CVS" echo="true"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/delete.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/delete.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/delete.xml	(revision 16139)
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<project name="delete-test" basedir="." default="test1">
+
+  <target name="test1">
+    <delete/>
+  </target>
+
+  <target name="test2">
+    <copydir src="." 
+             dest="taskdefs.tmp"/>
+    <delete file="taskdefs.tmp"/>
+  </target>
+
+  <target name="test4">
+    <copydir src="." 
+             dest="taskdefs.tmp"/>
+    <delete dir="taskdefs.tmp"/>
+  </target>
+
+  <target name="cleanup">
+    <delete dir="taskdefs.tmp" />
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/deltree.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/deltree.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/deltree.xml	(revision 16139)
@@ -0,0 +1,13 @@
+<?xml version="1.0"?>
+
+<project name="deltree-test" basedir="." default="test1">
+
+  <target name="test1">
+    <deltree/>
+  </target>
+
+  <target name="test2">
+    <deltree dir="taskdefs.tmp"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/dependset.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/dependset.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/dependset.xml	(revision 16139)
@@ -0,0 +1,44 @@
+<?xml version="1.0"?>
+
+<project name="dependset-test" basedir="." default="test1">
+
+  <target name="test1">
+    <dependset/>
+  </target>
+
+  <target name="test2">
+    <dependset>
+      <srcfilelist dir="." includes="test2.tmp"/>
+    </dependset>
+  </target>
+
+  <target name="test3">
+    <dependset>
+      <targetfileset dir="." files="test3.tmp"/>
+    </dependset>
+  </target>
+
+  <target name="test4">
+    <touch file="test4.tmp"/>
+    <dependset>
+      <srcfilelist dir="." files="test4.tmp"/>
+      <targetfileset dir="." includes="i-do-not-exist"/>
+    </dependset>
+  </target>
+
+  <target name="test5">
+    <touch file="older.tmp"/>
+    <sleep seconds="3" />
+    <touch file="newer.tmp"/>
+    <dependset>
+      <srcfilelist dir="." files="newer.tmp"/>
+      <targetfilelist dir="." files="older.tmp"/>
+    </dependset>
+  </target>
+  
+  <target name="cleanup"> 
+    <delete file="test4.tmp"/>
+    <delete file="older.tmp"/>
+    <delete file="newer.tmp"/>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/dirname.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/dirname.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/dirname.xml	(revision 16139)
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<project name="xxx-test" basedir="." default="test1">
+
+  <target name="test1">
+    <dirname/>
+  </target>
+
+  <target name="test2">
+    <dirname property="propname"/>
+  </target>
+
+  <target name="test3">
+    <dirname file="filename"/>
+  </target>
+
+  <target name="test4">
+    <dirname property="local.dir" file="/usr/local/foo.txt"/>
+  </target>
+
+  <target name="test5">
+    <dirname property="base.dir" file="foo.txt"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/dynamictask.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/dynamictask.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/dynamictask.xml	(revision 16139)
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+
+<project name="dynamic-test" default="simple">
+
+  <path id="testclasses">
+    <pathelement location="../../../../build/testcases" />
+    <pathelement path="${java.class.path}" />
+  </path>
+
+  <target name="simple">
+    <taskdef name="dyna"
+             classname="org.apache.tools.ant.taskdefs.DynamicTask">
+      <classpath refid="testclasses" />
+    </taskdef>
+    <dyna prop1="1" prop2="2">
+      <sub prop3="3"/>
+      <anything prop4="4"/>
+    </dyna>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/echo.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/echo.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/echo.xml	(revision 16139)
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<project name="echo-test" basedir="." default="test1">
+
+  <target name="test1">
+    <echo/>
+  </target>
+
+  <target name="test2">
+    <echo message="OUTPUT OF ECHO"/>
+  </target>
+
+  <target name="test3">
+    <echo>
+    This 
+    is
+    a 
+    multiline
+    message
+    </echo>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/email/mail.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/email/mail.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/email/mail.xml	(revision 16139)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<project name="mail-test" basedir="." default="test1">
+
+  <target name="test1">
+    <!-- this test is supposed to bring a build exception because user and password is not allowed with plain encoding -->
+    <mail host="localhost" port="25" from="joe@abc.com" to="laura@xyz.com" subject="hello" encoding="plain" user="joe" password="secret">
+        <message>
+            Hi Laura, how are you doing ?
+        </message>
+    </mail>
+  </target>
+  <target name="test2">
+      <!-- this test is supposed to bring a build exception because SSL is not allowed with plain encoding -->
+      <mail host="localhost" port="465" from="joe@abc.com" to="laura@xyz.com" subject="hello" encoding="plain" ssl="true">
+          <message>
+              Hi Laura, how are you doing ?
+          </message>
+      </mail>
+    </target>
+
+
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/exec/apply.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/exec/apply.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/exec/apply.xml	(revision 16139)
@@ -0,0 +1,438 @@
+<project name="apply-test" basedir=".">
+    <target name="init">
+        <property environment="env"/>
+        <!-- UNIX -->
+        <available file="sh" filepath="${env.PATH}" property="sh.executable"/>
+        <!-- CYGWIN -->
+        <available file="sh.exe" filepath="${env.PATH}" property="sh.exe.executable"/>
+        <condition property="test.can.run">
+            <or>
+                <isset property="sh.executable"/>
+                <isset property="sh.exe.executable"/>
+            </or>
+        </condition>
+        <!-- UNIX -->
+        <available file="sed" filepath="${env.PATH}" property="sed.executable"/>
+        <!-- CYGWIN -->
+        <available file="sed.exe" filepath="${env.PATH}" property="sed.exe.executable"/>
+        <condition property="sed.can.run">
+            <or>
+                <isset property="sed.executable"/>
+                <isset property="sed.exe.executable"/>
+            </or>
+        </condition>
+        <!-- UNIX -->
+        <available file="echo" filepath="${env.PATH}" property="echo.executable"/>
+        <!-- CYGWIN -->
+        <available file="echo.exe" filepath="${env.PATH}" property="echo.exe.executable"/>
+        <condition property="echo.can.run">
+            <or>
+                <isset property="echo.executable"/>
+                <isset property="echo.exe.executable"/>
+            </or>
+        </condition>
+    </target>
+
+    <target name="xyz">
+        <echo file="x">s/x/blah/g${line.separator}</echo>
+        <echo file="y">s/y/blah/g${line.separator}</echo>
+        <echo file="z">s/z/blah/g${line.separator}</echo>
+        <fileset id="xyz" dir="${basedir}" includes="x,y,z" />
+        <filelist id="xyzlist" dir="${basedir}" files="x,y,z" />
+    </target>
+
+    <target name="no-redirect" depends="init,xyz" if="test.can.run">
+        <apply executable="sh">
+            <arg value="parrot.sh"/>
+            <fileset refid="xyz" />
+        </apply>
+    </target>
+
+    <target name="redirect1" depends="init,xyz" if="test.can.run">
+        <apply executable="sh" output="redirect.out" append="true">
+            <arg value="parrot.sh"/>
+            <fileset refid="xyz" />
+        </apply>
+    </target>
+
+    <target name="redirect2" depends="init,xyz" if="test.can.run">
+        <apply executable="sh" output="redirect.out"
+               error="redirect.err" append="true">
+            <arg value="parrot.sh"/>
+            <fileset refid="xyz" />
+        </apply>
+    </target>
+
+    <target name="redirect3" depends="init,xyz" if="test.can.run">
+        <apply executable="sh" logerror="true" append="true"
+              output="redirect.out" outputproperty="redirect.out">
+            <arg value="parrot.sh"/>
+            <fileset refid="xyz" />
+        </apply>
+    </target>
+
+    <target name="redirect4" depends="init,xyz" if="test.can.run">
+        <apply executable="sh" append="true"
+              error="redirect.err" errorproperty="redirect.err"
+              output="redirect.out" outputproperty="redirect.out">
+            <arg value="parrot.sh"/>
+            <fileset refid="xyz" />
+        </apply>
+    </target>
+
+    <target name="redirect5" depends="init,xyz" if="sed.can.run">
+        <apply executable="sed" inputstring="x y z${line.separator}" append="true"
+              error="redirect.err" errorproperty="redirect.err"
+              output="redirect.out" outputproperty="redirect.out">
+            <arg value="-f" />
+            <fileset refid="xyz" />
+        </apply>
+    </target>
+
+    <target name="redirect6" depends="init,xyz" if="sed.can.run">
+        <echo file="redirect.in">x y z${line.separator}</echo>
+        <apply executable="sed" input="redirect.in" append="true"
+              error="redirect.err" errorproperty="redirect.err"
+              output="redirect.out" outputproperty="redirect.out">
+            <arg value="-f" />
+            <filelist refid="xyzlist" />
+        </apply>
+    </target>
+
+    <target name="redirect7" depends="init,xyz" if="sed.can.run">
+        <apply executable="sed" inputstring="x y z${line.separator}"
+               error="redirect.err" output="redirect.out"
+               outputproperty="redirect.out">
+            <arg value="-f" />
+            <fileset refid="xyz" />
+        </apply>
+    </target>
+    
+    <target name="redirector1" description="fail"
+            depends="init,xyz" if="test.can.run">
+        <apply executable="sh">
+            <arg value="parrot.sh"/>
+            <fileset refid="xyz" />
+            <redirector output="redirector.out" />
+            <redirector output="whocares" />
+        </apply>
+    </target>
+
+    <target name="redirector2" depends="init,xyz" if="test.can.run">
+        <apply executable="sh">
+            <arg value="parrot.sh"/>
+            <fileset refid="xyz" />
+            <redirector output="redirector.out" append="true" />
+        </apply>
+    </target>
+
+    <target name="redirector3" depends="init,xyz" if="test.can.run">
+        <apply executable="sh">
+            <arg value="parrot.sh"/>
+            <fileset refid="xyz" />
+            <redirector append="true"
+                        output="redirector.out" error="redirector.err" />
+        </apply>
+    </target>
+
+    <target name="redirector4" depends="init,xyz" if="test.can.run">
+        <apply executable="sh">
+            <arg value="parrot.sh"/>
+            <fileset refid="xyz" />
+            <redirector output="redirector.out" logerror="true"
+                        append="true" outputproperty="redirector.out" />
+        </apply>
+    </target>
+
+    <target name="redirector5" depends="init,xyz" if="test.can.run">
+        <apply executable="sh">
+            <redirector error="redirector.err" errorproperty="redirector.err"
+              output="redirector.out" outputproperty="redirector.out"
+              append="true" />
+            <arg value="parrot.sh"/>
+            <fileset refid="xyz" />
+        </apply>
+    </target>
+
+    <target name="redirector6" depends="init,xyz" if="test.can.run">
+        <apply executable="sh">
+            <redirector append="true" outputproperty="redirector.out"
+                        errorproperty="redirector.err">
+                <outputmapper type="merge" to="redirector.out" />
+                <errormapper type="merge" to="redirector.err" />
+            </redirector>
+            <arg value="parrot.sh" />
+            <filelist refid="xyzlist" />
+        </apply>
+    </target>
+
+    <target name="redirector7" depends="init,xyz" if="test.can.run">
+        <apply executable="sh">
+            <redirector append="true" outputproperty="redirector.out"
+                        errorproperty="redirector.err">
+                <outputmapper type="merge" to="redirector.out" />
+                <errormapper type="merge" to="redirector.err" />
+                <errorfilterchain>
+                    <replacestring from="err" to="ERROR!!!" />
+                </errorfilterchain>
+            </redirector>
+            <arg value="parrot.sh" />
+            <fileset refid="xyz" />
+        </apply>
+    </target>
+
+    <target name="redirector8" depends="init,xyz" if="sed.can.run">
+        <echo file="redirector.in">x y z${line.separator}</echo>
+        <apply executable="sed">
+            <redirector append="true" outputproperty="redirector.out"
+                        errorproperty="redirector.err">
+                <inputmapper type="merge" to="redirector.in" />
+                <outputmapper type="merge" to="redirector.out" />
+                <errormapper type="merge" to="redirector.err" />
+            </redirector>
+            <arg value="-f" />
+            <fileset refid="xyz" />
+        </apply>
+    </target>
+
+    <target name="redirector9" depends="init,xyz" if="sed.can.run">
+        <echo file="redirector.in">x before y before z${line.separator}</echo>
+        <apply executable="sed">
+            <redirector outputproperty="redirector.out"
+                        errorproperty="redirector.err" append="true">
+                <inputfilterchain>
+                    <replacestring from="before" to="after" />
+                </inputfilterchain>
+                <inputmapper type="merge" to="redirector.in" />
+                <outputmapper type="merge" to="redirector.out" />
+                <errormapper type="merge" to="redirector.err" />
+            </redirector>
+            <arg value="-f" />
+            <fileset refid="xyz" />
+        </apply>
+    </target>
+
+    <target name="redirector10" depends="init,xyz" if="sed.can.run">
+        <echo file="redirector.in">x before y before z${line.separator}</echo>
+        <apply executable="sed">
+            <redirector outputproperty="redirector.out"
+                        errorproperty="redirector.err" append="true">
+                <outputfilterchain>
+                    <replacestring from="before" to="after" />
+                </outputfilterchain>
+                <outputmapper type="merge" to="redirector.out" />
+                <errormapper type="merge" to="redirector.err" />
+            </redirector>
+            <arg value="-f" />
+            <srcfile />
+            <arg value="redirector.in"/>
+            <filelist refid="xyzlist" />
+        </apply>
+    </target>
+
+    <target name="redirector11" depends="init,xyz" if="sed.can.run">
+        <apply executable="sed">
+            <redirector outputproperty="redirector.out"
+                        errorproperty="redirector.err"
+                        inputstring="x before y before z${line.separator}"
+                        append="true">
+                <inputfilterchain>
+                    <replacestring from="before" to="after" />
+                </inputfilterchain>
+                <outputmapper type="merge" to="redirector.out" />
+                <errormapper type="merge" to="redirector.err" />
+            </redirector>
+            <arg value="-f" />
+            <fileset refid="xyz" />
+        </apply>
+    </target>
+
+    <target name="redirector12" depends="init,xyz" if="sed.can.run">
+        <echo file="redirector.in">x before y before z${line.separator}</echo>
+        <apply executable="sed" output="redirector.out" error="redirector.err">
+            <redirector outputproperty="redirector.out"
+                        errorproperty="redirector.err" append="true">
+                <outputfilterchain>
+                    <replacestring from="before" to="after" />
+                </outputfilterchain>
+                <outputmapper type="glob" from="nomatch" to="nomatchout" />
+                <errormapper type="glob" from="nomatch" to="nomatcherr" />
+            </redirector>
+            <arg value="-f" />
+            <srcfile />
+            <arg value="redirector.in"/>
+            <filelist refid="xyzlist" />
+        </apply>
+    </target>
+
+    <target name="redirector13" depends="init,xyz" if="test.can.run">
+        <apply executable="sh">
+            <redirector>
+                <outputfilterchain>
+                    <replacestring from="out" to="OUTPUT???" />
+                </outputfilterchain>
+                <errorfilterchain>
+                    <replacestring from="err" to="ERROR!!!" />
+                </errorfilterchain>
+            </redirector>
+            <arg value="parrot.sh" />
+            <fileset refid="xyz" />
+        </apply>
+    </target>
+
+    <target name="redirector14" depends="init,xyz" if="sed.can.run">
+        <echo file="redirector.in">z before y before x${line.separator}</echo>
+        <apply executable="sed">
+            <redirector append="true"
+                        inputstring="x before y before z${line.separator}">
+                <outputfilterchain>
+                    <replacestring from="before" to="after" />
+                </outputfilterchain>
+                <inputmapper type="glob" from="x" to="redirector.in" />
+                <outputmapper type="glob" from="y" to="redirector.out" />
+                <errormapper type="glob" from="z" to="redirector.err" />
+            </redirector>
+            <arg value="-f" />
+            <fileset refid="xyz" />
+        </apply>
+    </target>
+
+    <target name="redirector14b" depends="init,xyz" if="sed.can.run">
+        <apply executable="sed">
+            <redirector append="true"
+                        inputstring="x before y before z${line.separator}">
+                <outputfilterchain>
+                    <replacestring from="before" to="after" />
+                </outputfilterchain>
+                <inputmapper type="glob" from="x" to="redirector.in" />
+                <outputmapper type="glob" from="y" to="redirector.out" />
+                <errormapper type="glob" from="z" to="redirector.err" />
+            </redirector>
+            <arg value="-f" />
+            <fileset file="y" />
+        </apply>
+    </target>
+
+    <target name="pad">
+        <condition property="pad" value="">
+            <or>
+                <not>
+                    <os family="dos" />
+                </not>
+                <not>
+                    <or>
+                        <equals arg1="${ant.java.version}" arg2="1.1" />
+                        <equals arg1="${ant.java.version}" arg2="1.2" />
+                    </or>
+                </not>
+            </or>
+        </condition>
+
+        <condition property="pad" value=" ">
+            <and>
+                <os family="dos" />
+                <or>
+                    <equals arg1="${ant.java.version}" arg2="1.1" />
+                    <equals arg1="${ant.java.version}" arg2="1.2" />
+                </or>
+            </and>
+        </condition>
+
+    </target>
+
+    <target name="ignoremissing" depends="init,xyz,pad" if="echo.can.run">
+        <filelist id="xylist" dir="${basedir}" files="x,y" />
+        <delete file="z" />
+
+        <pathconvert property="xy" pathsep="${pad}${line.separator}">
+            <path>
+                <filelist refid="xylist" />
+            </path>
+        </pathconvert>
+
+        <pathconvert property="xyz" pathsep="${pad}${line.separator}">
+            <path>
+                <filelist refid="xyzlist" />
+            </path>
+        </pathconvert>
+
+        <apply executable="echo" ignoremissing="true"
+               outputproperty="ignoretrue" append="true">
+            <filelist refid="xyzlist" />
+        </apply>
+
+        <apply executable="echo" ignoremissing="false"
+               outputproperty="ignorefalse" append="true">
+            <filelist refid="xyzlist" />
+        </apply>
+
+        <fail>
+            <condition>
+                <not>
+                    <and>
+                        <equals arg1="${xy}${pad}" arg2="${ignoretrue}" />
+                        <equals arg1="${xyz}${pad}" arg2="${ignorefalse}" />
+                    </and>
+                </not>
+            </condition>
+        </fail>
+    </target>
+
+    <target name="force" depends="init,xyz,pad" if="echo.can.run">
+        <presetdef name="ekko">
+            <apply executable="echo" append="true" dest="${basedir}">
+                <filelist refid="xyzlist" />
+                <mapper type="identity" />
+            </apply>
+        </presetdef>
+
+        <pathconvert property="xyz" pathsep="${pad}${line.separator}">
+            <path>
+                <filelist refid="xyzlist" />
+            </path>
+        </pathconvert>
+
+        <ekko outputproperty="foo" />
+        <ekko outputproperty="bar" force="true" />
+        <fail>
+            <condition>
+                <not>
+                    <and>
+                        <equals arg1="${foo}" arg2="" />
+                        <equals arg1="${bar}" arg2="${xyz}" />
+                    </and>
+                </not>
+            </condition>
+        </fail>
+    </target>
+
+    <target name="testNoDest" depends="init,xyz" if="echo.can.run">
+      <presetdef name="ekko">
+        <apply executable="echo" addsourcefile="false" force="true">
+          <filelist dir="${basedir}" files="x" />
+          <globmapper from="*" to="${basedir}/*" />
+          <targetfile />
+        </apply>
+      </presetdef>
+      <ekko outputproperty="dest" dest="${basedir}" />
+      <ekko outputproperty="nodest" />
+      <fail>
+        <condition>
+          <or>
+            <available file="${dest}" />
+            <not>
+              <available file="${nodest}" />
+            </not>
+          </or>
+        </condition>
+      </fail>
+    </target>
+
+    <target name="cleanup">
+        <delete>
+            <fileset refid="xyz" />
+            <fileset dir="${basedir}" includes="redirect.*" />
+            <fileset dir="${basedir}" includes="redirector.*" />
+        </delete>
+    </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/exec/exec.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/exec/exec.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/exec/exec.xml	(revision 16139)
@@ -0,0 +1,339 @@
+<project name="exec-test" default="spawn" basedir=".">
+    <target name="init">
+        <!-- this property can be overriden programatically in the Java test case -->
+        <property name="timeToWait" value="10"/>
+        <!-- this property can be overriden programatically in the Java test case -->
+        <property name="logFile" value="${java.io.tmpdir}/spawn.log"/>
+        <property environment="env"/>
+        <!-- UNIX -->
+        <available file="sh" filepath="${env.PATH}" property="sh.executable"/>
+        <!-- CYGWIN -->
+        <available file="sh.exe" filepath="${env.PATH}" property="sh.exe.executable"/>
+        <condition property="test.can.run">
+            <or>
+                <isset property="sh.executable"/>
+                <isset property="sh.exe.executable"/>
+            </or>
+        </condition>
+        <!-- UNIX -->
+        <available file="wc" filepath="${env.PATH}" property="wc.executable"/>
+        <!-- CYGWIN -->
+        <available file="wc.exe" filepath="${env.PATH}" property="wc.exe.executable"/>
+        <condition property="wc.can.run">
+            <or>
+                <isset property="wc.executable"/>
+                <isset property="wc.exe.executable"/>
+            </or>
+        </condition>
+        <!-- UNIX -->
+        <available file="cat" filepath="${env.PATH}" property="cat.executable"/>
+        <!-- CYGWIN -->
+        <available file="cat.exe" filepath="${env.PATH}" property="cat.exe.executable"/>
+        <condition property="cat.can.run">
+            <or>
+                <isset property="cat.executable"/>
+                <isset property="cat.exe.executable"/>
+            </or>
+        </condition>
+    </target>
+
+    <target name="spawn" depends="init" if="test.can.run">
+        <exec executable="sh" spawn="true">
+            <arg value="spawn.sh"/>
+            <arg value="${timeToWait}" />
+            <arg value="${logFile}" />
+        </exec>
+    </target>
+
+    <target name="no-redirect" depends="init" if="test.can.run">
+        <exec executable="sh">
+            <arg value="parrot.sh"/>
+            <arg value="${ant.file}" />
+        </exec>
+    </target>
+
+    <target name="redirect1" depends="init" if="test.can.run">
+        <exec executable="sh" output="redirect.out">
+            <arg value="parrot.sh"/>
+            <arg value="${ant.file}" />
+        </exec>
+    </target>
+
+    <target name="redirect2" depends="init" if="test.can.run">
+        <exec executable="sh" output="redirect.out" error="redirect.err">
+            <arg value="parrot.sh"/>
+            <arg value="${ant.file}" />
+        </exec>
+    </target>
+
+    <target name="redirect3" depends="init" if="test.can.run">
+        <exec executable="sh" logerror="true"
+              output="redirect.out" outputproperty="redirect.out">
+            <arg value="parrot.sh"/>
+            <arg value="${ant.file}" />
+        </exec>
+    </target>
+
+    <target name="redirect4" depends="init" if="test.can.run">
+        <exec executable="sh"
+              error="redirect.err" errorproperty="redirect.err"
+              output="redirect.out" outputproperty="redirect.out">
+            <arg value="parrot.sh"/>
+            <arg value="${ant.file}" />
+        </exec>
+    </target>
+
+    <target name="redirect5" depends="init" if="wc.can.run">
+        <exec executable="wc" inputstring="x y z"
+              error="redirect.err" errorproperty="redirect.err"
+              output="redirect.out" outputproperty="redirect.out">
+            <arg value="-w"/>
+        </exec>
+    </target>
+
+    <target name="redirect6" depends="init" if="wc.can.run">
+        <echo file="redirect.in">x y z</echo>
+        <exec executable="wc" input="redirect.in"
+              error="redirect.err" errorproperty="redirect.err"
+              output="redirect.out" outputproperty="redirect.out">
+            <arg value="-w"/>
+        </exec>
+    </target>
+
+    <target name="redirect7" depends="init" if="wc.can.run">
+        <exec executable="wc" inputstring="x y z"
+              error="redirect.err"
+              output="redirect.out" outputproperty="redirect.out">
+            <arg value="-w"/>
+        </exec>
+    </target>
+
+    <target name="redirector1" description="fail"
+            depends="init" if="test.can.run">
+        <exec executable="sh">
+            <arg value="parrot.sh"/>
+            <arg value="${ant.file}" />
+            <redirector output="redirector.out" />
+            <redirector output="whocares" />
+        </exec>
+    </target>
+
+    <target name="redirector2" depends="init" if="test.can.run">
+        <exec executable="sh">
+            <arg value="parrot.sh"/>
+            <arg value="${ant.file}" />
+            <redirector output="redirector.out" />
+        </exec>
+    </target>
+
+    <target name="redirector3" depends="init" if="test.can.run">
+        <exec executable="sh">
+            <arg value="parrot.sh"/>
+            <arg value="${ant.file}" />
+            <redirector output="redirector.out" error="redirector.err" />
+        </exec>
+    </target>
+
+    <target name="redirector4" depends="init" if="test.can.run">
+        <exec executable="sh">
+            <arg value="parrot.sh"/>
+            <arg value="${ant.file}" />
+            <redirector output="redirector.out" logerror="true"
+                        outputproperty="redirector.out" />
+        </exec>
+    </target>
+
+    <target name="redirector5" depends="init" if="test.can.run">
+        <exec executable="sh">
+            <redirector error="redirector.err" errorproperty="redirector.err"
+              output="redirector.out" outputproperty="redirector.out" />
+            <arg value="parrot.sh"/>
+            <arg value="${ant.file}" />
+        </exec>
+    </target>
+
+    <target name="redirector6" depends="init" if="test.can.run">
+        <exec executable="sh">
+            <redirector outputproperty="redirector.out"
+                        errorproperty="redirector.err">
+                <outputmapper type="merge" to="redirector.out" />
+                <errormapper type="merge" to="redirector.err" />
+            </redirector>
+            <arg value="parrot.sh" />
+            <arg value="${ant.file}" />
+        </exec>
+    </target>
+
+    <target name="redirector7" depends="init" if="test.can.run">
+        <exec executable="sh">
+            <redirector outputproperty="redirector.out"
+                        errorproperty="redirector.err">
+                <outputmapper type="merge" to="redirector.out" />
+                <errormapper type="merge" to="redirector.err" />
+                <errorfilterchain>
+                    <replacestring from="err" to="ERROR!!!" />
+                </errorfilterchain>
+            </redirector>
+            <arg value="parrot.sh" />
+            <arg value="${ant.file}" />
+        </exec>
+    </target>
+
+    <target name="redirector8" depends="init" if="wc.can.run">
+        <echo file="redirector.in">x y z</echo>
+        <exec executable="wc">
+            <redirector outputproperty="redirector.out"
+                        errorproperty="redirector.err">
+                <inputmapper type="merge" to="redirector.in" />
+                <outputmapper type="merge" to="redirector.out" />
+                <errormapper type="merge" to="redirector.err" />
+            </redirector>
+            <arg value="-w"/>
+        </exec>
+    </target>
+
+    <target name="redirector9" depends="init" if="cat.can.run">
+        <echo file="redirector.in">blah before blah</echo>
+        <exec executable="cat">
+            <redirector outputproperty="redirector.out"
+                        errorproperty="redirector.err">
+                <inputfilterchain>
+                    <replacestring from="before" to="after" />
+                </inputfilterchain>
+                <inputmapper type="merge" to="redirector.in" />
+                <outputmapper type="merge" to="redirector.out" />
+                <errormapper type="merge" to="redirector.err" />
+            </redirector>
+        </exec>
+    </target>
+
+    <target name="redirector10" depends="init" if="cat.can.run">
+        <echo file="redirector.in">blah before blah</echo>
+        <exec executable="cat">
+            <redirector outputproperty="redirector.out"
+                        errorproperty="redirector.err">
+                <outputfilterchain>
+                    <replacestring from="before" to="after" />
+                </outputfilterchain>
+                <outputmapper type="merge" to="redirector.out" />
+                <errormapper type="merge" to="redirector.err" />
+            </redirector>
+            <arg value="redirector.in"/>
+        </exec>
+    </target>
+
+    <target name="redirector11" depends="init" if="cat.can.run">
+        <exec executable="cat">
+            <redirector outputproperty="redirector.out"
+                        errorproperty="redirector.err"
+                        inputstring="blah before blah">
+                <inputfilterchain>
+                    <replacestring from="before" to="after" />
+                </inputfilterchain>
+                <outputmapper type="merge" to="redirector.out" />
+                <errormapper type="merge" to="redirector.err" />
+            </redirector>
+        </exec>
+    </target>
+
+    <target name="redirector12" depends="init" if="cat.can.run">
+        <echo file="redirector.in">blah before blah</echo>
+        <exec executable="cat" output="redirector.out" error="redirector.err">
+            <redirector outputproperty="redirector.out"
+                        errorproperty="redirector.err">
+                <outputfilterchain>
+                    <replacestring from="before" to="after" />
+                </outputfilterchain>
+                <outputmapper type="glob" from="nomatch" to="nomatchout" />
+                <errormapper type="glob" from="nomatch" to="nomatcherr" />
+            </redirector>
+            <arg value="redirector.in"/>
+        </exec>
+    </target>
+
+    <target name="redirector13" depends="init" if="test.can.run">
+        <exec executable="sh">
+            <redirector>
+                <outputfilterchain>
+                    <replacestring from="out" to="OUTPUT???" />
+                </outputfilterchain>
+                <errorfilterchain>
+                    <replacestring from="err" to="ERROR!!!" />
+                </errorfilterchain>
+            </redirector>
+            <arg value="parrot.sh" />
+            <arg value="${ant.file}" />
+        </exec>
+    </target>
+
+    <target name="redirector14" depends="init" if="cat.can.run">
+        <exec executable="cat">
+            <redirector inputstring="blah before blah">
+                <outputfilterchain>
+                    <replacestring from="before" to="after" />
+                </outputfilterchain>
+                <outputmapper type="glob" from="nomatch" to="nomatchout" />
+                <errormapper type="glob" from="nomatch" to="nomatcherr" />
+            </redirector>
+        </exec>
+    </target>
+
+    <target name="redirector15" depends="init" if="cat.can.run">
+        <exec executable="cat">
+            <redirector input="input/iso8859-1" output="redirector.out"
+                        inputencoding="ISO8859_1" outputencoding="UTF8" />
+        </exec>
+    </target>
+
+    <target name="redirector16" depends="init" if="test.can.run">
+        <exec executable="sh">
+            <redirector inputstring="exit"
+                        output="redirector16.out" error="redirector16.err" />
+        </exec>
+        <condition property="16pass">
+            <and>
+                <available file="redirector16.out" type="file" />
+                <available file="redirector16.err" type="file" />
+            </and>
+        </condition>
+        <fail unless="16pass">Files were not created.</fail>
+    </target>
+
+    <target name="redirector17" depends="init" if="test.can.run">
+        <exec executable="sh">
+            <redirector inputstring="exit" createemptyfiles="false"
+                        output="redirector17.out" error="redirector17.err" />
+        </exec>
+        <condition property="17fail">
+            <or>
+                <available file="redirector17.out" type="file" />
+                <available file="redirector17.err" type="file" />
+            </or>
+        </condition>
+        <fail if="17fail">Files were created.</fail>
+    </target>
+
+    <target name="redirector18" depends="init" if="test.can.run">
+        <exec executable="sh">
+            <arg value="parrot.sh"/>
+            <arg value="${ant.file}" />
+            <redirector alwayslog="true" logerror="true"
+                        outputproperty="redirector.out" />
+        </exec>
+        <fail message="property redirector.out has unexpected content">
+            <condition>
+                <not>
+                    <equals arg1="${ant.file} out" arg2="${redirector.out}" />
+                </not>
+            </condition>
+        </fail>
+    </target>
+
+    <target name="cleanup">
+        <delete>
+            <fileset file="${logFile}" />
+            <fileset dir="${basedir}" includes="redirect*" />
+            <fileset dir="${basedir}" includes="redirector*" />
+        </delete>
+    </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/exec/parrot.sh
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/exec/parrot.sh	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/exec/parrot.sh	(revision 16139)
@@ -0,0 +1,6 @@
+# Copyright (c) 2004 The Apache Software Foundation.  All rights reserved.
+for arg in $@ ; do
+	echo $arg out
+	sleep 1
+	echo $arg err>&2
+done
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/exec/spawn.sh
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/exec/spawn.sh	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/exec/spawn.sh	(revision 16139)
@@ -0,0 +1,15 @@
+sleeptime=10
+logfile=spawn.log
+if  [ $# -ge 1 ]; then
+   sleeptime=$1
+   echo $sleeptime
+fi
+if  [ $# -ge 2 ]; then
+   logfile=$2
+   echo $logfile
+fi
+echo hello
+rm  $logfile
+sleep $sleeptime
+echo bye bye > $logfile
+echo bye bye
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/expected/asf-logo.gif.md5
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/expected/asf-logo.gif.md5	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/expected/asf-logo.gif.md5	(revision 16139)
@@ -0,0 +1,1 @@
+0541d3df42520911f268abc730f3afe0
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/expected/copy.filterset.filtered
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/expected/copy.filterset.filtered	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/expected/copy.filterset.filtered	(revision 16139)
@@ -0,0 +1,1 @@
+This is the Apache Ant Project.
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fail.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fail.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fail.xml	(revision 16139)
@@ -0,0 +1,114 @@
+<?xml version="1.0"?>
+
+<project name="fail-test" basedir="." default="test1">
+
+  <target name="test1">
+    <fail/>
+  </target>
+
+  <target name="test2">
+    <fail message="test2"/>
+  </target>
+
+  <target name="testText">
+    <fail>testText</fail>
+  </target>
+  
+  <target name="testIf">
+    <fail if="foo" />
+  </target>
+
+  <target name="testUnless">
+    <fail unless="foo" />
+  </target>
+
+  <target name="testIfAndUnless">
+    <fail unless="unless" if="if"/>
+  </target>
+  
+  <target name="testNested1" description="should fail with default message">
+    <fail>
+      <condition>
+          <and />
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testNested2" description="should pass">
+    <fail>
+      <condition>
+          <or />
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testNested3" description="should fail">
+    <fail message="testNested3">
+      <condition>
+          <and />
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testNested4a" description="should error">
+    <fail if="if">
+      <condition>
+          <and />
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testNested4b" description="should error">
+    <fail unless="unless">
+      <condition>
+          <and />
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testNested4c" description="should error">
+    <fail if="if" unless="unless">
+      <condition>
+          <and />
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testNested5" description="should error">
+    <fail>
+      <condition>
+          <or />
+      </condition>
+      <condition>
+          <and />
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testNested6" description="should fail with message">
+    <fail>
+      <condition>
+          <and />
+      </condition>
+testNested6
+testNested6
+testNested6
+    </fail>
+  </target>
+
+  <target name="testNested7a" description="should error">
+    <fail>
+      <condition />
+    </fail>
+  </target>
+
+  <target name="testNested7b" description="should error">
+    <fail>
+      <condition>
+          <and />
+          <and />
+      </condition>
+    </fail>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/filter.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/filter.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/filter.xml	(revision 16139)
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+
+<project name="filter-test" basedir="." default="test1">
+
+  <target name="test1">
+    <filter/>
+  </target>
+
+  <target name="test2">
+    <filter token=""/>
+  </target>
+
+  <target name="test3">
+    <filter value=""/>
+  </target>
+
+  <target name="test4">
+    <filter token="" value=""/>
+  </target>
+
+  <target name="test5">
+    <filter token="year" value="2000" />
+    <copy file="filter1.txt" tofile="filtered.tmp" filtering="yes" overwrite="yes" />
+  </target>
+
+  <target name="test6">
+    <filter token="year" value="2000" />
+    <copy todir="./taskdefs.tmp" filtering="yes" overwrite="yes">
+      <fileset dir="." includes="filter1.txt" />
+    </copy>
+  </target>
+
+  <target name="test7">
+    <filter token="ROOT" value="root" />
+    <copy file="filter2.txt" tofile="filtered.tmp" filtering="yes" overwrite="yes" />
+  </target>
+
+  <target name="test8">
+    <filter token="ROOT" value="root" />
+    <copy todir="./taskdefs.tmp" filtering="yes" overwrite="yes">
+      <fileset dir="." includes="filter2.txt"/>
+    </copy>
+  </target>
+
+  <target name="test9">
+    <filter filtersfile="filterdefs.properties" />
+    <copy todir="./taskdefs.tmp" filtering="yes" overwrite="yes">
+      <fileset dir="." includes="filter3.txt"/>
+    </copy>
+  </target>
+
+  <target name="cleanup">
+    <delete dir="taskdefs.tmp" />
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/filter1.txt
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/filter1.txt	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/filter1.txt	(revision 16139)
@@ -0,0 +1,1 @@
+@year@
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/filter2.txt
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/filter2.txt	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/filter2.txt	(revision 16139)
@@ -0,0 +1,1 @@
+<%@ include file="@ROOT@/some/include.jsp"%>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/filter3.txt
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/filter3.txt	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/filter3.txt	(revision 16139)
@@ -0,0 +1,1 @@
+@property@
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/filterdefs.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/filterdefs.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/filterdefs.properties	(revision 16139)
@@ -0,0 +1,1 @@
+property=included
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/build.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/build.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/build.xml	(revision 16139)
@@ -0,0 +1,165 @@
+<?xml version="1.0"?>
+<project default="cleanup" basedir=".">
+  <target name="init">
+    <mkdir dir="result" />
+  </target>
+
+  <target name="cleanup">
+    <delete dir="result" />
+  </target>
+
+  <target name="test1" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="Junk1.java"
+             javafiles="true"
+             tab="add"
+             eol="crlf"
+             eof="asis"
+             />
+  </target>
+
+  <target name="test2" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="Junk2.java"
+             javafiles="true"
+             tab="add"
+             cr="add"
+             eol="crlf"
+             eof="asis"
+             />
+  </target>
+
+  <target name="test3" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="Junk3.java"
+             javafiles="true"
+             tab="remove"
+             eol="lf"
+             eof="asis"
+             />
+  </target>
+
+  <target name="test4" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="Junk4.java"
+             javafiles="true"
+             tab="remove"
+             eol="lf"
+             eof="asis"
+             />
+  </target>
+
+  <target name="test5" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="Junk5.java"
+             tab="remove"
+             eol="lf"
+             eof="asis"
+             />
+  </target>
+
+  <target name="test6" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="Junk6.java"
+             tab="add"
+             cr="remove"
+             eol="crlf"
+             eof="asis"
+             />
+  </target>
+
+  <target name="test7" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="Junk7.java"
+             tab="add"
+             cr="add"
+             eof="asis"
+             />
+  </target>
+
+  <target name="test8" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="Junk8.java"
+             javafiles="true"
+             tab="add"
+             cr="add"
+             eof="add"
+             />
+  </target>
+
+  <target name="test9" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="Junk9.java"
+             javafiles="true"
+             tab="remove"
+             cr="remove"
+             eof="remove"
+             />
+  </target>
+
+  <target name="testMacLines" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="Mac2Unix"
+             eol="lf"
+             />
+  </target>
+
+  <target name="testEncoding" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="input.crlf.utf16"
+             javafiles="false"
+             cr="remove"
+             encoding="UnicodeBig"
+             />
+  </target>
+
+  <target name="testLongLines" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="longlines.crlf"
+             javafiles="false"
+             cr="remove"
+             />
+  </target>
+
+  <target name="testCrCrLfSequence-unix" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="crcrlf"
+             eol="lf"
+             />
+  </target>
+
+  <target name="testCrCrLfSequence-dos" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="crcrlf"
+             eol="crlf"
+             />
+  </target>
+
+  <target name="testCrCrLfSequence-mac" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="crcrlf"
+             eol="cr"
+             />
+  </target>
+
+  <target name="testFixlastDos" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="fixlastfalse.lf"
+             eol="crlf"
+             />
+  </target>
+
+  <target name="testFixlastFalseMac" depends="init">
+    <fixcrlf srcdir="input" destdir="result"
+             includes="fixlastfalse.lf"
+             eol="cr"
+             fixlast="false"
+             />
+  </target>
+
+  <!-- Bugzilla Report 20840 -->
+  <target name="createParentDirs" depends="init">
+    <fixcrlf srcdir="." destdir="result" includes="input/Junk1.java"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk1.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk1.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk1.java	(revision 16139)
@@ -0,0 +1,12 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+	System.out.println("I have	a tab");
+	//	Here is a comment with an embedded tab
+	if (mybool) {	/* Here is a multi-line
+			   (with embedded'	'tab)
+	    Comment */char mychar = '	';	//<-tab->	<-
+	} // end of if (mybool)
+	
+    } // end of main ()
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk2.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk2.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk2.java	(revision 16139)
@@ -0,0 +1,12 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+	System.out.println("I have	a tab");
+	//	Here is a comment with an embedded tab
+	if (mybool) {	/* Here is a multi-line
+			   (with embedded'	'tab)
+	    Comment */char mychar = '	';	//<-tab->	<-
+	} // end of if (mybool)
+	
+    } // end of main ()
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk3.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk3.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk3.java	(revision 16139)
@@ -0,0 +1,12 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+        System.out.println("I have	a tab");
+        //      Here is a comment with an embedded tab
+        if (mybool) {   /* Here is a multi-line
+                           (with embedded'      'tab)
+            Comment */char mychar = '	';      //<-tab->       <-
+        } // end of if (mybool)
+        
+    } // end of main ()
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk4.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk4.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk4.java	(revision 16139)
@@ -0,0 +1,12 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+        System.out.println("I have	a tab");
+        //      Here is a comment with an embedded tab
+        if (mybool) {   /* Here is a multi-line
+                           (with embedded'      'tab)
+            Comment */char mychar = '	';      //<-tab->       <-
+        } // end of if (mybool)
+        
+    } // end of main ()
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk5.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk5.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk5.java	(revision 16139)
@@ -0,0 +1,12 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+        System.out.println("I have      a tab");
+        //      Here is a comment with an embedded tab
+        if (mybool) {   /* Here is a multi-line
+                           (with embedded'      'tab)
+            Comment */char mychar = '   ';      //<-tab->       <-
+        } // end of if (mybool)
+        
+    } // end of main ()
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk6.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk6.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk6.java	(revision 16139)
@@ -0,0 +1,12 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+	System.out.println("I have	a tab");
+	//	Here is a comment with an embedded tab
+	if (mybool) {	/* Here is a multi-line
+			   (with embedded'	'tab)
+	    Comment */char mychar = '	';	//<-tab->	<-
+	} // end of if (mybool)
+	
+    } // end of main ()
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk7.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk7.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk7.java	(revision 16139)
@@ -0,0 +1,12 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+	System.out.println("I have	a tab");
+	//	Here is a comment with an embedded tab
+	if (mybool) {	/* Here is a multi-line
+			   (with embedded'	'tab)
+	    Comment */char mychar = '	';	//<-tab->	<-
+	} // end of if (mybool)
+	
+    } // end of main ()
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk8.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk8.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk8.java	(revision 16139)
@@ -0,0 +1,13 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+	System.out.println("I have	a tab");
+	//	Here is a comment with an embedded tab
+	if (mybool) {	/* Here is a multi-line
+			   (with embedded'	'tab)
+	    Comment */char mychar = '	';	//<-tab->	<-
+	} // end of if (mybool)
+	
+    } // end of main ()
+}
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk9.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk9.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Junk9.java	(revision 16139)
@@ -0,0 +1,12 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+        System.out.println("I have	a tab");
+        //      Here is a comment with an embedded tab
+        if (mybool) {   /* Here is a multi-line
+                           (with embedded'      'tab)
+            Comment */char mychar = '	';      //<-tab->       <-
+        } // end of if (mybool)
+        
+    } // end of main ()
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Mac2Unix
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Mac2Unix	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/Mac2Unix	(revision 16139)
@@ -0,0 +1,4 @@
+line1
+line2
+
+line3
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/crcrlf.dos
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/crcrlf.dos	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/crcrlf.dos	(revision 16139)
@@ -0,0 +1,2 @@
+1
+2
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/crcrlf.mac
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/crcrlf.mac	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/crcrlf.mac	(revision 16139)
@@ -0,0 +1,2 @@
+1
+2
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/crcrlf.unix
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/crcrlf.unix	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/crcrlf.unix	(revision 16139)
@@ -0,0 +1,2 @@
+1
+2
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/fixlast.dos
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/fixlast.dos	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/fixlast.dos	(revision 16139)
@@ -0,0 +1,2 @@
+12345
+6789
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/fixlastfalse.mac
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/fixlastfalse.mac	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/fixlastfalse.mac	(revision 16139)
@@ -0,0 +1,2 @@
+12345
+6789
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/longlines.lf
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/longlines.lf	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/expected/longlines.lf	(revision 16139)
@@ -0,0 +1,2 @@
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+abc
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk1.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk1.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk1.java	(revision 16139)
@@ -0,0 +1,12 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+	System.out.println("I have	a tab");
+	//	Here is a comment with an embedded tab
+	if (mybool) {	/* Here is a multi-line
+			   (with embedded'	'tab)
+	    Comment */char mychar = '	';	//<-tab->	<-
+	} // end of if (mybool)
+	
+    } // end of main ()
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk2.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk2.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk2.java	(revision 16139)
@@ -0,0 +1,12 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+	System.out.println("I have	a tab");
+	//	Here is a comment with an embedded tab
+	if (mybool) {	/* Here is a multi-line
+			   (with embedded'	'tab)
+	    Comment */char mychar = '	';	//<-tab->	<-
+	} // end of if (mybool)
+	
+    } // end of main ()
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk3.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk3.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk3.java	(revision 16139)
@@ -0,0 +1,12 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+	System.out.println("I have	a tab");
+	//	Here is a comment with an embedded tab
+	if (mybool) {	/* Here is a multi-line
+			   (with embedded'	'tab)
+	    Comment */char mychar = '	';	//<-tab->	<-
+	} // end of if (mybool)
+	
+    } // end of main ()
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk4.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk4.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk4.java	(revision 16139)
@@ -0,0 +1,12 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+        System.out.println("I have	a tab");
+        //      Here is a comment with an embedded tab
+        if (mybool) {   /* Here is a multi-line
+                           (with embedded'      'tab)
+            Comment */char mychar = '	';      //<-tab->       <-
+        } // end of if (mybool)
+        
+    } // end of main ()
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk5.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk5.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk5.java	(revision 16139)
@@ -0,0 +1,12 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+        System.out.println("I have	a tab");
+        //      Here is a comment with an embedded tab
+        if (mybool) {   /* Here is a multi-line
+                           (with embedded'      'tab)
+            Comment */char mychar = '	';      //<-tab->       <-
+        } // end of if (mybool)
+        
+    } // end of main ()
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk6.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk6.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk6.java	(revision 16139)
@@ -0,0 +1,12 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+        System.out.println("I have	a tab");
+        //      Here is a comment with an embedded tab
+        if (mybool) {   /* Here is a multi-line
+                           (with embedded'      'tab)
+            Comment */char mychar = '	';      //<-tab->       <-
+        } // end of if (mybool)
+        
+    } // end of main ()
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk7.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk7.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk7.java	(revision 16139)
@@ -0,0 +1,12 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+        System.out.println("I have	a tab");
+        //      Here is a comment with an embedded tab
+        if (mybool) {   /* Here is a multi-line
+                           (with embedded'      'tab)
+            Comment */char mychar = '	';      //<-tab->       <-
+        } // end of if (mybool)
+        
+    } // end of main ()
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk8.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk8.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk8.java	(revision 16139)
@@ -0,0 +1,12 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+	System.out.println("I have	a tab");
+	//	Here is a comment with an embedded tab
+	if (mybool) {	/* Here is a multi-line
+			   (with embedded'	'tab)
+	    Comment */char mychar = '	';	//<-tab->	<-
+	} // end of if (mybool)
+	
+    } // end of main ()
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk9.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk9.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Junk9.java	(revision 16139)
@@ -0,0 +1,13 @@
+public class Junk1 {
+    public boolean mybool;
+    public static void main (String[] args) {
+	System.out.println("I have	a tab");
+	//	Here is a comment with an embedded tab
+	if (mybool) {	/* Here is a multi-line
+			   (with embedded'	'tab)
+	    Comment */char mychar = '	';	//<-tab->	<-
+	} // end of if (mybool)
+	
+    } // end of main ()
+}
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Mac2Unix
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Mac2Unix	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/Mac2Unix	(revision 16139)
@@ -0,0 +1,4 @@
+line1
+line2
+
+line3
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/crcrlf
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/crcrlf	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/crcrlf	(revision 16139)
@@ -0,0 +1,3 @@
+1
+
+2
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/fixlastfalse.lf
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/fixlastfalse.lf	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/fixlastfalse.lf	(revision 16139)
@@ -0,0 +1,2 @@
+12345
+6789
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/longlines.crlf
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/longlines.crlf	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/fixcrlf/input/longlines.crlf	(revision 16139)
@@ -0,0 +1,2 @@
+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
+abc
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/foo.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/foo.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/foo.properties	(revision 16139)
@@ -0,0 +1,1 @@
+foo=Foo
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/get.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/get.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/get.xml	(revision 16139)
@@ -0,0 +1,87 @@
+<?xml version="1.0"?>
+
+<project name="xxx-test" basedir="." default="test1">
+
+  <target name="test1">
+    <get/>
+  </target>
+
+  <target name="test2">
+    <get src=""/>
+  </target>
+
+  <target name="test3">
+    <get src="" dest=""/>
+  </target>
+
+  <target name="test4">
+    <get src="" dest=""/>
+  </target>
+
+  <target name="test5">
+    <get src="http://www.apache.org/" dest=""/>
+  </target>
+
+  <target name="test6">
+    <get src="http://www.apache.org/" dest="get.tmp"/>
+
+    <fileset id="t6" file="get.tmp" />
+    <pathconvert property="t6" refid="t6" setonempty="false" />
+
+    <fail message="get failed">
+      <condition>
+        <not>
+          <isset property="t6" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testUseTimestamp" depends="-90s,-timestamp" />
+
+  <target name="-90s">
+    <property name="off" value="-90" />
+    <property name="unit" value="second" />
+  </target>
+
+  <target name="testUseTomorrow" depends="+1d,-timestamp" />
+
+  <target name="+1d">
+    <property name="off" value="1" />
+    <property name="unit" value="day" />
+  </target>
+
+  <target name="-timestamp">
+    <property name="pat" value="yyyyMMddHHmm" />
+
+    <tstamp>
+      <format property="dt" pattern="${pat}" offset="${off}" unit="${unit}" />
+    </tstamp>
+
+    <touch file="get.tmp" datetime="${dt}" pattern="${pat}" />
+
+    <get src="http://www.apache.org/" dest="get.tmp"
+         usetimestamp="true" verbose="true" />
+
+    <fileset id="ts" file="get.tmp">
+      <date when="equal" datetime="${dt}" pattern="${pat}" />
+    </fileset>
+
+    <pathconvert property="ts" refid="ts" setonempty="false" />
+
+    <fail message="get w/ timestamp should have failed.">
+      <condition>
+        <not>
+          <isset property="ts" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="cleanup">
+    <delete>
+      <fileset dir="${basedir}" includes="get.tmp" />
+    </delete>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/gunzip.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/gunzip.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/gunzip.xml	(revision 16139)
@@ -0,0 +1,27 @@
+<?xml version="1.0"?>
+
+<project name="xxx-test" basedir="." default="test1">
+
+  <target name="test1">
+    <gunzip/>
+  </target>
+
+  <target name="test2">
+    <gunzip src=""/>
+  </target>
+
+  <target name="cleanup">
+    <delete file="asf-logo.gif" />
+  </target>
+
+  <target name="testGzipTask">
+    <ant antfile="gzip.xml" target="realTest" />
+    <gunzip src="asf-logo.gif.gz" dest="asf-logo.gif" />
+    <ant antfile="gzip.xml" target="cleanup" />
+  </target>
+
+  <target name="realTest">
+    <gunzip src="expected/asf-logo.gif.gz" dest="asf-logo.gif" />
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/gzip.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/gzip.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/gzip.xml	(revision 16139)
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+
+<project name="xxx-test" basedir="." default="test1">
+
+  <target name="test1">
+    <gzip/>
+  </target>
+
+  <target name="test2">
+    <gzip src=""/>
+  </target>
+
+  <target name="test3">
+    <gzip zipfile=""/>
+  </target>
+
+  <target name="test4">
+    <gzip src="gzip.xml" zipfile="." />
+  </target>
+
+  <target name="realTest">
+    <gzip src="../asf-logo.gif" zipfile="asf-logo.gif.gz" />
+  </target>
+
+  <target name="testDateCheck">
+    <touch file="asf-logo.gif.gz"/>
+    <gzip src="../asf-logo.gif" zipfile="asf-logo.gif.gz" />
+  </target>
+
+  <target name="cleanup">
+    <delete file="asf-logo.gif.gz" />
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/bad.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/bad.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/bad.xml	(revision 16139)
@@ -0,0 +1,3 @@
+<project>
+<<<
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/import.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/import.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/import.xml	(revision 16139)
@@ -0,0 +1,17 @@
+<project name="import-test" default="main" basedir=".">
+  <echo>Before import</echo>        
+
+  <import file="imported.xml"/>
+
+  <echo message="After import"/>
+  
+  <target name="import-init">
+     <echo message="In import-init" />
+  </target>
+
+  <target name="main" depends="imported">
+    <echo message="In main"/>
+  </target>
+
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/import_bad_import.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/import_bad_import.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/import_bad_import.xml	(revision 16139)
@@ -0,0 +1,3 @@
+<project>
+  <import file="bad.xml"/>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/import_same_target.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/import_same_target.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/import_same_target.xml	(revision 16139)
@@ -0,0 +1,4 @@
+<project>
+  <target name="t"/>
+  <target name="t"/>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/imported.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/imported.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/imported.xml	(revision 16139)
@@ -0,0 +1,9 @@
+<project name="imported-test" default="imported" basedir=".">
+
+  <echo message="In imported top"/>
+ 
+  <target name="imported" depends="import-init" >
+    <echo message="In imported target" />
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/same_target.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/same_target.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/same_target.xml	(revision 16139)
@@ -0,0 +1,3 @@
+<project>
+  <import file="import_same_target.xml"/>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/subdir/importinsequential-inner.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/subdir/importinsequential-inner.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/subdir/importinsequential-inner.xml	(revision 16139)
@@ -0,0 +1,8 @@
+<project>
+  <target name="within-imported">
+    <property name="foo" value="bar"/>
+    <path id="baz">
+      <pathelement location="."/>
+    </path>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/subdir/importinsequential.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/subdir/importinsequential.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/subdir/importinsequential.xml	(revision 16139)
@@ -0,0 +1,5 @@
+<project>
+  <sequential>
+    <import file="importinsequential-inner.xml"/>
+  </sequential>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/subdir/importintarget-inner.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/subdir/importintarget-inner.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/subdir/importintarget-inner.xml	(revision 16139)
@@ -0,0 +1,6 @@
+<project>
+  <property name="foo" value="bar"/>
+  <path id="baz">
+    <pathelement location="."/>
+  </path>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/subdir/importintarget.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/subdir/importintarget.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/subdir/importintarget.xml	(revision 16139)
@@ -0,0 +1,7 @@
+<project>
+  <target name="do-import">
+    <import file="importintarget-inner.xml"/>
+  </target>
+
+  <target name="no-import"/>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/subdir/serial.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/subdir/serial.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/subdir/serial.xml	(revision 16139)
@@ -0,0 +1,5 @@
+<project name="serial">
+  <import file="../unnamed1.xml"/>
+  <import file="../unnamed2.xml"/>
+</project>
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/symlinks/d1/p1.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/symlinks/d1/p1.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/symlinks/d1/p1.xml	(revision 16139)
@@ -0,0 +1,4 @@
+<project name="p1" default="run">
+    <import file="../d2/p2.xml"/>
+    <import file="../d3b/p3.xml"/>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/symlinks/d2/p2.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/symlinks/d2/p2.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/symlinks/d2/p2.xml	(revision 16139)
@@ -0,0 +1,1 @@
+<project name="p2"/>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/symlinks/d3a/p3.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/symlinks/d3a/p3.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/symlinks/d3a/p3.xml	(revision 16139)
@@ -0,0 +1,1 @@
+<project name="p3"/>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/unnamed1.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/unnamed1.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/unnamed1.xml	(revision 16139)
@@ -0,0 +1,5 @@
+<project default="test">
+  <import file="unnamed2.xml"/>
+
+  <echo message="Unnamed1.xml" level="info"/>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/unnamed2.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/unnamed2.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/unnamed2.xml	(revision 16139)
@@ -0,0 +1,3 @@
+<project default="test">
+  <echo message="Unnamed2.xml" level="info"/>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/import/unnamedImport.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/import/unnamedImport.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/import/unnamedImport.xml	(revision 16139)
@@ -0,0 +1,8 @@
+<project name="unnamed-import" default="test">
+
+  <import file="unnamed1.xml"/>
+
+  <target name="test">
+    <echo level="info">Tests import of unnamed projects</echo>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/initializeclass.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/initializeclass.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/initializeclass.xml	(revision 16139)
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+
+<project name="test" default="all">
+  <target name="all"/>
+
+  <target name="forked">
+    <java fork="true" output="forkedout" failonerror="true"
+          className="org.apache.tools.ant.taskdefs.dir1.B">
+
+      <classpath>
+        <pathelement path="../../../../build/testcases"/>
+        <pathelement location="${java.home}/lib/classes.zip" />
+      </classpath>
+    </java>
+  </target>
+
+  <target name="unforked">
+    <java className="org.apache.tools.ant.taskdefs.dir1.B">
+      <classpath>
+        <pathelement path="../../../../build/testcases"/>
+        <pathelement location="${java.home}/lib/classes.zip" />
+      </classpath>
+    </java>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/input.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/input.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/input.properties	(revision 16139)
@@ -0,0 +1,11 @@
+Press\ Return\ key\ to\ continue...=test
+All\ data\ is\ going\ to\ be\ deleted\ from\ DB\ continue?=test
+All\ data\ is\ going\ to\ be\ deleted\ from\ db\ continue\ (y/n)?=y
+Please\ enter\ db-username\:=scott
+#
+# JDK 1.1 doesn't seem to handle blanks in the property key
+#
+Press_Return_key_to_continue...=test
+All_data_is_going_to_be_deleted_from_DB_continue?=test
+All_data_is_going_to_be_deleted_from_db_continue_(y/n)?=y
+Please_enter_db_username=scott
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/input.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/input.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/input.xml	(revision 16139)
@@ -0,0 +1,57 @@
+<?xml version="1.0"?>
+
+<project name="input-test" basedir="." default="test1">
+
+  <target name="test1">
+    <input>Press Return key to continue...</input>
+  </target>
+
+  <target name="test2">
+    <input message="Press Return key to continue..." />
+  </target>
+
+  <target name="test3">
+    <input message="All data is going to be deleted from DB continue?"
+           validargs="y,n"
+           />
+  </target>
+
+  <target name="test5">
+    <input message="All data is going to be deleted from db continue (y/n)?"
+           validargs="y,n"
+           />
+  </target>
+
+  <target name="test6">
+    <input message="Please enter db-username:"
+           addproperty="db.user"
+           />
+  </target>
+
+  <target name="test1.1">
+    <input>Press_Return_key_to_continue...</input>
+  </target>
+
+  <target name="test2.1">
+    <input message="Press_Return_key_to_continue..." />
+  </target>
+
+  <target name="test3.1">
+    <input message="All_data_is_going_to_be_deleted_from_DB_continue?"
+           validargs="y,n"
+           />
+  </target>
+
+  <target name="test5.1">
+    <input message="All_data_is_going_to_be_deleted_from_db_continue_(y/n)?"
+           validargs="y,n"
+           />
+  </target>
+
+  <target name="test6.1">
+    <input message="Please_enter_db_username"
+           addproperty="db.user"
+           />
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/jar.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/jar.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/jar.xml	(revision 16139)
@@ -0,0 +1,225 @@
+<?xml version="1.0"?>
+
+<project name="jar-test" basedir="." default="test1">
+
+  <property name="tmp.jar" location="tmp.jar"/>
+  <property name="tmp.dir" location="jartmp"/>
+  <property name="tmp.zip" location="tmp.zip"/>
+  <property name="tmp1.dir" location="jartmp1"/>
+  <property name="tmp2.dir" location="jartmp2"/>
+
+  <target name="test1">
+    <jar/>
+  </target>
+
+  <target name="test2">
+    <jar
+    	jarfile="jar.tmp"
+    	manifest="none"
+    />
+  </target>
+
+  <target name="test3">
+    <jar
+    	destfile="jar.tmp"
+	whenempty="format C: /y" 
+    />
+  </target>
+
+  <target name="test4">
+   <jar
+    	destfile="${tmp.jar}"
+	basedir="."
+	includes="jar.xml"
+    />
+  </target>
+
+  <target name="testNoRecreateWithUpdate">
+    <jar
+         destfile="${tmp.jar}"
+         basedir="."
+         includes="jar.xml"
+         update="true"
+    />
+  </target>
+
+  <target name="testRecreateNewerFileSetup" depends="test4">
+    <touch file="jar.xml"/>
+  </target>
+
+  <target name="testRecreateWithoutUpdateAdditionalFiles">
+    <jar
+         destfile="${tmp.jar}"
+         includes="*.xml"
+         basedir="."
+    />
+  </target>
+
+  <target name="testRecreateWithUpdateAdditionalFiles">
+    <jar
+         destfile="${tmp.jar}"
+         basedir="."
+         includes="*.xml"
+         update="true"
+    />
+  </target>
+
+  <target name="testRecreateWithoutUpdateNewerFile">
+    <jar
+         destfile="${tmp.jar}"
+         basedir="."
+         includes="jar.xml"
+    />
+  </target>
+
+  <target name="testRecreateWithUpdateNewerFile">
+    <jar
+         destfile="${tmp.jar}"
+         basedir="."
+         includes="jar.xml"
+         update="true"
+    />
+  </target>
+
+  <target name="testManifestStaysIntact">
+    <mkdir dir="${tmp.dir}"/>
+    <manifest file="${tmp.dir}/manifest">
+      <attribute name="Foo" value="bar"/>
+    </manifest>
+    <jar destfile="${tmp.jar}" basedir="." includes="jar.xml"
+         manifest="${tmp.dir}/manifest"/>
+    <sleep seconds="3"/>
+    <touch file="jar.xml"/>
+    <jar destfile="${tmp.jar}" basedir="." includes="jar.xml"
+         update="true"/>
+    <unjar src="${tmp.jar}" dest="${tmp.dir}"/>
+  </target>    
+
+  <target name="testNoRecreateBasedirExcludesWithUpdate">
+    <jar
+         destfile="${tmp.jar}"
+         basedir="."
+         includes="j*.xml"
+         excludes="java.xml"
+         update="true"
+    />
+  </target>
+
+  <target name="testNoRecreateBasedirExcludesWithoutUpdate">
+    <jar
+         destfile="${tmp.jar}"
+         basedir="."
+         includes="j*.xml"
+         excludes="java.xml"
+    />
+  </target>
+
+  <target name="makezip">
+    <zip destfile="${tmp.zip}"
+         basedir="." includes="j*.xml"/>
+  </target>
+
+  <target name="testNoRecreateZipfilesetExcludesWithUpdate"
+          depends="makezip">
+    <jar destfile="${tmp.jar}"
+         update="true">
+      <zipfileset src="${tmp.zip}" excludes="java.xml"/>
+    </jar>
+  </target>
+
+  <target name="testNoRecreateZipfilesetExcludesWithoutUpdate"
+          depends="makezip">
+    <jar destfile="${tmp.jar}">
+      <zipfileset src="${tmp.zip}" excludes="java.xml"/>
+    </jar>
+  </target>
+
+  <target name="testRecreateZipfilesetWithoutUpdateAdditionalFiles"
+          depends="makezip">
+    <jar destfile="${tmp.jar}">
+      <zipfileset src="${tmp.zip}"/>
+    </jar>
+  </target>
+
+  <target name="testRecreateZipfilesetWithUpdateAdditionalFiles"
+          depends="makezip">
+    <jar destfile="${tmp.jar}"
+         update="true">
+      <zipfileset src="${tmp.zip}"/>
+    </jar>
+  </target>
+
+  <target name="testRecreateZipfilesetWithoutUpdateNewerFile"
+          depends="makezip">
+    <jar destfile="${tmp.jar}">
+      <zipfileset src="${tmp.zip}" includes="jar.xml"/>
+    </jar>
+  </target>
+
+  <target name="testRecreateZipfilesetWithUpdateNewerFile"
+          depends="makezip">
+    <jar destfile="${tmp.jar}"
+         update="true">
+      <zipfileset src="${tmp.zip}" includes="jar.xml"/>
+    </jar>
+  </target>
+
+  <target name="cleanup">
+    <delete file="${tmp.jar}" />
+    <delete dir="${tmp.dir}"/>
+    <delete file="${tmp.zip}" />
+    <delete dir="${tmp1.dir}"/>
+    <delete dir="${tmp2.dir}"/>
+  </target>
+
+  <target name="testCreateWithEmptyFilesetSetUp">
+    <mkdir dir="${tmp1.dir}"/>
+    <mkdir dir="${tmp2.dir}"/>
+    <echo file="${tmp2.dir}/foo.txt" message="foo"/>
+  </target>
+
+  <target name="testCreateWithEmptyFileset">
+    <jar destfile="${tmp.jar}">
+      <fileset dir="${tmp1.dir}">
+        <include name="**/*.doesNotExist"/>
+      </fileset>
+      <fileset dir="${tmp2.dir}">
+        <include name="**/foo.txt"/>
+      </fileset>
+    </jar>
+  </target>
+
+  <!-- bug 17780 -->
+  <target name="testUpdateIfOnlyManifestHasChanged"
+          depends="test4">
+    <jar destfile="${tmp.jar}" update="true">
+      <manifest>
+        <attribute name="Foo" value="bar"/>
+      </manifest>
+    </jar>
+    <mkdir dir="${tmp.dir}"/>
+    <unzip src="${tmp.jar}" dest="${tmp.dir}"/>
+  </target>
+
+  <!-- bugs 10262 and 16972 -->
+  <target name="testIndexTests">
+    <mkdir dir="${tmp.dir}/META-INF"/>
+    <touch file="${tmp.dir}/META-INF/INDEX.LIST"/>
+    <touch file="${tmp.dir}/foo"/>
+    <mkdir dir="${tmp.dir}/sub"/>
+    <touch file="${tmp.dir}/sub/foo"/>
+    <jar destfile="${tmp.jar}" index="yes" basedir="${tmp.dir}"/>
+  </target>
+    <!-- bug 32802 -->
+  <target name="testManifestOnlyJar">
+    <mkdir dir="${tmp.dir}"/>  
+    <jar destfile="${tmp.jar}" duplicate="preserve">
+      <manifest>
+        <attribute name="Foo" value="bar"/>
+      </manifest>
+    </jar>
+    <mkdir dir="${tmp.dir}"/>
+    <unzip src="${tmp.jar}" dest="${tmp.dir}"/>
+
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/java.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/java.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/java.xml	(revision 16139)
@@ -0,0 +1,379 @@
+<?xml version="1.0"?>
+
+<project name="java-test" basedir="." default="foo">
+  <fail unless="tests-classpath.value"
+      message="the property tests-classpath.value is required by this test" />
+  <!-- this property gets overridden programmatically-->
+  <property name="timeToWait" value="4"/>
+  <!-- this property gets overridden programmatically-->
+  <property name="logFile" value="spawn.log"/>
+  <property name="tmp" location="${java.io.tmpdir}"/>
+  <property name="app"
+    value="org.apache.tools.ant.taskdefs.JavaTest$$EntryPoint" />
+
+  <property name="app2"
+    value="org.apache.tools.ant.taskdefs.JavaTest$$ExceptingEntryPoint" />
+
+  <property name="spawnapp"
+    value="org.apache.tools.ant.taskdefs.JavaTest$$SpawnEntryPoint" />
+
+  <property name="pipeapp"
+    value="org.apache.tools.ant.taskdefs.JavaTest$$PipeEntryPoint" />
+
+  <target name="testNoJarNoClassname">
+    <java/>
+  </target>
+
+  <target name="testJarNoFork">
+    <java jar="test.jar" fork="false"/>
+  </target>
+
+
+  <target name="testJarAndClassName">
+    <java jar="test.jar" classname="${app}" />
+  </target>
+
+  <target name="testClassnameAndJar">
+    <java classname="${app}" jar="test.jar"  />
+  </target>
+
+  <target name="testRun">
+    <fail unless="tests-classpath.value" />
+    <java classname="${app}"
+      classpath="${tests-classpath.value}"/>
+  </target>
+
+  <target name="testRunFail">
+    <java classname="${app}"
+      classpath="${tests-classpath.value}"
+      >
+      <arg value="2"/>
+    </java>
+  </target>
+
+  <target name="testRunFailFoe">
+    <java classname="${app}"
+      classpath="${tests-classpath.value}"
+      failonerror="true">
+      <arg value="2"/>
+    </java>
+  </target>
+
+  <target name="testRunFailFoeFork">
+    <java classname="${app}"
+        classpath="${tests-classpath.value}"
+        failonerror="true"
+        fork="true">
+        <arg value="2"/>
+    </java>
+  </target>
+
+  <target name="testExcepting">
+    <java classname="${app2}"
+        classpath="${tests-classpath.value}"
+        >
+    </java>
+  </target>
+
+  <target name="testExceptingFork">
+    <java classname="${app2}"
+          classpath="${tests-classpath.value}"
+          fork="true">
+    </java>
+  </target>
+
+  <target name="testExceptingFoe">
+    <java classname="${app2}"
+        classpath="${tests-classpath.value}"
+        failonerror="true">
+    </java>
+  </target>
+
+  <target name="testExceptingFoeFork">
+    <java classname="${app2}"
+        classpath="${tests-classpath.value}"
+        failonerror="true"
+        fork="true">
+    </java>
+  </target>
+
+  <target name="testResultPropertyZero">
+    <java classname="${app}"
+        classpath="${tests-classpath.value}"
+        resultproperty="exitcode"
+        fork="true"
+        >
+    </java>
+    <echo message="exitcode = ${exitcode}"/>
+  </target>
+
+  <target name="testResultPropertyNonZero">
+    <java classname="${app}"
+        classpath="${tests-classpath.value}"
+        resultproperty="exitcode"
+        failonerror="false"
+        fork="true"
+        >
+        <arg value="2"/>
+    </java>
+    <echo message="exitcode = ${exitcode}"/>
+  </target>
+
+   <target name="testResultPropertyZeroNoFork">
+     <java classname="${app}"
+         classpath="${tests-classpath.value}"
+         resultproperty="exitcode"
+         fork="false"
+         >
+         <permissions/>
+     </java>
+     <echo message="exitcode = ${exitcode}"/>
+   </target>
+ 
+    <target name="testResultPropertyNonZeroNoFork">
+        <java classname="${app}"
+              classpath="${tests-classpath.value}"
+              resultproperty="exitcode"
+              failonerror="false"
+              fork="false">
+            <arg value="-1"/>
+            <permissions/>
+        </java>
+        <echo message="exitcode = ${exitcode}"/>
+    </target>
+
+    <target name="testRunFailWithFailOnError">
+      <java classname="${app}"
+        classpath="${tests-classpath.value}"
+        failonerror="true"
+        >
+        <arg value="2"/>
+      </java>
+    </target>
+
+    <target name="testRunSuccessWithFailOnError">
+      <java classname="${app}"
+        classpath="${tests-classpath.value}"
+        failonerror="true"
+        >
+        <arg value="0"/>
+      </java>
+    </target>
+
+    <target name="testSpawn">
+        <java classname="${spawnapp}" fork="true" spawn="true" classpath="${tests-classpath.value}">
+            <arg value="${timeToWait}"/>
+            <arg value="${logFile}" />
+        </java>
+    </target>
+
+    <!--redirection testcases don't want to run under junit unless forked-->
+    <target name="redirect1">
+        <condition property="sleeptime" value="2000">
+            <os family="windows"/>
+        </condition>
+        <property name="sleeptime" value="1"/>
+        <property name="outfile" location="${tmp}/redirect.out" />
+
+        <java classname="${pipeapp}"
+              classpath="${tests-classpath.value}"
+              inputstring="foo"
+              fork="true"
+              output="${outfile}"
+              errorproperty="redirect.err">
+            <arg value="out" />
+        </java>
+
+        <!-- let dumb Windows catch up -->
+        <waitfor>
+            <available file="${outfile}" />
+        </waitfor>
+        <sleep milliseconds="${sleeptime}"/>
+        <loadfile property="redirect.out.contents" srcfile="${outfile}" />
+
+        <condition property="r1file">
+            <equals arg1="${redirect.out.contents}" arg2="foo" />
+        </condition>
+
+        <fail unless="r1file">${outfile}:
+&quot;${redirect.out.contents}&quot; expected &quot;foo&quot;</fail>
+
+        <condition property="r1prop">
+            <equals arg1="${redirect.err}" arg2="" />
+        </condition>
+
+        <fail unless="r1prop">
+redirect.err=&quot;${redirect.err}&quot; should be empty</fail>
+
+    </target>
+
+    <target name="redirect2" depends="redirect1">
+        <property name="outfile" location="${tmp}/redirect.out" />
+
+        <java classname="${pipeapp}"
+              classpath="${tests-classpath.value}"
+              inputstring="bar"
+              append="true"
+              fork="true"
+              output="${outfile}"
+              errorproperty="redirect.err">
+            <arg value="both" />
+        </java>
+
+        <!-- let dumb Windows catch up -->
+        <waitfor>
+            <available file="${outfile}" />
+        </waitfor>
+        <sleep milliseconds="${sleeptime}"/>
+
+        <loadfile property="redirect.out.contents2" srcfile="${outfile}" />
+
+        <condition property="r2file">
+            <equals arg1="${redirect.out.contents2}" arg2="foobar" />
+        </condition>
+
+        <fail unless="r2file">${outfile}:
+&quot;${redirect.out.contents2}&quot; expected &quot;foobar&quot;</fail>
+
+        <condition property="r2prop">
+            <!-- property should not change -->
+            <equals arg1="${redirect.err}" arg2="" />
+        </condition>
+
+        <fail unless="r2prop">
+redirect.err=&quot;${redirect.err}&quot; should be empty</fail>
+
+    </target>
+
+    <target name="redirect3">
+        <property name="outfile" location="${tmp}/redirect.out" />
+        <property name="errfile" location="${tmp}/redirect.err" />
+
+        <java classname="${pipeapp}"
+              classpath="${tests-classpath.value}"
+              inputstring="foo"
+              fork="true"
+              output="${outfile}"
+              error="${errfile}">
+            <arg value="both" />
+        </java>
+
+        <!-- let dumb Windows catch up -->
+        <waitfor>
+            <and>
+                <available file="${outfile}" />
+                <available file="${errfile}" />
+            </and>
+        </waitfor>
+
+        <loadfile property="redirect.out.contents" srcfile="${outfile}" />
+
+        <condition property="r3file">
+            <equals arg1="${redirect.out.contents}" arg2="foo" />
+        </condition>
+
+        <fail unless="r3file">${outfile}:
+&quot;${redirect.out.contents}&quot; expected &quot;foo&quot;</fail>
+
+        <condition property="r3match">
+            <filesmatch file1="${outfile}" file2="${errfile}" />
+        </condition>
+
+        <fail unless="r3file">${errfile} differs from ${outfile}</fail>
+
+    </target>
+
+    <target name="redirector1">
+        <property name="outfile" location="${tmp}/redirector.out" />
+        <property name="errfile" location="${tmp}/redirector.err" />
+
+        <java taskname="foo" classname="${pipeapp}" fork="true"
+              classpath="${tests-classpath.value}">
+            <redirector inputstring="foo"
+                        output="${outfile}"
+                        error="${errfile}"
+                        createemptyfiles="false" />
+            <arg value="out" />
+        </java>
+
+        <!-- let dumb Windows catch up -->
+        <waitfor>
+            <available file="${outfile}" />
+        </waitfor>
+
+        <loadfile property="redirector.out.contents" srcfile="${outfile}" />
+
+        <condition property="ror1out">
+            <equals arg1="${redirector.out.contents}" arg2="foo" />
+        </condition>
+
+        <fail unless="ror1out">${outfile}:
+&quot;${redirector.out.contents}&quot; expected &quot;foo&quot;</fail>
+
+        <condition property="ror1noerr">
+            <not>
+                <available file="${errfile}" />
+            </not>
+        </condition>
+        <fail unless="ror1noerr">${errfile} exists but should not</fail>
+    </target>
+
+    <target name="redirector2" depends="redirector1">
+        <property name="outfile" location="${tmp}/redirector.out" />
+        <property name="errfile" location="${tmp}/redirector.err" />
+
+        <!-- fork here; some VMs can be ill-behaved with files,
+            such as W!nd0ws -->
+        <java taskname="foo" classname="${pipeapp}" fork="true"
+              classpath="${tests-classpath.value}">
+            <redirector inputstring="foo"
+                        append="true"
+                        output="${outfile}"
+                        error="${errfile}"
+                        createemptyfiles="false">
+                <errorfilterchain>
+                    <replacestring from="foo" to="bar" />
+                </errorfilterchain>
+            </redirector>
+            <arg value="both" />
+        </java>
+
+        <!-- let dumb Windows catch up -->
+        <waitfor>
+            <and>
+                <available file="${outfile}" />
+                <available file="${errfile}" />
+            </and>
+        </waitfor>
+
+        <loadfile property="redirector.out.contents2"
+                  srcfile="${outfile}" />
+
+        <loadfile property="redirector.err.contents"
+                  srcfile="${errfile}" />
+
+        <condition property="ror2out">
+            <equals arg1="${redirector.out.contents2}" arg2="foofoo" />
+        </condition>
+
+        <fail unless="ror1out">${outfile}:
+&quot;${redirector.out.contents}&quot; expected &quot;foofoo&quot;</fail>
+
+        <condition property="ror2err">
+            <equals arg1="${redirector.err.contents}" arg2="bar" />
+        </condition>
+
+        <fail unless="ror1out">${errfile}:
+&quot;${redirector.err.contents}&quot; expected &quot;bar&quot;</fail>
+
+    </target>
+
+    <target name="cleanup">
+        <delete>
+            <fileset file="${logFile}" />
+            <fileset dir="${tmp}" includes="redirect*" />
+        </delete>
+    </target>
+
+    <target name="foo" />
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/length.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/length.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/length.xml	(revision 16139)
@@ -0,0 +1,288 @@
+<project name="length">
+  <property name="dir" location="lengthtestdir" />
+  <property name="dir.a" location="${dir}/a" />
+  <property name="dir.b" location="${dir}/b" />
+  <property name="zipfile" location="lengthtest.zip" />
+
+  <target name="init">
+    <mkdir dir="${dir.a}" />
+    <mkdir dir="${dir.b}" />
+    <property name="foo" location="${dir.a}/foo" />
+    <property name="bar" location="${dir.b}/bar" />
+    <echo file="${foo}" message="foo" />
+    <echo file="${bar}" message="bar" />
+  </target>
+
+  <target name="testEach" depends="init">
+    <length mode="each" property="length.each">
+      <fileset id="fs" dir="${dir}" />
+    </length>
+    <length string="${length.each}" property="length.length.each" />
+    <length string="${foo}${bar}........${line.separator}"
+            property="length.expected" />
+    <fail>
+      <!-- test that both files are represented, and that the
+           output is the expected length; do not assume order. -->
+      <condition>
+        <not>
+          <and>
+            <contains string="${length.each}" substring="${foo} : 3" />
+            <contains string="${length.each}" substring="${bar} : 3" />
+            <equals arg1="${length.length.each}" arg2="${length.expected}" />
+          </and>
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testEachCondition" depends="init">
+    <length mode="each" property="length.each">
+      <fileset id="fs" dir="${dir}" />
+    </length>
+    <length string="${foo}${bar}........${line.separator}"
+            property="length.expected" />
+    <fail>
+      <!-- test that both files are represented, and that the
+           output is the expected length; do not assume order. -->
+      <condition>
+        <not>
+          <and>
+            <contains string="${length.each}" substring="${foo} : 3" />
+            <contains string="${length.each}" substring="${bar} : 3" />
+            <length string="${length.each}" length="${length.expected}" />
+          </and>
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testAll" depends="init">
+    <length property="length.all">
+      <fileset id="foo" file="${dir.a}/foo" />
+      <fileset id="bar" file="${dir.b}/bar" />
+    </length>
+    <fail>
+      <condition>
+        <not>
+          <equals arg1="6" arg2="${length.all}" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testAllCondition" depends="init">
+    <fail>
+      <condition>
+        <not>
+          <length length="6">
+            <fileset id="foo" file="${dir.a}/foo" />
+            <fileset id="bar" file="${dir.b}/bar" />
+          </length>
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testFile" depends="init">
+    <length property="length.foo" file="${dir.a}/foo" />
+    <fail>
+      <condition>
+        <not>
+          <equals arg1="3" arg2="${length.foo}" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testFileCondition" depends="init">
+    <fail>
+      <condition>
+        <not>
+          <length length="3" file="${dir.a}/foo" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testBoth" depends="init">
+    <length property="length.foo" file="${dir.a}/foo">
+      <fileset file="${dir.b}/bar" />
+    </length>
+    <fail>
+      <condition>
+        <not>
+          <equals arg1="6" arg2="${length.foo}" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testBothCondition" depends="init">
+    <fail>
+      <condition>
+        <not>
+          <length length="6" file="${dir.a}/foo">
+            <fileset file="${dir.b}/bar" />
+          </length>
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testDupes" depends="init">
+    <length property="length.foo" file="${dir.a}/foo">
+      <fileset dir="${dir}" />
+    </length>
+    <fail>
+      <condition>
+        <not>
+          <equals arg1="9" arg2="${length.foo}" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testDupesCondition" depends="init">
+    <fail>
+      <condition>
+        <not>
+          <length length="9" file="${dir.a}/foo">
+            <fileset dir="${dir}" />
+          </length>
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testString">
+    <length string="foo" property="length.string" />
+    <fail>
+      <condition>
+        <not>
+          <equals arg1="3" arg2="${length.string}" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testStringCondition">
+    <fail>
+      <condition>
+        <not>
+          <length string="foo" length="3" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testTrimString">
+    <length string=" foo " trim="true" property="length.string" />
+    <fail>
+      <condition>
+        <not>
+          <equals arg1="3" arg2="${length.string}" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testTrimStringCondition">
+    <fail>
+      <condition>
+        <not>
+          <length string=" foo " trim="true" length="3" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testNoTrimString">
+    <length string=" foo " property="length.string" />
+    <fail>
+      <condition>
+        <not>
+          <equals arg1="5" arg2="${length.string}" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testNoTrimStringCondition">
+    <fail>
+      <condition>
+        <not>
+          <length string=" foo " length="5" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testTrimFile" description="should fail">
+    <length file="${ant.file}" trim="false" />
+  </target>
+
+  <target name="testStringFile" description="should fail">
+    <length string="foo" file="${ant.file}" />
+  </target>
+
+  <target name="testImmutable">
+    <length string="foo" property="length.string" />
+    <length string="foobar" property="length.string" />
+    <fail>
+      <condition>
+        <not>
+          <equals arg1="3" arg2="${length.string}" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="zip" depends="init">
+    <zip destfile="${zipfile}">
+      <fileset file="${foo}" />
+      <fileset file="${bar}" />
+    </zip>
+  </target>
+
+  <target name="testZipFileSet" depends="zip">
+    <length property="length.zipfile1">
+      <zipfileset src="${zipfile}" />
+    </length>
+    <length property="length.zipfile2">
+      <zipfileset src="${zipfile}" includes="bar" />
+    </length>
+    <fail>
+      <condition>
+        <not>
+          <and>
+            <equals arg1="6" arg2="${length.zipfile1}" />
+            <equals arg1="3" arg2="${length.zipfile2}" />
+          </and>
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testZipFileSetCondition" depends="zip">
+    <fail>
+      <condition>
+        <not>
+          <and>
+            <length length="6">
+              <zipfileset src="${zipfile}" />
+            </length>
+            <length length="3">
+              <zipfileset src="${zipfile}" includes="bar" />
+            </length>
+          </and>
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="cleanup">
+    <delete dir="${dir}" />
+    <delete file="${zipfile}" />
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/loadfile.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/loadfile.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/loadfile.xml	(revision 16139)
@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="loadfile-test" basedir="." default="testLoadAFile">
+
+
+  <target name="init">
+  </target>
+
+  <target name="testNoSourcefileDefined" depends="init">
+    <loadfile property="foo" />
+  </target>
+
+  <target name="testNoPropertyDefined"
+    depends="init">
+    <loadfile srcFile="somefile" />
+  </target>
+
+
+  <target name="testNoSourcefilefound"
+    depends="init">
+    <loadfile property="missing" srcFile="somefile" />
+  </target>
+
+  <target name="testFailOnError"
+    depends="init">
+    <loadfile
+      property="testFailOnError"
+      srcFile="somefile"
+      failonerror="false"/>
+  </target>
+
+  <target name="testLoadAFile"
+    depends="init">
+    <echo
+      message="What's it going to be then, eh?"
+      file="loadfile1.tmp"
+      />
+    <loadfile property="testLoadAFile" srcFile="loadfile1.tmp" />
+    <echo>${testLoadAFile}</echo>
+  </target>
+
+  <target name="testLoadAFileEnc"
+    depends="init">
+    <loadfile property="testLoadAFileEnc"
+      srcFile="loadfile.xml"
+      encoding="ISO-8859-1"/>
+  </target>
+
+  <target name="testEvalProps"
+    depends="init">
+    <property name="weather" value="rain" />
+    <echo
+      message="All these moments will be lost in time, like teardrops in the ${weather}"
+      file="loadfile1.tmp"
+      />
+    <loadfile property="testEvalProps"
+        srcFile="loadfile1.tmp">
+        <filterchain>
+          <expandproperties/>
+        </filterchain>
+    </loadfile>
+    <echo>${testEvalProps}</echo>
+  </target>
+
+  <target name="testFilterChain"
+    depends="init">
+      <echo file="loadfile1.tmp">#Line 1
+REM Line 2
+--Line 3
+Line 4
+Hello World!</echo>
+      <loadfile srcFile="loadfile1.tmp" 
+        property="testFilterChain">
+        <filterchain>
+          <headfilter lines="5"/>
+          <striplinecomments>
+            <comment value="--"/>
+            <comment value="REM "/>
+            <comment value="#"/>
+          </striplinecomments>
+          <filterreader classname="org.apache.tools.ant.filters.TailFilter">
+            <param name="lines" value="1"/>
+          </filterreader>
+          <linecontains>
+            <contains value="World!"/>
+          </linecontains>
+        </filterchain>
+      </loadfile>
+  </target>
+
+  <target name="testStripJavaComments"
+    depends="init">
+      <echo file="loadfile1.tmp">
+/*
+Comment "1"
+*/
+public class test1 {
+    //Some comment
+    int x = 1/2;
+    private static final String GREETING="*/Hello/*";
+    private static final String GREETING1="/*Hello*/";
+
+    public static void main( String args[] ) {
+    }
+}</echo>
+      <echo file="nocomments.tmp">
+
+public class test1 {
+    
+    int x = 1/2;
+    private static final String GREETING="*/Hello/*";
+    private static final String GREETING1="/*Hello*/";
+
+    public static void main( String args[] ) {
+    }
+}</echo>
+      <loadfile srcFile="loadfile1.tmp" 
+        property="testStripJavaComments">
+        <filterchain>
+          <stripjavacomments/>
+        </filterchain>
+      </loadfile>
+      <loadfile srcFile="nocomments.tmp" 
+        property="expected"/>
+  </target>
+
+  <target name="testOneLine"
+    depends="init">
+    <echo
+      message="1,&#10;2,&#13;3,&#13;&#10;4"
+      file="loadfile1.tmp"
+      />
+    <loadfile property="testOneLine"
+        srcFile="loadfile1.tmp">
+      <filterchain>
+        <striplinebreaks/>
+      </filterchain>
+    </loadfile>
+    <echo>${testOneLine}</echo>
+  </target>
+
+
+  <target name="cleanup">
+    <delete file="loadfile1.tmp"/>
+    <delete file="nocomments.tmp"/>
+  </target>
+
+  </project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/loadproperties.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/loadproperties.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/loadproperties.xml	(revision 16139)
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<project name="loadproperties-test" 
+         basedir="." 
+         default="testPrefixedProperties">
+
+  <target name="init">
+  </target>
+
+  <target name="testPrefixedProperties" depends="init">
+    <property name="server" value="localhost"/>
+    <echo file="properties.tmp">
+#http.@PORT@ = 90
+http.@PORT@ = 80
+http.@SERVER@ = ${server}
+    </echo>
+    <loadproperties srcFile="properties.tmp">
+      <filterchain>
+        <striplinecomments>
+          <comment value="#"/>
+        </striplinecomments>
+        <prefixlines prefix="server1."/>
+        <replacetokens>
+          <token key="PORT" value="port"/>
+          <token key="SERVER" value="server"/>
+        </replacetokens>
+        <expandproperties/>
+      </filterchain>
+    </loadproperties>
+    <property name="server1.http.url" 
+      value="http://${server1.http.server}:${server1.http.port}"/>
+  </target>
+
+  <target name="testPropertiesFromResource" depends="init">
+    <echo file="properties.tmp">
+#tpfr.a=a
+tpfr.a=A
+tpfr.b=b\
+       e
+tpfr.c=@C@
+    </echo>
+    <loadproperties resource="properties.tmp" classpath="${basedir}">
+      <filterchain>
+        <replacetokens>
+          <token key="C" value="sea"/>
+        </replacetokens>
+      </filterchain>
+    </loadproperties>
+    <condition property="testPropertiesFromResource.ok">
+        <equals arg1="Abesea" arg2="${tpfr.a}${tpfr.b}${tpfr.c}" />
+    </condition>
+    <fail unless="testPropertiesFromResource.ok">
+$${tpfr.a}$${tpfr.b}$${tpfr.c}=&quot;${tpfr.a}${tpfr.b}${tpfr.c}&quot;
+    </fail>
+  </target>
+
+  <target name="cleanup">
+    <delete file="properties.tmp"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/macrodef.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/macrodef.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/macrodef.xml	(revision 16139)
@@ -0,0 +1,243 @@
+<project>
+
+  <target name="simple">
+    <macrodef name="my.echo">
+      <attribute name="text"/>
+      <sequential>
+        <echo message="@{text}"/>
+      </sequential>
+    </macrodef>
+    <my.echo text="Hello World"/>
+  </target>
+
+  <target name="text">
+    <macrodef name="my.echo">
+      <attribute name="text"/>
+      <sequential>
+        <echo>@{text}</echo>
+      </sequential>
+    </macrodef>
+    <my.echo text="Inner Text"/>
+  </target>
+
+  <target name="duplicate.attribute">
+    <macrodef name="my.echo">
+      <attribute name="text"/>
+      <attribute name="text"/>
+      <sequential>
+        <echo>@{text}</echo>
+      </sequential>
+    </macrodef>
+  </target>
+
+  <target name="duplicate.element">
+    <macrodef name="my.echo">
+      <element name="text"/>
+      <element name="text"/>
+      <sequential>
+        <text/>
+      </sequential>
+    </macrodef>
+  </target>
+
+  <target name="uri">
+    <macrodef name="echo" uri="abc">
+      <attribute name="text"/>
+      <sequential>
+        <echo message="@{text}"/>
+      </sequential>
+    </macrodef>
+    <x:echo xmlns:x="abc" text="Hello World"/>
+  </target>
+
+  <target name="nested">
+    <macrodef name="nested">
+      <element name="nested"/>
+      <sequential>
+        <nested/>
+      </sequential>
+    </macrodef>
+
+    <nested>
+      <nested>
+        <echo>A nested element</echo>
+      </nested>
+    </nested>
+  </target>
+
+  <target name="double">
+    <macrodef name="double">
+      <attribute name="prop"/>
+      <sequential>
+        <echo>@@{prop} is '@{prop}', value of $${@{prop}} is '${@{prop}}'</echo>
+      </sequential>
+    </macrodef>
+    <property name="property" value="A property value"/>
+    <double prop="property"/>
+  </target>
+
+  <target name="ignorecase">
+    <macrodef name="ignore">
+      <attribute name="MyAttribute"/>
+      <sequential>
+        <echo>@{myattribute} is @{MYATTRIBUTE}</echo>
+      </sequential>
+    </macrodef>
+    <ignore myattribute="a"/>
+    <ignore Myattribute="b"/>
+  </target>
+
+  <target name="ignore-element-case">
+    <macrodef name="ignore">
+      <element name="MyElement"/>
+      <sequential>
+        <myElement/>
+        <MyElEmEnT/>
+      </sequential>
+    </macrodef>
+    <ignore>
+      <MYELEMENT>
+        <echo>nested element</echo>
+      </MYELEMENT>
+    </ignore>
+  </target>
+
+  <target name="textelement">
+    <macrodef name="echotest">
+      <text name="text" optional="yes"/>
+      <sequential>
+        <echo>@{text}</echo>
+      </sequential>
+    </macrodef>
+    <echotest>
+      Hello world
+    </echotest>
+  </target>
+
+  <target name="text.trim">
+    <macrodef name="echotest">
+      <text name="text" trim="yes"/>
+      <sequential>
+        <echo>[@{text}]</echo>
+      </sequential>
+    </macrodef>
+    <echotest>
+      Hello world
+    </echotest>
+  </target>
+
+  <target name="duplicatetextname">
+    <macrodef name="echotest">
+      <attribute name="text"/>
+      <text name="text"/>
+      <sequential>
+        <echo>@{text}</echo>
+      </sequential>
+    </macrodef>
+  </target>
+
+  <target name="duplicatetextname2">
+    <macrodef name="echotest">
+      <text name="text"/>
+      <attribute name="text"/>
+      <sequential>
+        <echo>@{text}</echo>
+      </sequential>
+    </macrodef>
+  </target>
+
+  <target name="escape">
+    <macrodef name="escape">
+      <attribute name="a"/>
+      <attribute name="b"/>
+      <sequential>
+        <echo>a@b or a@@b is @{a}@@@{b}</echo>
+      </sequential>
+    </macrodef>
+    <escape a="avalue" b="bvalue"/>
+  </target>
+
+  <target name="attribute.description">
+    <macrodef name="d">
+      <attribute name="description"/>
+      <attribute name="d" default="p"/>
+      <sequential>
+        <echo>description is @{description}</echo>
+      </sequential>
+    </macrodef>
+    <d description="hello world"/>
+  </target>
+
+  <target name="implicit">
+    <macrodef name="implicit">
+      <element name="implicit" implicit="yes"/>
+      <sequential>
+        <echo>Before implicit</echo>
+        <implicit/>
+        <echo>After implicit</echo>
+      </sequential>
+    </macrodef>
+
+    <implicit>
+      <echo>In implicit</echo>
+    </implicit>
+  </target>
+
+  <target name="implicit.notoptional">
+    <macrodef name="implicit">
+      <element name="implicit" implicit="yes"/>
+      <sequential>
+        <echo>Before implicit</echo>
+        <implicit/>
+        <echo>After implicit</echo>
+      </sequential>
+    </macrodef>
+
+    <implicit>
+    </implicit>
+  </target>
+
+  <target name="implicit.optional">
+    <macrodef name="implicit">
+      <element name="implicit" optional="yes" implicit="yes"/>
+      <sequential>
+        <echo>Before implicit</echo>
+        <implicit/>
+        <echo>After implicit</echo>
+      </sequential>
+    </macrodef>
+
+    <implicit>
+    </implicit>
+  </target>
+
+  <target name="implicit.explicit">
+    <macrodef name="implicit">
+      <element name="explicit" optional="yes"/>
+      <element name="implicit" optional="yes" implicit="yes"/>
+      <sequential>
+        <implicit/>
+        <explicit/>
+      </sequential>
+    </macrodef>
+  </target>
+
+  <property name="default.override" value="old"/>
+  <macrodef name="simple.override">
+    <attribute name="attr" default="${default.override}"/>
+    <sequential>
+      <echo>value is @{attr}</echo>
+    </sequential>
+  </macrodef>
+
+  <target name="override.default">
+    <antcall target="override.call">
+      <param name="default.override" value="new"/>
+    </antcall>
+  </target>
+
+  <target name="override.call">
+    <simple.override/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/manifest.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/manifest.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/manifest.xml	(revision 16139)
@@ -0,0 +1,211 @@
+<?xml version="1.0"?>
+
+<!-- Manifest tests build file
+     author: Conor MacNeill -->
+<project name="manifest-test" basedir="." default="test1">
+
+  <target name="test1">
+    <jar file="mftest1.jar" manifest="manifests/test1.mf"/>
+    <unjar src="mftest1.jar" dest="manifests">
+      <patternset>
+        <include name="META-INF/MANIFEST.MF"/>
+      </patternset>
+    </unjar>
+  </target>
+  
+  <target name="test2">
+    <jar file="mftest2.jar" manifest="manifests/test2.mf"/>
+    <unjar src="mftest2.jar" dest="manifests">
+      <patternset>
+        <include name="META-INF/MANIFEST.MF"/>
+      </patternset>
+    </unjar>
+  </target>
+  
+  <target name="test3">
+    <jar file="mftest3.jar" manifest="manifests/test3.mf"/>
+  </target>
+  
+  <target name="test4">
+    <jar file="mftest4.jar" manifest="manifests/test4.mf"/>
+  </target>
+  
+  <target name="test5">
+    <jar file="mftest5.jar" manifest="manifests/test5.mf"/>
+  </target>
+  
+  <target name="test6">
+    <jar file="mftest6.jar" manifest="manifests/test6.mf"/>
+  </target>
+  
+  <target name="test7">
+    <jar file="mftest7.jar" manifest="manifests/test7.mf"/>
+  </target>
+  
+  <target name="test8">
+    <jar file="mftest8.jar">
+      <manifest>
+        <attribute name="Class-Path" value="fubar"/>
+        <section name="Test">
+          <attribute name="TestAttr" value="Test"/>
+        </section>
+      </manifest>
+    </jar>
+    <unjar src="mftest8.jar" dest="manifests">
+      <patternset>
+        <include name="META-INF/MANIFEST.MF"/>
+      </patternset>
+    </unjar>
+  </target>
+
+  <target name="test9">
+    <jar file="mftest9.jar">
+      <manifest>
+        <attribute name="Class-Path" value="fubar"/>
+        <section name="Test">
+          <attribute name="Name" value="Test"/>
+        </section>
+      </manifest>
+    </jar>
+  </target>
+
+  <target name="test10">
+    <jar file="mftest10.jar">
+      <manifest>
+        <attribute value="fubar"/>
+      </manifest>
+    </jar>
+  </target>
+
+  <target name="test11">
+    <jar file="mftest11.jar">
+      <manifest>
+        <attribute name="Test"/>
+      </manifest>
+    </jar>
+  </target>
+
+  <target name="test12">
+    <jar file="mftest12.jar">
+      <manifest>
+        <section>
+          <attribute name="TestAttr" value="Test"/>
+        </section>
+      </manifest>
+    </jar>
+  </target>
+
+  <target name="test13">
+    <jar file="mftest13.jar">
+      <manifest>
+        <attribute name="Test" value="Test1"/>
+        <attribute name="Test" value="Test2"/>
+      </manifest>
+    </jar>
+  </target>
+
+  <target name="test14">
+    <jar file="mftest14.jar">
+      <manifest>
+        <attribute name="Class-path" value="Test1"/>
+        <attribute name="Class-path" value="Test2"/>
+        <attribute name="Class-Path" value="Test3"/>
+        <attribute name="class-Path" value="Test4"/>
+      </manifest>
+    </jar>
+    <unjar src="mftest14.jar" dest="manifests">
+      <patternset>
+        <include name="META-INF/MANIFEST.MF"/>
+      </patternset>
+    </unjar>
+  </target>
+
+  <target name="testNoFile">
+    <manifest />
+  </target>
+
+  <target name="testLongLine">
+    <jar file="mftestLongLine.jar">
+      <manifest>
+        <attribute name="Class-path" 
+                   value="${test.longline}"/>
+      </manifest>
+    </jar>
+    <unjar src="mftestLongLine.jar" dest="manifests">
+      <patternset>
+        <include name="META-INF/MANIFEST.MF"/>
+      </patternset>
+    </unjar>
+  </target>
+
+  <target name="testOrder1">
+    <jar file="mftestOrder1.jar">
+      <manifest>
+        <section name="Test1">
+          <attribute name="TestAttr1" value="Test1"/>
+          <attribute name="TestAttr2" value="Test2"/>
+        </section>
+        <section name="Test2">
+          <attribute name="TestAttrx" value="Testx"/>
+        </section>
+      </manifest>
+    </jar>
+    <unjar src="mftestOrder1.jar" dest="manifests">
+      <patternset>
+        <include name="META-INF/MANIFEST.MF"/>
+      </patternset>
+    </unjar>
+  </target>
+
+  <target name="testOrder2">
+    <jar file="mftestOrder2.jar">
+      <manifest>
+        <section name="Test2">
+          <attribute name="TestAttrx" value="Testx"/>
+        </section>
+        <section name="Test1">
+          <attribute name="TestAttr2" value="Test2"/>
+          <attribute name="TestAttr1" value="Test1"/>
+        </section>
+      </manifest>
+    </jar>
+    <unjar src="mftestOrder2.jar" dest="manifests">
+      <patternset>
+        <include name="META-INF/MANIFEST.MF"/>
+      </patternset>
+    </unjar>
+  </target>
+
+  
+  
+  <target name="testReplace">
+    <copy file="manifests/test2.mf" toFile="mftest.mf" />
+    <manifest file="mftest.mf" />
+  </target>
+
+  <target name="testUpdate">
+    <copy file="manifests/test2.mf" toFile="mftest.mf" />
+    <manifest file="mftest.mf" mode="update">
+      <attribute name="Foo" value="Bar" />
+    </manifest>
+
+    <copy file="manifests/test2.mf" toFile="mftest2.mf" />
+    <manifest file="mftest2.mf" mode="update">
+      <section name="Test">
+        <attribute name="Foo" value="Bar" />
+      </section>
+    </manifest>
+    <manifest file="mftest2.mf" mode="update">
+      <section name="Test">
+        <attribute name="Foo" value="Baz" />
+      </section>
+    </manifest>
+  </target>
+
+  <target name="clean">
+    <delete>
+      <fileset dir="." includes="mftest*"/>
+    </delete>
+    <delete dir="manifests/META-INF"/>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test1.mf
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test1.mf	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test1.mf	(revision 16139)
@@ -0,0 +1,1 @@
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test2.mf
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test2.mf	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test2.mf	(revision 16139)
@@ -0,0 +1,2 @@
+Manifest-Version: 2.0
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test3.mf
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test3.mf	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test3.mf	(revision 16139)
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Header-without-colon maybe mistyped
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test4.mf
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test4.mf	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test4.mf	(revision 16139)
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+
+ Can't start with a continuation line
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test5.mf
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test5.mf	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test5.mf	(revision 16139)
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Name: test5 
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test6.mf
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test6.mf	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test6.mf	(revision 16139)
@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+
+Test: test6
+Class-Path: fubar 
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test7.mf
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test7.mf	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/manifests/test7.mf	(revision 16139)
@@ -0,0 +1,4 @@
+Manifest-Version: 1.0
+Class-Path: fubar
+From: Jack 
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/mkdir.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/mkdir.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/mkdir.xml	(revision 16139)
@@ -0,0 +1,17 @@
+<?xml version="1.0"?>
+
+<project name="xxx-test" basedir="." default="test1">
+
+  <target name="test1">
+    <mkdir/>
+  </target>
+
+  <target name="test2">
+    <mkdir dir="template.xml"/>
+  </target>
+
+  <target name="test3">
+    <mkdir dir="testdir.tmp"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/move.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/move.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/move.xml	(revision 16139)
@@ -0,0 +1,229 @@
+<?xml version="1.0"?>
+
+<project name="move-test" basedir="." default="testFilterSet">
+
+  <target name="testFilterSet">
+    <copy file="copy.filterset" tofile="move.filterset"/>
+    <move file="move.filterset" tofile="move.filterset.tmp">
+      <filterset>
+        <filter token="TITLE" value="Apache Ant Project"/>
+      </filterset>
+    </move>
+  </target>
+
+  <target name="testFilterChain">
+    <copy file="copy.filterset" tofile="move.filterchain"/>
+    <move file="move.filterchain" tofile="move.filterchain.tmp">
+      <filterchain>
+        <replacetokens>
+          <token key="TITLE" value="Apache Ant Project"/>
+        </replacetokens>
+      </filterchain>
+    </move>
+  </target>
+
+  <!-- Bugzilla Report 11732 -->
+  <target name="testDirectoryRemoval">
+    <mkdir dir="A/B"/>
+    <mkdir dir="A/C"/>
+    <mkdir dir="A/D"/>
+    <touch file="A/B/1"/>
+    <touch file="A/C/2"/>
+    <touch file="A/D/3"/>
+    <mkdir dir="E"/>
+    <move todir="E" includeemptydirs="true">
+      <fileset dir="A">
+        <include name="C"/>
+        <include name="D"/>
+        <include name="C/**"/>
+        <include name="D/**"/>
+      </fileset>
+    </move>
+  </target>
+
+  <!-- Bugzilla Report 18886 -->
+  <target name="testDirectoryRetaining">
+    <mkdir dir="A"/>
+    <touch file="A/1"/>
+    <mkdir dir="E"/>
+    <move todir="E" includeemptydirs="true">
+      <fileset dir="A" includes="1"/>
+    </move>
+  </target>
+
+  <target name="testCompleteDirectoryMove">
+    <mkdir dir="A"/>
+    <touch file="A/1"/>
+    <move todir="E">
+      <fileset dir="A"/>
+    </move>
+  </target>
+
+  <target name="testMoveFileAndFileset">
+    <mkdir dir="A" />
+    <touch>
+      <filelist dir="A" files="1,2,3" />
+    </touch>
+    <move todir="E" file="A/1">
+      <fileset dir="A" includes="2,3" />
+    </move>
+    <fail message="A unavailable">
+      <condition>
+        <not>
+          <available file="A" type="dir" />
+        </not>
+      </condition>
+    </fail>
+    <fail message="A/1 not moved">
+      <condition>
+        <or>
+          <available file="A/1" type="file" />
+          <not>
+            <available file="E/1" type="file" />
+          </not>
+        </or>
+      </condition>
+    </fail>
+    <fail message="A/2 not moved">
+      <condition>
+        <or>
+          <available file="A/2" type="file" />
+          <not>
+            <available file="E/2" type="file" />
+          </not>
+        </or>
+      </condition>
+    </fail>
+    <fail message="A/3 not moved">
+      <condition>
+        <or>
+          <available file="A/3" type="file" />
+          <not>
+            <available file="E/3" type="file" />
+          </not>
+        </or>
+      </condition>
+    </fail>
+  </target>
+
+  <macrodef name="verifymove">
+    <attribute name="newfile" />
+    <attribute name="olddir" />
+    <sequential>
+        <fail message="@{newfile} not available">
+          <condition>
+            <not>
+              <available file="@{newfile}" type="file" />
+            </not>
+          </condition>
+        </fail>
+        <fail message="@{olddir} remains">
+          <condition>
+            <available file="@{olddir}" type="dir" />
+          </condition>
+        </fail>
+    </sequential>
+  </macrodef>
+
+  <target name="testCompleteDirectoryMoveToExistingDir">
+    <mkdir dir="A" />
+    <touch file="A/1" />
+    <mkdir dir="E" />
+    <touch file="E/2" />
+    <move todir="E">
+      <fileset dir="A" />
+    </move>
+    <verifymove newfile="E/1" olddir="A" />
+    <fail message="E/2 unavailable">
+      <condition>
+        <not>
+          <available file="E/2" type="file" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testCompleteDirectoryMoveFileToFile">
+    <mkdir dir="A"/>
+    <touch file="A/1"/>
+    <move file="A" tofile="E" />
+    <verifymove newfile="E/1" olddir="A" />
+  </target>
+
+  <target name="testCompleteDirectoryMoveFileToDir">
+    <mkdir dir="A"/>
+    <touch file="A/1"/>
+    <move file="A" todir="E" />
+    <verifymove newfile="E/A/1" olddir="A" />
+  </target>
+
+  <target name="testCompleteDirectoryMoveFileAndFileset">
+    <mkdir dir="A/1" />
+    <touch file="A/2" />
+    <move file="A/1" todir="E">
+      <fileset dir="A" includes="2" />
+    </move>
+    <fail message="A unavailable">
+      <condition>
+        <not>
+          <available file="A" type="dir" />
+        </not>
+      </condition>
+    </fail>
+    <fail message="E/1 unavailable">
+      <condition>
+        <not>
+          <available file="E/1" type="dir" />
+        </not>
+      </condition>
+    </fail>
+    <fail message="E/2 unavailable">
+      <condition>
+        <not>
+          <available file="E/2" type="file" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testCompleteDirectoryMoveFileToExistingFile">
+    <mkdir dir="A"/>
+    <touch file="A/1"/>
+    <touch file="E"/>
+    <move file="A" tofile="E" />
+  </target>
+
+  <target name="testCompleteDirectoryMoveFileToExistingDir">
+    <mkdir dir="A"/>
+    <touch file="A/1"/>
+    <mkdir dir="E"/>
+    <move file="A" tofile="E" />
+    <verifymove newfile="E/1" olddir="A" />
+  </target>
+
+  <target name="testCompleteDirectoryMoveFileToDirWithExistingFile">
+    <mkdir dir="A"/>
+    <touch file="A/1"/>
+    <mkdir dir="E"/>
+    <touch file="E/A"/>
+    <move file="A" todir="E" />
+  </target>
+
+  <target name="testCompleteDirectoryMoveFileToDirWithExistingDir">
+    <mkdir dir="A"/>
+    <touch file="A/1"/>
+    <mkdir dir="E"/>
+    <mkdir dir="E/A"/>
+    <move file="A" todir="E" />
+    <verifymove newfile="E/A/1" olddir="A" />
+  </target>
+
+  <target name="cleanup"> 
+    <delete file="move.filterset.tmp"/>
+    <delete file="move.filterchain.tmp"/>
+    <delete dir="A" />
+    <delete file="B" />
+    <delete dir="E" />
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/multimap.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/multimap.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/multimap.xml	(revision 16139)
@@ -0,0 +1,176 @@
+<?xml version="1.0"?>
+<project name="test" basedir=".">
+  <path id="testclasses">
+    <pathelement location="../../../../build/testcases" />
+    <pathelement path="${java.class.path}" />
+  </path>
+
+  <mapper id="testmapper"
+          classname="org.apache.tools.ant.taskdefs.MultiMapTest$TestMapper"
+          classpathref="testclasses"/>
+
+  <property name="map.ext" value=".copy2"/>
+  <property name="rootScratchDir" value="test_multi_mapper_scratch_area"/>
+  <property name="baseScratchSrc" value="${rootScratchDir}/src"/>
+  <property name="baseScratchDest" value="${rootScratchDir}/dest"/>
+
+  <target name="setup">
+    <delete dir="${baseScratchSrc}"/>
+    <mkdir dir="${baseScratchSrc}"/>
+    <delete dir="${baseScratchDest}"/>
+    <mkdir dir="${baseScratchDest}"/>
+    <touch file="${baseScratchSrc}/somefile.txt"/>
+  </target>
+
+  <!-- test simple single file to multiple file move -->
+  <target name="multicopy" depends="setup">
+    <copy todir="${baseScratchDest}" enablemultiplemappings="true">
+      <fileset dir="${baseScratchSrc}" includes="somefile.txt"/>
+      <mapper refid="testmapper"/>
+    </copy>
+    <condition property="multicopy.outcome">
+      <and>
+        <available file="${baseScratchDest}/somefile.txt"/>
+        <available file="${baseScratchDest}/somefile.txt${map.ext}"/>
+      </and>
+    </condition>
+    <fail unless="multicopy.outcome">multicopy failed</fail>
+  </target>
+
+  <target name="multimove" depends="setup">
+    <move todir="${baseScratchDest}" enablemultiplemappings="true">
+      <fileset dir="${baseScratchSrc}" includes="somefile.txt"/>
+      <mapper refid="testmapper"/>
+    </move>
+    <condition property="test2.outcome">
+      <and>
+        <available file="${baseScratchDest}/somefile.txt"/>
+        <available file="${baseScratchDest}/somefile.txt${map.ext}"/>
+        <not>
+          <available file="${baseScratchSrc}/somefile.txt"/>
+        </not>
+        <not>
+          <available file="${baseScratchSrc}/somefile.txt${map.ext}"/>
+        </not>
+      </and>
+    </condition>
+    <fail unless="test2.outcome">mulitmove failed</fail>
+  </target>
+  
+  <!-- 
+       test traditional single file to single file copy explicitly telling
+       task to ignore multiple mappings
+       -->
+
+  <target name="singlecopy" depends="setup">
+    <copy todir="${baseScratchDest}" enablemultiplemappings="false">
+      <fileset dir="${baseScratchSrc}" includes="somefile.txt"/>
+      <mapper refid="testmapper"/>
+    </copy>
+    <condition property="singlecopy.outcome">
+      <and>
+        <available file="${baseScratchDest}/somefile.txt"/>
+        <not>
+          <available file="${baseScratchDest}/somefile.txt${map.ext}"/>
+        </not>
+        <available file="${baseScratchSrc}/somefile.txt"/>
+      </and>
+    </condition>
+    <fail unless="singlecopy.outcome">singlecopy failed</fail>
+  </target>
+
+  <target name="singlemove" depends="setup">
+    <move todir="${baseScratchDest}" enablemultiplemappings="false">
+      <fileset dir="${baseScratchSrc}" includes="somefile.txt"/>
+      <mapper refid="testmapper"/>
+    </move>
+    <condition property="singlemove.outcome">
+      <and>
+        <available file="${baseScratchDest}/somefile.txt"/>
+        <not>
+          <available file="${baseScratchDest}/somefile.txt${map.ext}"/>
+        </not>
+        <not>
+          <available file="${baseScratchSrc}/somefile.txt"/>
+        </not>
+      </and>
+    </condition>
+    <fail unless="singlemove.outcome">singlemove failed</fail>
+  </target>
+
+  <!-- test dir w/ file + empty dir multimap copy -->
+  <target name="copywithempty">
+    <delete dir="${baseScratchSrc}"/>
+    <mkdir dir="${baseScratchSrc}/dirwithfile"/>
+    <mkdir dir="${baseScratchSrc}/emptydir"/>
+    <touch file="${baseScratchSrc}/dirwithfile/somefile.txt"/>
+
+    <delete dir="${baseScratchDest}"/>
+    <mkdir dir="${baseScratchDest}"/>
+
+    <copy todir="${baseScratchDest}" enablemultiplemappings="true">
+      <fileset dir="${baseScratchSrc}" includes="**/*"/>
+      <mapper refid="testmapper"/>
+    </copy>
+    <condition property="copywithempty.outcome">
+      <and>
+        <available file="${baseScratchDest}/dirwithfile"/>
+        <available file="${baseScratchDest}/dirwithfile${map.ext}"/>
+        <available file="${baseScratchDest}/dirwithfile/somefile.txt"/>
+        <available file="${baseScratchDest}/dirwithfile/somefile.txt${map.ext}"/>
+        <not>
+          <available file="${baseScratchDest}/dirwithfile${map.ext}/somefile.txt"/>
+        </not>
+        <not>
+          <available file="${baseScratchDest}/dirwithfile${map.ext}/somefile.txt${map.ext}"/>
+        </not>
+        <available file="${baseScratchDest}/emptydir"/>
+        <available file="${baseScratchDest}/emptydir${map.ext}"/>
+      </and>
+    </condition>
+    <fail unless="copywithempty.outcome">copywithempty failed</fail>
+  </target>
+  <!-- test dir w/ file + empty dir multimap move -->
+  <target name="movewithempty">
+    <delete dir="${baseScratchSrc}"/>
+    <mkdir dir="${baseScratchSrc}/dirwithfile"/>
+    <mkdir dir="${baseScratchSrc}/emptydir"/>
+    <touch file="${baseScratchSrc}/dirwithfile/somefile.txt"/>
+
+    <delete dir="${baseScratchDest}"/>
+    <mkdir dir="${baseScratchDest}"/>
+
+    <move todir="${baseScratchDest}" enablemultiplemappings="true">
+      <fileset dir="${baseScratchSrc}" includes="**/*"/>
+      <mapper refid="testmapper"/>
+    </move>
+    <condition property="movewithempty.outcome">
+      <and>
+        <available file="${baseScratchDest}/dirwithfile"/>
+        <available file="${baseScratchDest}/dirwithfile${map.ext}"/>
+        <available file="${baseScratchDest}/dirwithfile/somefile.txt"/>
+        <available file="${baseScratchDest}/dirwithfile/somefile.txt${map.ext}"/>
+        <not>
+          <available file="${baseScratchDest}/dirwithfile${map.ext}/somefile.txt"/>
+        </not>
+        <not>
+          <available file="${baseScratchDest}/dirwithfile${map.ext}/somefile.txt${map.ext}"/>
+        </not>
+        <available file="${baseScratchDest}/emptydir"/>
+        <available file="${baseScratchDest}/emptydir${map.ext}"/>
+        <not>
+          <available file="${baseScratchSrc}/dirwithfile"/>
+        </not>
+        <not>
+          <available file="${baseScratchSrc}/emptydir"/>
+        </not>
+      </and>
+    </condition>
+    <fail unless="movewithempty.outcome">movewithempty failed</fail>
+  </target>
+
+  <target name="cleanup">
+    <delete dir="${rootScratchDir}"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/WsdlToDotnet.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/WsdlToDotnet.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/WsdlToDotnet.xml	(revision 16139)
@@ -0,0 +1,183 @@
+<?xml version="1.0"?>
+
+<project name="wsdl-to-java-jtest" basedir="." default="init">
+
+
+  <property environment="env"/>
+  <property name="build.dir" location="wsdl/build"/>
+  <property name="cache.dir" location="${build.dir}/cache"/>
+  <property name="src.dir" location="${build.dir}/src"/>
+  <property name="classes.dir" location="${build.dir}/classes"/>
+  <property name="local.wsdl" 
+    location="wsdl/StockQuoteService.wsdl" />
+  <property name="out.csc" location="${src.dir}/out.cs"/>
+  <property name="out.app" location="${classes.dir}/out.dll"/>
+  <property name="out.type" value="module"/>
+  <property name="out.vbc" location="${src.dir}/out.vb"/>
+  <property name="endpoint"
+    value="http://nagoya.apache.org:5049/Axis/StockQuoteService.jws" />
+  <property name="endpoint.wsdl" 
+    value="http://nagoya.apache.org:5049/Axis/StockQuoteService.jws?wsdl" />
+  
+  <target name="init" depends="validate">
+    <mkdir dir="${build.dir}"/>
+    <mkdir dir="${cache.dir}"/>
+    <mkdir dir="${src.dir}"/>
+    <mkdir dir="${classes.dir}"/>
+  </target>
+  
+  <target name="probe_for_apps" >
+   <condition property="wsdl.found">
+      <or>
+        <available file="wsdl"     filepath="${env.PATH}" />
+        <available file="wsdl.exe" filepath="${env.PATH}" />
+        <available file="wsdl.exe" filepath="${env.Path}" />
+      </or>
+    </condition>
+   <echo> wsdl.found=${wsdl.found}</echo>
+   <condition property="csc.found">
+      <or>
+        <available file="csc"     filepath="${env.PATH}" />
+        <available file="csc.exe" filepath="${env.PATH}" />
+        <available file="csc.exe" filepath="${env.Path}" />
+      </or>
+    </condition>
+   <echo> csc.found=${csc.found}</echo>
+   <condition property="vbc.found">
+      <or>
+        <available file="vbc"     filepath="${env.PATH}" />
+        <available file="vbc.exe" filepath="${env.PATH}" />
+        <available file="vbc.exe" filepath="${env.Path}" />
+      </or>
+    </condition>
+   <echo> vbc.found=${vbc.found}</echo>
+   <condition property="dotnetapps.found">
+      <and>
+        <isset property="csc.found"/>
+        <isset property="vbc.found"/>
+        <isset property="wsdl.found"/>
+      </and>
+    </condition>
+   <echo> dotnetapps.found=${dotnetapps.found}</echo>
+  </target>
+  
+  <target name="teardown">
+    <delete dir="${build.dir}"/>
+  </target>  
+
+  <target name="validate" depends="probe_for_apps" >
+    <fail unless="dotnetapps.found">Needed .net apps are missing</fail>
+  </target>  
+  
+  
+  <target name="testNoParams">
+    <wsdltodotnet/>
+  </target>
+
+  <target name="testNoSrc">
+    <wsdltodotnet destFile="${out.csc}"/>
+  </target>
+
+  <target name="testDestIsDir" depends="init">
+    <wsdltodotnet destFile="${build.dir}"
+      srcFile="${local.wsdl}"/>
+  </target>
+  
+  <target name="testBothSrc" depends="init">
+    <wsdltodotnet destFile="${out.csc}"
+      srcFile="${local.wsdl}"
+      url="${endpoint.wsdl}"
+      />
+  </target>
+
+  <target name="testSrcIsDir" depends="init">
+    <wsdltodotnet destFile="${out.csc}"
+      srcFile="${build.dir}"
+      />
+  </target>
+
+  <target name="testSrcIsMissing" depends="init">
+    <wsdltodotnet destFile="${out.csc}"
+      srcFile="${build.dir}/invalidfile.wsdl"
+      />
+  </target>
+  
+  <target name="testLocalWsdl" depends="init">
+    <wsdltodotnet destFile="${out.csc}"
+      srcFile="${local.wsdl}"
+      />
+    <csc 
+      srcDir="${src.dir}"
+      destFile="${out.app}"
+      targetType="${out.type}"
+      />
+    <available property="app.created" file="${out.app}"/>
+    <fail unless="app.created">No app created</fail>
+  </target>
+  
+  <target name="testLocalWsdlServer" depends="init">
+    <wsdltodotnet destFile="${out.csc}"
+      srcFile="${local.wsdl}"
+      server="true"
+      />
+    <csc 
+      srcDir="${src.dir}"
+      destFile="${out.app}"
+      targetType="${out.type}" 
+      fileAlign="512"
+      />
+    <available property="app.created" file="${out.app}"/>
+    <fail unless="app.created">No app created</fail>
+  </target>
+  
+  <target name="testInvalidExtraOps" depends="init">
+    <wsdltodotnet destFile="${out.csc}"
+      srcFile="${local.wsdl}"
+      extraOptions="/newOption:not-one-known-of"
+      />
+  </target>
+
+
+  
+  <target name="testLocalWsdlVB" depends="init">
+    <wsdltodotnet destFile="${out.vbc}"
+      language="VB"
+      srcFile="${local.wsdl}"
+      />
+    <vbc 
+      srcDir="${src.dir}"
+      destFile="${out.app}"
+      targetType="${out.type}"
+      />
+    <available property="app.created" file="${out.app}"/>
+    <fail unless="app.created">No app created</fail>
+  </target>
+  
+  <target name="testLocalWsdlServerVB" 
+    depends="init" >
+    <wsdltodotnet destFile="${out.vbc}"
+      language="VB"
+      srcFile="${local.wsdl}"
+      server="true"
+      />
+    <vbc 
+      srcDir="${src.dir}"
+      destFile="${out.app}"
+      targetType="${out.type}" 
+      optionExplicit="true"
+      optionStrict="false"
+      optionCompare="text"
+      />
+    <available property="app.created" file="${out.app}"/>
+    <fail unless="app.created">No app created</fail>
+  </target>
+  
+  <target name="testInvalidExtraOpsVB" depends="init" if="vbc.found">
+    <wsdltodotnet destFile="${out.vbc}"
+      language="VB"
+      srcFile="${local.wsdl}"
+      extraOptions="/newOption:not-one-known-of"
+      />
+  </target>    
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/antlr/antlr.g
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/antlr/antlr.g	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/antlr/antlr.g	(revision 16139)
@@ -0,0 +1,60 @@
+class CalcParser extends Parser;
+options {
+	buildAST = true;	// uses CommonAST by default
+}
+
+expr
+	:	mexpr (PLUS^ mexpr)* SEMI!
+	;
+
+mexpr
+	:	atom (STAR^ atom)*
+	;
+
+atom:	INT
+	;
+
+class CalcLexer extends Lexer;
+
+WS	:	(' '
+	|	'\t'
+	|	'\n'
+	|	'\r')
+		{ _ttype = Token.SKIP; }
+	;
+
+LPAREN:	'('
+	;
+
+RPAREN:	')'
+	;
+
+STAR:	'*'
+	;
+
+PLUS:	'+'
+	;
+
+SEMI:	';'
+	;
+
+protected
+DIGIT
+	:	'0'..'9'
+	;
+
+INT	:	(DIGIT)+
+	;
+
+class CalcTreeWalker extends TreeParser;
+
+expr returns [float r]
+{
+	float a,b;
+	r=0;
+}
+	:	#(PLUS a=expr b=expr)	{r = a+b;}
+	|	#(STAR a=expr b=expr)	{r = a*b;}
+	|	i:INT			{r = (float)Integer.parseInt(i.getText());}
+	;
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/antlr/antlr.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/antlr/antlr.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/antlr/antlr.xml	(revision 16139)
@@ -0,0 +1,100 @@
+<?xml version="1.0"?>
+
+<project name="antlr-test" basedir="." default="test1">
+
+  <property name="tmp.dir" location="antlr.tmp"/>
+
+
+  <target name="setup">
+    <mkdir dir="${tmp.dir}"/>
+  </target>
+
+  <target name="test1">
+    <antlr/>
+  </target>
+
+  <target name="test2">
+    <antlr target="antlr.g" outputdirectory="${tmp.dir}"/>
+  </target>
+
+  <target name="test3" depends="setup">
+    <antlr target="antlr.g" outputdirectory="${tmp.dir}"/>
+  </target>
+
+  <target name="test4" depends="setup">
+    <antlr target="java.g" outputdirectory="${tmp.dir}"/>
+    <antlr dir="${tmp.dir}" target="java.tree.g" outputdirectory="${tmp.dir}"/>
+  </target>
+
+  <target name="test5" depends="setup">
+    <antlr target="java.tree.g" outputdirectory="${tmp.dir}" fork="yes"/>
+  </target>
+
+  <target name="test6" depends="setup">
+    <antlr target="java.g" outputdirectory="${tmp.dir}" />
+    <antlr dir="${tmp.dir}"
+           target="java.tree.g"
+           outputdirectory="${tmp.dir}"
+           fork="yes"/>
+  </target>
+
+  <target name="test7">
+    <antlr target="antlr.xml"/>
+  </target>
+
+  <target name="test8" depends="setup">
+    <antlr target="extended.calc.g" outputdirectory="${tmp.dir}" glib="non-existant-file.g"/>
+  </target>
+
+  <target name="test9" depends="setup">
+    <!-- Note that I had to copy the grammars over to the temporary directory.  -->
+    <!-- This is because ANTLR expects the super grammar and its generated java -->
+    <!-- files to be in the same directory, which won't be the case if I use    -->
+    <!-- the output directory option. -->
+    <copy file="antlr.g" todir="${tmp.dir}"/>
+    <copy file="extended.calc.g" todir="${tmp.dir}"/>
+    <antlr target="${tmp.dir}/antlr.g"/>
+    <antlr target="${tmp.dir}/extended.calc.g" glib="${tmp.dir}/antlr.g"/>
+  </target>
+
+  <target name="test10" depends="setup">
+    <antlr target="antlr.g" outputdirectory="${tmp.dir}" html="yes"/>
+  </target>
+
+  <target name="test11" depends="setup">
+    <antlr target="antlr.g" outputdirectory="${tmp.dir}" diagnostic="yes"/>
+  </target>
+
+  <target name="test12" depends="setup">
+    <antlr target="antlr.g" outputdirectory="${tmp.dir}" trace="yes"/>
+  </target>
+
+  <target name="test13" depends="setup">
+    <antlr target="antlr.g" outputdirectory="${tmp.dir}" traceLexer="yes" traceParser="yes" traceTreeWalker="yes"/>
+  </target>
+
+  <!-- test9 will have been run before that -->
+  <target name="noRecompile">
+    <antlr target="${tmp.dir}/extended.calc.g" glib="${tmp.dir}/antlr.g"/>
+  </target>
+
+  <!-- test9 will have been run before that -->
+  <target name="normalRecompile">
+    <sleep seconds="2"/>
+    <touch file="${tmp.dir}/extended.calc.g"/>
+    <antlr target="${tmp.dir}/extended.calc.g" glib="${tmp.dir}/antlr.g"/>
+  </target>
+
+  <!-- test9 will have been run before that -->
+  <target name="supergrammarChangeRecompile">
+    <sleep seconds="2"/>
+    <touch file="${tmp.dir}/antlr.g"/>
+    <antlr target="${tmp.dir}/extended.calc.g" glib="${tmp.dir}/antlr.g"/>
+  </target>
+
+  <target name="cleanup">
+    <delete dir="${tmp.dir}" />
+    <delete file="../../../../../../CalcParserTokenTypes.txt"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/antlr/extended.calc.g
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/antlr/extended.calc.g	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/antlr/extended.calc.g	(revision 16139)
@@ -0,0 +1,7 @@
+// Not really a great extension, but it is only a test after all!
+
+class ExtendedCalcParser extends CalcParser;
+
+exprList
+    : LPAREN (expr)* RPAREN
+    ;
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/antlr/java.g
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/antlr/java.g	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/antlr/java.g	(revision 16139)
@@ -0,0 +1,1165 @@
+/** Java 1.2 Recognizer
+ *
+ * Run 'java Main <directory full of java files>'
+ *
+ * Contributing authors:
+ *		John Mitchell		johnm@non.net
+ *		Terence Parr		parrt@magelang.com
+ *		John Lilley			jlilley@empathy.com
+ *		Scott Stanchfield	thetick@magelang.com
+ *		Markus Mohnen       mohnen@informatik.rwth-aachen.de
+ *		Peter Williams		pwilliams@netdynamics.com
+ *
+ * Version 1.00 December 9, 1997 -- initial release
+ * Version 1.01 December 10, 1997
+ *		fixed bug in octal def (0..7 not 0..8)
+ * Version 1.10 August 1998 (parrt)
+ *		added tree construction
+ *		fixed definition of WS,comments for mac,pc,unix newlines
+ *		added unary plus
+ * Version 1.11 (Nov 20, 1998)
+ *		Added "shutup" option to turn off last ambig warning.
+ *		Fixed inner class def to allow named class defs as statements
+ *		synchronized requires compound not simple statement
+ *		add [] after builtInType DOT class in primaryExpression
+ *		"const" is reserved but not valid..removed from modifiers
+ * Version 1.12 (Feb 2, 1999)
+ *		Changed LITERAL_xxx to xxx in tree grammar.
+ *		Updated java.g to use tokens {...} now for 2.6.0 (new feature).
+ *
+ * Version 1.13 (Apr 23, 1999)
+ *		Didn't have (stat)? for else clause in tree parser.
+ *		Didn't gen ASTs for interface extends.  Updated tree parser too.
+ *		Updated to 2.6.0.
+ * Version 1.14 (Jun 20, 1999)
+ *		Allowed final/abstract on local classes.
+ *		Removed local interfaces from methods
+ *		Put instanceof precedence where it belongs...in relationalExpr
+ *			It also had expr not type as arg; fixed it.
+ *		Missing ! on SEMI in classBlock
+ *		fixed: (expr) + "string" was parsed incorrectly (+ as unary plus).
+ *		fixed: didn't like Object[].class in parser or tree parser
+ * Version 1.15 (Jun 26, 1999)
+ *		Screwed up rule with instanceof in it. :(  Fixed.
+ *		Tree parser didn't like (expr).something; fixed.
+ *		Allowed multiple inheritance in tree grammar. oops.
+ * Version 1.16 (August 22, 1999)
+ *		Extending an interface built a wacky tree: had extra EXTENDS.
+ *		Tree grammar didn't allow multiple superinterfaces.
+ *		Tree grammar didn't allow empty var initializer: {}
+ * Version 1.17 (October 12, 1999)
+ *		ESC lexer rule allowed 399 max not 377 max.
+ *		java.tree.g didn't handle the expression of synchronized
+ *			statements.
+ *
+ * Version tracking now done with following ID:
+ *
+ *
+ * BUG:
+ * 		Doesn't like boolean.class!
+ *
+ * class Test {
+ *   public static void main( String args[] ) {
+ *     if (boolean.class.equals(boolean.class)) {
+ *       System.out.println("works");
+ *     }
+ *   }
+ * }
+ *
+ * This grammar is in the PUBLIC DOMAIN
+ */
+class JavaRecognizer extends Parser;
+options {
+	k = 2;                           // two token lookahead
+	exportVocab=Java;                // Call its vocabulary "Java"
+	codeGenMakeSwitchThreshold = 2;  // Some optimizations
+	codeGenBitsetTestThreshold = 3;
+	defaultErrorHandler = false;     // Don't generate parser error handlers
+	buildAST = true;
+}
+
+tokens {
+	BLOCK; MODIFIERS; OBJBLOCK; SLIST; CTOR_DEF; METHOD_DEF; VARIABLE_DEF; 
+	INSTANCE_INIT; STATIC_INIT; TYPE; CLASS_DEF; INTERFACE_DEF; 
+	PACKAGE_DEF; ARRAY_DECLARATOR; EXTENDS_CLAUSE; IMPLEMENTS_CLAUSE;
+	PARAMETERS; PARAMETER_DEF; LABELED_STAT; TYPECAST; INDEX_OP; 
+	POST_INC; POST_DEC; METHOD_CALL; EXPR; ARRAY_INIT; 
+	IMPORT; UNARY_MINUS; UNARY_PLUS; CASE_GROUP; ELIST; FOR_INIT; FOR_CONDITION; 
+	FOR_ITERATOR; EMPTY_STAT; FINAL="final"; ABSTRACT="abstract";
+}
+	
+// Compilation Unit: In Java, this is a single file.  This is the start
+//   rule for this parser
+compilationUnit
+	:	// A compilation unit starts with an optional package definition
+		(	packageDefinition
+		|	/* nothing */
+		)
+
+		// Next we have a series of zero or more import statements
+		( importDefinition )*
+
+		// Wrapping things up with any number of class or interface
+		//    definitions
+		( typeDefinition )*
+
+		EOF!
+	;
+
+
+// Package statement: "package" followed by an identifier.
+packageDefinition
+	options {defaultErrorHandler = true;} // let ANTLR handle errors
+	:	p:"package"^ {#p.setType(PACKAGE_DEF);} identifier SEMI!
+	;
+
+
+// Import statement: import followed by a package or class name
+importDefinition
+	options {defaultErrorHandler = true;}
+	:	i:"import"^ {#i.setType(IMPORT);} identifierStar SEMI!
+	;
+
+// A type definition in a file is either a class or interface definition.
+typeDefinition
+	options {defaultErrorHandler = true;}
+	:	m:modifiers!
+		( classDefinition[#m]
+		| interfaceDefinition[#m]
+		)
+	|	SEMI!
+	;
+
+/** A declaration is the creation of a reference or primitive-type variable
+ *  Create a separate Type/Var tree for each var in the var list.
+ */
+declaration!
+	:	m:modifiers t:typeSpec[false] v:variableDefinitions[#m,#t]
+		{#declaration = #v;}
+	;
+
+// A list of zero or more modifiers.  We could have used (modifier)* in
+//   place of a call to modifiers, but I thought it was a good idea to keep
+//   this rule separate so they can easily be collected in a Vector if
+//   someone so desires
+modifiers
+	:	( modifier )*
+		{#modifiers = #([MODIFIERS, "MODIFIERS"], #modifiers);}
+	;
+
+
+// A type specification is a type name with possible brackets afterwards
+//   (which would make it an array type).
+typeSpec[boolean addImagNode]
+	: classTypeSpec[addImagNode]
+	| builtInTypeSpec[addImagNode]
+	;
+
+// A class type specification is a class type with possible brackets afterwards
+//   (which would make it an array type).
+classTypeSpec[boolean addImagNode]
+	:	identifier (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)*
+		{
+			if ( addImagNode ) {
+				#classTypeSpec = #(#[TYPE,"TYPE"], #classTypeSpec);
+			}
+		}
+	;
+
+// A builtin type specification is a builtin type with possible brackets
+// afterwards (which would make it an array type).
+builtInTypeSpec[boolean addImagNode]
+	:	builtInType (lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)*
+		{
+			if ( addImagNode ) {
+				#builtInTypeSpec = #(#[TYPE,"TYPE"], #builtInTypeSpec);
+			}
+		}
+	;
+
+// A type name. which is either a (possibly qualified) class name or
+//   a primitive (builtin) type
+type
+	:	identifier
+	|	builtInType
+	;
+
+// The primitive types.
+builtInType
+	:	"void"
+	|	"boolean"
+	|	"byte"
+	|	"char"
+	|	"short"
+	|	"int"
+	|	"float"
+	|	"long"
+	|	"double"
+	;
+
+// A (possibly-qualified) java identifier.  We start with the first IDENT
+//   and expand its name by adding dots and following IDENTS
+identifier
+	:	IDENT  ( DOT^ IDENT )*
+	;
+
+identifierStar
+	:	IDENT
+		( DOT^ IDENT )*
+		( DOT^ STAR  )?
+	;
+
+
+// modifiers for Java classes, interfaces, class/instance vars and methods
+modifier
+	:	"private"
+	|	"public"
+	|	"protected"
+	|	"static"
+	|	"transient"
+	|	"final"
+	|	"abstract"
+	|	"native"
+	|	"threadsafe"
+	|	"synchronized"
+//	|	"const"			// reserved word; leave out
+	|	"volatile"
+	;
+
+
+// Definition of a Java class
+classDefinition![AST modifiers]
+	:	"class" IDENT
+		// it _might_ have a superclass...
+		sc:superClassClause
+		// it might implement some interfaces...
+		ic:implementsClause
+		// now parse the body of the class
+		cb:classBlock
+		{#classDefinition = #(#[CLASS_DEF,"CLASS_DEF"],
+							   modifiers,IDENT,sc,ic,cb);}
+	;
+
+superClassClause!
+	:	( "extends" id:identifier )?
+		{#superClassClause = #(#[EXTENDS_CLAUSE,"EXTENDS_CLAUSE"],id);}
+	;
+
+// Definition of a Java Interface
+interfaceDefinition![AST modifiers]
+	:	"interface" IDENT
+		// it might extend some other interfaces
+		ie:interfaceExtends
+		// now parse the body of the interface (looks like a class...)
+		cb:classBlock
+		{#interfaceDefinition = #(#[INTERFACE_DEF,"INTERFACE_DEF"],
+									modifiers,IDENT,ie,cb);}
+	;
+
+
+// This is the body of a class.  You can have fields and extra semicolons,
+// That's about it (until you see what a field is...)
+classBlock
+	:	LCURLY!
+			( field | SEMI! )*
+		RCURLY!
+		{#classBlock = #([OBJBLOCK, "OBJBLOCK"], #classBlock);}
+	;
+
+// An interface can extend several other interfaces...
+interfaceExtends
+	:	(
+		e:"extends"!
+		identifier ( COMMA! identifier )*
+		)?
+		{#interfaceExtends = #(#[EXTENDS_CLAUSE,"EXTENDS_CLAUSE"],
+							#interfaceExtends);}
+	;
+
+// A class can implement several interfaces...
+implementsClause
+	:	(
+			i:"implements"! identifier ( COMMA! identifier )*
+		)?
+		{#implementsClause = #(#[IMPLEMENTS_CLAUSE,"IMPLEMENTS_CLAUSE"],
+								 #implementsClause);}
+	;
+
+// Now the various things that can be defined inside a class or interface...
+// Note that not all of these are really valid in an interface (constructors,
+//   for example), and if this grammar were used for a compiler there would
+//   need to be some semantic checks to make sure we're doing the right thing...
+field!
+	:	// method, constructor, or variable declaration
+		mods:modifiers
+		(	h:ctorHead s:compoundStatement // constructor
+			{#field = #(#[CTOR_DEF,"CTOR_DEF"], mods, h, s);}
+
+		|	cd:classDefinition[#mods]       // inner class
+			{#field = #cd;}
+			
+		|	id:interfaceDefinition[#mods]   // inner interface
+			{#field = #id;}
+
+		|	t:typeSpec[false]  // method or variable declaration(s)
+			(	IDENT  // the name of the method
+
+				// parse the formal parameter declarations.
+				LPAREN! param:parameterDeclarationList RPAREN!
+
+				rt:returnTypeBrackersOnEndOfMethodHead[#t]
+
+				// get the list of exceptions that this method is declared to throw
+				(tc:throwsClause)?
+
+				( s2:compoundStatement | SEMI )
+				{#field = #(#[METHOD_DEF,"METHOD_DEF"],
+						     mods,
+							 #(#[TYPE,"TYPE"],rt),
+							 IDENT,
+							 param,
+							 tc,
+							 s2);}
+			|	v:variableDefinitions[#mods,#t] SEMI
+//				{#field = #(#[VARIABLE_DEF,"VARIABLE_DEF"], v);}
+				{#field = #v;}
+			)
+		)
+
+    // "static { ... }" class initializer
+	|	"static" s3:compoundStatement
+		{#field = #(#[STATIC_INIT,"STATIC_INIT"], s3);}
+
+    // "{ ... }" instance initializer
+	|	s4:compoundStatement
+		{#field = #(#[INSTANCE_INIT,"INSTANCE_INIT"], s4);}
+	;
+
+variableDefinitions[AST mods, AST t]
+	:	variableDeclarator[getASTFactory().dupTree(mods),
+						   getASTFactory().dupTree(t)]
+		(	COMMA!
+			variableDeclarator[getASTFactory().dupTree(mods),
+							   getASTFactory().dupTree(t)]
+		)*
+	;
+
+/** Declaration of a variable.  This can be a class/instance variable,
+ *   or a local variable in a method
+ * It can also include possible initialization.
+ */
+variableDeclarator![AST mods, AST t]
+	:	id:IDENT d:declaratorBrackets[t] v:varInitializer
+		{#variableDeclarator = #(#[VARIABLE_DEF,"VARIABLE_DEF"], mods, #(#[TYPE,"TYPE"],d), id, v);}
+	;
+
+declaratorBrackets[AST typ]
+	:	{#declaratorBrackets=typ;}
+		(lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)*
+	;
+
+varInitializer
+	:	( ASSIGN^ initializer )?
+	;
+
+// This is an initializer used to set up an array.
+arrayInitializer
+	:	lc:LCURLY^ {#lc.setType(ARRAY_INIT);}
+			(	initializer
+				(
+					// CONFLICT: does a COMMA after an initializer start a new
+					//           initializer or start the option ',' at end?
+					//           ANTLR generates proper code by matching
+					//			 the comma as soon as possible.
+					options {
+						warnWhenFollowAmbig = false;
+					}
+				:
+					COMMA! initializer
+				)*
+				(COMMA!)?
+			)?
+		RCURLY!
+	;
+
+
+// The two "things" that can initialize an array element are an expression
+//   and another (nested) array initializer.
+initializer
+	:	expression
+	|	arrayInitializer
+	;
+
+// This is the header of a method.  It includes the name and parameters
+//   for the method.
+//   This also watches for a list of exception classes in a "throws" clause.
+ctorHead
+	:	IDENT  // the name of the method
+
+		// parse the formal parameter declarations.
+		LPAREN! parameterDeclarationList RPAREN!
+
+		// get the list of exceptions that this method is declared to throw
+		(throwsClause)?
+	;
+
+// This is a list of exception classes that the method is declared to throw
+throwsClause
+	:	"throws"^ identifier ( COMMA! identifier )*
+	;
+
+
+returnTypeBrackersOnEndOfMethodHead[AST typ]
+	:	{#returnTypeBrackersOnEndOfMethodHead = typ;}
+		(lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)*
+	;
+
+// A list of formal parameters
+parameterDeclarationList
+	:	( parameterDeclaration ( COMMA! parameterDeclaration )* )?
+		{#parameterDeclarationList = #(#[PARAMETERS,"PARAMETERS"],
+									#parameterDeclarationList);}
+	;
+
+// A formal parameter.
+parameterDeclaration!
+	:	pm:parameterModifier t:typeSpec[false] id:IDENT
+		pd:parameterDeclaratorBrackets[#t]
+		{#parameterDeclaration = #(#[PARAMETER_DEF,"PARAMETER_DEF"],
+									pm, #([TYPE,"TYPE"],pd), id);}
+	;
+
+parameterDeclaratorBrackets[AST t]
+	:	{#parameterDeclaratorBrackets = t;}
+		(lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);} RBRACK!)*
+	;
+
+parameterModifier
+	:	(f:"final")?
+		{#parameterModifier = #(#[MODIFIERS,"MODIFIERS"], f);}
+	;
+
+// Compound statement.  This is used in many contexts:
+//   Inside a class definition prefixed with "static":
+//      it is a class initializer
+//   Inside a class definition without "static":
+//      it is an instance initializer
+//   As the body of a method
+//   As a completely indepdent braced block of code inside a method
+//      it starts a new scope for variable definitions
+
+compoundStatement
+	:	lc:LCURLY^ {#lc.setType(SLIST);}
+			// include the (possibly-empty) list of statements
+			(statement)*
+		RCURLY!
+	;
+
+
+statement
+	// A list of statements in curly braces -- start a new scope!
+	:	compoundStatement
+
+	// class definition
+	|	classDefinition[#[MODIFIERS, "MODIFIERS"]]
+
+	// final class definition
+	|	"final"! classDefinition[#(#[MODIFIERS, "MODIFIERS"],#[FINAL,"final"])]
+
+	// abstract class definition
+	|	"abstract"! classDefinition[#(#[MODIFIERS, "MODIFIERS"],#[ABSTRACT,"abstract"])]
+
+	// declarations are ambiguous with "ID DOT" relative to expression
+	// statements.  Must backtrack to be sure.  Could use a semantic
+	// predicate to test symbol table to see what the type was coming
+	// up, but that's pretty hard without a symbol table ;)
+	|	(declaration)=> declaration SEMI!
+
+	// An expression statement.  This could be a method call,
+	// assignment statement, or any other expression evaluated for
+	// side-effects.
+	|	expression SEMI!
+
+	// Attach a label to the front of a statement
+	|	IDENT c:COLON^ {#c.setType(LABELED_STAT);} statement
+
+	// If-else statement
+	|	"if"^ LPAREN! expression RPAREN! statement
+		(
+			// CONFLICT: the old "dangling-else" problem...
+			//           ANTLR generates proper code matching
+			//			 as soon as possible.  Hush warning.
+			options {
+				warnWhenFollowAmbig = false;
+			}
+		:
+			"else"! statement
+		)?
+
+	// For statement
+	|	"for"^
+			LPAREN!
+				forInit SEMI!   // initializer
+				forCond	SEMI!   // condition test
+				forIter         // updater
+			RPAREN!
+			statement                     // statement to loop over
+
+	// While statement
+	|	"while"^ LPAREN! expression RPAREN! statement
+
+	// do-while statement
+	|	"do"^ statement "while"! LPAREN! expression RPAREN! SEMI!
+
+	// get out of a loop (or switch)
+	|	"break"^ (IDENT)? SEMI!
+
+	// do next iteration of a loop
+	|	"continue"^ (IDENT)? SEMI!
+
+	// Return an expression
+	|	"return"^ (expression)? SEMI!
+
+	// switch/case statement
+	|	"switch"^ LPAREN! expression RPAREN! LCURLY!
+			( casesGroup )*
+		RCURLY!
+
+	// exception try-catch block
+	|	tryBlock
+
+	// throw an exception
+	|	"throw"^ expression SEMI!
+
+	// synchronize a statement
+	|	"synchronized"^ LPAREN! expression RPAREN! compoundStatement
+
+	// empty statement
+	|	s:SEMI {#s.setType(EMPTY_STAT);}
+	;
+
+
+casesGroup
+	:	(	// CONFLICT: to which case group do the statements bind?
+			//           ANTLR generates proper code: it groups the
+			//           many "case"/"default" labels together then
+			//           follows them with the statements
+			options {
+				warnWhenFollowAmbig = false;
+			}
+			:
+			aCase
+		)+
+		caseSList
+		{#casesGroup = #([CASE_GROUP, "CASE_GROUP"], #casesGroup);}
+	;
+
+aCase
+	:	("case"^ expression | "default") COLON!
+	;
+
+caseSList
+	:	(statement)*
+		{#caseSList = #(#[SLIST,"SLIST"],#caseSList);}
+	;
+
+// The initializer for a for loop
+forInit
+		// if it looks like a declaration, it is
+	:	(	(declaration)=> declaration
+		// otherwise it could be an expression list...
+		|	expressionList
+		)?
+		{#forInit = #(#[FOR_INIT,"FOR_INIT"],#forInit);}
+	;
+
+forCond
+	:	(expression)?
+		{#forCond = #(#[FOR_CONDITION,"FOR_CONDITION"],#forCond);}
+	;
+
+forIter
+	:	(expressionList)?
+		{#forIter = #(#[FOR_ITERATOR,"FOR_ITERATOR"],#forIter);}
+	;
+
+// an exception handler try/catch block
+tryBlock
+	:	"try"^ compoundStatement
+		(handler)*
+		( "finally"^ compoundStatement )?
+	;
+
+
+// an exception handler
+handler
+	:	"catch"^ LPAREN! parameterDeclaration RPAREN! compoundStatement
+	;
+
+
+// expressions
+// Note that most of these expressions follow the pattern
+//   thisLevelExpression :
+//       nextHigherPrecedenceExpression
+//           (OPERATOR nextHigherPrecedenceExpression)*
+// which is a standard recursive definition for a parsing an expression.
+// The operators in java have the following precedences:
+//    lowest  (13)  = *= /= %= += -= <<= >>= >>>= &= ^= |=
+//            (12)  ?:
+//            (11)  ||
+//            (10)  &&
+//            ( 9)  |
+//            ( 8)  ^
+//            ( 7)  &
+//            ( 6)  == !=
+//            ( 5)  < <= > >=
+//            ( 4)  << >>
+//            ( 3)  +(binary) -(binary)
+//            ( 2)  * / %
+//            ( 1)  ++ -- +(unary) -(unary)  ~  !  (type)
+//                  []   () (method call)  . (dot -- identifier qualification)
+//                  new   ()  (explicit parenthesis)
+//
+// the last two are not usually on a precedence chart; I put them in
+// to point out that new has a higher precedence than '.', so you
+// can validy use
+//     new Frame().show()
+// 
+// Note that the above precedence levels map to the rules below...
+// Once you have a precedence chart, writing the appropriate rules as below
+//   is usually very straightfoward
+
+
+
+// the mother of all expressions
+expression
+	:	assignmentExpression
+		{#expression = #(#[EXPR,"EXPR"],#expression);}
+	;
+
+
+// This is a list of expressions.
+expressionList
+	:	expression (COMMA! expression)*
+		{#expressionList = #(#[ELIST,"ELIST"], expressionList);}
+	;
+
+
+// assignment expression (level 13)
+assignmentExpression
+	:	conditionalExpression
+		(	(	ASSIGN^
+            |   PLUS_ASSIGN^
+            |   MINUS_ASSIGN^
+            |   STAR_ASSIGN^
+            |   DIV_ASSIGN^
+            |   MOD_ASSIGN^
+            |   SR_ASSIGN^
+            |   BSR_ASSIGN^
+            |   SL_ASSIGN^
+            |   BAND_ASSIGN^
+            |   BXOR_ASSIGN^
+            |   BOR_ASSIGN^
+            )
+			assignmentExpression
+		)?
+	;
+
+
+// conditional test (level 12)
+conditionalExpression
+	:	logicalOrExpression
+		( QUESTION^ assignmentExpression COLON! conditionalExpression )?
+	;
+
+
+// logical or (||)  (level 11)
+logicalOrExpression
+	:	logicalAndExpression (LOR^ logicalAndExpression)*
+	;
+
+
+// logical and (&&)  (level 10)
+logicalAndExpression
+	:	inclusiveOrExpression (LAND^ inclusiveOrExpression)*
+	;
+
+
+// bitwise or non-short-circuiting or (|)  (level 9)
+inclusiveOrExpression
+	:	exclusiveOrExpression (BOR^ exclusiveOrExpression)*
+	;
+
+
+// exclusive or (^)  (level 8)
+exclusiveOrExpression
+	:	andExpression (BXOR^ andExpression)*
+	;
+
+
+// bitwise or non-short-circuiting and (&)  (level 7)
+andExpression
+	:	equalityExpression (BAND^ equalityExpression)*
+	;
+
+
+// equality/inequality (==/!=) (level 6)
+equalityExpression
+	:	relationalExpression ((NOT_EQUAL^ | EQUAL^) relationalExpression)*
+	;
+
+
+// boolean relational expressions (level 5)
+relationalExpression
+	:	shiftExpression
+		(	(	(	LT^
+				|	GT^
+				|	LE^
+				|	GE^
+				)
+				shiftExpression
+			)*
+		|	"instanceof"^ typeSpec[true]
+		)
+	;
+
+
+// bit shift expressions (level 4)
+shiftExpression
+	:	additiveExpression ((SL^ | SR^ | BSR^) additiveExpression)*
+	;
+
+
+// binary addition/subtraction (level 3)
+additiveExpression
+	:	multiplicativeExpression ((PLUS^ | MINUS^) multiplicativeExpression)*
+	;
+
+
+// multiplication/division/modulo (level 2)
+multiplicativeExpression
+	:	unaryExpression ((STAR^ | DIV^ | MOD^ ) unaryExpression)*
+	;
+
+unaryExpression
+	:	INC^ unaryExpression
+	|	DEC^ unaryExpression
+	|	MINUS^ {#MINUS.setType(UNARY_MINUS);} unaryExpression
+	|	PLUS^  {#PLUS.setType(UNARY_PLUS);} unaryExpression
+	|	unaryExpressionNotPlusMinus
+	;
+
+unaryExpressionNotPlusMinus
+	:	BNOT^ unaryExpression
+	|	LNOT^ unaryExpression
+
+	|	(	// subrule allows option to shut off warnings
+			options {
+				// "(int" ambig with postfixExpr due to lack of sequence
+				// info in linear approximate LL(k).  It's ok.  Shut up.
+				generateAmbigWarnings=false;
+			}
+		:	// If typecast is built in type, must be numeric operand
+			// Also, no reason to backtrack if type keyword like int, float...
+			lpb:LPAREN^ {#lpb.setType(TYPECAST);} builtInTypeSpec[true] RPAREN!
+			unaryExpression
+
+			// Have to backtrack to see if operator follows.  If no operator
+			// follows, it's a typecast.  No semantic checking needed to parse.
+			// if it _looks_ like a cast, it _is_ a cast; else it's a "(expr)"
+		|	(LPAREN classTypeSpec[true] RPAREN unaryExpressionNotPlusMinus)=>
+			lp:LPAREN^ {#lp.setType(TYPECAST);} classTypeSpec[true] RPAREN!
+			unaryExpressionNotPlusMinus
+
+		|	postfixExpression
+		)
+	;
+
+// qualified names, array expressions, method invocation, post inc/dec
+postfixExpression
+	:	primaryExpression // start with a primary
+
+		(	// qualified id (id.id.id.id...) -- build the name
+			DOT^ ( IDENT
+				| "this"
+				| "class"
+				| newExpression
+				| "super" LPAREN ( expressionList )? RPAREN
+				)
+			// the above line needs a semantic check to make sure "class"
+			//   is the _last_ qualifier.
+
+			// allow ClassName[].class
+		|	( lbc:LBRACK^ {#lbc.setType(ARRAY_DECLARATOR);} RBRACK! )+
+			DOT^ "class"
+
+			// an array indexing operation
+		|	lb:LBRACK^ {#lb.setType(INDEX_OP);} expression RBRACK!
+
+			// method invocation
+			// The next line is not strictly proper; it allows x(3)(4) or
+			//  x[2](4) which are not valid in Java.  If this grammar were used
+			//  to validate a Java program a semantic check would be needed, or
+			//   this rule would get really ugly...
+		|	lp:LPAREN^ {#lp.setType(METHOD_CALL);}
+				argList
+			RPAREN!
+		)*
+
+		// possibly add on a post-increment or post-decrement.
+		// allows INC/DEC on too much, but semantics can check
+		(	in:INC^ {#in.setType(POST_INC);}
+	 	|	de:DEC^ {#de.setType(POST_DEC);}
+		|	// nothing
+		)
+
+		// look for int.class and int[].class
+	|	builtInType 
+		( lbt:LBRACK^ {#lbt.setType(ARRAY_DECLARATOR);} RBRACK! )*
+		DOT^ "class"
+	;
+
+// the basic element of an expression
+primaryExpression
+	:	IDENT
+	|	newExpression
+	|	constant
+	|	"super"
+	|	"true"
+	|	"false"
+	|	"this"
+	|	"null"
+	|	LPAREN! assignmentExpression RPAREN!
+	;
+
+/** object instantiation.
+ *  Trees are built as illustrated by the following input/tree pairs:
+ *  
+ *  new T()
+ *  
+ *  new
+ *   |
+ *   T --  ELIST
+ *           |
+ *          arg1 -- arg2 -- .. -- argn
+ *  
+ *  new int[]
+ *
+ *  new
+ *   |
+ *  int -- ARRAY_DECLARATOR
+ *  
+ *  new int[] {1,2}
+ *
+ *  new
+ *   |
+ *  int -- ARRAY_DECLARATOR -- ARRAY_INIT
+ *                                  |
+ *                                EXPR -- EXPR
+ *                                  |      |
+ *                                  1      2
+ *  
+ *  new int[3]
+ *  new
+ *   |
+ *  int -- ARRAY_DECLARATOR
+ *                |
+ *              EXPR
+ *                |
+ *                3
+ *  
+ *  new int[1][2]
+ *  
+ *  new
+ *   |
+ *  int -- ARRAY_DECLARATOR
+ *               |
+ *         ARRAY_DECLARATOR -- EXPR
+ *               |              |
+ *             EXPR             1
+ *               |
+ *               2
+ *  
+ */
+newExpression
+	:	"new"^ type
+		(	LPAREN! argList RPAREN! (classBlock)?
+
+			//java 1.1
+			// Note: This will allow bad constructs like
+			//    new int[4][][3] {exp,exp}.
+			//    There needs to be a semantic check here...
+			// to make sure:
+			//   a) [ expr ] and [ ] are not mixed
+			//   b) [ expr ] and an init are not used together
+
+		|	newArrayDeclarator (arrayInitializer)?
+		)
+	;
+
+argList
+	:	(	expressionList
+		|	/*nothing*/
+			{#argList = #[ELIST,"ELIST"];}
+		)
+	;
+
+newArrayDeclarator
+	:	(
+			// CONFLICT:
+			// newExpression is a primaryExpression which can be
+			// followed by an array index reference.  This is ok,
+			// as the generated code will stay in this loop as
+			// long as it sees an LBRACK (proper behavior)
+			options {
+				warnWhenFollowAmbig = false;
+			}
+		:
+			lb:LBRACK^ {#lb.setType(ARRAY_DECLARATOR);}
+				(expression)?
+			RBRACK!
+		)+
+	;
+
+constant
+	:	NUM_INT
+	|	CHAR_LITERAL
+	|	STRING_LITERAL
+	|	NUM_FLOAT
+	;
+
+
+//----------------------------------------------------------------------------
+// The Java scanner
+//----------------------------------------------------------------------------
+class JavaLexer extends Lexer;
+
+options {
+	exportVocab=Java;      // call the vocabulary "Java"
+	testLiterals=false;    // don't automatically test for literals
+	k=4;                   // four characters of lookahead
+}
+
+
+
+// OPERATORS
+QUESTION		:	'?'		;
+LPAREN			:	'('		;
+RPAREN			:	')'		;
+LBRACK			:	'['		;
+RBRACK			:	']'		;
+LCURLY			:	'{'		;
+RCURLY			:	'}'		;
+COLON			:	':'		;
+COMMA			:	','		;
+//DOT			:	'.'		;
+ASSIGN			:	'='		;
+EQUAL			:	"=="	;
+LNOT			:	'!'		;
+BNOT			:	'~'		;
+NOT_EQUAL		:	"!="	;
+DIV				:	'/'		;
+DIV_ASSIGN		:	"/="	;
+PLUS			:	'+'		;
+PLUS_ASSIGN		:	"+="	;
+INC				:	"++"	;
+MINUS			:	'-'		;
+MINUS_ASSIGN	:	"-="	;
+DEC				:	"--"	;
+STAR			:	'*'		;
+STAR_ASSIGN		:	"*="	;
+MOD				:	'%'		;
+MOD_ASSIGN		:	"%="	;
+SR				:	">>"	;
+SR_ASSIGN		:	">>="	;
+BSR				:	">>>"	;
+BSR_ASSIGN		:	">>>="	;
+GE				:	">="	;
+GT				:	">"		;
+SL				:	"<<"	;
+SL_ASSIGN		:	"<<="	;
+LE				:	"<="	;
+LT				:	'<'		;
+BXOR			:	'^'		;
+BXOR_ASSIGN		:	"^="	;
+BOR				:	'|'		;
+BOR_ASSIGN		:	"|="	;
+LOR				:	"||"	;
+BAND			:	'&'		;
+BAND_ASSIGN		:	"&="	;
+LAND			:	"&&"	;
+SEMI			:	';'		;
+
+
+// Whitespace -- ignored
+WS	:	(	' '
+		|	'\t'
+		|	'\f'
+		// handle newlines
+		|	(	"\r\n"  // Evil DOS
+			|	'\r'    // Macintosh
+			|	'\n'    // Unix (the right way)
+			)
+			{ newline(); }
+		)
+		{ _ttype = Token.SKIP; }
+	;
+
+// Single-line comments
+SL_COMMENT
+	:	"//"
+		(~('\n'|'\r'))* ('\n'|'\r'('\n')?)
+		{$setType(Token.SKIP); newline();}
+	;
+
+// multiple-line comments
+ML_COMMENT
+	:	"/*"
+		(	/*	'\r' '\n' can be matched in one alternative or by matching
+				'\r' in one iteration and '\n' in another.  I am trying to
+				handle any flavor of newline that comes in, but the language
+				that allows both "\r\n" and "\r" and "\n" to all be valid
+				newline is ambiguous.  Consequently, the resulting grammar
+				must be ambiguous.  I'm shutting this warning off.
+			 */
+			options {
+				generateAmbigWarnings=false;
+			}
+		:
+			{ LA(2)!='/' }? '*'
+		|	'\r' '\n'		{newline();}
+		|	'\r'			{newline();}
+		|	'\n'			{newline();}
+		|	~('*'|'\n'|'\r')
+		)*
+		"*/"
+		{$setType(Token.SKIP);}
+	;
+
+
+// character literals
+CHAR_LITERAL
+	:	'\'' ( ESC | ~'\'' ) '\''
+	;
+
+// string literals
+STRING_LITERAL
+	:	'"' (ESC|~('"'|'\\'))* '"'
+	;
+
+
+// escape sequence -- note that this is protected; it can only be called
+//   from another lexer rule -- it will not ever directly return a token to
+//   the parser
+// There are various ambiguities hushed in this rule.  The optional
+// '0'...'9' digit matches should be matched here rather than letting
+// them go back to STRING_LITERAL to be matched.  ANTLR does the
+// right thing by matching immediately; hence, it's ok to shut off
+// the FOLLOW ambig warnings.
+protected
+ESC
+	:	'\\'
+		(	'n'
+		|	'r'
+		|	't'
+		|	'b'
+		|	'f'
+		|	'"'
+		|	'\''
+		|	'\\'
+		|	('u')+ HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT 
+		|	('0'..'3')
+			(
+				options {
+					warnWhenFollowAmbig = false;
+				}
+			:	('0'..'7')
+				(	
+					options {
+						warnWhenFollowAmbig = false;
+					}
+				:	'0'..'7'
+				)?
+			)?
+		|	('4'..'7')
+			(
+				options {
+					warnWhenFollowAmbig = false;
+				}
+			:	('0'..'9')
+			)?
+		)
+	;
+
+
+// hexadecimal digit (again, note it's protected!)
+protected
+HEX_DIGIT
+	:	('0'..'9'|'A'..'F'|'a'..'f')
+	;
+
+
+// a dummy rule to force vocabulary to be all characters (except special
+//   ones that ANTLR uses internally (0 to 2)
+protected
+VOCAB
+	:	'\3'..'\377'
+	;
+
+
+// an identifier.  Note that testLiterals is set to true!  This means
+// that after we match the rule, we look in the literals table to see
+// if it's a literal or really an identifer
+IDENT
+	options {testLiterals=true;}
+	:	('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'_'|'0'..'9'|'$')*
+	;
+
+
+// a numeric literal
+NUM_INT
+	{boolean isDecimal=false;}
+	:	'.' {_ttype = DOT;}
+			(('0'..'9')+ (EXPONENT)? (FLOAT_SUFFIX)? { _ttype = NUM_FLOAT; })?
+	|	(	'0' {isDecimal = true;} // special case for just '0'
+			(	('x'|'X')
+				(											// hex
+					// the 'e'|'E' and float suffix stuff look
+					// like hex digits, hence the (...)+ doesn't
+					// know when to stop: ambig.  ANTLR resolves
+					// it correctly by matching immediately.  It
+					// is therefor ok to hush warning.
+					options {
+						warnWhenFollowAmbig=false;
+					}
+				:	HEX_DIGIT
+				)+
+			|	('0'..'7')+									// octal
+			)?
+		|	('1'..'9') ('0'..'9')*  {isDecimal=true;}		// non-zero decimal
+		)
+		(	('l'|'L')
+		
+		// only check to see if it's a float if looks like decimal so far
+		|	{isDecimal}?
+			(	'.' ('0'..'9')* (EXPONENT)? (FLOAT_SUFFIX)?
+			|	EXPONENT (FLOAT_SUFFIX)?
+			|	FLOAT_SUFFIX
+			)
+			{ _ttype = NUM_FLOAT; }
+		)?
+	;
+
+
+// a couple protected methods to assist in matching floating point numbers
+protected
+EXPONENT
+	:	('e'|'E') ('+'|'-')? ('0'..'9')+
+	;
+
+
+protected
+FLOAT_SUFFIX
+	:	'f'|'F'|'d'|'D'
+	;
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/antlr/java.tree.g
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/antlr/java.tree.g	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/antlr/java.tree.g	(revision 16139)
@@ -0,0 +1,313 @@
+/** Java 1.2 AST Recognizer Grammar
+ *
+ * Author:
+ *	Terence Parr	parrt@jguru.com
+ *
+ * Version tracking now done with following ID:
+ *
+ *
+ * This grammar is in the PUBLIC DOMAIN
+ *
+ * BUGS
+ */
+class JavaTreeParser extends TreeParser;
+
+options {
+	importVocab = Java;
+}
+
+compilationUnit
+	:	(packageDefinition)?
+		(importDefinition)*
+		(typeDefinition)*
+	;
+
+packageDefinition
+	:	#( PACKAGE_DEF identifier )
+	;
+
+importDefinition
+	:	#( IMPORT identifierStar )
+	;
+
+typeDefinition
+	:	#(CLASS_DEF modifiers IDENT extendsClause implementsClause objBlock )
+	|	#(INTERFACE_DEF modifiers IDENT extendsClause interfaceBlock )
+	;
+
+typeSpec
+	:	#(TYPE typeSpecArray)
+	;
+
+typeSpecArray
+	:	#( ARRAY_DECLARATOR typeSpecArray )
+	|	type
+	;
+
+type:	identifier
+	|	builtInType
+	;
+
+builtInType
+    :   "void"
+    |   "boolean"
+    |   "byte"
+    |   "char"
+    |   "short"
+    |   "int"
+    |   "float"
+    |   "long"
+    |   "double"
+    ;
+
+modifiers
+	:	#( MODIFIERS (modifier)* )
+	;
+
+modifier
+    :   "private"
+    |   "public"
+    |   "protected"
+    |   "static"
+    |   "transient"
+    |   "final"
+    |   "abstract"
+    |   "native"
+    |   "threadsafe"
+    |   "synchronized"
+    |   "const"
+    |   "volatile"
+    ;
+
+extendsClause
+	:	#(EXTENDS_CLAUSE (identifier)* )
+	;
+
+implementsClause
+	:	#(IMPLEMENTS_CLAUSE (identifier)* )
+	;
+
+
+interfaceBlock
+	:	#(	OBJBLOCK
+			(	methodDecl
+			|	variableDef
+			)*
+		)
+	;
+	
+objBlock
+	:	#(	OBJBLOCK
+			(	ctorDef
+			|	methodDef
+			|	variableDef
+			|	typeDefinition
+			|	#(STATIC_INIT slist)
+			|	#(INSTANCE_INIT slist)
+			)*
+		)
+	;
+
+ctorDef
+	:	#(CTOR_DEF modifiers methodHead slist)
+	;
+
+methodDecl
+	:	#(METHOD_DEF modifiers typeSpec methodHead)
+	;
+
+methodDef
+	:	#(METHOD_DEF modifiers typeSpec methodHead (slist)?)
+	;
+
+variableDef
+	:	#(VARIABLE_DEF modifiers typeSpec variableDeclarator varInitializer)
+	;
+
+parameterDef
+	:	#(PARAMETER_DEF modifiers typeSpec IDENT )
+	;
+
+objectinitializer
+	:	#(INSTANCE_INIT slist)
+	;
+
+variableDeclarator
+	:	IDENT
+	|	LBRACK variableDeclarator
+	;
+
+varInitializer
+	:	#(ASSIGN initializer)
+	|
+	;
+
+initializer
+	:	expression
+	|	arrayInitializer
+	;
+
+arrayInitializer
+	:	#(ARRAY_INIT (initializer)*)
+	;
+
+methodHead
+	:	IDENT #( PARAMETERS (parameterDef)* ) (throwsClause)?
+	;
+
+throwsClause
+	:	#( "throws" (identifier)* )
+	;
+
+identifier
+	:	IDENT
+	|	#( DOT identifier IDENT )
+	;
+
+identifierStar
+	:	IDENT
+	|	#( DOT identifier (STAR|IDENT) )
+	;
+
+slist
+	:	#( SLIST (stat)* )
+	;
+
+stat:	typeDefinition
+	|	variableDef
+	|	expression
+	|	#(LABELED_STAT IDENT stat)
+	|	#("if" expression stat (stat)? )
+	|	#(	"for"
+			#(FOR_INIT (variableDef | elist)?)
+			#(FOR_CONDITION (expression)?)
+			#(FOR_ITERATOR (elist)?)
+			stat
+		)
+	|	#("while" expression stat)
+	|	#("do" stat expression)
+	|	#("break" (IDENT)? )
+	|	#("continue" (IDENT)? )
+	|	#("return" (expression)? )
+	|	#("switch" expression (caseGroup)*)
+	|	#("throw" expression)
+	|	#("synchronized" expression stat)
+	|	tryBlock
+	|	slist // nested SLIST
+	|	EMPTY_STAT
+	;
+
+caseGroup
+	:	#(CASE_GROUP (#("case" expression) | "default")+ slist)
+	;
+
+tryBlock
+	:	#( "try" slist (handler)* (#("finally" slist))? )
+	;
+
+handler
+	:	#( "catch" parameterDef slist )
+	;
+
+elist
+	:	#( ELIST (expression)* )
+	;
+
+expression
+	:	#(EXPR expr)
+	;
+
+expr:	#(QUESTION expr expr expr)	// trinary operator
+	|	#(ASSIGN expr expr)			// binary operators...
+	|	#(PLUS_ASSIGN expr expr)
+	|	#(MINUS_ASSIGN expr expr)
+	|	#(STAR_ASSIGN expr expr)
+	|	#(DIV_ASSIGN expr expr)
+	|	#(MOD_ASSIGN expr expr)
+	|	#(SR_ASSIGN expr expr)
+	|	#(BSR_ASSIGN expr expr)
+	|	#(SL_ASSIGN expr expr)
+	|	#(BAND_ASSIGN expr expr)
+	|	#(BXOR_ASSIGN expr expr)
+	|	#(BOR_ASSIGN expr expr)
+	|	#(LOR expr expr)
+	|	#(LAND expr expr)
+	|	#(BOR expr expr)
+	|	#(BXOR expr expr)
+	|	#(BAND expr expr)
+	|	#(NOT_EQUAL expr expr)
+	|	#(EQUAL expr expr)
+	|	#(LT expr expr)
+	|	#(GT expr expr)
+	|	#(LE expr expr)
+	|	#(GE expr expr)
+	|	#(SL expr expr)
+	|	#(SR expr expr)
+	|	#(BSR expr expr)
+	|	#(PLUS expr expr)
+	|	#(MINUS expr expr)
+	|	#(DIV expr expr)
+	|	#(MOD expr expr)
+	|	#(STAR expr expr)
+	|	#(INC expr)
+	|	#(DEC expr)
+	|	#(POST_INC expr)
+	|	#(POST_DEC expr)
+	|	#(BNOT expr)
+	|	#(LNOT expr)
+	|	#("instanceof" expr expr)
+	|	#(UNARY_MINUS expr)
+	|	#(UNARY_PLUS expr)
+	|	primaryExpression
+	;
+
+primaryExpression
+    :   IDENT
+    |   #(	DOT
+			(	expr
+				(	IDENT
+				|	arrayIndex
+				|	"this"
+				|	"class"
+				|	#( "new" IDENT elist )
+				)
+			|	#(ARRAY_DECLARATOR type)
+			|	builtInType ("class")?
+			)
+		)
+	|	arrayIndex
+	|	#(METHOD_CALL primaryExpression elist)
+	|	#(TYPECAST typeSpec expr)
+	|   newExpression
+	|   constant
+    |   "super"
+    |   "true"
+    |   "false"
+    |   "this"
+    |   "null"
+	|	typeSpec // type name used with instanceof
+	;
+
+arrayIndex
+	:	#(INDEX_OP primaryExpression expression)
+	;
+
+constant
+    :   NUM_INT
+    |   CHAR_LITERAL
+    |   STRING_LITERAL
+    |   NUM_FLOAT
+    ;
+
+newExpression
+	:	#(	"new" type
+			(	newArrayDeclarator (arrayInitializer)?
+			|	elist
+			)
+		)
+			
+	;
+
+newArrayDeclarator
+	:	#( ARRAY_DECLARATOR (newArrayDeclarator)? (expression)? )
+	;
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/depend.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/depend.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/depend.xml	(revision 16139)
@@ -0,0 +1,163 @@
+<?xml version="1.0"?>
+
+<project name="depend" basedir="." default="help">
+  <property name="src1.dir" value="src1"/>
+  <property name="src2.dir" value="src2"/>
+  <property name="src3.dir" value="src3"/>
+  <property name="src4.dir" value="src4"/>
+  <property name="src5.dir" value="src5"/>
+  
+  <property name="tempsrc.dir" value="working"/>
+  <property name="cache.dir" value="working"/>
+  <property name="classes.dir" value="classes"/>
+  <target name="help">
+    <echo>This buildfile is used as part of Ant's test suite.</echo>
+  </target>
+
+  <target name="basesetup" depends="clean">
+    <mkdir dir="${tempsrc.dir}"/>
+  </target>
+  
+  <target name="src1setup" depends="basesetup">
+    <copy todir="${tempsrc.dir}">
+      <fileset dir="${src1.dir}"/>
+    </copy>
+  </target>
+  
+  <target name="src2setup" depends="basesetup">
+    <copy todir="${tempsrc.dir}">
+      <fileset dir="${src2.dir}"/>
+    </copy>
+  </target>
+
+  <target name="src3setup" depends="basesetup">
+    <copy todir="${tempsrc.dir}">
+      <fileset dir="${src3.dir}"/>
+    </copy>
+  </target>
+
+  <target name="src4setup" depends="basesetup">
+    <copy todir="${tempsrc.dir}">
+      <fileset dir="${src4.dir}"/>
+    </copy>
+  </target>
+
+  <target name="src5setup" depends="basesetup">
+    <copy todir="${tempsrc.dir}">
+      <fileset dir="${src5.dir}"/>
+    </copy>
+  </target>
+
+  <target name="compile">
+    <mkdir dir="${classes.dir}"/>
+    <javac srcdir="${tempsrc.dir}" destdir="${classes.dir}"/>
+  </target>
+  
+  <target name="clean">
+    <delete dir="${classes.dir}"/>
+    <delete dir="${tempsrc.dir}"/>
+  </target>
+
+  <target name="testdirect" depends="src1setup, compile">
+    <sleep seconds="3"/>
+    <delete file="${tempsrc.dir}/C.java"/>
+    <copy file="${src1.dir}/C.java" tofile="${tempsrc.dir}/C.java"/>
+    <depend srcdir="${tempsrc.dir}" destdir="${classes.dir}"/>
+    <fileset id="result" dir="${classes.dir}"/>
+  </target>
+
+  <target name="testclosure" depends="src1setup, compile">
+    <sleep seconds="3"/>
+    <delete file="${tempsrc.dir}/C.java"/>
+    <copy file="${src1.dir}/C.java" tofile="${tempsrc.dir}/C.java"/>
+    <depend srcdir="${tempsrc.dir}" destdir="${classes.dir}" closure="yes"/>
+    <fileset id="result" dir="${classes.dir}"/>
+  </target>
+
+  <target name="testbasicset" depends="src1setup, compile">
+    <classfileset id="result" dir="${classes.dir}" rootclass="A"/>
+  </target>
+
+  <target name="testsmallset" depends="src1setup, compile">
+    <classfileset id="result" dir="${classes.dir}" rootclass="B"/>
+  </target>
+
+  <target name="testcomboset" depends="src1setup, compile">
+    <classfileset id="result" dir="${classes.dir}" rootclass="B">
+      <include name="**/C.class"/>
+    </classfileset>
+  </target>
+  
+  <target name="testbyreference" depends="src1setup, compile">
+    <classfileset id="classSet" dir="${classes.dir}" rootclass="A">
+      <include name="**/C.class"/>
+    </classfileset>
+    <jar destfile="${tempsrc.dir}/test.jar">
+      <fileset refid="classSet"/>
+    </jar>
+  </target>
+  
+  <target name="testmethodparam" depends="src1setup, compile">
+    <classfileset id="result" dir="${classes.dir}" rootclass="E"/>
+  </target>
+  
+  <target name="testinner" depends="src2setup, compile">
+    <sleep seconds="3"/>
+    <delete file="${tempsrc.dir}/B.java"/>
+    <copy file="${src2.dir}/B.java" tofile="${tempsrc.dir}/B.java"/>
+    <depend srcdir="${tempsrc.dir}" destdir="${classes.dir}" closure="yes"/>
+    <fileset id="result" dir="${classes.dir}"/>
+  </target>
+
+  <target name="testinnerinner" depends="src3setup, compile">
+    <sleep seconds="3"/>
+    <delete file="${tempsrc.dir}/B.java"/>
+    <copy file="${src3.dir}/B.java" tofile="${tempsrc.dir}/B.java"/>
+    <depend srcdir="${tempsrc.dir}" destdir="${classes.dir}" closure="yes"/>
+    <fileset id="result" dir="${classes.dir}"/>
+  </target>
+  
+  <target name="testmethodparaminner" depends="src4setup, compile">
+    <classfileset id="result" dir="${classes.dir}" rootclass="test.MethodParam"/>
+  </target>  
+  
+  <target name="testnosource" depends="src1setup, compile">
+    <depend destdir="${classes.dir}" closure="yes"/>
+  </target>
+
+  <target name="testemptysource" depends="src1setup, compile">
+    <depend srcdir="" destdir="${classes.dir}" closure="yes"/>
+  </target>
+
+  <target name="testinnerclosure" depends="src4setup">
+    <mkdir dir="${classes.dir}"/>
+    <path id="path.compile">
+        <pathelement location="${classes.dir}"/>
+    </path>
+
+    <javac srcdir="${tempsrc.dir}" destdir="${classes.dir}"
+           classpathref="path.compile"/>
+    
+    <sleep seconds="3"/>
+    <depend srcdir="${tempsrc.dir}" destdir="${classes.dir}" 
+            closure="yes" dump="yes"
+            classpathref="path.compile"/>
+           
+    <fileset id="result" dir="${classes.dir}"/>
+  </target>
+  
+  <target name="testcache" depends="src1setup, compile">
+    <depend cache="${cache.dir}" srcdir="${tempsrc.dir}" 
+            destdir="${classes.dir}" closure="yes"/>
+    <depend cache="${cache.dir}" srcdir="${tempsrc.dir}" 
+            destdir="${classes.dir}" closure="yes"/>
+  </target>
+
+  <target name="testnonpublic" depends="src5setup, compile">
+    <sleep seconds="3"/>
+    <delete file="${tempsrc.dir}/B.java"/>
+    <copy file="${src2.dir}/B.java" tofile="${tempsrc.dir}/B.java"/>
+    <depend srcdir="${tempsrc.dir}" destdir="${classes.dir}" closure="yes"/>
+    <fileset id="result" dir="${classes.dir}"/>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src1/A.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src1/A.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src1/A.java	(revision 16139)
@@ -0,0 +1,4 @@
+public class A extends B {
+    private D d = new D();
+}
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src1/B.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src1/B.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src1/B.java	(revision 16139)
@@ -0,0 +1,3 @@
+public class B extends C {
+}
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src1/C.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src1/C.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src1/C.java	(revision 16139)
@@ -0,0 +1,3 @@
+public class C {
+}
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src1/D.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src1/D.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src1/D.java	(revision 16139)
@@ -0,0 +1,3 @@
+public class D {
+}
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src1/E.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src1/E.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src1/E.java	(revision 16139)
@@ -0,0 +1,6 @@
+public class E {
+    E() {
+        System.out.println(A.class);
+    }
+}
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src2/A.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src2/A.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src2/A.java	(revision 16139)
@@ -0,0 +1,5 @@
+public class A {
+    static private class Inner extends B {
+    }
+}
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src2/B.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src2/B.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src2/B.java	(revision 16139)
@@ -0,0 +1,3 @@
+public class B {
+}
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src3/A.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src3/A.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src3/A.java	(revision 16139)
@@ -0,0 +1,7 @@
+public class A {
+    static private class Inner {
+        static private class Inner2 extends B {
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src3/B.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src3/B.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src3/B.java	(revision 16139)
@@ -0,0 +1,3 @@
+public class B {
+}
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src4/test/ContainsOnlyInner.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src4/test/ContainsOnlyInner.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src4/test/ContainsOnlyInner.java	(revision 16139)
@@ -0,0 +1,8 @@
+package test;
+
+public class ContainsOnlyInner {
+    void method1() {
+        System.out.println(Outer.Inner.class);
+    }    
+}
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src4/test/MethodParam.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src4/test/MethodParam.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src4/test/MethodParam.java	(revision 16139)
@@ -0,0 +1,8 @@
+package test;
+
+public class MethodParam {
+    void method1() {
+        System.out.print(ContainsOnlyInner.class);
+    }
+}
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src4/test/Outer.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src4/test/Outer.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src4/test/Outer.java	(revision 16139)
@@ -0,0 +1,7 @@
+package test;
+
+public class Outer {
+    static class Inner {
+    }
+}
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src5/A.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src5/A.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src5/A.java	(revision 16139)
@@ -0,0 +1,6 @@
+public class A {
+    APrivate dependency = new APrivate();
+}
+
+class APrivate extends B {
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src5/B.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src5/B.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/depend/src5/B.java	(revision 16139)
@@ -0,0 +1,3 @@
+public class B {
+}
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet.xml	(revision 16139)
@@ -0,0 +1,360 @@
+<?xml version="1.0"?>
+
+<project name="dotnet" basedir="." default="init">
+  <property environment="env"/>
+  <property name="build.dir" location="dotnet/build"/>
+  <property name="src.dir" location="dotnet"/>
+
+  <property name="out.csc" location="${src.dir}/out.cs"/>
+  <property name="out.app" location="${build.dir}/out.exe"/>
+  <property name="out.type" value="exe"/>
+
+  <target name="probe_for_apps" >
+   <condition property="ilasm.found">
+      <or>
+        <available file="ilasm"     filepath="${env.PATH}" />
+        <available file="ilasm.exe" filepath="${env.PATH}" />
+        <available file="ilasm.exe" filepath="${env.Path}" />
+      </or>
+    </condition>
+   <echo> ilasm.found=${ilasm.found}</echo>
+   <condition property="csc.found">
+      <or>
+        <available file="csc"     filepath="${env.PATH}" />
+        <available file="csc.exe" filepath="${env.PATH}" />
+        <available file="csc.exe" filepath="${env.Path}" />
+      </or>
+    </condition>
+   <echo> csc.found=${csc.found}</echo>
+   <!-- visual basic compiler -->
+   <condition property="vbc.found">
+      <or>
+        <available file="vbc"     filepath="${env.PATH}" />
+        <available file="vbc.exe" filepath="${env.PATH}" />
+        <available file="vbc.exe" filepath="${env.Path}" />
+      </or>
+    </condition>
+   <echo> vbc.found=${vbc.found}</echo>
+   <!-- visual J# compiler -->
+   <condition property="jsharp.found">
+      <or>
+        <available file="vjc"     filepath="${env.PATH}" />
+        <available file="vjc.exe" filepath="${env.PATH}" />
+        <available file="vjc.exe" filepath="${env.Path}" />
+      </or>
+    </condition>
+   <echo> jsharp.found=${jsharp.found}</echo>
+
+   <!-- Mono C# compiler -->
+   <condition property="mcs.found">
+      <available file="mcs"     filepath="${env.PATH}" />
+    </condition>
+   <echo> mcs.found=${mcs.found}</echo>
+
+   <!-- any C# compiler -->
+   <condition property="c#.found">
+      <or>
+        <isset property="csc.found"/>
+        <isset property="mcs.found"/>
+      </or>
+   </condition>
+
+   <!-- Mono's ilasm -->
+   <condition property="mono.ilasm.found">
+      <available file="ilasm"     filepath="${env.PATH}" />
+    </condition>
+   <echo> mono.ilasm.found=${mono.ilasm.found}</echo>
+
+   <condition property="ildasm.found">
+      <or>
+        <available file="ildasm"     filepath="${env.PATH}" />
+        <available file="ildasm.exe" filepath="${env.PATH}" />
+        <available file="ildasm.exe" filepath="${env.Path}" />
+      </or>
+    </condition>
+   <echo> ildasm.found=${ildasm.found}</echo>
+
+   <condition property="dotnetapps.found">
+      <or>
+        <and>
+          <isset property="mcs.found"/>
+          <isset property="mono.ilasm.found"/>
+        </and>
+        <and>
+          <isset property="csc.found"/>
+<!--         <isset property="vbc.found"/> -->
+          <isset property="ilasm.found"/>
+        </and>
+      </or>
+    </condition>
+   <echo> dotnetapps.found=${dotnetapps.found}</echo>
+
+   <condition property="mono.executable" value="mono">
+      <and>
+        <not>
+          <os family="mac"/>
+        </not>
+        <or>
+          <available file="mono"     filepath="${env.PATH}" />
+          <available file="mono.exe" filepath="${env.PATH}" />
+        </or>
+      </and>
+   </condition>
+   <property name="mono.executable" value="mint"/>
+
+   <!-- now set a prop of the compiler name to whatever we found --> 
+   <condition property="cs.compiler" value="csc">
+      <isset property="csc.found"/>
+   </condition>   
+
+   <condition property="cs.compiler" value="mcs">
+      <isset property="mcs.found"/>
+   </condition>   
+   
+  </target>
+
+  <target name="init" depends="probe_for_apps">
+    <mkdir dir="${build.dir}"/>
+  </target>
+
+  <target name="teardown">
+    <delete dir="${build.dir}"/>
+  </target>
+
+  <target name="validate_csc" depends="init">
+    <fail unless="c#.found">Needed C# compiler is missing</fail>
+  </target>
+
+  <target name="validate_ilasm" depends="init">
+    <fail unless="ilasm.found">Needed ilasm is missing</fail>
+  </target>
+
+  <target name="validate_jsharp" depends="init">
+    <fail unless="jsharp.found">No vjc on the path</fail>
+  </target>
+
+
+  <target name="testCSC" depends="testCSC-Mono,testCSC-MS"/>
+
+  <target name="testCSC-MS" depends="validate_csc" if="csc.found">
+    <property name="testCSC.exe"
+      location="${build.dir}/ExampleCsc.exe" />
+    <csc
+      destFile="${testCSC.exe}"
+      targetType="exe"
+      >
+    </csc>
+    <available property="app.created" file="${testCSC.exe}"/>
+    <fail unless="app.created">No app ${testCSC.exe} created</fail>
+    <exec executable="${testCSC.exe}" failonerror="true" />
+    <delete file="${testCSC.exe}"/>
+  </target>
+
+  <target name="testCSC-Mono" depends="validate_csc" if="mcs.found">
+    <property name="testCSC.exe"
+      location="${build.dir}/ExampleCsc.exe" />
+    <csc
+      destFile="${testCSC.exe}"
+      targetType="exe"
+      includedefaultreferences="true"
+      >
+    </csc>
+    <available property="app.created" file="${testCSC.exe}"/>
+    <fail unless="app.created">No app ${testCSC.exe} created</fail>
+    <exec executable="${mono.executable}" failonerror="true">
+      <arg value="${testCSC.exe}"/>
+    </exec>
+    <delete file="${testCSC.exe}"/>
+  </target>
+
+  <target name="testCSCintrinsicFileset"
+          depends="testCSCintrinsicFileset-MS,testCSCintrinsicFileset-Mono"/>
+
+  <target name="testCSCintrinsicFileset-MS" depends="validate_csc" if="csc.found">
+    <property name="testCSC.exe"
+      location="${build.dir}/ExampleCsc.exe"/>
+    <csc
+      destFile="${testCSC.exe}"
+      targetType="exe"
+      srcDir="."
+      >
+    </csc>
+    <available property="app.created" file="${testCSC.exe}"/>
+    <fail unless="app.created">No app ${testCSC.exe} created</fail>
+    <exec executable="${testCSC.exe}" failonerror="true" />
+    <delete file="${testCSC.exe}"/>
+  </target>
+
+  <target name="testCSCintrinsicFileset-Mono" depends="validate_csc" if="mcs.found">
+    <property name="testCSC.exe"
+      location="${build.dir}/ExampleCsc.exe"/>
+    <csc
+      destFile="${testCSC.exe}"
+      targetType="exe"
+      srcDir="."
+      includedefaultreferences="true"
+      >
+    </csc>
+    <available property="app.created" file="${testCSC.exe}"/>
+    <fail unless="app.created">No app ${testCSC.exe} created</fail>
+    <exec executable="${mono.executable}" failonerror="true">
+      <arg value="${testCSC.exe}"/>
+    </exec>
+    <delete file="${testCSC.exe}"/>
+  </target>
+
+  <target name="testCSCdll" depends="testCSCdll-MS,testCSCdll-Mono"/>
+
+  <target name="testCSCdll-MS" depends="validate_csc" if="csc.found">
+    <property name="testCSC.dll"
+      location="${build.dir}/Example2.dll" />
+    <csc
+      destFile="${testCSC.dll}"
+      targetType="library"
+      executable="csc"
+      >
+      <src dir="${src.dir}" includes="example2.cs"/>
+    </csc>
+    <available property="dll.created" file="${testCSC.dll}"/>
+    <fail unless="dll.created">No file ${testCSC.dll} created</fail>
+  </target>
+
+  <target name="testCSCdll-Mono" depends="validate_csc" if="mcs.found">
+    <property name="testCSC.dll"
+      location="${build.dir}/Example2.dll" />
+    <csc
+      destFile="${testCSC.dll}"
+      targetType="library"
+      includedefaultreferences="true"
+      >
+      <src dir="${src.dir}" includes="example2.cs"/>
+    </csc>
+    <available property="dll.created" file="${testCSC.dll}"/>
+    <fail unless="dll.created">No file ${testCSC.dll} created</fail>
+  </target>
+
+  <target name="testCscReferences"
+    depends="testCscReferences-MS,testCscReferences-Mono"/>
+
+  <target name="testCscReferences-MS" depends="validate_csc,testCSCdll-MS"
+          if="csc.found">
+    <property name="testCscReferences.exe"
+      location="${build.dir}/ExampleCsc2.exe" />
+    <csc
+      destFile="${testCscReferences.exe}"
+      targetType="exe"
+      >
+      <src file="${src.dir}/example.cs"/>
+      <reference file="${testCSC.dll}" />
+      <define name="RELEASE" />
+      <define name="DEBUG" if="undefined.property"/>
+      <define name="def3" unless="undefined.property"/>
+    </csc>
+    <available property="refapp.created" file="${testCscReferences.exe}"/>
+    <fail unless="refapp.created">No app ${testCscReferences.exe} created</fail>
+    <exec executable="${testCscReferences.exe}" failonerror="true" />
+  </target>
+
+  <target name="testCscReferences-Mono" depends="validate_csc,testCSCdll-Mono"
+          if="mcs.found">
+    <property name="testCscReferences.exe"
+      location="${build.dir}/ExampleCsc2.exe" />
+    <csc
+      destFile="${testCscReferences.exe}"
+      targetType="exe"
+      includedefaultreferences="true"
+      >
+      <src file="${src.dir}/example.cs"/>
+      <reference file="${testCSC.dll}" />
+      <define name="RELEASE" />
+      <define name="DEBUG" if="undefined.property"/>
+      <define name="def3" unless="undefined.property"/>
+    </csc>
+    <available property="refapp.created" file="${testCscReferences.exe}"/>
+    <fail unless="refapp.created">No app ${testCscReferences.exe} created</fail>
+    <exec executable="${mono.executable}" failonerror="true">
+      <arg value="${testCscReferences.exe}"/>
+    </exec>
+  </target>
+
+  <target name="testILASM"  depends="validate_ilasm"  if="ilasm.found">
+    <property name="testILASM.exe"
+      location="${build.dir}/ExampleIlasm.exe" />
+    <ilasm
+      destFile="${testILASM.exe}"
+      targetType="exe"
+      >
+      <src dir="${src.dir}" includes="*.il"/>
+    </ilasm>
+    <available property="ilasm.created" file="${testILASM.exe}"/>
+    <fail unless="ilasm.created">No app ${testILASM.exe} created</fail>
+    <exec executable="${testILASM.exe}" failonerror="true" />
+  </target>
+
+  <!-- not including this in the test as it creates an exe in the src dir -->
+
+  <target name="testIlasmNoDestFile"  depends="validate_ilasm">
+    <ilasm
+      targetType="exe"
+      >
+      <src dir="${src.dir}" includes="**/*.il"/>
+    </ilasm>
+  </target>
+
+  <!-- just here to look at fileset refid conversion by hand -->
+  <target name="echoFileset">
+    <fileset id="ilasm" dir="${src.dir}" includes="**/*.il" />
+    <property name="ilasm.string" refid="ilasm"/>
+    <echo>${ilasm.string}</echo>
+  </target>
+
+  <target name="testILDASM"   depends="testILASM" if="ildasm.found">
+    <property name="testILDASM.il"
+      location="${build.dir}/ExampleIldasm.il" />
+    <ildasm
+      srcFile="${testILASM.exe}"
+      destFile="${testILDASM.il}"
+      metadata="true"
+      header="true"
+      linenumbers="true"
+      encoding="ascii"
+      />
+    <available property="ildasm.created" file="${testILDASM.il}"/>
+    <fail unless="ildasm.created">No file ${testILDASM.il} created</fail>
+  </target>
+
+  <!-- this is an error -->
+  <target name="testILDASM_empty"   depends="validate_ilasm" >
+    <ildasm/>
+  </target>
+
+  <target name="jsharp" depends="init" if="jsharp.found" >
+    <property name="jsharp.exe"
+      location="${build.dir}/jsharp.exe" />
+    <jsharpc
+        destFile="${jsharp.exe}"
+        targetType="exe"
+        >
+      <src dir="dotnet" includes="*.java"/>
+    </jsharpc>
+    <exec executable="${jsharp.exe}" failonerror="true" />
+  </target>
+
+  <target name="testCSCresponseFile" depends="validate_csc" >
+    <property name="testCSCresponseFile.exe"
+      location="${build.dir}/testCSCresponseFile.exe" />
+    <csc
+      destFile="${testCSCresponseFile.exe}"
+      targetType="exe"
+      executable="${cs.compiler}"
+      useResponseFile="true"
+      >
+    </csc>
+    <available property="app.created" file="${testCSCresponseFile.exe}"/>
+    <fail unless="app.created">No app ${testCSCresponseFile.exe} created</fail>
+    <delete file="${testCSCresponseFile.exe}"/>
+  </target>  
+
+
+</project>
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/HelloWorld.wsdl
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/HelloWorld.wsdl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/HelloWorld.wsdl	(revision 16139)
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<definitions name="HelloWorld"
+targetNamespace="http://hello.jaxrpc.samples/"
+xmlns:tns="http://hello.jaxrpc.samples/"
+xmlns="http://schemas.xmlsoap.org/wsdl/"
+xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">
+  <types />
+
+  <message name="sayHello">
+    <part name="String_1" type="xsd:string" />
+  </message>
+
+  <message name="sayHelloResponse">
+    <part name="result" type="xsd:string" />
+  </message>
+
+  <portType name="Hello">
+    <operation name="sayHello" parameterOrder="String_1">
+      <input message="tns:sayHello" />
+
+      <output message="tns:sayHelloResponse" />
+    </operation>
+  </portType>
+
+  <binding name="HelloBinding" type="tns:Hello">
+    <operation name="sayHello">
+      <input>
+        <soap:body
+        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+        use="encoded" namespace="http://hello.jaxrpc.samples/" />
+      </input>
+
+      <output>
+        <soap:body
+        encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
+        use="encoded" namespace="http://hello.jaxrpc.samples/" />
+      </output>
+
+      <soap:operation soapAction="" />
+    </operation>
+
+    <soap:binding transport="http://schemas.xmlsoap.org/soap/http"
+    style="rpc" />
+  </binding>
+
+  <service name="HelloWorld">
+    <port name="HelloPort" binding="tns:HelloBinding">
+      <soap:address
+      location="http://localhost:8080/axis/Hello" />
+    </port>
+  </service>
+</definitions>
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/example.cs
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/example.cs	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/example.cs	(revision 16139)
@@ -0,0 +1,62 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001-2003 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "Ant" and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+ 
+using System;
+
+public class Example {
+
+    public static void Main(String[] args) {
+            Example2.echo();
+    }
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/example.il
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/example.il	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/example.il	(revision 16139)
@@ -0,0 +1,78 @@
+//created from ildasm of example.cs
+
+
+
+.assembly extern /*23000001*/ mscorlib
+{
+  .publickeytoken = (B7 7A 5C 56 19 34 E0 89 )                         // .z\V.4..
+  .ver 1:0:3300:0
+}
+.assembly /*20000001*/ example
+{
+  // --- The following custom attribute is added automatically, do not uncomment -------
+  //  .custom /*0C000001:0A000001*/ instance void [mscorlib/* 23000001 */]System.Diagnostics.DebuggableAttribute/* 01000002 */::.ctor(bool,
+  //                                                                                                                                  bool) /* 0A000001 */ = ( 01 00 00 01 00 00 ) 
+  .hash algorithm 0x00008004
+  .ver 0:0:0:0
+}
+.module example.exe
+// MVID: {641E4709-F7B6-4BB0-BC5D-49C2CF1F1CF8}
+.imagebase 0x00400000
+.subsystem 0x00000003
+.file alignment 512
+.corflags 0x00000001
+// Image base: 0x03180000
+//
+// ============== CLASS STRUCTURE DECLARATION ==================
+//
+.class /*02000002*/ public auto ansi beforefieldinit Example
+       extends [mscorlib/* 23000001 */]System.Object/* 01000001 */
+{
+} // end of class Example
+
+
+// =============================================================
+
+
+// =============== GLOBAL FIELDS AND METHODS ===================
+
+
+// =============================================================
+
+
+// =============== CLASS MEMBERS DECLARATION ===================
+//   note that class flags, 'extends' and 'implements' clauses
+//          are provided here for information only
+
+.class /*02000002*/ public auto ansi beforefieldinit Example
+       extends [mscorlib/* 23000001 */]System.Object/* 01000001 */
+{
+  .method /*06000001*/ public hidebysig static 
+          void  Main(string[] args) cil managed
+  // SIG: 00 01 01 1D 0E
+  {
+    .entrypoint
+    // Method begins at RVA 0x2050
+    // Code size       11 (0xb)
+    .maxstack  1
+    IL_0000:  /* 72   | (70)000001       */ ldstr      "hello, I look like Java, but I'm really .NET" /* 70000001 */
+    IL_0005:  /* 28   | (0A)000002       */ call       void [mscorlib/* 23000001 */]System.Console/* 01000003 */::WriteLine(string) /* 0A000002 */
+    IL_000a:  /* 2A   |                  */ ret
+  } // end of method Example::Main
+
+  .method /*06000002*/ public hidebysig specialname rtspecialname 
+          instance void  .ctor() cil managed
+  // SIG: 20 00 01
+  {
+    // Method begins at RVA 0x2068
+    // Code size       7 (0x7)
+    .maxstack  1
+    IL_0000:  /* 02   |                  */ ldarg.0
+    IL_0001:  /* 28   | (0A)000003       */ call       instance void [mscorlib/* 23000001 */]System.Object/* 01000001 */::.ctor() /* 0A000003 */
+    IL_0006:  /* 2A   |                  */ ret
+  } // end of method Example::.ctor
+
+} // end of class Example
+
+
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/example2.cs
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/example2.cs	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/example2.cs	(revision 16139)
@@ -0,0 +1,68 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2001-2003 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "Ant" and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+ 
+using System;
+
+/**
+ * this is just here to create confusion
+ */
+public class Example2 {
+
+    public int some_variable=3;
+    
+    public static void echo() {
+        Console.WriteLine("hello, I look like Java, but I'm really .NET");
+    }
+    
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/example2.il
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/example2.il	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/example2.il	(revision 16139)
@@ -0,0 +1,31 @@
+
+.class public auto ansi beforefieldinit Example2
+       extends [mscorlib]System.Object
+{
+} // end of class Example2
+
+// =============== CLASS MEMBERS DECLARATION ===================
+//   note that class flags, 'extends' and 'implements' clauses
+//          are provided here for information only
+
+.class public auto ansi beforefieldinit Example2
+       extends [mscorlib]System.Object
+{
+  .field public int32 some_variable
+  .method public hidebysig specialname rtspecialname 
+          instance void  .ctor() cil managed
+  {
+    // Code size       14 (0xe)
+    .maxstack  2
+    IL_0000:  ldarg.0
+    IL_0001:  ldc.i4.3
+    IL_0002:  stfld      int32 Example2::some_variable
+    IL_0007:  ldarg.0
+    IL_0008:  call       instance void [mscorlib]System.Object::.ctor()
+    IL_000d:  ret
+  } // end of method Example2::.ctor
+
+} // end of class Example2
+
+
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/jsharp.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/jsharp.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/dotnet/jsharp.java	(revision 16139)
@@ -0,0 +1,28 @@
+/* 
+ * Copyright  2003-2004 Apache Software Foundation
+ *
+ *  Licensed 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.
+ * 
+ */
+ 
+
+/**
+ * this is a j# class, not a java one.
+ */
+public class jsharp {
+    
+    public static void main(String args[]) {
+		System.out.println("hello, I look like Java, but I'm really J#");
+    }
+    
+}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/echoproperties.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/echoproperties.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/echoproperties.properties	(revision 16139)
@@ -0,0 +1,1 @@
+test.infile=true
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/echoproperties.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/echoproperties.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/echoproperties.xml	(revision 16139)
@@ -0,0 +1,100 @@
+<project name="test" default="def" basedir=".">
+
+  <target name="def">
+  </target>
+
+  <target name="setup">
+    <property name="a.set" value="true" />
+    <property name="b.set" value="false" />
+  </target>
+
+  <target name="testEchoToLog" depends="setup">
+    <echoproperties />
+  </target>
+
+  <target name="testEchoToLogXml" depends="setup">
+    <echoproperties format="xml" />
+  </target>
+
+  <target name="testReadAndEchoToLog" depends="setup">
+    <echoproperties srcfile="echoproperties.properties" />
+  </target>
+
+  <target name="testReadBadFile" depends="setup">
+    <echoproperties srcfile="." />
+  </target>
+
+  <target name="testReadBadFileFail" depends="setup">
+    <echoproperties srcfile="." failonerror="yes" />
+  </target>
+
+  <target name="testReadBadFileNoFail" depends="setup">
+    <echoproperties srcfile="." failonerror="no" />
+  </target>
+
+  <target name="testEchoToBadFile" depends="setup">
+    <echoproperties destfile="." />
+  </target>
+
+  <target name="testEchoToBadFileFail" depends="setup">
+    <echoproperties destfile="." failonerror="yes" />
+  </target>
+
+  <target name="testEchoToBadFileNoFail" depends="setup">
+    <echoproperties destfile="." failonerror="no" />
+  </target>
+
+  <target name="testEchoToGoodFile" depends="setup">
+    <echoproperties destfile="test.properties" />
+  </target>
+
+  <target name="testEchoToGoodFileXml" depends="setup">
+    <echoproperties destfile="test.xml" format="xml" />
+  </target>
+
+  <target name="testEchoToGoodFileFail" depends="setup">
+    <echoproperties destfile="test.properties" failonerror="yes" />
+  </target>
+
+  <target name="testEchoToGoodFileNoFail" depends="setup">
+    <echoproperties destfile="test.properties" failonerror="no" />
+  </target>
+
+  <target name="testEchoPrefix" depends="setup">
+    <echoproperties destfile="test-prefix.properties" prefix="a." />
+  </target>
+
+  <target name="testEchoPrefixAsPropertyset" depends="setup">
+    <echoproperties destfile="test-prefix.properties">
+      <propertyset>
+        <propertyref prefix="a."/>
+      </propertyset>
+    </echoproperties>
+  </target>
+
+  <target name="testEchoPrefixAsNegatedPropertyset" depends="setup">
+    <echoproperties destfile="test-prefix.properties">
+      <propertyset negate="true">
+        <propertyref prefix="b."/>
+      </propertyset>
+    </echoproperties>
+  </target>
+
+  <target name="testEchoPrefixAsDoublyNegatedPropertyset" depends="setup">
+    <echoproperties destfile="test-prefix.properties">
+      <propertyset negate="true">
+        <propertyset negate="true">
+          <propertyref prefix="a."/>
+        </propertyset>
+      </propertyset>
+    </echoproperties>
+  </target>
+
+  <target name="cleanup">
+    <delete file="test.properties" failonerror="no" />
+    <delete file="test-prefix.properties" failonerror="no" />
+    <delete file="test.xml" failonerror="no" />
+  </target>
+
+</project>
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/i18n/translate/expected/de/template.txt
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/i18n/translate/expected/de/template.txt	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/i18n/translate/expected/de/template.txt	(revision 16139)
@@ -0,0 +1,1 @@
+Diese ist eine Demo Datei für die translate_Aufgabe.
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/i18n/translate/input/resources_ger_DE.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/i18n/translate/input/resources_ger_DE.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/i18n/translate/input/resources_ger_DE.properties	(revision 16139)
@@ -0,0 +1,10 @@
+This=Diese
+is=ist
+a=eine
+demo=Demo
+file=Datei
+#note for people understanding german
+# \u00fc = u umlaut
+for=für
+the=die
+_task=_Aufgabe
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/i18n/translate/input/template.txt
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/i18n/translate/input/template.txt	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/i18n/translate/input/template.txt	(revision 16139)
@@ -0,0 +1,1 @@
+@This@ @is@ @a@ @demo@ @file@ @for@ @the@ translate@_task@.
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/i18n/translate/translate.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/i18n/translate/translate.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/i18n/translate/translate.xml	(revision 16139)
@@ -0,0 +1,29 @@
+<?xml version="1.0"?>
+<project name="translate-test" default="test1" basedir=".">
+    <property name="input.dir" value="input"/>
+    <property name="output.dir" value="output"/>
+    <property name="expected.dir" value="expected"/>
+    <target name="setup">
+        <mkdir dir="${output.dir}/de"/>
+    </target>
+    <target name="test1" depends="setup">
+      <translate toDir="${output.dir}/de"
+        starttoken="@"
+        endtoken="@"
+        bundle="${input.dir}/resources"
+        bundlecountry="DE"
+        bundlelanguage="ger"
+        forceoverwrite="yes"
+        srcencoding="ISO8859_1"
+        destencoding="ISO8859_1"
+        bundleencoding="Cp1252">
+        <fileset dir="${input.dir}">
+            <include name="template.txt"/>
+        </fileset>
+      </translate>
+     </target>
+     <target name="cleanup">
+         <delete dir="${output.dir}" quiet="true"/>
+     </target>
+</project>
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/image/image.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/image/image.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/image/image.xml	(revision 16139)
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+
+<project name="image-test" default="main" basedir=".">
+
+   <target name="main" depends="testSimpleScale">
+   </target>
+
+    <target name="init">
+        <property name="src.dir" location="${basedir}/src"/>
+        <property name="dest.dir" location="${basedir}/dest"/>
+        <mkdir dir="${dest.dir}"/>
+    </target>
+    
+    <target name="cleanup">
+        <delete dir="${dest.dir}"/>   
+    </target>
+   
+   	<!-- this should produce a single file in the dest dir -->
+	 	<target name="testSimpleScale" depends="init">
+			<image includes="*.jpg" srcdir="${src.dir}" destdir="${dest.dir}" overwrite="no" failonerror="no">
+				<scale width="300" proportions="width"/>
+			</image>   
+		</target>   
+
+   	<!-- this should put some text in the log -->
+	 	<target name="testEchoToLog" depends="init">
+			<image includes="*.jpg" srcdir="${src.dir}" destdir="${dest.dir}" overwrite="no" failonerror="no">
+				<scale width="300" proportions="width"/>
+			</image>   
+		</target>   
+		
+   	<!-- this should produce a single file in the dest dir -->
+	 	<target name="testFailOnError" depends="init">
+			<image includes="*.jpg" srcdir="${src.dir}" destdir="${dest.dir}" overwrite="no" failonerror="yes">
+				<scale width="300" proportions="width"/>
+			</image>   
+		</target>   		
+		
+		<!-- this should produce a single file in the dest dir, overwriting any existing file -->
+		<target name="testOverwriteTrue" depends="init">
+			<image includes="*.jpg" srcdir="${src.dir}" destdir="${dest.dir}" overwrite="true" failonerror="no">
+				<scale width="300" proportions="width"/>
+			</image>   
+		</target>   
+
+		<!-- this should not overwrite the existing file -->
+		<target name="testOverwriteFalse" depends="init">
+			<image includes="*.jpg" srcdir="${src.dir}" destdir="${dest.dir}" overwrite="false" failonerror="no">
+				<scale width="300" proportions="width"/>
+			</image>   
+		</target>   
+   
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jdepend/jdepend.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jdepend/jdepend.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jdepend/jdepend.xml	(revision 16139)
@@ -0,0 +1,53 @@
+<project>
+  <property name="testclasses" location="../../../../../../build/testcases"/>
+  
+  <path id="all-test-classes.id">
+    <pathelement location="../../../../build/testcases" />
+    <pathelement path="${java.class.path}" />
+  </path>
+
+  <path id="example-classes.id">
+    <pathelement location="${testclasses}/org/apache/tools/ant/util/facade" />
+  </path>
+
+  <path id="test-classes.id">
+    <pathelement location="${testclasses}" />
+  </path>
+
+  <target name="simple">
+    <jdepend>
+      <classespath refid="example-classes.id"/>
+    </jdepend>
+  </target>
+
+  <target name="xml">
+    <jdepend format="xml">
+      <classespath refid="example-classes.id"/>
+    </jdepend>
+  </target>
+
+  <target name="fork">
+    <jdepend fork="yes" includeruntime="yes">
+      <classespath refid="example-classes.id"/>
+    </jdepend>
+  </target>
+
+  <target name="fork-xml">
+    <jdepend fork="yes" format="xml" includeruntime="yes">
+      <classespath refid="example-classes.id"/>
+    </jdepend>
+  </target>
+
+  <target name="fork-timeout">
+    <jdepend fork="yes" timeout="10" includeruntime="yes">
+      <classespath refid="test-classes.id"/>
+    </jdepend>
+  </target>
+
+  <target name="fork-timeout-not">
+    <jdepend fork="yes" timeout="100000" includeruntime="yes">
+      <classespath refid="example-classes.id"/>
+    </jdepend>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/1nvalid-classname.jsp
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/1nvalid-classname.jsp	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/1nvalid-classname.jsp	(revision 16139)
@@ -0,0 +1,9 @@
+<%@ page language="java" %>
+<html>
+<head/>
+<body>
+
+my name is <%= this.getClass().getName()  %>
+
+</body>
+</html>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/WEB-INF/web.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/WEB-INF/web.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/WEB-INF/web.xml	(revision 16139)
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN"
+  "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">
+<web-app/>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/default.jsp
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/default.jsp	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/default.jsp	(revision 16139)
@@ -0,0 +1,9 @@
+<%@ page language="java" %>
+<html>
+<head/>
+<body>
+
+It is now <%= System.currentTimeMillis() %>
+
+</body>
+</html>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/missing_tld.jsp
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/missing_tld.jsp	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/missing_tld.jsp	(revision 16139)
@@ -0,0 +1,16 @@
+<%@ page language="java" %>
+<%@ taglib uri="/WEB-INF/tlds/struts-bean.tld" prefix="bean" %>
+<%@ taglib uri="/WEB-INF/tlds/struts-html.tld" prefix="html" %>
+<%@ taglib uri="/WEB-INF/tlds/struts-template.tld" prefix="template" %>
+<html:html locale="true">
+<head>
+<title>shouldnt compile</title>
+<html:base/>
+</head>
+<body>
+
+This page should not compile because refers to TLDs that arent around.
+
+</body>
+
+</html:html>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/simple.jsp
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/simple.jsp	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/simple.jsp	(revision 16139)
@@ -0,0 +1,9 @@
+<%@ page language="java" %>
+<html>
+<head/>
+<body>
+
+It is now <%= System.currentTimeMillis() %>
+
+</body>
+</html>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/uriroot.jsp
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/uriroot.jsp	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/uriroot.jsp	(revision 16139)
@@ -0,0 +1,9 @@
+<%@ page language="java" %>
+<html>
+<head/>
+<body>
+
+It is now <%= System.currentTimeMillis() %>
+
+</body>
+</html>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/xml.jsp
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/xml.jsp	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jsp/xml.jsp	(revision 16139)
@@ -0,0 +1,16 @@
+<?xml version="1.0" ?>
+<!-- :mode=xml:indentSize=2 -->
+<!-- note the lack of a language setting here. crimson whined when ISO-8859-1 was set,
+	 that it thought it was loading a file of type ISO_8859_1 and
+	 so there was a mismatch, even though the mismatch is only 
+	 between hyphen types -->
+<jsp:root
+  xmlns:jsp="http://java.sun.com/JSP/Page"
+  version="1.2"
+  >
+<jsp:directive.page language="java" />
+<jsp:directive.page contentType="application/xml" />
+<timestamp>
+<jsp:expression>System.currentTimeMillis()</jsp:expression>
+</timestamp>
+</jsp:root>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jspc.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jspc.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/jspc.xml	(revision 16139)
@@ -0,0 +1,117 @@
+<?xml version="1.0"?>
+
+<project name="jspc-test" default="main" basedir=".">
+
+   <target name="main" depends="testSimple">
+   </target>
+
+    <target name="init">
+        <property name="jsp.dir" location="jsp"/>
+        <property name="jsp.output.dir" location="${jsp.dir}/java"/>
+        <property name="jsp.verbosity" value="3"/>
+        <property name="jsp.compiler" value="jasper41"/>
+        <mkdir dir="${jsp.output.dir}"/>
+    </target>
+    
+    <target name="cleanup">
+        <delete dir="${jsp.output.dir}"/>   
+    </target>
+   
+   <!-- this should fail -->
+   <!-- it should not create an output file, but it does, which needs
+        cleanup -->
+   <target name="testNoTld" depends="init">
+        <jspc
+           destdir="${jsp.output.dir}"
+           srcdir="${jsp.dir}"
+           compiler="${jsp.compiler}"
+           verbose="${jsp.verbosity}">
+            <include
+               name="missing_tld.jsp"/>
+        </jspc>
+   </target>
+
+   <!-- this should compile to simple.java -->
+    <!-- also, stick to the default compiler here to ensure it still works-->
+   <target name="testSimple" depends="init">
+        <jspc
+           destdir="${jsp.output.dir}"
+           srcdir="${jsp.dir}"
+           verbose="${jsp.verbosity}">
+            <include
+               name="simple.jsp"/>
+        </jspc>
+   </target>   
+
+    <!-- this should compile to uriroot.java -->
+    <target name="testUriroot" depends="init">
+        <jspc
+           destdir="${jsp.output.dir}"
+           uriroot="${jsp.dir}"
+           srcdir="${jsp.dir}"
+           compiler="${jsp.compiler}"
+           verbose="${jsp.verbosity}">
+            <include
+                name="uriroot.jsp"/>
+        </jspc>
+    </target> 
+   
+   <!-- this should compile an xml format jsp page to xml.java -->
+    <target name="testXml" depends="init">
+      <jspc
+        destdir="${jsp.output.dir}"
+        uriroot="${jsp.dir}"
+        srcdir="${jsp.dir}"
+        compiler="${jsp.compiler}"
+        verbose="${jsp.verbosity}">
+          <include name="xml.jsp"/>
+      </jspc>
+   </target>
+   
+   <!-- this should compile default.jsp to mangled(%default).java -->
+   <target name="testKeyword" depends="init">
+        <jspc
+           destdir="${jsp.output.dir}"
+           srcdir="${jsp.dir}"
+           compiler="${jsp.compiler}"
+           verbose="${jsp.verbosity}">
+            <include
+               name="default.jsp"/>
+        </jspc>
+   </target>  
+   
+   <!-- this should compile default.jsp to mangled(%default).java -->
+   <target name="testInvalidClassname" depends="init">
+        <jspc
+           destdir="${jsp.output.dir}"
+           srcdir="${jsp.dir}"
+           compiler="${jsp.compiler}"
+           verbose="${jsp.verbosity}">
+            <include
+               name="1nvalid-classname.jsp"/>
+        </jspc>
+   </target>  
+
+   <!-- non jsp pages should be ignored -->
+   <target name="testNotAJspFile" depends="init">
+        <jspc
+           destdir="${jsp.output.dir}"
+           srcdir="${jsp.dir}"
+           compiler="${jsp.compiler}"
+           verbose="${jsp.verbosity}">
+            <include
+               name="wrong_type.txt"/>
+        </jspc>
+   </target>
+   
+   <!-- test for webapp compilation -->
+   <target name="testWebapp" depends="init">
+        <jspc
+           destdir="${jsp.output.dir}"
+           compiler="${jsp.compiler}"
+           verbose="${jsp.verbosity}">
+            <webapp basedir="${jsp.dir}" />
+        </jspc>
+   </target>  
+   
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/junit.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/junit.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/junit.xml	(revision 16139)
@@ -0,0 +1,45 @@
+<?xml version="1.0"?>
+
+<project name="junit-test" basedir="." default="outputTests">
+  <property name="showoutput" value="false" />
+  <path id="test">
+    <pathelement path="${java.class.path}" />
+    <pathelement location="../../../../../build/testcases" />
+  </path>
+
+  <target name="testForkedOutput">
+    <junit fork="yes" haltonerror="true" haltonfailure="true" 
+           showoutput="${showoutput}">
+      <test name="org.example.junit.Output" />
+      <classpath refid="test" />
+    </junit>
+  </target>
+
+  <target name="testNonForkedOutput">
+    <junit fork="false" haltonerror="true" haltonfailure="true"
+           showoutput="${showoutput}">
+      <test name="org.example.junit.Output" />
+      <classpath refid="test" />
+    </junit>
+  </target>
+
+  <target name="testForkedThreadedOutput">
+    <junit fork="yes" haltonerror="true" haltonfailure="true"
+           showoutput="${showoutput}">
+      <test name="org.example.junit.ThreadedOutput" />
+      <classpath refid="test" />
+    </junit>
+  </target>
+
+  <target name="testNonForkedThreadedOutput">
+    <junit fork="false" haltonerror="true" haltonfailure="true"
+           showoutput="${showoutput}">
+      <test name="org.example.junit.ThreadedOutput" />
+      <classpath refid="test" />
+    </junit>
+  </target>
+
+  <target name="outputTests" 
+          depends="testForkedOutput,testNonForkedOutput,testForkedThreadedOutput,testNonForkedThreadedOutput" />
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/junitreport.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/junitreport.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/junitreport.xml	(revision 16139)
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+
+<project name="junitreport-test" basedir="." default="reports">
+
+    <property name="jrdir" location="junitreport"/>
+    <property name="outputdir" location="${jrdir}/test"/>
+
+    <!-- reports1 take care of transformation of 2 test result files and 
+         produce reports according to the default format (frames) 
+         needed for testNoFileJunitNoFrames -->
+    <target name="reports1">
+        <mkdir dir="${outputdir}"/>
+        <mkdir dir="${outputdir}/html"/>
+        <junitreport todir="${outputdir}">
+            <fileset dir="${jrdir}">
+                <include name="TEST-*.xml"/>
+            </fileset>
+            <report todir="${outputdir}/html"/>
+        </junitreport>
+    </target>
+    
+    <target name="clean">
+        <delete dir="${outputdir}"/>
+    </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/junitreport/TEST-sampleproject.coins.CoinTest.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/junitreport/TEST-sampleproject.coins.CoinTest.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/junitreport/TEST-sampleproject.coins.CoinTest.xml	(revision 16139)
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<testsuite errors="1" failures="1" name="sampleproject.coins.CoinTest" tests="11" time="0.171">
+  <properties>
+    <property name="testsrc" value="test/"></property>
+    <property name="java.runtime.name" value="Java(TM) 2 Runtime Environment, Standard Edition"></property>
+    <property name="sun.boot.library.path" value="/usr/java/jdk1.3.1_03/jre/lib/i386"></property>
+    <property name="java.vm.version" value="1.3.1_03-b03"></property>
+    <property name="ant.version" value="Apache Ant version 1.5 compiled on July 9 2002"></property>
+    <property name="ant.java.version" value="1.3"></property>
+    <property name="java.vm.vendor" value="Sun Microsystems Inc."></property>
+    <property name="java.vendor.url" value="http://java.sun.com/"></property>
+    <property name="path.separator" value=":"></property>
+    <property name="java.vm.name" value="Java HotSpot(TM) Client VM"></property>
+    <property name="file.encoding.pkg" value="sun.io"></property>
+    <property name="classes" value="classes"></property>
+    <property name="java.vm.specification.name" value="Java Virtual Machine Specification"></property>
+    <property name="user.dir" value="/home/jkf/programming/gretant_sourceforge/sampleproject"></property>
+    <property name="java.runtime.version" value="1.3.1_03-b03"></property>
+    <property name="java.awt.graphicsenv" value="sun.awt.X11GraphicsEnvironment"></property>
+    <property name="basedir" value="/home/jkf/programming/gretant_sourceforge/sampleproject"></property>
+    <property name="os.arch" value="i386"></property>
+    <property name="java.io.tmpdir" value="/tmp"></property>
+    <property name="line.separator" value="
+"></property>
+    <property name="java.vm.specification.vendor" value="Sun Microsystems Inc."></property>
+    <property name="java.awt.fonts" value=""></property>
+    <property name="os.name" value="Linux"></property>
+    <property name="ant.home" value="/opt/jakarta-ant-1.5/"></property>
+    <property name="ant.project.name" value="sample"></property>
+    <property name="reportdir" value="reports"></property>
+    <property name="java.library.path" value="/usr/java/jdk1.3.1_03/jre/lib/i386:/usr/java/jdk1.3.1_03/jre/lib/i386/native_threads/:/usr/java/jdk1.3.1_03/jre/lib/i386/client:/usr/java/jdk1.3.1_03/jre/../lib/i386"></property>
+    <property name="src" value="code/"></property>
+    <property name="debug" value="on"></property>
+    <property name="java.specification.name" value="Java Platform API Specification"></property>
+    <property name="java.class.version" value="47.0"></property>
+    <property name="os.version" value="2.4.18-5"></property>
+    <property name="ant.file" value="/home/jkf/programming/gretant_sourceforge/sampleproject/build.xml"></property>
+    <property name="unitreport" value="cl-unit.xml"></property>
+    <property name="user.home" value="/home/jkf"></property>
+    <property name="user.timezone" value="Europe/Amsterdam"></property>
+    <property name="java.awt.printerjob" value="sun.awt.motif.PSPrinterJob"></property>
+    <property name="java.specification.version" value="1.3"></property>
+    <property name="file.encoding" value="ISO-8859-15"></property>
+    <property name="java.class.path" value="/opt/jakarta-ant-1.5//lib/xml-apis.jar:/opt/jakarta-ant-1.5//lib/xercesImpl.jar:/opt/jakarta-ant-1.5//lib/xalan.jar:/opt/jakarta-ant-1.5//lib/optional.jar:/opt/jakarta-ant-1.5//lib/junit.jar:/opt/jakarta-ant-1.5//lib/Gretel.jar:/opt/jakarta-ant-1.5//lib/gretant.jar:/opt/jakarta-ant-1.5//lib/cup-runtime.jar:/opt/jakarta-ant-1.5//lib/bcel.jar:/opt/jakarta-ant-1.5//lib/ant.jar:/usr/java/jdk1.3/lib/tools.jar"></property>
+    <property name="user.name" value="jkf"></property>
+    <property name="coverreport" value="cl-cover.xml"></property>
+    <property name="java.vm.specification.version" value="1.0"></property>
+    <property name="java.home" value="/usr/java/jdk1.3.1_03/jre"></property>
+    <property name="java.specification.vendor" value="Sun Microsystems Inc."></property>
+    <property name="user.language" value="en"></property>
+    <property name="java.vm.info" value="mixed mode"></property>
+    <property name="java.version" value="1.3.1_03"></property>
+    <property name="java.ext.dirs" value="/usr/java/jdk1.3.1_03/jre/lib/ext"></property>
+    <property name="sun.boot.class.path" value="/usr/java/jdk1.3.1_03/jre/lib/rt.jar:/usr/java/jdk1.3.1_03/jre/lib/i18n.jar:/usr/java/jdk1.3.1_03/jre/lib/sunrsasign.jar:/usr/java/jdk1.3.1_03/jre/classes"></property>
+    <property name="java.vendor" value="Sun Microsystems Inc."></property>
+    <property name="file.separator" value="/"></property>
+    <property name="testclasses" value="testclasses"></property>
+    <property name="java.vendor.url.bug" value="http://java.sun.com/cgi-bin/bugreport.cgi"></property>
+    <property name="sun.io.unicode.encoding" value="UnicodeLittle"></property>
+    <property name="sun.cpu.endian" value="little"></property>
+    <property name="gretclasses" value="gretclasses"></property>
+    <property name="user.region" value="US"></property>
+    <property name="sun.cpu.isalist" value=""></property>
+  </properties>
+  <testcase name="testEquals" time="0.014"></testcase>
+  <testcase name="testHashCode" time="0.0010"></testcase>
+  <testcase name="testToString" time="0.0010"></testcase>
+  <testcase name="testGetImageURL" time="0.0"></testcase>
+  <testcase name="testGetCountry" time="0.0010"></testcase>
+  <testcase name="testGetDenomination" time="0.0"></testcase>
+  <testcase name="testGetYear" time="0.0"></testcase>
+  <testcase name="testGetSubType" time="0.0"></testcase>
+  <testcase name="testFail" time="0.0080">
+    <failure message="DOEG" type="junit.framework.AssertionFailedError">junit.framework.AssertionFailedError: DOEG
+	at sampleproject.coins.CoinTest.testFail(CoinTest.java:229)
+</failure>
+  </testcase>
+  <testcase name="testException" time="0.0010">
+    <error message="RTE" type="java.lang.RuntimeException">java.lang.RuntimeException: RTE
+	at sampleproject.coins.CoinTest.testException(CoinTest.java:234)
+</error>
+  </testcase>
+  <testcase name="testSuccess" time="0.0"></testcase>
+  <system-out><![CDATA[testEquals
+testHashCode
+Hashcodes: 1434557225 1434557225 1434557226 1463186376 1434556908 1516980401 1434557225
+testToString
+<Coin=NL,1 Euro,1999,Var a/>
+<Coin=NL,1 Euro,1999,null/>
+testGetImageURL
+testGetCountry
+testGetDenomination
+testGetYear
+testGetSubType
+testFail
+testException
+testSuccess
+]]></system-out>
+  <system-err><![CDATA[]]></system-err>
+</testsuite>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/junitreport/TEST-sampleproject.util.UniqueStringTest.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/junitreport/TEST-sampleproject.util.UniqueStringTest.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/junitreport/TEST-sampleproject.util.UniqueStringTest.xml	(revision 16139)
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<testsuite errors="0" failures="0" name="sampleproject.util.UniqueStringTest" tests="5" time="0.038">
+  <properties>
+    <property name="testsrc" value="test/"></property>
+    <property name="java.runtime.name" value="Java(TM) 2 Runtime Environment, Standard Edition"></property>
+    <property name="sun.boot.library.path" value="/usr/java/jdk1.3.1_03/jre/lib/i386"></property>
+    <property name="java.vm.version" value="1.3.1_03-b03"></property>
+    <property name="ant.version" value="Apache Ant version 1.5 compiled on July 9 2002"></property>
+    <property name="ant.java.version" value="1.3"></property>
+    <property name="java.vm.vendor" value="Sun Microsystems Inc."></property>
+    <property name="java.vendor.url" value="http://java.sun.com/"></property>
+    <property name="path.separator" value=":"></property>
+    <property name="java.vm.name" value="Java HotSpot(TM) Client VM"></property>
+    <property name="file.encoding.pkg" value="sun.io"></property>
+    <property name="classes" value="classes"></property>
+    <property name="java.vm.specification.name" value="Java Virtual Machine Specification"></property>
+    <property name="user.dir" value="/home/jkf/programming/gretant_sourceforge/sampleproject"></property>
+    <property name="java.runtime.version" value="1.3.1_03-b03"></property>
+    <property name="java.awt.graphicsenv" value="sun.awt.X11GraphicsEnvironment"></property>
+    <property name="basedir" value="/home/jkf/programming/gretant_sourceforge/sampleproject"></property>
+    <property name="os.arch" value="i386"></property>
+    <property name="java.io.tmpdir" value="/tmp"></property>
+    <property name="line.separator" value="
+"></property>
+    <property name="java.vm.specification.vendor" value="Sun Microsystems Inc."></property>
+    <property name="java.awt.fonts" value=""></property>
+    <property name="os.name" value="Linux"></property>
+    <property name="ant.home" value="/opt/jakarta-ant-1.5/"></property>
+    <property name="ant.project.name" value="sample"></property>
+    <property name="reportdir" value="reports"></property>
+    <property name="java.library.path" value="/usr/java/jdk1.3.1_03/jre/lib/i386:/usr/java/jdk1.3.1_03/jre/lib/i386/native_threads/:/usr/java/jdk1.3.1_03/jre/lib/i386/client:/usr/java/jdk1.3.1_03/jre/../lib/i386"></property>
+    <property name="src" value="code/"></property>
+    <property name="debug" value="on"></property>
+    <property name="java.specification.name" value="Java Platform API Specification"></property>
+    <property name="java.class.version" value="47.0"></property>
+    <property name="os.version" value="2.4.18-5"></property>
+    <property name="ant.file" value="/home/jkf/programming/gretant_sourceforge/sampleproject/build.xml"></property>
+    <property name="unitreport" value="cl-unit.xml"></property>
+    <property name="user.home" value="/home/jkf"></property>
+    <property name="user.timezone" value="Europe/Amsterdam"></property>
+    <property name="java.awt.printerjob" value="sun.awt.motif.PSPrinterJob"></property>
+    <property name="java.specification.version" value="1.3"></property>
+    <property name="file.encoding" value="ISO-8859-15"></property>
+    <property name="java.class.path" value="/opt/jakarta-ant-1.5//lib/xml-apis.jar:/opt/jakarta-ant-1.5//lib/xercesImpl.jar:/opt/jakarta-ant-1.5//lib/xalan.jar:/opt/jakarta-ant-1.5//lib/optional.jar:/opt/jakarta-ant-1.5//lib/junit.jar:/opt/jakarta-ant-1.5//lib/Gretel.jar:/opt/jakarta-ant-1.5//lib/gretant.jar:/opt/jakarta-ant-1.5//lib/cup-runtime.jar:/opt/jakarta-ant-1.5//lib/bcel.jar:/opt/jakarta-ant-1.5//lib/ant.jar:/usr/java/jdk1.3/lib/tools.jar"></property>
+    <property name="user.name" value="jkf"></property>
+    <property name="coverreport" value="cl-cover.xml"></property>
+    <property name="java.vm.specification.version" value="1.0"></property>
+    <property name="java.home" value="/usr/java/jdk1.3.1_03/jre"></property>
+    <property name="java.specification.vendor" value="Sun Microsystems Inc."></property>
+    <property name="user.language" value="en"></property>
+    <property name="java.vm.info" value="mixed mode"></property>
+    <property name="java.version" value="1.3.1_03"></property>
+    <property name="java.ext.dirs" value="/usr/java/jdk1.3.1_03/jre/lib/ext"></property>
+    <property name="sun.boot.class.path" value="/usr/java/jdk1.3.1_03/jre/lib/rt.jar:/usr/java/jdk1.3.1_03/jre/lib/i18n.jar:/usr/java/jdk1.3.1_03/jre/lib/sunrsasign.jar:/usr/java/jdk1.3.1_03/jre/classes"></property>
+    <property name="java.vendor" value="Sun Microsystems Inc."></property>
+    <property name="file.separator" value="/"></property>
+    <property name="testclasses" value="testclasses"></property>
+    <property name="java.vendor.url.bug" value="http://java.sun.com/cgi-bin/bugreport.cgi"></property>
+    <property name="sun.io.unicode.encoding" value="UnicodeLittle"></property>
+    <property name="sun.cpu.endian" value="little"></property>
+    <property name="gretclasses" value="gretclasses"></property>
+    <property name="user.region" value="US"></property>
+    <property name="sun.cpu.isalist" value=""></property>
+  </properties>
+  <testcase name="testEquals" time="0.0"></testcase>
+  <testcase name="testHashCode" time="0.0"></testcase>
+  <testcase name="testToString" time="0.0010"></testcase>
+  <testcase name="testgetUniqueString" time="0.0"></testcase>
+  <testcase name="testSerialization" time="0.024"></testcase>
+  <system-out><![CDATA[testEquals
+testHashCode
+testToString
+testgetUniqueString
+testSerialization
+]]></system-out>
+  <system-err><![CDATA[]]></system-err>
+</testsuite>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/net/ftp.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/net/ftp.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/net/ftp.xml	(revision 16139)
@@ -0,0 +1,108 @@
+<project name="ftp-test" basedir=".">
+    <property file="../../../../../../ftp.properties"/>
+    <property name="ftp.user" value="${user.name}"/>
+    <property name="ftp.host" value="localhost"/>
+    <property name="ftp.port" value="21" />
+    <property name="ftp.password" value="sunshine" />
+    <property name="ftp.filesep" value="/"/>
+    <property name="tmp.dir" location="tmp"/>
+    <property name="tmp.get.dir" location="tmp.get"/>
+    <fileset dir="${tmp.get.dir}" id="fileset-destination-with-selector">
+        <include name="alpha/**"/>
+        <filename name="**/alpha.xml" />
+    </fileset>
+    <fileset dir="${tmp.dir}" id="fileset-source-without-selector">
+        <include name="alpha/**"/>
+    </fileset>
+    <fileset dir="${tmp.get.dir}" id="fileset-destination-without-selector">
+        <include name="alpha/**"/>
+    </fileset>
+    <fileset dir="${tmp.get.dir}" id="fileset-destination-followsymlinks" followsymlinks="true">
+        <include name="alpha/**"/>
+    </fileset>
+    <fileset dir="${tmp.get.dir}" id="fileset-destination-nofollowsymlinks" followsymlinks="false">
+        <include name="alpha/**"/>
+    </fileset>
+    <target name="setup">
+        <mkdir dir="${tmp.get.dir}"/>
+        <mkdir dir="${tmp.dir}/alpha/beta/gamma"/>
+        <touch file="${tmp.dir}/alpha/beta/gamma/gamma.xml"/>
+        <touch file="${tmp.dir}/alpha/beta/beta.xml"/>
+    </target>
+    <target name="ftp-get-with-selector">
+        <ftp action="get"
+            server="${ftp.host}"
+            userid="${ftp.user}"
+            password="${ftp.password}"
+            separator="${ftp.filesep}"
+            remotedir="${tmp.dir}">
+            <fileset refid="fileset-destination-with-selector"/>
+        </ftp>
+    </target>
+    <target name="children-of-excluded-dir-setup" depends="setup">
+        <mkdir dir="${tmp.dir}/delta"/>
+        <touch file="${tmp.dir}/delta/delta.xml"/>
+    </target>
+    <target name="cleanup">
+        <delete dir="${tmp.dir}" quiet="true"/>
+        <delete dir="${tmp.get.dir}" quiet="true"/>
+    </target>
+    <target name="symlink-setup" depends="setup">
+        <mkdir dir="${tmp.dir}/epsilon/gamma"/>
+        <delete dir="${tmp.dir}/alpha/beta"/>
+        <symlink link="${tmp.dir}/alpha/beta" resource="${tmp.dir}/epsilon"/>
+        <touch file="${tmp.dir}/alpha/beta/gamma/gamma.xml"/>
+    </target>
+    <target name="ftp-get-directory-symbolic-link" depends="symlink-setup">
+        <ftp action="get"
+            server="${ftp.host}"
+            userid="${ftp.user}"
+            password="${ftp.password}"
+            separator="${ftp.filesep}"
+            remotedir="${tmp.dir}"
+            >
+            <fileset refid="fileset-destination-followsymlinks"/>
+        </ftp>
+    </target>
+    <target name="ftp-get-directory-no-symbolic-link" depends="symlink-setup">
+        <ftp action="get"
+            server="${ftp.host}"
+            userid="${ftp.user}"
+            password="${ftp.password}"
+            separator="${ftp.filesep}"
+            remotedir="${tmp.dir}"
+            >
+            <fileset refid="fileset-destination-nofollowsymlinks"/>
+        </ftp>
+    </target>
+    <target name="symlink-file-setup" depends="setup">
+        <delete file="${tmp.dir}/alpha/beta/gamma/gamma.xml"/>
+        <symlink link="${tmp.dir}/alpha/beta/gamma/gamma.xml"
+            resource="${tmp.dir}/alpha/beta/beta.xml"/>
+    </target>
+    <target name="ftp-delete">
+        <!-- this target can produce an error if the rmdir does not work -->
+        <!-- there can be problems with the rmdir action if the directories are not removed in a proper order -->
+        <!-- which means beginning by the leaves of the tree, going back to the trunk -->
+        <ftp action="del"
+            server="${ftp.host}"
+            userid="${ftp.user}"
+            password="${ftp.password}"
+            separator="${ftp.filesep}"
+            remotedir="${tmp.dir}">
+            <fileset dir="${tmp.get.dir}">
+                <include name="**"/>
+            </fileset>
+        </ftp>
+        <ftp action="rmdir"
+            server="${ftp.host}"
+            userid="${ftp.user}"
+            password="${ftp.password}"
+            separator="${ftp.filesep}"
+            remotedir="${tmp.dir}">
+            <fileset dir="${tmp.get.dir}">
+                <include name="**"/>
+            </fileset>
+        </ftp>
+    </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/perforce/changerenumbered.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/perforce/changerenumbered.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/perforce/changerenumbered.xml	(revision 16139)
@@ -0,0 +1,33 @@
+<!-- author Antoine Levy-Lambert  -->
+<!-- this file demonstrates that p4.change will be modified by p4submit -->
+<!-- if the change number is modified by the Perforce daemon during the submission -->
+<project name="build1" default="runtest">
+  <target name="runtest">
+    <p4change/>
+    <property name="change1" value="${p4.change}" />
+    <echo>
+doing a first change ${change1}
+</echo>
+    <p4change/>
+    <property name="change2" value="${p4.change}" />
+    <echo>
+doing a second change ${change2}
+</echo>
+    <p4edit view="//depot/foobar" change="${change1}" />
+    <p4edit view="//depot/hello" change="${change2}" />
+    <echo>
+before submitting of hello change ${change2} p4.change is now ${p4.change}
+</echo>
+    <p4submit change="${change2}"/>
+    <echo>
+after submitting of hello p4.change is now ${p4.change}
+</echo>
+    <echo>
+before submitting of foobar change ${change1}
+</echo>
+    <p4submit change="${change1}"/>
+    <echo>
+after submitting of foobar p4.change is now ${p4.change}
+</echo>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/perforce/needsresolve.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/perforce/needsresolve.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/perforce/needsresolve.xml	(revision 16139)
@@ -0,0 +1,56 @@
+<!-- author Antoine Levy-Lambert -->
+<!-- this test shows that p4 submit can now indicate that a file needs to be resolved -->
+<!-- before running the test, edit this xml and change the 5 properties at the top to values which make sense on your system-->
+<!-- the test uses two Perforce client specs which must exist beforehand -->
+<!-- also using both client specs you should be able to edit the file ${depot_file_spec} -->
+<project name="testresolve" default= "test">
+  <property name="first_client" value="levyant_dev_ant"/>
+  <property name="first_client_root" value="C:\dev\depot"/>
+  <property name="second_client" value="levyant_cygwin_test"/>
+  <property name="second_client_root" value="C:\dev\test"/>
+  <property name="depot_file_spec" value="//depot/foobar"/>
+  <target name="test">
+    <p4change client="${first_client}"/>
+    <property name="change1" value="${p4.change}" />
+    <p4change client="${second_client}"/>
+    <property name="change2" value="${p4.change}" />
+    <sequential>
+      <antcall target="edit">
+	<param name="p4.client" value="${first_client}"/>
+	<param name="client_root" value="${first_client_root}"/>
+	<param name="change" value="${change1}"/>
+      </antcall>
+      <antcall target="edit">
+	<param name="p4.client" value="${second_client}"/>
+	<param name="client_root" value="${second_client_root}"/>
+	<param name="change" value="${change2}"/>
+      </antcall>
+      <antcall target="submit">
+	<param name="p4.client" value="${first_client}"/>
+	<param name="change" value="${change1}"/>
+      </antcall>
+      <antcall target="submit">
+	<param name="p4.client" value="${second_client}"/>
+	<param name="change" value="${change2}"/>
+      </antcall>
+    </sequential>
+  </target>
+  <target name="edit">
+    <echo>
+doing a  change ${change} on  client ${p4.client}
+</echo>
+    <p4edit change="${change}" view="${depot_file_spec}"/>
+    <mkdir dir="${client_root}/depot"/>
+    <echo file="${client_root}/depot/foobar">
+hello ${p4.client} ${change}
+</echo>
+  </target>
+  <target name="submit">
+    <p4submit change="${change}" needsresolveproperty="needsresolve" changeproperty="mychange"/>
+    <echo>
+p4.needsresolve ${p4.needsresolve} after submit
+        needsresolveproperty ${needsresolve} after submit
+        changeproperty ${mychange} after submit
+</echo>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/propertyfile.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/propertyfile.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/propertyfile.xml	(revision 16139)
@@ -0,0 +1,89 @@
+<?xml version="1.0"?>
+
+<project name="propertyfile-test" default="main" basedir=".">
+
+   <property file="propertyfile.build.properties"/>
+
+   <target name="main">
+      <fail>
+         This file is for testing purposes only...
+         @see PropertyFileTest.java for more info.
+      </fail>
+   </target>
+
+   <target name="update-existing-properties">
+      <propertyfile
+          file="${test.propertyfile}"
+          comment="unit test for the property file task..." >
+        <entry  key="firstname" value="${firstname}" />
+        <entry  key="lastname"  value="${lastname}" />
+        <entry  key="email"     value="${email}" />
+        <entry  key="phone"     default="${phone}" />
+        <entry  key="age"       default="${age}" type="int"/>
+        <entry  key="date"      default="${date}" type="date"/>
+      </propertyfile>
+
+   </target>
+
+   <target name="exercise">
+     <propertyfile file="${test.propertyfile}">
+        <entry key="existing.prop"
+               type="int"
+               default="23"/>
+        <entry key="ethans.birth"
+               value="2002/01/21 12:18"
+               type="date"/>
+        <entry key="first.birthday"
+               value="1"
+               default="2002/01/21"
+               pattern="yyyy/MM/dd"
+               unit="year"
+               type="date"
+               operation="+"/>
+        <entry key="int.with.default"
+               value="1"
+               default="2"
+               operation="+"
+               type="int"/>
+        <entry key="int.without.value"
+               default="5"
+               operation="+"
+               type="int"/>
+        <entry key="int.without.default"
+               value="1"
+               operation="+"
+               type="int"/>
+        <entry key="string.with.default"
+               value="&gt;"
+               default="--"
+               operation="+"/>
+        <entry key="string.without.default"
+               value="."
+               operation="+"/>
+        <entry key="olderThanAWeek"
+               type="date"
+               default="0201"
+               operation="-"
+               value="8"
+               pattern="MMdd"/>
+     </propertyfile>
+     <property file="${test.propertyfile}"/>
+   </target>
+    <target name="createfile">
+       <echo file="${overwrite.test.propertyfile}">
+ foo=3
+       </echo>
+    </target>
+    <target name="bugDemo1" depends="createfile,bugDemoInit"/>
+    <target name="bugDemo2" depends="bugDemoInit">
+        <property file="${overwrite.test.propertyfile}"/>
+    </target>
+    <target name="bugDemoInit">
+       <propertyfile file="${overwrite.test.propertyfile}">
+          <entry key="foo" default="0" value="1" operation="+" type="int"/>
+       </propertyfile>
+    </target>
+
+</project>
+
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/pvcs.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/pvcs.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/pvcs.xml	(revision 16139)
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+
+<project name="pvcs-test" basedir="." default="test1">
+
+  <taskdef name="pvcs" classname="org.apache.tools.ant.taskdefs.Pvcs"/>
+
+  <target name="test1">
+    <pvcs/>
+  </target>
+
+  <target name="test2">
+    <pvcs pvcsbin="/home/tc/projects/pvcsant/src/etc/testcases/taskdefs" repository="/mnt/pvcs"/>
+  </target>
+
+  <target name="test3">
+    <pvcs pvcsbin="\home\cvs\pvcsant\src\etc\testcases\taskdefs" repository="/mnt/pvcs" pvcsproject="/qviknet"/>
+  </target>
+
+  <target name="test4">
+    <pvcs pvcsbin="\home\cvs\pvcsant\src\etc\testcases\taskdefs" repository="/mnt/pvcs" pvcsproject="/qviknet" workspace="/@/Public/buildws"/>
+  </target>
+
+  <target name="test5" description="Get the latest from PVCS">
+    <pvcs pvcsbin="/home/cvs/pvcsant/src/etc/testcases/taskdefs" 
+			 repository="//ct4serv2/pvcs/monitor"/>
+  </target>
+
+  <target name="test6" description="No pcli to be found">
+    <pvcs pvcsbin="/never/heard/of/a/directory/structure/like/this" 
+			 repository="//ct4serv2/pvcs/monitor"/>
+  </target>
+
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/replaceregexp.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/replaceregexp.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/replaceregexp.properties	(revision 16139)
@@ -0,0 +1,2 @@
+OldAbc=Def
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/replaceregexp.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/replaceregexp.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/replaceregexp.xml	(revision 16139)
@@ -0,0 +1,54 @@
+<project name="test" default="def" basedir=".">
+  <property name="tmpregexp" value="tmpregexp"/>
+  <target name="def">
+    <fail>This build file should only be run from within the testcase</fail>
+  </target>
+
+  <target name="setup">
+    <copy file="replaceregexp.properties" tofile="test.properties" />
+  </target>
+
+  <target name="setup-nl">
+    <copy file="replaceregexp2.properties" tofile="test.properties" />
+  </target>
+
+  <target name="testReplace" depends="setup">
+    <replaceregexp file="test.properties" byline="true">
+      <regexp pattern="Old(.*)=(.*)" />
+      <substitution expression="NewProp=\1\2" />
+    </replaceregexp>
+  </target>
+  <!-- use in conjunction with testDirectoryDateDoesNotChange to make sure something will happen -->
+  <target name="touchDirectory">
+    <mkdir dir="${tmpregexp}"/>
+    <copy file="replaceregexp.properties" tofile="${tmpregexp}/test.properties" />
+    <sleep seconds="2"/>
+  </target>
+  <target name="testDirectoryDateDoesNotChange">
+    <replaceregexp file="${tmpregexp}/test.properties" byline="true">
+      <regexp pattern="foo" />
+        <substitution expression="bar"/>
+      </replaceregexp>
+  </target>
+
+  <target name="testDontAddNewline1" depends="setup-nl">
+    <replaceregexp file="test.properties" byline="false">
+      <regexp pattern="Old(.*)=(.*)" />
+      <substitution expression="NewProp=\1\2" />
+    </replaceregexp>
+  </target>
+
+  <target name="testDontAddNewline2" depends="setup-nl">
+    <replaceregexp file="test.properties" byline="true">
+      <regexp pattern="Old(.*)=(.*)" />
+      <substitution expression="NewProp=\1\2" />
+    </replaceregexp>
+  </target>
+
+  <target name="cleanup">
+    <delete file="test.properties" />
+    <delete dir="${tmpregexp}" quiet="true"/>
+  </target>
+
+</project>
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/replaceregexp2.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/replaceregexp2.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/replaceregexp2.properties	(revision 16139)
@@ -0,0 +1,1 @@
+OldAbc=Def
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/replaceregexp2.result.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/replaceregexp2.result.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/replaceregexp2.result.properties	(revision 16139)
@@ -0,0 +1,1 @@
+NewProp=AbcDef
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/script.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/script.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/script.xml	(revision 16139)
@@ -0,0 +1,49 @@
+<project name="testproject" default="def" basedir=".">
+
+  <target name="def">
+    <fail>This build-file is intended to be run from the test cases</fail>
+  </target>
+
+  <target name="setup1">
+
+    <script language="javascript"> <![CDATA[
+
+      for (i=1; i<=10; i++) {
+        echo = testproject.createTask("echo");
+        setup1.addTask(echo);
+        echo.setMessage(i*i);
+      }
+
+    ]]> </script>
+
+  </target>
+
+  <target name="example1" depends="setup1"/>
+
+  <target name="sub">
+    <echo id="theEcho"/>
+  </target>
+
+  <target name="sub1">
+    <script language="javascript"><![CDATA[
+      theEcho.setMessage("In sub1");
+      sub.execute();
+    ]]></script>
+  </target>
+
+  <target name="sub2">
+    <script language="javascript"><![CDATA[
+      theEcho.setMessage("In sub2");
+      sub.execute();
+    ]]></script>
+  </target>
+
+  <target name="example2" depends="sub1,sub2"/>
+
+  <target name="useBeanshell">
+    <script language="beanshell"><![CDATA[
+       self.log("I'm here", org.apache.tools.ant.Project.MSG_INFO);
+    ]]></script>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/script/scriptdef.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/script/scriptdef.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/script/scriptdef.xml	(revision 16139)
@@ -0,0 +1,128 @@
+<project name="testproject" default="def" basedir=".">
+
+  <target name="def">
+    <fail>This build-file is intended to be run from the test cases</fail>
+  </target>
+
+  <target name="simple">
+    <scriptdef name="scripttest" language="javascript">
+      <attribute name="attr1"/>
+      <element name="fileset" type="fileset"/>
+      <![CDATA[
+        
+        project.log("Attribute attr1 = " + attributes.get("attr1"));
+        project.log("Fileset basedir = "
+          + elements.get("fileset").get(0).getDir(project));
+        
+      ]]>
+    </scriptdef>
+    
+    <fileset id="testfileset" dir="."/>
+    <scripttest attr1="test">
+      <fileset refid="testfileset"/>
+    </scripttest>
+  </target>
+
+  <target name="nolang">
+    <scriptdef name="nolang">
+      <![CDATA[
+        java.lang.System.out.println("Hello");
+      ]]>
+    </scriptdef>
+  </target>
+
+  <target name="noname">
+    <scriptdef language="javascript">
+      <![CDATA[
+        java.lang.System.out.println("Hello");
+      ]]>
+    </scriptdef>
+  </target>
+
+  <target name="nestedbyclassname">
+    <scriptdef name="scripttest" language="javascript">
+      <attribute name="attr1"/>
+      <element name="fileset" classname="org.apache.tools.ant.types.FileSet"/>
+      <![CDATA[
+        
+        project.log("Attribute attr1 = " + attributes.get("attr1"));
+        project.log("Fileset basedir = "
+          + elements.get("fileset").get(0).getDir(project));
+        
+      ]]>
+    </scriptdef>
+    
+    <fileset id="testfileset" dir="."/>
+    <scripttest attr1="test">
+      <fileset refid="testfileset"/>
+    </scripttest>
+  </target>
+  
+  <target name="noelement">
+    <scriptdef name="scripttest" language="javascript">
+      <attribute name="attr1"/>
+      <element name="fileset" type="fileset"/>
+      <![CDATA[
+        java.lang.System.out.println("Attribute attr1 = " + attributes.get("attr1"));
+      ]]>
+    </scriptdef>
+    
+    <scripttest attr1="test">
+    </scripttest>
+  </target>
+
+  <target name="exception">
+    <scriptdef name="scripttest" language="javascript">
+      <attribute name="attr1"/>
+      <element name="fileset" classname="org.apache.tools.ant.types.FileSet"/>
+      <![CDATA[
+        
+        java.lang.System.out.println("Attribute attr1 = " + attributes.get("attr1"));
+        java.lang.System.out.println("Fileset basedir = "
+          + elements.get("fileset").get(0).getDir(project));
+        
+      ]]>
+    </scriptdef>
+    
+    <scripttest attr1="test">
+    </scripttest>
+  </target>
+  
+  <target name="doubledef">
+    <scriptdef name="task1" language="javascript">
+      <![CDATA[
+        project.log("Task1");
+      ]]>
+    </scriptdef>
+    <scriptdef name="task2" language="javascript">
+      <![CDATA[
+        project.log("Task2");
+      ]]>
+    </scriptdef>
+    <task1/>
+    <task2/>
+  </target>
+  
+  <target name="doubleAttributeDef">
+    <scriptdef name="scripttest" language="javascript">
+      <attribute name="attr1"/>
+      <attribute name="attr1"/>
+    </scriptdef>
+  </target>
+  
+  <target name="property">
+    <scriptdef name="scripttest" language="javascript">
+      <attribute name="attr1"/>
+      <![CDATA[
+        
+        project.log("Attribute value = " + attributes.get("attr1"));
+      ]]>
+    </scriptdef>
+
+    <property name="testproperty" value="test"/>    
+    <scripttest attr1="${testproperty}">
+    </scripttest>
+  </target>
+
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/script_reference.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/script_reference.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/script_reference.xml	(revision 16139)
@@ -0,0 +1,10 @@
+<project name="test-reference" default="script">
+  <target name="script">
+    <script language="javascript">
+    </script>
+  </target>
+  <target name="def">
+    <taskdef name="my.echo" classname="org.apache.tools.ant.taskdefs.Echo"/>
+    <my.echo id="my.echo.ref" message="hello world"/>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/sitraka/covreport-test.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/sitraka/covreport-test.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/sitraka/covreport-test.xml	(revision 16139)
@@ -0,0 +1,9949 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- !DOCTYPE snapshot SYSTEM "snapshot.dtd" -->
+<snapshot version="1.0">
+  <execution_log generator="Coverage 3.0.0" program_name="org.apache.tools.ant.Main" program_start="18 nov. 01 14:31:55" snapshot_created="18 nov. 01 14:35:40" elapsed_time="224623" machine_name="UNKNOWN-P7VGK60" machine_type="Pentium" os_name="Windows NT" os_version="5" />
+  <cov.data calls="12264994" hit_methods="992" total_methods="3055" hit_lines="2260" total_lines="17094" />
+  <package name="java.io">
+    <cov.data calls="210142" hit_methods="150" total_methods="527" hit_lines="277" total_lines="3259" />
+    <class name="PrintStream" source="PrintStream.java">
+      <cov.data calls="435" hit_methods="9" total_methods="31" hit_lines="0" total_lines="191" />
+      <method name="&lt;init&gt;(java.io.OutputStream, boolean)">
+        <cov.data calls="2" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="&lt;init&gt;(java.io.OutputStream)">
+        <cov.data calls="2" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="println(String)">
+        <cov.data calls="12" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="println(char[])">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="println(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="println(float)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="println(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="println(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="println(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="println(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="println()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="println(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="flush()">
+        <cov.data calls="108" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="print(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="print(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="print(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="print(float)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="print(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="print(char[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="print(String)">
+        <cov.data calls="31" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="print(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="print(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="write(char[])">
+        <cov.data calls="0" hit_lines="0" total_lines="19" />
+      </method>
+      <method name="write(byte[], int, int)">
+        <cov.data calls="43" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="write(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="write(String)">
+        <cov.data calls="31" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="newLine()">
+        <cov.data calls="12" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="close()">
+        <cov.data calls="0" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="ensureOpen()">
+        <cov.data calls="194" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="checkError()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="setError()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="BufferedOutputStream" source="BufferedOutputStream.java">
+      <cov.data calls="169" hit_methods="3" total_methods="6" hit_lines="0" total_lines="35" />
+      <method name="&lt;init&gt;(java.io.OutputStream, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="&lt;init&gt;(java.io.OutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="flush()">
+        <cov.data calls="63" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="write(byte[], int, int)">
+        <cov.data calls="43" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="write(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="flushBuffer()">
+        <cov.data calls="63" hit_lines="0" total_lines="5" />
+      </method>
+    </class>
+    <class name="Writer" source="Writer.java">
+      <cov.data calls="1222" hit_methods="2" total_methods="9" hit_lines="0" total_lines="35" />
+      <method name="&lt;init&gt;(Object)">
+        <cov.data calls="27" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="flush()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="write(char[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="write(String)">
+        <cov.data calls="1195" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="write(String, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="write(char[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="write(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="close()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="FilterInputStream" source="FilterInputStream.java">
+      <cov.data calls="584" hit_methods="3" total_methods="10" hit_lines="0" total_lines="15" />
+      <method name="&lt;init&gt;(java.io.InputStream)">
+        <cov.data calls="502" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="reset()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="read(byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="read(byte[])">
+        <cov.data calls="21" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="read()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="skip(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="available()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="close()">
+        <cov.data calls="61" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="mark(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="markSupported()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="InputStream" source="InputStream.java">
+      <cov.data calls="1374" hit_methods="2" total_methods="10" hit_lines="0" total_lines="57" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1372" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="reset()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="read(byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="29" />
+      </method>
+      <method name="read(byte[])">
+        <cov.data calls="2" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="read()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="skip(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="21" />
+      </method>
+      <method name="available()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="close()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="mark(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="markSupported()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="BufferedWriter" source="BufferedWriter.java">
+      <cov.data calls="3663" hit_methods="9" total_methods="12" hit_lines="0" total_lines="93" />
+      <method name="&lt;init&gt;(java.io.Writer, int)">
+        <cov.data calls="13" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="&lt;init&gt;(java.io.Writer)">
+        <cov.data calls="13" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="flush()">
+        <cov.data calls="1" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="min(int, int)">
+        <cov.data calls="1103" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="write(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="write(String, int, int)">
+        <cov.data calls="1199" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="write(char[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="29" />
+      </method>
+      <method name="newLine()">
+        <cov.data calls="17" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="close()">
+        <cov.data calls="10" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="ensureOpen()">
+        <cov.data calls="1253" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="flushBuffer()">
+        <cov.data calls="54" hit_lines="0" total_lines="8" />
+      </method>
+    </class>
+    <class name="Win32FileSystem" source="Win32FileSystem.java">
+      <cov.data calls="105583" hit_methods="18" total_methods="39" hit_lines="0" total_lines="229" />
+      <method name="hashCode(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compare(java.io.File, java.io.File)">
+        <cov.data calls="874" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isAbsolute(java.io.File)">
+        <cov.data calls="1" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setReadOnly(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="access(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="checkAccess(java.io.File, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="list(java.io.File)">
+        <cov.data calls="449" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="delete(java.io.File)">
+        <cov.data calls="11" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isLetter(char)">
+        <cov.data calls="17707" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="initIDs()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="prefixLength(String)">
+        <cov.data calls="17601" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="deleteOnExit(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="listRoots()">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="normalize(String, int, int)">
+        <cov.data calls="131" hit_lines="0" total_lines="58" />
+      </method>
+      <method name="normalize(String)">
+        <cov.data calls="17608" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="getDefaultParent()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="resolve(String, String)">
+        <cov.data calls="14200" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="resolve(java.io.File)">
+        <cov.data calls="3704" hit_lines="0" total_lines="39" />
+      </method>
+      <method name="canonicalize(String)">
+        <cov.data calls="33" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getBooleanAttributes(java.io.File)">
+        <cov.data calls="9298" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getLastModifiedTime(java.io.File)">
+        <cov.data calls="3123" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getLength(java.io.File)">
+        <cov.data calls="16" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="createFileExclusively(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="createDirectory(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="rename(java.io.File, java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setLastModifiedTime(java.io.File, long)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getSeparator()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getPathSeparator()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isSlash(char)">
+        <cov.data calls="6518" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="slashify(String)">
+        <cov.data calls="14201" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="normalizePrefix(String, int, StringBuffer)">
+        <cov.data calls="106" hit_lines="0" total_lines="28" />
+      </method>
+      <method name="getDriveDirectory(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="getDriveDirectory(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="driveIndex(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getUserPath()">
+        <cov.data calls="2" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDrive(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="listRoots0()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="FileSystem" source="FileSystem.java">
+      <cov.data calls="0" hit_methods="0" total_methods="26" hit_lines="0" total_lines="1" />
+      <method name="hashCode(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compare(java.io.File, java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isAbsolute(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setReadOnly(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="checkAccess(java.io.File, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="list(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="delete(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="prefixLength(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="deleteOnExit(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="listRoots()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="normalize(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getDefaultParent()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="resolve(String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="resolve(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="canonicalize(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getBooleanAttributes(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getLastModifiedTime(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getLength(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="createFileExclusively(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="createDirectory(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="rename(java.io.File, java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setLastModifiedTime(java.io.File, long)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getFileSystem()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getSeparator()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getPathSeparator()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="File" source="File.java">
+      <cov.data calls="36322" hit_methods="19" total_methods="50" hit_lines="0" total_lines="260" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compareTo(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compareTo(java.io.File)">
+        <cov.data calls="874" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(java.io.File, String)">
+        <cov.data calls="14195" hit_lines="0" total_lines="17" />
+      </method>
+      <method name="&lt;init&gt;(String, String)">
+        <cov.data calls="5" hit_lines="0" total_lines="17" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="3401" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="&lt;init&gt;(String, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="874" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="356" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getName()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="length()">
+        <cov.data calls="16" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getParent()">
+        <cov.data calls="5" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="isAbsolute()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getCanonicalPath()">
+        <cov.data calls="33" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setReadOnly()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="list()">
+        <cov.data calls="449" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="list(java.io.FilenameFilter)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="writeObject(java.io.ObjectOutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="delete()">
+        <cov.data calls="11" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getPath()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getCanonicalFile()">
+        <cov.data calls="5" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toURL()">
+        <cov.data calls="5" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="exists()">
+        <cov.data calls="1562" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getAbsolutePath()">
+        <cov.data calls="3671" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getPrefixLength()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getParentFile()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getAbsoluteFile()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="canRead()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="canWrite()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="isDirectory()">
+        <cov.data calls="4107" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="isFile()">
+        <cov.data calls="3629" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="isHidden()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="lastModified()">
+        <cov.data calls="3123" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="createNewFile()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="deleteOnExit()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="listFiles(java.io.FilenameFilter)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="listFiles()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="listFiles(java.io.FileFilter)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="mkdir()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="mkdirs()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="renameTo(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="setLastModified(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="listRoots()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="generateFile(String, String, java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getTempDir()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="checkAndCreate(String, SecurityManager)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="createTempFile(String, String, java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="createTempFile(String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="FilterOutputStream" source="FilterOutputStream.java">
+      <cov.data calls="3" hit_methods="1" total_methods="6" hit_lines="0" total_lines="21" />
+      <method name="&lt;init&gt;(java.io.OutputStream)">
+        <cov.data calls="3" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="flush()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="write(byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="write(byte[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="write(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="close()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+    </class>
+    <class name="ObjectStreamField" source="ObjectStreamField.java">
+      <cov.data calls="0" hit_methods="0" total_methods="21" hit_lines="0" total_lines="113" />
+      <method name="compareTo(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="&lt;init&gt;(String, char, reflect.Field, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="&lt;init&gt;(String, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="&lt;init&gt;(reflect.Field)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(String, Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="31" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getName()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isPrimitive()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getField()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="class$(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getType()">
+        <cov.data calls="0" hit_lines="0" total_lines="26" />
+      </method>
+      <method name="getTypeCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getTypeString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getOffset()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setOffset(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="typeEquals(java.io.ObjectStreamField)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="setField(reflect.Field)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="constructSearchKey(String, Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setSearchKeyTypeString(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="ObjectStreamClass" source="ObjectStreamClass.java">
+      <cov.data calls="0" hit_methods="0" total_methods="61" hit_lines="0" total_lines="984" />
+      <method name="&lt;init&gt;(String, long)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="&lt;init&gt;(Class, java.io.ObjectStreamClass, boolean, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="35" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="getName()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getSuperclass()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getFields()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="getField(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="getField(String, Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getDeclaredMethod(String, Class[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="access$000(java.io.ObjectStreamClass)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="class$(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$200(Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$300()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$400(java.io.ObjectStreamClass, String, Class[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="init()">
+        <cov.data calls="0" hit_lines="0" total_lines="201" />
+      </method>
+      <method name="write(java.io.ObjectOutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="38" />
+      </method>
+      <method name="access$500(java.io.ObjectStreamClass, reflect.Method)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="typeEquals(java.io.ObjectStreamClass)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getSignature(reflect.Constructor)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="getSignature(reflect.Method)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="getSignature(Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="38" />
+      </method>
+      <method name="compareClassNames(String, String, char)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="lookup(Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="lookupInternal(Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="41" />
+      </method>
+      <method name="getSerialVersionUID()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="forClass()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getFieldsNoCopy()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="validateLocalClass(Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="48" />
+      </method>
+      <method name="setClass(Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="86" />
+      </method>
+      <method name="setSuperclass(java.io.ObjectStreamClass)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="hasWriteObject()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="hasExternalizableBlockDataMode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="localClassDescriptor()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isSerializable()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isExternalizable()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isNonSerializable()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="computeFieldInfo()">
+        <cov.data calls="0" hit_lines="0" total_lines="81" />
+      </method>
+      <method name="computeSerialVersionUID(Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="125" />
+      </method>
+      <method name="read(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="52" />
+      </method>
+      <method name="lightCopy(java.io.ObjectStreamClass)">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="initProxyClassDesc(Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="verifyInstanceDeserialization()">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="findDescriptorFor(Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="27" />
+      </method>
+      <method name="insertDescriptorFor(java.io.ObjectStreamClass)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="initNative()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getFieldIDs(java.io.ObjectStreamField[], long[], long[])">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="hasStaticInitializer(Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isResolvable()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isReplaceable()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="invokeMethod(reflect.Method, Object, Object[])">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="checkSuperMethodAccess(reflect.Method)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="isSameClassPackage(Class, Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="initStaticMethodArgs()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="access$002(java.io.ObjectStreamClass, java.io.ObjectStreamField[])">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$102(java.io.ObjectStreamClass, long)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$600(java.io.ObjectStreamClass)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$700()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$800()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$900()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$1002(java.io.ObjectStreamClass, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="BufferedInputStream" source="BufferedInputStream.java">
+      <cov.data calls="0" hit_methods="0" total_methods="14" hit_lines="0" total_lines="113" />
+      <method name="&lt;init&gt;(java.io.InputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.InputStream, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="reset()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="fill()">
+        <cov.data calls="0" hit_lines="0" total_lines="24" />
+      </method>
+      <method name="read(byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="read()">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="skip(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="21" />
+      </method>
+      <method name="available()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="close()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="mark(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="markSupported()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="ensureOpen()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="read1(byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="17" />
+      </method>
+    </class>
+    <class name="ObjectStreamClass$ObjectStreamClassEntry" source="ObjectStreamClass.java">
+      <cov.data calls="0" hit_methods="0" total_methods="1" hit_lines="0" total_lines="2" />
+      <method name="&lt;init&gt;(java.io.ObjectStreamClass)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="FileOutputStream" source="FileOutputStream.java">
+      <cov.data calls="24500" hit_methods="9" total_methods="15" hit_lines="0" total_lines="40" />
+      <method name="&lt;init&gt;(String, boolean)">
+        <cov.data calls="12" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="&lt;init&gt;(java.io.File)">
+        <cov.data calls="12" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.FileDescriptor)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="12" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="finalize()">
+        <cov.data calls="4" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="write(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="write(byte[])">
+        <cov.data calls="11024" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="write(byte[], int, int)">
+        <cov.data calls="1192" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="open(String)">
+        <cov.data calls="12" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="close()">
+        <cov.data calls="16" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getFD()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="initIDs()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="openAppend(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="writeBytes(byte[], int, int)">
+        <cov.data calls="12216" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="OutputStreamWriter" source="OutputStreamWriter.java">
+      <cov.data calls="263" hit_methods="7" total_methods="11" hit_lines="0" total_lines="103" />
+      <method name="&lt;init&gt;(java.io.OutputStream)">
+        <cov.data calls="13" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.OutputStream, sun.io.CharToByteConverter)">
+        <cov.data calls="13" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="&lt;init&gt;(java.io.OutputStream, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="flush()">
+        <cov.data calls="11" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="write(String, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="write(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="write(char[], int, int)">
+        <cov.data calls="54" hit_lines="0" total_lines="37" />
+      </method>
+      <method name="close()">
+        <cov.data calls="10" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="ensureOpen()">
+        <cov.data calls="108" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="flushBuffer()">
+        <cov.data calls="54" hit_lines="0" total_lines="19" />
+      </method>
+      <method name="getEncoding()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+    </class>
+    <class name="FileDescriptor" source="FileDescriptor.java">
+      <cov.data calls="340" hit_methods="1" total_methods="6" hit_lines="0" total_lines="8" />
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="340" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="initIDs()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="valid()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="sync()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="ObjectStreamClass$CompareMemberByName" source="ObjectStreamClass.java">
+      <cov.data calls="0" hit_methods="0" total_methods="3" hit_lines="0" total_lines="13" />
+      <method name="&lt;init&gt;(java.io.ObjectStreamClass$1)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compare(Object, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+    </class>
+    <class name="OutputStream" source="OutputStream.java">
+      <cov.data calls="48" hit_methods="1" total_methods="6" hit_lines="0" total_lines="17" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="48" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="flush()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="write(byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="write(byte[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="write(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="close()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="ObjectStreamClass$CompareClassByName" source="ObjectStreamClass.java">
+      <cov.data calls="0" hit_methods="0" total_methods="3" hit_lines="0" total_lines="5" />
+      <method name="&lt;init&gt;(java.io.ObjectStreamClass$1)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compare(Object, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+    </class>
+    <class name="FileInputStream" source="FileInputStream.java">
+      <cov.data calls="3461" hit_methods="9" total_methods="15" hit_lines="0" total_lines="30" />
+      <method name="&lt;init&gt;(java.io.File)">
+        <cov.data calls="328" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.FileDescriptor)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="328" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="finalize()">
+        <cov.data calls="304" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="read()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="read(byte[])">
+        <cov.data calls="50" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="read(byte[], int, int)">
+        <cov.data calls="697" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="open(String)">
+        <cov.data calls="328" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="readBytes(byte[], int, int)">
+        <cov.data calls="747" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="skip(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="available()">
+        <cov.data calls="45" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="close()">
+        <cov.data calls="634" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getFD()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="initIDs()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="DataInputStream" source="DataInputStream.java">
+      <cov.data calls="122" hit_methods="2" total_methods="19" hit_lines="10" total_lines="120" />
+      <method name="&lt;init&gt;(java.io.InputStream)">
+        <cov.data calls="61" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="readLine()">
+        <cov.data calls="0" hit_lines="0" total_lines="24" />
+      </method>
+      <method name="readInt()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="read(byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="read(byte[])">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="readByte()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="readShort()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="readUTF()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="readUTF(java.io.DataInput)">
+        <cov.data calls="0" hit_lines="0" total_lines="31" />
+      </method>
+      <method name="readChar()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="readFloat()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="readFully(byte[], int, int)">
+        <cov.data calls="61" hit_lines="8" total_lines="10" />
+      </method>
+      <method name="readFully(byte[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="skipBytes(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="readBoolean()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="readUnsignedByte()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="readUnsignedShort()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="readLong()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="readDouble()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="ByteArrayInputStream" source="ByteArrayInputStream.java">
+      <cov.data calls="157" hit_methods="3" total_methods="11" hit_lines="19" total_lines="50" />
+      <method name="&lt;init&gt;(byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="&lt;init&gt;(byte[])">
+        <cov.data calls="29" hit_lines="7" total_lines="7" />
+      </method>
+      <method name="reset()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="read(byte[], int, int)">
+        <cov.data calls="64" hit_lines="11" total_lines="14" />
+      </method>
+      <method name="read()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="skip(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="available()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="close()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="mark(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="markSupported()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="ensureOpen()">
+        <cov.data calls="64" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="ByteArrayOutputStream" source="ByteArrayOutputStream.java">
+      <cov.data calls="173" hit_methods="6" total_methods="13" hit_lines="23" total_lines="45" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="31" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="31" hit_lines="5" total_lines="6" />
+      </method>
+      <method name="toString(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toString(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="reset()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="size()">
+        <cov.data calls="108" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="write(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="write(byte[], int, int)">
+        <cov.data calls="1" hit_lines="11" total_lines="13" />
+      </method>
+      <method name="close()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="ensureOpen()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="toByteArray()">
+        <cov.data calls="1" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="writeTo(java.io.OutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="IOException" source="IOException.java">
+      <cov.data calls="13" hit_methods="1" total_methods="2" hit_lines="2" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="13" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="FileNotFoundException" source="FileNotFoundException.java">
+      <cov.data calls="13" hit_methods="1" total_methods="3" hit_lines="2" total_lines="6" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="13" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="Reader" source="Reader.java">
+      <cov.data calls="72" hit_methods="1" total_methods="11" hit_lines="5" total_lines="33" />
+      <method name="&lt;init&gt;(Object)">
+        <cov.data calls="72" hit_lines="5" total_lines="6" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="reset()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="read(char[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="read(char[])">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="read()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="skip(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="close()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="mark(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="markSupported()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="ready()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="BufferedReader" source="BufferedReader.java">
+      <cov.data calls="30939" hit_methods="11" total_methods="16" hit_lines="84" total_lines="184" />
+      <method name="&lt;init&gt;(java.io.Reader)">
+        <cov.data calls="12" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.Reader, int)">
+        <cov.data calls="36" hit_lines="10" total_lines="11" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="reset()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="readLine(boolean)">
+        <cov.data calls="286" hit_lines="33" total_lines="37" />
+      </method>
+      <method name="readLine()">
+        <cov.data calls="286" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="fill()">
+        <cov.data calls="39" hit_lines="8" total_lines="23" />
+      </method>
+      <method name="read()">
+        <cov.data calls="14947" hit_lines="9" total_lines="14" />
+      </method>
+      <method name="read(char[], int, int)">
+        <cov.data calls="24" hit_lines="8" total_lines="14" />
+      </method>
+      <method name="skip(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="23" />
+      </method>
+      <method name="close()">
+        <cov.data calls="27" hit_lines="6" total_lines="8" />
+      </method>
+      <method name="mark(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="markSupported()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="ensureOpen()">
+        <cov.data calls="15257" hit_lines="2" total_lines="3" />
+      </method>
+      <method name="read1(char[], int, int)">
+        <cov.data calls="24" hit_lines="3" total_lines="17" />
+      </method>
+      <method name="ready()">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+    </class>
+    <class name="InputStreamReader" source="InputStreamReader.java">
+      <cov.data calls="448" hit_methods="11" total_methods="14" hit_lines="54" total_lines="78" />
+      <method name="&lt;init&gt;(java.io.InputStream, String)">
+        <cov.data calls="4" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.InputStream, sun.io.ByteToCharConverter)">
+        <cov.data calls="36" hit_lines="8" total_lines="9" />
+      </method>
+      <method name="&lt;init&gt;(java.io.InputStream)">
+        <cov.data calls="32" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="fill(char[], int, int)">
+        <cov.data calls="63" hit_lines="15" total_lines="16" />
+      </method>
+      <method name="read(char[], int, int)">
+        <cov.data calls="63" hit_lines="5" total_lines="8" />
+      </method>
+      <method name="read()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="close()">
+        <cov.data calls="27" hit_lines="7" total_lines="9" />
+      </method>
+      <method name="ensureOpen()">
+        <cov.data calls="79" hit_lines="2" total_lines="3" />
+      </method>
+      <method name="getEncoding()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="ready()">
+        <cov.data calls="16" hit_lines="3" total_lines="4" />
+      </method>
+      <method name="malfunction()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="convertInto(char[], int, int)">
+        <cov.data calls="39" hit_lines="6" total_lines="9" />
+      </method>
+      <method name="flushInto(char[], int, int)">
+        <cov.data calls="34" hit_lines="3" total_lines="4" />
+      </method>
+      <method name="inReady()">
+        <cov.data calls="55" hit_lines="1" total_lines="2" />
+      </method>
+    </class>
+    <class name="FilePermission" source="FilePermission.java">
+      <cov.data calls="116" hit_methods="6" total_methods="17" hit_lines="32" total_lines="122" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(String, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(String, String)">
+        <cov.data calls="23" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="access$000(java.io.FilePermission)">
+        <cov.data calls="23" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="access$100(java.io.FilePermission)">
+        <cov.data calls="23" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="implies(java.security.Permission)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="init(int)">
+        <cov.data calls="23" hit_lines="11" total_lines="24" />
+      </method>
+      <method name="writeObject(java.io.ObjectOutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getActions()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getActions(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="newPermissionCollection()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="impliesIgnoreMask(java.io.FilePermission)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="getMask()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getMask(String)">
+        <cov.data calls="23" hit_lines="14" total_lines="32" />
+      </method>
+    </class>
+    <class name="FilePermission$1" source="FilePermission.java">
+      <cov.data calls="46" hit_methods="2" total_methods="2" hit_lines="5" total_lines="8" />
+      <method name="&lt;init&gt;(java.io.FilePermission)">
+        <cov.data calls="23" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="run()">
+        <cov.data calls="23" hit_lines="4" total_lines="7" />
+      </method>
+    </class>
+    <class name="FileReader" source="FileReader.java">
+      <cov.data calls="26" hit_methods="1" total_methods="3" hit_lines="2" total_lines="6" />
+      <method name="&lt;init&gt;(java.io.File)">
+        <cov.data calls="26" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.FileDescriptor)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="FileWriter" source="FileWriter.java">
+      <cov.data calls="10" hit_methods="1" total_methods="4" hit_lines="2" total_lines="8" />
+      <method name="&lt;init&gt;(String, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.File)">
+        <cov.data calls="10" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.FileDescriptor)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="StringWriter" source="StringWriter.java">
+      <cov.data calls="0" hit_methods="0" total_methods="11" hit_lines="0" total_lines="35" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="flush()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="write(char[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="write(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="write(String, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="write(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="close()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="ensureOpen()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getBuffer()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="StringReader" source="StringReader.java">
+      <cov.data calls="0" hit_methods="0" total_methods="10" hit_lines="0" total_lines="55" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="reset()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="read(char[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="read()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="skip(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="close()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="mark(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="markSupported()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="ensureOpen()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="ready()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+    </class>
+    <class name="UnsupportedEncodingException" source="UnsupportedEncodingException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="PrintWriter" source="PrintWriter.java">
+      <cov.data calls="40" hit_methods="11" total_methods="34" hit_lines="37" total_lines="136" />
+      <method name="&lt;init&gt;(java.io.Writer, boolean)">
+        <cov.data calls="1" hit_lines="7" total_lines="7" />
+      </method>
+      <method name="&lt;init&gt;(java.io.OutputStream)">
+        <cov.data calls="1" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.OutputStream, boolean)">
+        <cov.data calls="1" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.Writer)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="println(String)">
+        <cov.data calls="4" hit_lines="4" total_lines="5" />
+      </method>
+      <method name="println(char[])">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="println(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="println(float)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="println(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="println(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="println(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="println(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="println()">
+        <cov.data calls="5" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="println(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="flush()">
+        <cov.data calls="1" hit_lines="4" total_lines="6" />
+      </method>
+      <method name="print(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="print(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="print(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="print(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="print(float)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="print(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="print(char[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="print(String)">
+        <cov.data calls="4" hit_lines="3" total_lines="4" />
+      </method>
+      <method name="print(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="write(char[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="write(char[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="write(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="write(String, int, int)">
+        <cov.data calls="4" hit_lines="4" total_lines="7" />
+      </method>
+      <method name="write(String)">
+        <cov.data calls="4" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="newLine()">
+        <cov.data calls="5" hit_lines="5" total_lines="9" />
+      </method>
+      <method name="close()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="ensureOpen()">
+        <cov.data calls="10" hit_lines="2" total_lines="3" />
+      </method>
+      <method name="checkError()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="setError()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+  </package>
+  <package name="java.util">
+    <cov.data calls="3780020" hit_methods="248" total_methods="768" hit_lines="718" total_lines="4461" />
+    <class name="AbstractList" source="AbstractList.java">
+      <cov.data calls="68484" hit_methods="2" total_methods="17" hit_lines="0" total_lines="72" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="68456" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="indexOf(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="lastIndexOf(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="get(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="set(int, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="add(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="add(int, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="remove(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="iterator()">
+        <cov.data calls="28" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="addAll(int, java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="subList(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="removeRange(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="listIterator(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="listIterator()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Stack" source="Stack.java">
+      <cov.data calls="6615" hit_methods="5" total_methods="6" hit_lines="0" total_lines="23" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="938" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="push(Object)">
+        <cov.data calls="4256" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="pop()">
+        <cov.data calls="709" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="peek()">
+        <cov.data calls="710" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="empty()">
+        <cov.data calls="2" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="search(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+    </class>
+    <class name="HashMap" source="HashMap.java">
+      <cov.data calls="7403" hit_methods="16" total_methods="30" hit_lines="0" total_lines="336" />
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="507" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="92" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.util.Map)">
+        <cov.data calls="3" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(int, float)">
+        <cov.data calls="603" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="put(Object, Object)">
+        <cov.data calls="2183" hit_lines="0" total_lines="38" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="get(Object)">
+        <cov.data calls="3526" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="access$000(java.util.HashMap, int)">
+        <cov.data calls="9" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$100(java.util.HashMap)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="values()">
+        <cov.data calls="0" hit_lines="0" total_lines="17" />
+      </method>
+      <method name="size()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$200(java.util.HashMap)">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="remove(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="40" />
+      </method>
+      <method name="access$300(java.util.HashMap)">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="loadFactor()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="keySet()">
+        <cov.data calls="3" hit_lines="0" total_lines="22" />
+      </method>
+      <method name="entrySet()">
+        <cov.data calls="6" hit_lines="0" total_lines="58" />
+      </method>
+      <method name="isEmpty()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="containsValue(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="containsKey(Object)">
+        <cov.data calls="46" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="rehash()">
+        <cov.data calls="407" hit_lines="0" total_lines="21" />
+      </method>
+      <method name="putAll(java.util.Map)">
+        <cov.data calls="6" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="writeObject(java.io.ObjectOutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="19" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="capacity()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getHashIterator(int)">
+        <cov.data calls="9" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="access$308(java.util.HashMap)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$110(java.util.HashMap)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Properties" source="Properties.java">
+      <cov.data calls="711" hit_methods="10" total_methods="18" hit_lines="0" total_lines="228" />
+      <method name="&lt;init&gt;(java.util.Properties)">
+        <cov.data calls="11" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="11" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setProperty(String, String)">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getProperty(String, String)">
+        <cov.data calls="2" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getProperty(String)">
+        <cov.data calls="228" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="load(java.io.InputStream)">
+        <cov.data calls="4" hit_lines="0" total_lines="71" />
+      </method>
+      <method name="enumerate(java.util.Hashtable)">
+        <cov.data calls="2" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="list(java.io.PrintStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="list(java.io.PrintWriter)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="continueLine(String)">
+        <cov.data calls="150" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="loadConvert(String)">
+        <cov.data calls="300" hit_lines="0" total_lines="42" />
+      </method>
+      <method name="saveConvert(String, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="38" />
+      </method>
+      <method name="save(java.io.OutputStream, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="store(java.io.OutputStream, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="17" />
+      </method>
+      <method name="writeln(java.io.BufferedWriter, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="propertyNames()">
+        <cov.data calls="2" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="toHex(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Collections$EmptyMap" source="Collections.java">
+      <cov.data calls="2" hit_methods="2" total_methods="12" hit_lines="0" total_lines="12" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(java.util.Collections$1)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="get(Object)">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="values()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="size()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="keySet()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="entrySet()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isEmpty()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="containsValue(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="containsKey(Object)">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Set" source="Set.java">
+      <cov.data calls="0" hit_methods="0" total_methods="15" hit_lines="0" total_lines="0" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="add(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="size()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="toArray(Object[])">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="toArray()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="contains(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="remove(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isEmpty()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="iterator()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="containsAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="addAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="removeAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="retainAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="Collection" source="Collection.java">
+      <cov.data calls="0" hit_methods="0" total_methods="15" hit_lines="0" total_lines="0" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="add(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="size()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="toArray(Object[])">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="toArray()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="contains(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="remove(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isEmpty()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="iterator()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="containsAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="addAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="removeAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="retainAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="Collections" source="Collections.java">
+      <cov.data calls="0" hit_methods="0" total_methods="39" hit_lines="0" total_lines="172" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="min(java.util.Collection, java.util.Comparator)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="min(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="access$100(Object, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="max(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="max(java.util.Collection, java.util.Comparator)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="synchronizedSet(java.util.Set, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="synchronizedSet(java.util.Set)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="synchronizedCollection(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="synchronizedCollection(java.util.Collection, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="copy(java.util.List, java.util.List)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="synchronizedList(java.util.List)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="synchronizedList(java.util.List, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="reverse(java.util.List)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="sort(java.util.List, java.util.Comparator)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="sort(java.util.List)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="binarySearch(java.util.List, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="29" />
+      </method>
+      <method name="binarySearch(java.util.List, Object, java.util.Comparator)">
+        <cov.data calls="0" hit_lines="0" total_lines="33" />
+      </method>
+      <method name="shuffle(java.util.List)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="shuffle(java.util.List, java.util.Random)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="swap(java.util.List, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="fill(java.util.List, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="unmodifiableCollection(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="unmodifiableSet(java.util.Set)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="unmodifiableSortedSet(java.util.SortedSet)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="unmodifiableList(java.util.List)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="unmodifiableMap(java.util.Map)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="unmodifiableSortedMap(java.util.SortedMap)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="synchronizedSortedSet(java.util.SortedSet)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="synchronizedMap(java.util.Map)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="synchronizedSortedMap(java.util.SortedMap)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="singleton(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="singletonList(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="singletonMap(Object, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="nCopies(int, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="reverseOrder()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="enumeration(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="eq(Object, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Collections$EmptySet" source="Collections.java">
+      <cov.data calls="0" hit_methods="0" total_methods="5" hit_lines="0" total_lines="5" />
+      <method name="&lt;init&gt;(java.util.Collections$1)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="size()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="contains(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="iterator()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="HashSet" source="HashSet.java">
+      <cov.data calls="325" hit_methods="2" total_methods="15" hit_lines="0" total_lines="56" />
+      <method name="&lt;init&gt;(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="&lt;init&gt;(int, float)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="29" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="add(Object)">
+        <cov.data calls="296" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="size()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="contains(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="remove(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isEmpty()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="writeObject(java.io.ObjectOutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="iterator()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Collections$EmptyList" source="Collections.java">
+      <cov.data calls="0" hit_methods="0" total_methods="5" hit_lines="0" total_lines="5" />
+      <method name="&lt;init&gt;(java.util.Collections$1)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="get(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="size()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="contains(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Collections$ReverseComparator" source="Collections.java">
+      <cov.data calls="0" hit_methods="0" total_methods="3" hit_lines="0" total_lines="5" />
+      <method name="&lt;init&gt;(java.util.Collections$1)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compare(Object, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+    </class>
+    <class name="HashMap$Entry" source="HashMap.java">
+      <cov.data calls="1902" hit_methods="3" total_methods="8" hit_lines="0" total_lines="19" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(int, Object, Object, java.util.HashMap$Entry)">
+        <cov.data calls="1900" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getValue()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getKey()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setValue(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+    </class>
+    <class name="AbstractSet" source="AbstractSet.java">
+      <cov.data calls="38" hit_methods="1" total_methods="4" hit_lines="0" total_lines="33" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="38" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="removeAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="14" />
+      </method>
+    </class>
+    <class name="Locale" source="Locale.java">
+      <cov.data calls="1595" hit_methods="11" total_methods="35" hit_lines="0" total_lines="408" />
+      <method name="hashCode()">
+        <cov.data calls="41" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="&lt;init&gt;(String, String)">
+        <cov.data calls="2" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(String, String, String)">
+        <cov.data calls="8" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="21" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="10" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="toLowerCase(String)">
+        <cov.data calls="8" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="toUpperCase(String)">
+        <cov.data calls="16" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getDefault()">
+        <cov.data calls="1479" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getLanguage()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="writeObject(java.io.ObjectOutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="setDefault(java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="getAvailableLocales()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getISOCountries()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="getISOLanguages()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="getLanguagesForCountry(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="23" />
+      </method>
+      <method name="getCountry()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getVariant()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getISO3Language()">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="getISO3Country()">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="getDisplayLanguage()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDisplayLanguage(java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="52" />
+      </method>
+      <method name="getDisplayCountry()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDisplayCountry(java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="52" />
+      </method>
+      <method name="getDisplayVariant()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDisplayVariant(java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="getDisplayName()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDisplayName(java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="72" />
+      </method>
+      <method name="getDisplayVariantArray(java.util.ResourceBundle)">
+        <cov.data calls="0" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="formatList(String[], String[])">
+        <cov.data calls="0" hit_lines="0" total_lines="23" />
+      </method>
+      <method name="composeList(java.text.MessageFormat, String[])">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="findStringMatch(String[][], String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="convertOldISOCodes(String)">
+        <cov.data calls="8" hit_lines="0" total_lines="9" />
+      </method>
+    </class>
+    <class name="AbstractMap" source="AbstractMap.java">
+      <cov.data calls="604" hit_methods="1" total_methods="16" hit_lines="0" total_lines="181" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="604" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="put(Object, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="23" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="get(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="values()">
+        <cov.data calls="0" hit_lines="0" total_lines="30" />
+      </method>
+      <method name="size()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="remove(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="22" />
+      </method>
+      <method name="keySet()">
+        <cov.data calls="0" hit_lines="0" total_lines="30" />
+      </method>
+      <method name="entrySet()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isEmpty()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="containsValue(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="containsKey(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="putAll(java.util.Map)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+    </class>
+    <class name="Iterator" source="Iterator.java">
+      <cov.data calls="0" hit_methods="0" total_methods="3" hit_lines="0" total_lines="0" />
+      <method name="next()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="remove()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="hasNext()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="Hashtable$EmptyIterator" source="Hashtable.java">
+      <cov.data calls="0" hit_methods="0" total_methods="4" hit_lines="0" total_lines="5" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="next()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="remove()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="hasNext()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Hashtable$Entry" source="Hashtable.java">
+      <cov.data calls="2987" hit_methods="1" total_methods="8" hit_lines="0" total_lines="22" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(int, Object, Object, java.util.Hashtable$Entry)">
+        <cov.data calls="2987" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getKey()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setValue(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+    </class>
+    <class name="HashMap$EmptyHashIterator" source="HashMap.java">
+      <cov.data calls="3" hit_methods="1" total_methods="4" hit_lines="0" total_lines="6" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="next()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="remove()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="hasNext()">
+        <cov.data calls="3" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Dictionary" source="Dictionary.java">
+      <cov.data calls="551" hit_methods="1" total_methods="8" hit_lines="0" total_lines="2" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="551" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="put(Object, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="get(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="size()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="remove(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="keys()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isEmpty()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="elements()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="ArrayList" source="ArrayList.java">
+      <cov.data calls="19553" hit_methods="7" total_methods="25" hit_lines="0" total_lines="177" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="72" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="72" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="indexOf(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="lastIndexOf(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="get(int)">
+        <cov.data calls="9671" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="set(int, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="add(int, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="add(Object)">
+        <cov.data calls="33" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="size()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toArray()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="toArray(Object[])">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="contains(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="remove(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="isEmpty()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="writeObject(java.io.ObjectOutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="trimToSize()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="ensureCapacity(int)">
+        <cov.data calls="33" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="addAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="addAll(int, java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="removeRange(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="RangeCheck(int)">
+        <cov.data calls="9671" hit_lines="0" total_lines="4" />
+      </method>
+    </class>
+    <class name="AbstractCollection" source="AbstractCollection.java">
+      <cov.data calls="68522" hit_methods="2" total_methods="15" hit_lines="0" total_lines="98" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="68494" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="28" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="add(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="size()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="toArray()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="toArray(Object[])">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="contains(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="remove(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="17" />
+      </method>
+      <method name="isEmpty()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="iterator()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="containsAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="addAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="removeAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="retainAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+    </class>
+    <class name="Random" source="Random.java">
+      <cov.data calls="18" hit_methods="4" total_methods="12" hit_lines="0" total_lines="48" />
+      <method name="&lt;init&gt;(long)">
+        <cov.data calls="1" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="next(int)">
+        <cov.data calls="8" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="nextDouble()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="nextInt(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="nextInt()">
+        <cov.data calls="8" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setSeed(long)">
+        <cov.data calls="1" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="nextBytes(byte[])">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="nextLong()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="nextBoolean()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="nextFloat()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="nextGaussian()">
+        <cov.data calls="0" hit_lines="0" total_lines="14" />
+      </method>
+    </class>
+    <class name="List" source="List.java">
+      <cov.data calls="0" hit_methods="0" total_methods="25" hit_lines="0" total_lines="0" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="indexOf(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="lastIndexOf(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="get(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="set(int, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="add(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="add(int, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="size()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="toArray()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="toArray(Object[])">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="contains(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="remove(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="remove(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isEmpty()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="iterator()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="containsAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="addAll(int, java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="addAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="removeAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="retainAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="subList(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="listIterator(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="listIterator()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="Map" source="Map.java">
+      <cov.data calls="0" hit_methods="0" total_methods="14" hit_lines="0" total_lines="0" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="put(Object, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="get(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="values()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="size()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="remove(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="keySet()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="entrySet()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isEmpty()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="containsValue(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="containsKey(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="putAll(java.util.Map)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="Hashtable$EmptyEnumerator" source="Hashtable.java">
+      <cov.data calls="31" hit_methods="1" total_methods="3" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="nextElement()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="hasMoreElements()">
+        <cov.data calls="31" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Hashtable" source="Hashtable.java">
+      <cov.data calls="12660" hit_methods="17" total_methods="35" hit_lines="0" total_lines="258" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="471" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.util.Map)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="79" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(int, float)">
+        <cov.data calls="551" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="put(Object, Object)">
+        <cov.data calls="3040" hit_lines="0" total_lines="30" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="23" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="get(Object)">
+        <cov.data calls="7038" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="access$100(java.util.Hashtable, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="values()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="size()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="contains(Object)">
+        <cov.data calls="10" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="access$200(java.util.Hashtable)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="236" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="remove(Object)">
+        <cov.data calls="69" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="access$400(java.util.Hashtable)">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="keys()">
+        <cov.data calls="25" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="keySet()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="entrySet()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="isEmpty()">
+        <cov.data calls="21" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="elements()">
+        <cov.data calls="28" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="containsValue(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="containsKey(Object)">
+        <cov.data calls="927" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="rehash()">
+        <cov.data calls="109" hit_lines="0" total_lines="21" />
+      </method>
+      <method name="putAll(java.util.Map)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="getEnumeration(int)">
+        <cov.data calls="53" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getIterator(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="writeObject(java.io.ObjectOutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="26" />
+      </method>
+      <method name="access$508(java.util.Hashtable)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$210(java.util.Hashtable)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$500(java.util.Hashtable)">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Enumeration" source="Enumeration.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="0" />
+      <method name="nextElement()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="hasMoreElements()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="Vector" source="Vector.java">
+      <cov.data calls="1425417" hit_methods="19" total_methods="47" hit_lines="0" total_lines="244" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="elementAt(int)">
+        <cov.data calls="520671" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="&lt;init&gt;(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="68138" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="68384" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(int, int)">
+        <cov.data calls="68384" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="28" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="indexOf(Object, int)">
+        <cov.data calls="191" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="indexOf(Object)">
+        <cov.data calls="131" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="lastIndexOf(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="lastIndexOf(Object, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="get(int)">
+        <cov.data calls="110" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="set(int, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="add(Object)">
+        <cov.data calls="1426" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="add(int, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="addElement(Object)">
+        <cov.data calls="345956" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="size()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toArray()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="toArray(Object[])">
+        <cov.data calls="2" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="contains(Object)">
+        <cov.data calls="60" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="copyInto(Object[])">
+        <cov.data calls="467" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="remove(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="remove(int)">
+        <cov.data calls="388" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="isEmpty()">
+        <cov.data calls="366" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="elements()">
+        <cov.data calls="1183" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="trimToSize()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="ensureCapacity(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="ensureCapacityHelper(int)">
+        <cov.data calls="347382" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="setSize(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="capacity()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="firstElement()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="lastElement()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="setElementAt(Object, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="removeElementAt(int)">
+        <cov.data calls="175" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="insertElementAt(Object, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="removeElement(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="removeAllElements()">
+        <cov.data calls="1975" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="containsAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="addAll(int, java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="addAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="removeAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="retainAll(java.util.Collection)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="subList(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="removeRange(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+    </class>
+    <class name="StringTokenizer" source="StringTokenizer.java">
+      <cov.data calls="1690960" hit_methods="9" total_methods="12" hit_lines="0" total_lines="84" />
+      <method name="&lt;init&gt;(String, String, boolean)">
+        <cov.data calls="63980" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="&lt;init&gt;(String, String)">
+        <cov.data calls="63944" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="4" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="nextElement()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="hasMoreElements()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="countTokens()">
+        <cov.data calls="4" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="nextToken()">
+        <cov.data calls="342747" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="nextToken(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="setMaxDelimChar()">
+        <cov.data calls="63980" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="skipDelimiters(int)">
+        <cov.data calls="406777" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="scanToken(int)">
+        <cov.data calls="342763" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="hasMoreTokens()">
+        <cov.data calls="406761" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="Comparator" source="Comparator.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="0" />
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="compare(Object, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="Map$Entry" source="Map.java">
+      <cov.data calls="0" hit_methods="0" total_methods="5" hit_lines="0" total_lines="0" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getKey()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setValue(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="EmptyStackException" source="EmptyStackException.java">
+      <cov.data calls="534" hit_methods="1" total_methods="1" hit_lines="2" total_lines="2" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="534" hit_lines="2" total_lines="2" />
+      </method>
+    </class>
+    <class name="BitSet" source="BitSet.java">
+      <cov.data calls="157" hit_methods="5" total_methods="20" hit_lines="18" total_lines="137" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="1" hit_lines="5" total_lines="6" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="17" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="length()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="get(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="set(int)">
+        <cov.data calls="51" hit_lines="9" total_lines="10" />
+      </method>
+      <method name="size()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="clear(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="ensureCapacity(int)">
+        <cov.data calls="2" hit_lines="2" total_lines="6" />
+      </method>
+      <method name="unitIndex(int)">
+        <cov.data calls="52" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="bit(int)">
+        <cov.data calls="51" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="recalculateUnitsInUse()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="andNot(java.util.BitSet)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="and(java.util.BitSet)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="or(java.util.BitSet)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="xor(java.util.BitSet)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+    </class>
+    <class name="EventObject" source="EventObject.java">
+      <cov.data calls="2097" hit_methods="1" total_methods="3" hit_lines="4" total_lines="7" />
+      <method name="&lt;init&gt;(Object)">
+        <cov.data calls="2097" hit_lines="4" total_lines="5" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getSource()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="HashMap$3" source="HashMap.java">
+      <cov.data calls="12" hit_methods="2" total_methods="6" hit_lines="2" total_lines="34" />
+      <method name="&lt;init&gt;(java.util.HashMap)">
+        <cov.data calls="6" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="size()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="contains(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="remove(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="iterator()">
+        <cov.data calls="6" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="HashMap$HashIterator" source="HashMap.java">
+      <cov.data calls="52" hit_methods="3" total_methods="4" hit_lines="27" total_lines="48" />
+      <method name="&lt;init&gt;(java.util.HashMap, int)">
+        <cov.data calls="6" hit_lines="8" total_lines="8" />
+      </method>
+      <method name="next()">
+        <cov.data calls="20" hit_lines="11" total_lines="14" />
+      </method>
+      <method name="remove()">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="hasNext()">
+        <cov.data calls="26" hit_lines="8" total_lines="8" />
+      </method>
+    </class>
+    <class name="HashMap$1" source="HashMap.java">
+      <cov.data calls="6" hit_methods="2" total_methods="6" hit_lines="2" total_lines="9" />
+      <method name="&lt;init&gt;(java.util.HashMap)">
+        <cov.data calls="3" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="size()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="contains(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="remove(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="iterator()">
+        <cov.data calls="3" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="Hashtable$Enumerator" source="Hashtable.java">
+      <cov.data calls="1201" hit_methods="3" total_methods="6" hit_lines="27" total_lines="55" />
+      <method name="&lt;init&gt;(java.util.Hashtable, int, boolean)">
+        <cov.data calls="22" hit_lines="9" total_lines="9" />
+      </method>
+      <method name="next()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="remove()">
+        <cov.data calls="0" hit_lines="0" total_lines="22" />
+      </method>
+      <method name="nextElement()">
+        <cov.data calls="581" hit_lines="10" total_lines="12" />
+      </method>
+      <method name="hasMoreElements()">
+        <cov.data calls="598" hit_lines="8" total_lines="8" />
+      </method>
+      <method name="hasNext()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Calendar" source="Calendar.java">
+      <cov.data calls="263389" hit_methods="22" total_methods="52" hit_lines="77" total_lines="202" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(java.util.TimeZone, java.util.Locale)">
+        <cov.data calls="7" hit_lines="10" total_lines="10" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="21" />
+      </method>
+      <method name="get(int)">
+        <cov.data calls="5720" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="set(int, int, int, int, int, int)">
+        <cov.data calls="951" hit_lines="7" total_lines="7" />
+      </method>
+      <method name="set(int, int, int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="set(int, int)">
+        <cov.data calls="5710" hit_lines="6" total_lines="6" />
+      </method>
+      <method name="set(int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="add(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isSet(int)">
+        <cov.data calls="19093" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getInstance(java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getInstance(java.util.TimeZone, java.util.Locale)">
+        <cov.data calls="4" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getInstance()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getInstance(java.util.TimeZone)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="951" hit_lines="7" total_lines="7" />
+      </method>
+      <method name="clear(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="writeObject(java.io.ObjectOutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="getAvailableLocales()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setTime(java.util.Date)">
+        <cov.data calls="12" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="getTime()">
+        <cov.data calls="4" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="setTimeZone(java.util.TimeZone)">
+        <cov.data calls="6657" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="getTimeZone()">
+        <cov.data calls="12405" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="setLenient(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="isLenient()">
+        <cov.data calls="2865" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getFirstDayOfWeek()">
+        <cov.data calls="17175" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getMinimalDaysInFirstWeek()">
+        <cov.data calls="11450" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getMaximum(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getLeastMaximum(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="before(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="computeTime()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="computeFields()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getTimeInMillis()">
+        <cov.data calls="955" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="setTimeInMillis(long)">
+        <cov.data calls="5725" hit_lines="8" total_lines="8" />
+      </method>
+      <method name="internalGet(int)">
+        <cov.data calls="63963" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="internalSet(int, int)">
+        <cov.data calls="103055" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="complete()">
+        <cov.data calls="5724" hit_lines="3" total_lines="6" />
+      </method>
+      <method name="after(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="roll(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="roll(int, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setFirstDayOfWeek(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setMinimalDaysInFirstWeek(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getMinimum(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getGreatestMinimum(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getActualMinimum(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="getActualMaximum(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="setWeekCountData(java.util.Locale)">
+        <cov.data calls="7" hit_lines="11" total_lines="11" />
+      </method>
+      <method name="updateTime()">
+        <cov.data calls="955" hit_lines="4" total_lines="4" />
+      </method>
+    </class>
+    <class name="GregorianCalendar" source="GregorianCalendar.java">
+      <cov.data calls="133644" hit_methods="23" total_methods="47" hit_lines="198" total_lines="514" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(java.util.TimeZone)">
+        <cov.data calls="1" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.util.TimeZone, java.util.Locale)">
+        <cov.data calls="7" hit_lines="6" total_lines="6" />
+      </method>
+      <method name="&lt;init&gt;(int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="&lt;init&gt;(int, int, int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="&lt;init&gt;(int, int, int, int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="2" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="7" total_lines="7" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="add(int, int)">
+        <cov.data calls="4" hit_lines="10" total_lines="59" />
+      </method>
+      <method name="getMaximum(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getLeastMaximum(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="computeTime()">
+        <cov.data calls="955" hit_lines="37" total_lines="46" />
+      </method>
+      <method name="computeFields()">
+        <cov.data calls="5725" hit_lines="31" total_lines="40" />
+      </method>
+      <method name="roll(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="95" />
+      </method>
+      <method name="roll(int, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getMinimum(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getGreatestMinimum(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getActualMinimum(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getActualMaximum(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="19" />
+      </method>
+      <method name="setGregorianChange(java.util.Date)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="getGregorianChange()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isLeapYear(int)">
+        <cov.data calls="13364" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="inDaylightTime()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getISOYear()">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="timeToFields(long, boolean)">
+        <cov.data calls="6680" hit_lines="41" total_lines="58" />
+      </method>
+      <method name="computeJulianDay(boolean, int)">
+        <cov.data calls="955" hit_lines="32" total_lines="59" />
+      </method>
+      <method name="millisToJulianDay(long)">
+        <cov.data calls="6680" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="julianDayToMillis(long)">
+        <cov.data calls="955" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="julianDayToDayOfWeek(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="floorDivide(int, int, int[])">
+        <cov.data calls="20040" hit_lines="3" total_lines="6" />
+      </method>
+      <method name="floorDivide(long, int, int[])">
+        <cov.data calls="7635" hit_lines="6" total_lines="6" />
+      </method>
+      <method name="floorDivide(int, int)">
+        <cov.data calls="2865" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="floorDivide(long, long)">
+        <cov.data calls="6680" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="aggregateStamp(int, int)">
+        <cov.data calls="2865" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="weekNumber(int, int)">
+        <cov.data calls="5725" hit_lines="5" total_lines="5" />
+      </method>
+      <method name="monthLength(int, int)">
+        <cov.data calls="13364" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="monthLength(int)">
+        <cov.data calls="13364" hit_lines="3" total_lines="4" />
+      </method>
+      <method name="prevMonthLength(int)">
+        <cov.data calls="6680" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="yearLength(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="yearLength()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="pinDayOfMonth()">
+        <cov.data calls="4" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="validateFields()">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="boundsCheck(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getEpochDay()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="internalGetEra()">
+        <cov.data calls="19093" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="NoSuchElementException" source="NoSuchElementException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="Vector$1" source="Vector.java">
+      <cov.data calls="5661" hit_methods="3" total_methods="3" hit_lines="6" total_lines="8" />
+      <method name="&lt;init&gt;(java.util.Vector)">
+        <cov.data calls="1183" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="nextElement()">
+        <cov.data calls="1595" hit_lines="3" total_lines="5" />
+      </method>
+      <method name="hasMoreElements()">
+        <cov.data calls="2883" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="AbstractList$Itr" source="AbstractList.java">
+      <cov.data calls="276" hit_methods="4" total_methods="6" hit_lines="11" total_lines="25" />
+      <method name="&lt;init&gt;(java.util.AbstractList, java.util.AbstractList$1)">
+        <cov.data calls="28" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(java.util.AbstractList)">
+        <cov.data calls="28" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="next()">
+        <cov.data calls="110" hit_lines="4" total_lines="6" />
+      </method>
+      <method name="remove()">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="hasNext()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="checkForComodification()">
+        <cov.data calls="110" hit_lines="2" total_lines="3" />
+      </method>
+    </class>
+    <class name="Date" source="Date.java">
+      <cov.data calls="14326" hit_methods="13" total_methods="40" hit_lines="42" total_lines="259" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="compareTo(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compareTo(java.util.Date)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(int, int, int, int, int, int)">
+        <cov.data calls="951" hit_lines="9" total_lines="11" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(int, int, int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(long)">
+        <cov.data calls="971" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="16" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="7" total_lines="7" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="getField(int)">
+        <cov.data calls="5706" hit_lines="7" total_lines="14" />
+      </method>
+      <method name="writeObject(java.io.ObjectOutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setField(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="setTime(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getTime()">
+        <cov.data calls="974" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getYear()">
+        <cov.data calls="951" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getMonth()">
+        <cov.data calls="951" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getDate()">
+        <cov.data calls="951" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getHours()">
+        <cov.data calls="951" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getMinutes()">
+        <cov.data calls="951" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getSeconds()">
+        <cov.data calls="951" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="parse(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="120" />
+      </method>
+      <method name="setDate(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="before(java.util.Date)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="after(java.util.Date)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setSeconds(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setHours(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setMinutes(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="UTC(int, int, int, int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="setYear(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setMonth(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getDay()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toLocaleString()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="toGMTString()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="getTimezoneOffset()">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="makeStaticCalendars()">
+        <cov.data calls="1" hit_lines="6" total_lines="6" />
+      </method>
+    </class>
+    <class name="ResourceBundle" source="ResourceBundle.java">
+      <cov.data calls="1532" hit_methods="17" total_methods="25" hit_lines="145" total_lines="213" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="8" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="class$(String)">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getBundle(String, java.util.Locale)">
+        <cov.data calls="10" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getBundle(String, java.util.Locale, ClassLoader)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getBundle(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getObject(String)">
+        <cov.data calls="1380" hit_lines="6" total_lines="7" />
+      </method>
+      <method name="getLoader()">
+        <cov.data calls="10" hit_lines="6" total_lines="6" />
+      </method>
+      <method name="getStringArray(String)">
+        <cov.data calls="28" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="setLocale(String, String)">
+        <cov.data calls="8" hit_lines="16" total_lines="20" />
+      </method>
+      <method name="setParent(java.util.ResourceBundle)">
+        <cov.data calls="8" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="getString(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getLocale()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getKeys()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getClassContext()">
+        <cov.data calls="10" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getBundleImpl(String, java.util.Locale, ClassLoader)">
+        <cov.data calls="10" hit_lines="30" total_lines="50" />
+      </method>
+      <method name="propagate(ClassLoader, java.util.Vector, java.util.Vector, Object)">
+        <cov.data calls="4" hit_lines="7" total_lines="7" />
+      </method>
+      <method name="throwMissingResourceException(String, java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="cleanUpConstructionList()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="findBundle(ClassLoader, String, String, Object, Object)">
+        <cov.data calls="12" hit_lines="25" total_lines="38" />
+      </method>
+      <method name="calculateBundleNames(String, java.util.Locale)">
+        <cov.data calls="4" hit_lines="18" total_lines="24" />
+      </method>
+      <method name="findBundleInCache(ClassLoader, String)">
+        <cov.data calls="18" hit_lines="5" total_lines="6" />
+      </method>
+      <method name="putBundleInCache(ClassLoader, String, Object)">
+        <cov.data calls="10" hit_lines="7" total_lines="8" />
+      </method>
+      <method name="loadBundle(ClassLoader, String)">
+        <cov.data calls="10" hit_lines="12" total_lines="21" />
+      </method>
+      <method name="handleGetObject(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="ResourceBundle$ResourceCacheKey" source="ResourceBundle.java">
+      <cov.data calls="270" hit_methods="7" total_methods="7" hit_lines="20" total_lines="27" />
+      <method name="hashCode()">
+        <cov.data calls="78" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(java.util.ResourceBundle$1)">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="26" hit_lines="7" total_lines="13" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="20" hit_lines="1" total_lines="2" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="48" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="setKeyValues(ClassLoader, String)">
+        <cov.data calls="96" hit_lines="7" total_lines="7" />
+      </method>
+    </class>
+    <class name="ListResourceBundle" source="ListResourceBundle.java">
+      <cov.data calls="82" hit_methods="3" total_methods="6" hit_lines="13" total_lines="24" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="5" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="access$000(java.util.ListResourceBundle)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getKeys()">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="handleGetObject(String)">
+        <cov.data calls="72" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="getContents()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="loadLookup()">
+        <cov.data calls="5" hit_lines="7" total_lines="8" />
+      </method>
+    </class>
+    <class name="ResourceBundle$1" source="ResourceBundle.java">
+      <cov.data calls="4" hit_methods="2" total_methods="2" hit_lines="3" total_lines="4" />
+      <method name="&lt;init&gt;(ClassLoader, String)">
+        <cov.data calls="2" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="run()">
+        <cov.data calls="2" hit_lines="2" total_lines="3" />
+      </method>
+    </class>
+    <class name="TimeZone" source="TimeZone.java">
+      <cov.data calls="14319" hit_methods="8" total_methods="24" hit_lines="28" total_lines="99" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="328" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="6667" hit_lines="3" total_lines="4" />
+      </method>
+      <method name="getDefault()">
+        <cov.data calls="6664" hit_lines="11" total_lines="12" />
+      </method>
+      <method name="class$(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getOffset(int, int, int, int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getOffset(int, int, int, int, int, int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setDefault(java.util.TimeZone)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getDisplayName(boolean, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDisplayName(boolean, int, java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="getDisplayName()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDisplayName(java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getTimeZone(String)">
+        <cov.data calls="3" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="getID()">
+        <cov.data calls="327" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getRawOffset()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="useDaylightTime()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="inDaylightTime(java.util.Date)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setRawOffset(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setID(String)">
+        <cov.data calls="328" hit_lines="3" total_lines="4" />
+      </method>
+      <method name="getAvailableIDs(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="getAvailableIDs()">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="getSystemTimeZoneID(String, String)">
+        <cov.data calls="1" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="hasSameRules(java.util.TimeZone)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="parseCustomTimeZone(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="31" />
+      </method>
+    </class>
+    <class name="SimpleTimeZone" source="SimpleTimeZone.java">
+      <cov.data calls="34071" hit_methods="10" total_methods="36" hit_lines="83" total_lines="276" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(int, String, int, int, int, int, int, int, int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(int, String, int, int, int, int, int, int, int, int, int, int, int)">
+        <cov.data calls="119" hit_lines="20" total_lines="21" />
+      </method>
+      <method name="&lt;init&gt;(int, String, int, int, int, int, int, int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(int, String)">
+        <cov.data calls="209" hit_lines="8" total_lines="8" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="6667" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="writeObject(java.io.ObjectOutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="getOffset(int, int, int, int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="getOffset(int, int, int, int, int, int, int, int)">
+        <cov.data calls="6680" hit_lines="10" total_lines="12" />
+      </method>
+      <method name="getRawOffset()">
+        <cov.data calls="6680" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getDSTSavings()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="useDaylightTime()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="inDaylightTime(java.util.Date)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="setRawOffset(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="hasSameRules(java.util.TimeZone)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="setStartYear(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setStartRule(int, int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="setStartRule(int, int, int, int, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="setStartRule(int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setEndRule(int, int, int, int, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="setEndRule(int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setEndRule(int, int, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="compareToRule(int, int, int, int, int, int, int, int, int, int, int, int)">
+        <cov.data calls="13358" hit_lines="5" total_lines="33" />
+      </method>
+      <method name="setDSTSavings(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="decodeRules()">
+        <cov.data calls="119" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="decodeStartRule()">
+        <cov.data calls="119" hit_lines="16" total_lines="23" />
+      </method>
+      <method name="decodeEndRule()">
+        <cov.data calls="119" hit_lines="16" total_lines="23" />
+      </method>
+      <method name="makeRulesCompatible()">
+        <cov.data calls="0" hit_lines="0" total_lines="37" />
+      </method>
+      <method name="packRules()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="unpackRules(byte[])">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="packTimes()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="unpackTimes(int[])">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+    </class>
+    <class name="TimeZone$1" source="TimeZone.java">
+      <cov.data calls="2" hit_methods="2" total_methods="2" hit_lines="3" total_lines="3" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="run()">
+        <cov.data calls="1" hit_lines="2" total_lines="2" />
+      </method>
+    </class>
+    <class name="TimeZoneData" source="TimeZone.java">
+      <cov.data calls="4" hit_methods="2" total_methods="3" hit_lines="7" total_lines="8" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="5" total_lines="5" />
+      </method>
+      <method name="get(String)">
+        <cov.data calls="3" hit_lines="2" total_lines="2" />
+      </method>
+    </class>
+  </package>
+  <package name="java.lang">
+    <cov.data calls="8076788" hit_methods="214" total_methods="807" hit_lines="45" total_lines="3726" />
+    <class name="ThreadGroup" source="ThreadGroup.java">
+      <cov.data calls="4" hit_methods="2" total_methods="35" hit_lines="0" total_lines="370" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(ThreadGroup, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="uncaughtException(Thread, Throwable)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getName()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="add(Thread)">
+        <cov.data calls="2" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="add(ThreadGroup)">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="getParent()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="setDaemon(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="remove(ThreadGroup)">
+        <cov.data calls="0" hit_lines="0" total_lines="22" />
+      </method>
+      <method name="remove(Thread)">
+        <cov.data calls="0" hit_lines="0" total_lines="21" />
+      </method>
+      <method name="stop()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="interrupt()">
+        <cov.data calls="0" hit_lines="0" total_lines="17" />
+      </method>
+      <method name="destroy()">
+        <cov.data calls="0" hit_lines="0" total_lines="27" />
+      </method>
+      <method name="suspend()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="resume()">
+        <cov.data calls="0" hit_lines="0" total_lines="17" />
+      </method>
+      <method name="activeCount()">
+        <cov.data calls="0" hit_lines="0" total_lines="17" />
+      </method>
+      <method name="enumerate(ThreadGroup[], int, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="30" />
+      </method>
+      <method name="enumerate(ThreadGroup[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="enumerate(Thread[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="enumerate(ThreadGroup[], boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="enumerate(Thread[], boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="enumerate(Thread[], int, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="31" />
+      </method>
+      <method name="isDaemon()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="checkAccess()">
+        <cov.data calls="2" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getMaxPriority()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isDestroyed()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setMaxPriority(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="19" />
+      </method>
+      <method name="parentOf(ThreadGroup)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="activeGroupCount()">
+        <cov.data calls="0" hit_lines="0" total_lines="17" />
+      </method>
+      <method name="stopOrSuspend(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="25" />
+      </method>
+      <method name="list(java.io.PrintStream, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="24" />
+      </method>
+      <method name="list()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="allowThreadSuspension(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+    </class>
+    <class name="ThreadLocal" source="ThreadLocal.java">
+      <cov.data calls="959" hit_methods="4" total_methods="4" hit_lines="0" total_lines="20" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="get()">
+        <cov.data calls="952" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="set(Object)">
+        <cov.data calls="5" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="initialValue()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="LinkageError" source="LinkageError.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="ClassCastException" source="ClassCastException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="Comparable" source="Comparable.java">
+      <cov.data calls="0" hit_methods="0" total_methods="1" hit_lines="0" total_lines="0" />
+      <method name="compareTo(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="Boolean" source="Boolean.java">
+      <cov.data calls="14" hit_methods="3" total_methods="10" hit_lines="0" total_lines="22" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(boolean)">
+        <cov.data calls="12" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="booleanValue()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getBoolean(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="toBoolean(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="StrictMath" source="StrictMath.java">
+      <cov.data calls="2" hit_methods="2" total_methods="35" hit_lines="0" total_lines="43" />
+      <method name="sin(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="cos(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="tan(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="atan2(double, double)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="sqrt(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="min(float, float)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="min(long, long)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="min(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="min(double, double)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="max(double, double)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="max(float, float)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="max(long, long)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="max(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="asin(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="acos(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="atan(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="toRadians(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toDegrees(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="exp(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="log(double)">
+        <cov.data calls="1" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="IEEEremainder(double, double)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="ceil(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="floor(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="rint(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="pow(double, double)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="round(float)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="round(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="initRNG()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="random()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="abs(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="abs(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="abs(float)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="abs(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Integer" source="Integer.java">
+      <cov.data calls="28" hit_methods="8" total_methods="28" hit_lines="0" total_lines="227" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compareTo(Integer)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="compareTo(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="1" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="toString(int, int)">
+        <cov.data calls="8" hit_lines="0" total_lines="27" />
+      </method>
+      <method name="toString(int)">
+        <cov.data calls="8" hit_lines="0" total_lines="41" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toHexString(int)">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(String, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="byteValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="shortValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="intValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="longValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="floatValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="doubleValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="decode(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="40" />
+      </method>
+      <method name="parseInt(String)">
+        <cov.data calls="4" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="parseInt(String, int)">
+        <cov.data calls="4" hit_lines="0" total_lines="64" />
+      </method>
+      <method name="toOctalString(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toBinaryString(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toUnsignedString(int, int)">
+        <cov.data calls="1" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="getInteger(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getInteger(String, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getInteger(String, Integer)">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+    </class>
+    <class name="Shutdown" source="Shutdown.java">
+      <cov.data calls="6" hit_methods="6" total_methods="12" hit_lines="0" total_lines="113" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="exit(int)">
+        <cov.data calls="1" hit_lines="0" total_lines="34" />
+      </method>
+      <method name="shutdown()">
+        <cov.data calls="0" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="add(Thread)">
+        <cov.data calls="0" hit_lines="0" total_lines="17" />
+      </method>
+      <method name="class$(String)">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="remove(Thread)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="runAllFinalizers()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setRunFinalizersOnExit(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="runHooks()">
+        <cov.data calls="1" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="halt(int)">
+        <cov.data calls="1" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="sequence()">
+        <cov.data calls="1" hit_lines="0" total_lines="14" />
+      </method>
+    </class>
+    <class name="ClassLoader" source="ClassLoader.java">
+      <cov.data calls="9689" hit_methods="27" total_methods="51" hit_lines="0" total_lines="477" />
+      <method name="&lt;init&gt;(ClassLoader)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="29" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="loadClass(String, boolean)">
+        <cov.data calls="1176" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="loadClass(String)">
+        <cov.data calls="618" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="loadClassInternal(String)">
+        <cov.data calls="547" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="findNative(ClassLoader, String)">
+        <cov.data calls="27" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="addClass(Class)">
+        <cov.data calls="442" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getSystemClassLoader()">
+        <cov.data calls="41" hit_lines="0" total_lines="20" />
+      </method>
+      <method name="checkPackageAccess(Class, java.security.ProtectionDomain)">
+        <cov.data calls="296" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="getPackage(String)">
+        <cov.data calls="637" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="setSigners(Class, Object[])">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getResourceAsStream(String)">
+        <cov.data calls="10" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getResource(String)">
+        <cov.data calls="44" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="getCallerClassLoader()">
+        <cov.data calls="593" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getGetClassLoaderPerm()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="isAncestor(ClassLoader)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="getSystemResourceAsStream(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getSystemResource(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="findClass(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="defineClass(String, byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="defineClass(String, byte[], int, int, java.security.ProtectionDomain)">
+        <cov.data calls="442" hit_lines="0" total_lines="19" />
+      </method>
+      <method name="defineClass(byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="checkCerts(String, java.security.CodeSource)">
+        <cov.data calls="442" hit_lines="0" total_lines="29" />
+      </method>
+      <method name="compareCerts(java.security.cert.Certificate[], java.security.cert.Certificate[])">
+        <cov.data calls="377" hit_lines="0" total_lines="35" />
+      </method>
+      <method name="resolveClass(Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="findSystemClass(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="getParent()">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="findBootstrapClass0(String)">
+        <cov.data calls="586" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="defineClass0(String, byte[], int, int, java.security.ProtectionDomain)">
+        <cov.data calls="442" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="resolveClass0(Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="findBootstrapClass(String)">
+        <cov.data calls="586" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="check()">
+        <cov.data calls="1028" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="findLoadedClass(String)">
+        <cov.data calls="1204" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getResources(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="findResources(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="findResource(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getBootstrapResource(String)">
+        <cov.data calls="22" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getSystemResources(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getBootstrapResources(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getBootstrapClassPath()">
+        <cov.data calls="22" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="definePackage(String, String, String, String, String, String, String, java.net.URL)">
+        <cov.data calls="65" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="getPackages()">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="findLibrary(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDefaultDomain()">
+        <cov.data calls="0" hit_lines="0" total_lines="20" />
+      </method>
+      <method name="initializePath(String)">
+        <cov.data calls="2" hit_lines="0" total_lines="29" />
+      </method>
+      <method name="loadLibrary(Class, String, boolean)">
+        <cov.data calls="5" hit_lines="0" total_lines="43" />
+      </method>
+      <method name="loadLibrary0(Class, java.io.File)">
+        <cov.data calls="5" hit_lines="0" total_lines="78" />
+      </method>
+      <method name="access$000()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$100()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="class$(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Error" source="Error.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="ArrayStoreException" source="ArrayStoreException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="Thread" source="Thread.java">
+      <cov.data calls="1203" hit_methods="6" total_methods="51" hit_lines="0" total_lines="216" />
+      <method name="&lt;init&gt;(Runnable, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(ThreadGroup, Runnable)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(Runnable)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(ThreadGroup, Runnable, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(ThreadGroup, String)">
+        <cov.data calls="2" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="run()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="exit()">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="registerNatives()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getName()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="currentThread()">
+        <cov.data calls="1193" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="dumpStack()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getThreadGroup()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setPriority(int)">
+        <cov.data calls="2" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="setDaemon(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="start()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="nextThreadNum()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="yield()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="sleep(long, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="sleep(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="init(ThreadGroup, Runnable, String)">
+        <cov.data calls="2" hit_lines="0" total_lines="36" />
+      </method>
+      <method name="stop()">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="stop(Throwable)">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="interrupt()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="interrupted()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isInterrupted(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isInterrupted()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="destroy()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isAlive()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="suspend()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="resume()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getPriority()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setName(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="activeCount()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="enumerate(Thread[])">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="countStackFrames()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="join()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="join(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="22" />
+      </method>
+      <method name="join(long, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="isDaemon()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="checkAccess()">
+        <cov.data calls="2" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getContextClassLoader()">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="setContextClassLoader(ClassLoader)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="setPriority0(int)">
+        <cov.data calls="2" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="stop0(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="suspend0()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="resume0()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="interrupt0()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="Object" source="Object.java">
+      <cov.data calls="18182" hit_methods="8" total_methods="14" hit_lines="0" total_lines="21" />
+      <method name="hashCode()">
+        <cov.data calls="2274" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="finalize()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="wait(long, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="wait(long)">
+        <cov.data calls="20" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="wait()">
+        <cov.data calls="10" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="registerNatives()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getClass()">
+        <cov.data calls="791" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="6204" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="8549" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="notify()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="notifyAll()">
+        <cov.data calls="333" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="Byte" source="Byte.java">
+      <cov.data calls="1" hit_methods="1" total_methods="20" hit_lines="0" total_lines="66" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compareTo(Byte)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compareTo(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(byte)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toString(byte)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(String, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="byteValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="shortValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="intValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="longValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="floatValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="doubleValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="parseByte(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="parseByte(String, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="decode(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="37" />
+      </method>
+    </class>
+    <class name="Double" source="Double.java">
+      <cov.data calls="4" hit_methods="3" total_methods="24" hit_lines="0" total_lines="36" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="compareTo(Double)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="compareTo(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toString(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isNaN(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isNaN()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isInfinite()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isInfinite(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="byteValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="shortValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="intValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="longValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="floatValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="doubleValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="parseDouble(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="doubleToLongBits(double)">
+        <cov.data calls="2" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="doubleToRawLongBits(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="longBitsToDouble(long)">
+        <cov.data calls="1" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="InheritableThreadLocal" source="InheritableThreadLocal.java">
+      <cov.data calls="2" hit_methods="1" total_methods="5" hit_lines="0" total_lines="34" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="get()">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="set(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="bequeath(Thread, Thread)">
+        <cov.data calls="2" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="childValue(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="ArithmeticException" source="ArithmeticException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="Short" source="Short.java">
+      <cov.data calls="1" hit_methods="1" total_methods="20" hit_lines="0" total_lines="69" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compareTo(Short)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compareTo(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(short)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toString(short)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(String, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="byteValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="shortValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="intValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="longValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="floatValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="doubleValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="decode(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="40" />
+      </method>
+      <method name="parseShort(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="parseShort(String, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+    </class>
+    <class name="Class" source="Class.java">
+      <cov.data calls="6366" hit_methods="23" total_methods="54" hit_lines="0" total_lines="146" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="registerNatives()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getName()">
+        <cov.data calls="268" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="forName(String)">
+        <cov.data calls="185" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="forName(String, boolean, ClassLoader)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="forName0(String, boolean, ClassLoader)">
+        <cov.data calls="185" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="newInstance()">
+        <cov.data calls="432" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="newInstance0()">
+        <cov.data calls="432" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isInstance(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isAssignableFrom(Class)">
+        <cov.data calls="2400" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isInterface()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isArray()">
+        <cov.data calls="448" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isPrimitive()">
+        <cov.data calls="123" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getClassLoader()">
+        <cov.data calls="53" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="getClassLoader0()">
+        <cov.data calls="71" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getSuperclass()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getPackage()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getInterfaces()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getComponentType()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getModifiers()">
+        <cov.data calls="502" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getSigners()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setSigners(Object[])">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getDeclaringClass()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getClasses()">
+        <cov.data calls="0" hit_lines="0" total_lines="27" />
+      </method>
+      <method name="getFields()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getMethods()">
+        <cov.data calls="29" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getConstructors()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getField(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getMethod(String, Class[])">
+        <cov.data calls="10" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getConstructor(Class[])">
+        <cov.data calls="368" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getDeclaredClasses()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getDeclaredFields()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getDeclaredMethods()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getDeclaredConstructors()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getDeclaredField(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getDeclaredMethod(String, Class[])">
+        <cov.data calls="1" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getDeclaredConstructor(Class[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getResourceAsStream(String)">
+        <cov.data calls="6" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="getResource(String)">
+        <cov.data calls="12" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="getProtectionDomain()">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="getProtectionDomain0()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setProtectionDomain0(java.security.ProtectionDomain)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getPrimitiveClass(String)">
+        <cov.data calls="7" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="checkMemberAccess(int, ClassLoader)">
+        <cov.data calls="408" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="resolveName(String)">
+        <cov.data calls="18" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="getFields0(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getMethods0(int)">
+        <cov.data calls="29" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getConstructors0(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getField0(String, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getMethod0(String, Class[], int)">
+        <cov.data calls="11" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getConstructor0(Class[], int)">
+        <cov.data calls="368" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getDeclaredClasses0()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="NoClassDefFoundError" source="NoClassDefFoundError.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="Compiler$1" source="Compiler.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="22" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="run()">
+        <cov.data calls="0" hit_lines="0" total_lines="21" />
+      </method>
+    </class>
+    <class name="Throwable" source="Throwable.java">
+      <cov.data calls="4198" hit_methods="3" total_methods="10" hit_lines="0" total_lines="27" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="980" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1119" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="printStackTrace(java.io.PrintWriter)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="printStackTrace(java.io.PrintStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="printStackTrace()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="fillInStackTrace()">
+        <cov.data calls="2099" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getMessage()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getLocalizedMessage()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="printStackTrace0(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="OutOfMemoryError" source="OutOfMemoryError.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="Compiler" source="Compiler.java">
+      <cov.data calls="0" hit_methods="0" total_methods="10" hit_lines="0" total_lines="2" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="registerNatives()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="access$000()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="initialize()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="compileClass(Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="compileClasses(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="command(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="enable()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="disable()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="StackOverflowError" source="StackOverflowError.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="Long" source="Long.java">
+      <cov.data calls="324" hit_methods="4" total_methods="28" hit_lines="0" total_lines="180" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compareTo(Long)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="compareTo(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(long)">
+        <cov.data calls="317" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="toString(long, int)">
+        <cov.data calls="3" hit_lines="0" total_lines="22" />
+      </method>
+      <method name="toString(long)">
+        <cov.data calls="3" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toHexString(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(String, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="byteValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="shortValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="intValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="longValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="floatValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="doubleValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="decode(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="40" />
+      </method>
+      <method name="toOctalString(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toBinaryString(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toUnsignedString(long, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="parseLong(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="parseLong(String, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="63" />
+      </method>
+      <method name="getLong(String, Long)">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="getLong(String, long)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getLong(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Number" source="Number.java">
+      <cov.data calls="318" hit_methods="1" total_methods="7" hit_lines="0" total_lines="3" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="318" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="byteValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="shortValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="intValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="longValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="floatValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="doubleValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="Runnable" source="Runnable.java">
+      <cov.data calls="0" hit_methods="0" total_methods="1" hit_lines="0" total_lines="0" />
+      <method name="run()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="Float" source="Float.java">
+      <cov.data calls="1156" hit_methods="2" total_methods="25" hit_lines="0" total_lines="38" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compareTo(Float)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="compareTo(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(float)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toString(float)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isNaN()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isNaN(float)">
+        <cov.data calls="1154" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="parseFloat(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isInfinite()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isInfinite(float)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="byteValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="shortValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="intValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="longValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="floatValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="doubleValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="floatToIntBits(float)">
+        <cov.data calls="2" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="floatToRawIntBits(float)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="intBitsToFloat(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="StringBuffer" source="StringBuffer.java">
+      <cov.data calls="445234" hit_methods="14" total_methods="42" hit_lines="0" total_lines="238" />
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="49319" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="144" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="48696" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="50737" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="append(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="append(float)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="append(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="append(int)">
+        <cov.data calls="8" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="append(char)">
+        <cov.data calls="74410" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="append(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="append(char[], int, int)">
+        <cov.data calls="10" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="append(char[])">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="append(Object)">
+        <cov.data calls="4181" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="append(String)">
+        <cov.data calls="90162" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="length()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="charAt(int)">
+        <cov.data calls="4374" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getChars(int, int, char[], int)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="substring(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="substring(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="replace(int, int, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="setShared()">
+        <cov.data calls="50737" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="copy()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="ensureCapacity(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="capacity()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="expandCapacity(int)">
+        <cov.data calls="70027" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="setLength(int)">
+        <cov.data calls="2172" hit_lines="0" total_lines="27" />
+      </method>
+      <method name="setCharAt(int, char)">
+        <cov.data calls="257" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="delete(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="deleteCharAt(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="insert(int, char)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="insert(int, char[])">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="insert(int, long)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="insert(int, float)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="insert(int, double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="insert(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="insert(int, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="insert(int, char[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="insert(int, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="17" />
+      </method>
+      <method name="insert(int, Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="reverse()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+    </class>
+    <class name="Character" source="Character.java">
+      <cov.data calls="42340" hit_methods="5" total_methods="33" hit_lines="0" total_lines="118" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compareTo(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compareTo(Character)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="toLowerCase(char)">
+        <cov.data calls="33070" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="toUpperCase(char)">
+        <cov.data calls="6135" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="charValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isLowerCase(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isUpperCase(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isTitleCase(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isDigit(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isDefined(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isLetter(char)">
+        <cov.data calls="3107" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isLetterOrDigit(char)">
+        <cov.data calls="24" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isJavaLetter(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isJavaLetterOrDigit(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isJavaIdentifierStart(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isJavaIdentifierPart(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isUnicodeIdentifierStart(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isUnicodeIdentifierPart(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isIdentifierIgnorable(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toTitleCase(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="26" />
+      </method>
+      <method name="digit(char, int)">
+        <cov.data calls="4" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="getNumericValue(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="26" />
+      </method>
+      <method name="isSpace(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isSpaceChar(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isWhitespace(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isISOControl(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getType(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="forDigit(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+    </class>
+    <class name="ClassNotFoundException" source="ClassNotFoundException.java">
+      <cov.data calls="962" hit_methods="1" total_methods="7" hit_lines="0" total_lines="28" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="962" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(String, Throwable)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="printStackTrace()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="printStackTrace(java.io.PrintStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="printStackTrace(java.io.PrintWriter)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="getException()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="String$CaseInsensitiveComparator" source="String.java">
+      <cov.data calls="874" hit_methods="1" total_methods="3" hit_lines="0" total_lines="20" />
+      <method name="&lt;init&gt;(String$1)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compare(Object, Object)">
+        <cov.data calls="874" hit_lines="0" total_lines="18" />
+      </method>
+    </class>
+    <class name="Exception" source="Exception.java">
+      <cov.data calls="2099" hit_methods="2" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="980" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1119" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="System" source="System.java">
+      <cov.data calls="376546" hit_methods="16" total_methods="34" hit_lines="0" total_lines="120" />
+      <method name="arraycopy(Object, int, Object, int, int)">
+        <cov.data calls="361119" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="exit(int)">
+        <cov.data calls="1" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="runFinalizersOnExit(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="initializeSystemClass()">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="setProperty(String, String)">
+        <cov.data calls="1" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="registerNatives()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getSecurityManager()">
+        <cov.data calls="15282" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="loadLibrary(String)">
+        <cov.data calls="5" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getProperty(String, String)">
+        <cov.data calls="2" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="getProperty(String)">
+        <cov.data calls="83" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="mapLibraryName(String)">
+        <cov.data calls="5" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="load(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="runFinalization()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="currentTimeMillis()">
+        <cov.data calls="29" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setIn(java.io.InputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="setOut(java.io.PrintStream)">
+        <cov.data calls="2" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="setErr(java.io.PrintStream)">
+        <cov.data calls="2" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="checkIO()">
+        <cov.data calls="4" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="setIn0(java.io.InputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setOut0(java.io.PrintStream)">
+        <cov.data calls="2" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setErr0(java.io.PrintStream)">
+        <cov.data calls="2" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setSecurityManager(SecurityManager)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="setSecurityManager0(SecurityManager)">
+        <cov.data calls="0" hit_lines="0" total_lines="28" />
+      </method>
+      <method name="identityHashCode(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="initProperties(java.util.Properties)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getProperties()">
+        <cov.data calls="2" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="setProperties(java.util.Properties)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="getenv(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="gc()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="nullInputStream()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="nullPrintStream()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getCallerClass()">
+        <cov.data calls="5" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="VirtualMachineError" source="VirtualMachineError.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="RuntimeException" source="RuntimeException.java">
+      <cov.data calls="576" hit_methods="2" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="5" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="571" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="String" source="String.java">
+      <cov.data calls="7164199" hit_methods="41" total_methods="65" hit_lines="0" total_lines="625" />
+      <method name="hashCode()">
+        <cov.data calls="13309" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="compareTo(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compareTo(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="29" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="&lt;init&gt;(char[])">
+        <cov.data calls="1858" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="&lt;init&gt;(char[], int, int)">
+        <cov.data calls="2903" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="&lt;init&gt;(byte[], int, int, int)">
+        <cov.data calls="3053" hit_lines="0" total_lines="27" />
+      </method>
+      <method name="&lt;init&gt;(byte[], int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(byte[], int, int, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(byte[], String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(byte[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(StringBuffer)">
+        <cov.data calls="50737" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="&lt;init&gt;(int, int, char[])">
+        <cov.data calls="349989" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="&lt;init&gt;(byte[], int, int, sun.io.ByteToCharConverter)">
+        <cov.data calls="0" hit_lines="0" total_lines="26" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="165634" hit_lines="0" total_lines="20" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="4251" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getBTCConverter(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="getCTBConverter(String)">
+        <cov.data calls="952" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="length()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="charAt(int)">
+        <cov.data calls="3734831" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getChars(int, int, char[], int)">
+        <cov.data calls="282564" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="getBytes()">
+        <cov.data calls="3" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getBytes(sun.io.CharToByteConverter)">
+        <cov.data calls="955" hit_lines="0" total_lines="22" />
+      </method>
+      <method name="getBytes(String)">
+        <cov.data calls="952" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getBytes(int, int, byte[], int)">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="equalsIgnoreCase(String)">
+        <cov.data calls="2005" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="compareToIgnoreCase(String)">
+        <cov.data calls="874" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="regionMatches(int, String, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="regionMatches(boolean, int, String, int, int)">
+        <cov.data calls="1923" hit_lines="0" total_lines="34" />
+      </method>
+      <method name="startsWith(String)">
+        <cov.data calls="70315" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="startsWith(String, int)">
+        <cov.data calls="82164" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="endsWith(String)">
+        <cov.data calls="11849" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="indexOf(int)">
+        <cov.data calls="904300" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="indexOf(int, int)">
+        <cov.data calls="905864" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="indexOf(String)">
+        <cov.data calls="1985" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="indexOf(String, int)">
+        <cov.data calls="2586" hit_lines="0" total_lines="45" />
+      </method>
+      <method name="lastIndexOf(String, int)">
+        <cov.data calls="2" hit_lines="0" total_lines="44" />
+      </method>
+      <method name="lastIndexOf(int)">
+        <cov.data calls="2489" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="lastIndexOf(int, int)">
+        <cov.data calls="3285" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="lastIndexOf(String)">
+        <cov.data calls="2" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="substring(int, int)">
+        <cov.data calls="350299" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="substring(int)">
+        <cov.data calls="1933" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="concat(String)">
+        <cov.data calls="1502" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="replace(char, char)">
+        <cov.data calls="9987" hit_lines="0" total_lines="25" />
+      </method>
+      <method name="toLowerCase(java.util.Locale)">
+        <cov.data calls="2792" hit_lines="0" total_lines="40" />
+      </method>
+      <method name="toLowerCase()">
+        <cov.data calls="1432" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toUpperCase()">
+        <cov.data calls="33" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toUpperCase(java.util.Locale)">
+        <cov.data calls="33" hit_lines="0" total_lines="65" />
+      </method>
+      <method name="trim()">
+        <cov.data calls="617" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="toCharArray()">
+        <cov.data calls="188295" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="valueOf(char[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(char[])">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(Object)">
+        <cov.data calls="4292" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="valueOf(int)">
+        <cov.data calls="8" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="valueOf(float)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="copyValueOf(char[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="copyValueOf(char[])">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="intern()">
+        <cov.data calls="1342" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="ThreadDeath" source="ThreadDeath.java">
+      <cov.data calls="0" hit_methods="0" total_methods="1" hit_lines="0" total_lines="1" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="NullPointerException" source="NullPointerException.java">
+      <cov.data calls="1" hit_methods="1" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="Runtime" source="Runtime.java">
+      <cov.data calls="12" hit_methods="3" total_methods="28" hit_lines="0" total_lines="106" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="exit(int)">
+        <cov.data calls="1" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="runFinalizersOnExit(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="loadLibrary(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="loadLibrary0(Class, String)">
+        <cov.data calls="5" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="load(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="runFinalization()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="halt(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="gc()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getRuntime()">
+        <cov.data calls="6" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="load0(Class, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="addShutdownHook(Thread)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="removeShutdownHook(Thread)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="execInternal(String[], String[], String)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="exec(String, String[])">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="exec(String[], String[])">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="exec(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="exec(String, String[], java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="exec(String[], String[], java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="24" />
+      </method>
+      <method name="exec(String[])">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="freeMemory()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="totalMemory()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="runFinalization0()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="traceInstructions(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="traceMethodCalls(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getLocalizedInputStream(java.io.InputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getLocalizedOutputStream(java.io.OutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="ClassLoader$4" source="ClassLoader.java">
+      <cov.data calls="10" hit_methods="2" total_methods="2" hit_lines="2" total_lines="2" />
+      <method name="&lt;init&gt;(java.io.File)">
+        <cov.data calls="5" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="run()">
+        <cov.data calls="5" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="ClassLoader$NativeLibrary" source="ClassLoader.java">
+      <cov.data calls="29" hit_methods="4" total_methods="7" hit_lines="5" total_lines="18" />
+      <method name="&lt;init&gt;(Class, String)">
+        <cov.data calls="1" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="finalize()">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="getFromClass()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="access$200(ClassLoader$NativeLibrary)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="load(String)">
+        <cov.data calls="1" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="find(String)">
+        <cov.data calls="26" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="unload()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="Math" source="Math.java">
+      <cov.data calls="605" hit_methods="4" total_methods="35" hit_lines="5" total_lines="51" />
+      <method name="sin(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="cos(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="tan(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="atan2(double, double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="sqrt(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="min(float, float)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="min(long, long)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="min(int, int)">
+        <cov.data calls="68" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="min(double, double)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="max(double, double)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="max(float, float)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="max(long, long)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="max(int, int)">
+        <cov.data calls="535" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="asin(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="acos(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="atan(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toRadians(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toDegrees(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="exp(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="log(double)">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="IEEEremainder(double, double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="ceil(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="floor(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="rint(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="pow(double, double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="round(float)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="round(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="initRNG()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="random()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="abs(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="abs(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="abs(float)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="abs(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Package" source="Package.java">
+      <cov.data calls="703" hit_methods="5" total_methods="29" hit_lines="21" total_lines="128" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(String, java.util.jar.Manifest, java.net.URL)">
+        <cov.data calls="0" hit_lines="0" total_lines="32" />
+      </method>
+      <method name="&lt;init&gt;(String, String, String, String, String, String, String, java.net.URL)">
+        <cov.data calls="65" hit_lines="10" total_lines="10" />
+      </method>
+      <method name="&lt;init&gt;(String, java.util.jar.Manifest, java.net.URL, Package$1)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="getName()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getPackage(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getPackage(Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="getPackages()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="access$000()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$100(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getSystemPackage(String)">
+        <cov.data calls="130" hit_lines="7" total_lines="9" />
+      </method>
+      <method name="getSystemPackages()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="access$200()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$400()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isSealed()">
+        <cov.data calls="377" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="isSealed(java.net.URL)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isCompatibleWith(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="20" />
+      </method>
+      <method name="getSpecificationTitle()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getSpecificationVersion()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getSpecificationVendor()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getImplementationTitle()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getImplementationVersion()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getImplementationVendor()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="defineSystemPackage(String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="loadManifest(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="getSystemPackage0(String)">
+        <cov.data calls="130" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getSystemPackages0()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="RuntimePermission" source="RuntimePermission.java">
+      <cov.data calls="18" hit_methods="1" total_methods="2" hit_lines="2" total_lines="4" />
+      <method name="&lt;init&gt;(String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="18" hit_lines="2" total_lines="2" />
+      </method>
+    </class>
+    <class name="IndexOutOfBoundsException" source="IndexOutOfBoundsException.java">
+      <cov.data calls="36" hit_methods="1" total_methods="2" hit_lines="2" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="36" hit_lines="2" total_lines="2" />
+      </method>
+    </class>
+    <class name="ArrayIndexOutOfBoundsException" source="ArrayIndexOutOfBoundsException.java">
+      <cov.data calls="36" hit_methods="1" total_methods="3" hit_lines="2" total_lines="6" />
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="36" hit_lines="2" total_lines="2" />
+      </method>
+    </class>
+    <class name="NoSuchMethodException" source="NoSuchMethodException.java">
+      <cov.data calls="47" hit_methods="1" total_methods="2" hit_lines="2" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="47" hit_lines="2" total_lines="2" />
+      </method>
+    </class>
+    <class name="Void" source="Void.java">
+      <cov.data calls="1" hit_methods="1" total_methods="2" hit_lines="1" total_lines="2" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="StringIndexOutOfBoundsException" source="StringIndexOutOfBoundsException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="3" hit_lines="0" total_lines="6" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="SecurityException" source="SecurityException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="IllegalAccessException" source="IllegalAccessException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="InstantiationException" source="InstantiationException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="IllegalArgumentException" source="IllegalArgumentException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="NumberFormatException" source="NumberFormatException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="Process" source="Process.java">
+      <cov.data calls="0" hit_methods="0" total_methods="7" hit_lines="0" total_lines="1" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="destroy()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getInputStream()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getOutputStream()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="exitValue()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getErrorStream()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="waitFor()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="ExceptionInInitializerError" source="ExceptionInInitializerError.java">
+      <cov.data calls="0" hit_methods="0" total_methods="7" hit_lines="0" total_lines="24" />
+      <method name="&lt;init&gt;(Throwable)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="printStackTrace()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="printStackTrace(java.io.PrintStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="printStackTrace(java.io.PrintWriter)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="getException()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="InterruptedException" source="InterruptedException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="CloneNotSupportedException" source="CloneNotSupportedException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="ClassFormatError" source="ClassFormatError.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="IllegalStateException" source="IllegalStateException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="ThreadLocal$SecureKey" source="ThreadLocal.java">
+      <cov.data calls="1" hit_methods="1" total_methods="2" hit_lines="1" total_lines="2" />
+      <method name="&lt;init&gt;(ThreadLocal)">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="threadLocal()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Shutdown$Lock" source="Shutdown.java">
+      <cov.data calls="2" hit_methods="2" total_methods="2" hit_lines="2" total_lines="2" />
+      <method name="&lt;init&gt;(Shutdown$1)">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+  </package>
+  <package name="java.lang.ref">
+    <cov.data calls="8161" hit_methods="27" total_methods="53" hit_lines="0" total_lines="208" />
+    <class name="Finalizer$FinalizerThread" source="Finalizer.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="8" />
+      <method name="&lt;init&gt;(ThreadGroup)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="run()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+    </class>
+    <class name="PhantomReference" source="PhantomReference.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="3" />
+      <method name="&lt;init&gt;(Object, ref.ReferenceQueue)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="get()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="WeakReference" source="WeakReference.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(Object, ref.ReferenceQueue)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="Reference$Lock" source="Reference.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="2" />
+      <method name="&lt;init&gt;(ref.Reference$1)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="SoftReference" source="SoftReference.java">
+      <cov.data calls="1134" hit_methods="3" total_methods="3" hit_lines="0" total_lines="9" />
+      <method name="&lt;init&gt;(Object, ref.ReferenceQueue)">
+        <cov.data calls="10" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(Object)">
+        <cov.data calls="60" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="get()">
+        <cov.data calls="1064" hit_lines="0" total_lines="3" />
+      </method>
+    </class>
+    <class name="Reference$ReferenceHandler" source="Reference.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="21" />
+      <method name="&lt;init&gt;(ThreadGroup, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="run()">
+        <cov.data calls="0" hit_lines="0" total_lines="19" />
+      </method>
+    </class>
+    <class name="ReferenceQueue$Null" source="ReferenceQueue.java">
+      <cov.data calls="0" hit_methods="0" total_methods="3" hit_lines="0" total_lines="3" />
+      <method name="&lt;init&gt;(ref.ReferenceQueue$1)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="enqueue(ref.Reference)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Finalizer" source="Finalizer.java">
+      <cov.data calls="3069" hit_methods="9" total_methods="17" hit_lines="0" total_lines="93" />
+      <method name="&lt;init&gt;(Object)">
+        <cov.data calls="397" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="register(Object)">
+        <cov.data calls="397" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="add()">
+        <cov.data calls="397" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="access$000()">
+        <cov.data calls="313" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$100(ref.Finalizer)">
+        <cov.data calls="313" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$200()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="invokeFinalizeMethod(Object)">
+        <cov.data calls="313" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="hasBeenFinalized()">
+        <cov.data calls="313" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="remove()">
+        <cov.data calls="313" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="runFinalizer()">
+        <cov.data calls="313" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="forkSecondaryFinalizer(Runnable)">
+        <cov.data calls="0" hit_lines="0" total_lines="17" />
+      </method>
+      <method name="runFinalization()">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="runAllFinalizers()">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="access$300()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$302(ref.Finalizer)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$400(ref.Finalizer)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="FinalReference" source="FinalReference.java">
+      <cov.data calls="397" hit_methods="1" total_methods="1" hit_lines="0" total_lines="2" />
+      <method name="&lt;init&gt;(Object, ref.ReferenceQueue)">
+        <cov.data calls="397" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="Reference" source="Reference.java">
+      <cov.data calls="2206" hit_methods="6" total_methods="10" hit_lines="0" total_lines="24" />
+      <method name="&lt;init&gt;(Object)">
+        <cov.data calls="60" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(Object, ref.ReferenceQueue)">
+        <cov.data calls="467" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="get()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$100()">
+        <cov.data calls="354" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$200()">
+        <cov.data calls="678" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="313" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="isEnqueued()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="enqueue()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$202(ref.Reference)">
+        <cov.data calls="334" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="ReferenceQueue$Lock" source="ReferenceQueue.java">
+      <cov.data calls="2" hit_methods="2" total_methods="2" hit_lines="0" total_lines="2" />
+      <method name="&lt;init&gt;(ref.ReferenceQueue$1)">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="ReferenceQueue" source="ReferenceQueue.java">
+      <cov.data calls="1353" hit_methods="6" total_methods="7" hit_lines="0" total_lines="37" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="enqueue(ref.Reference)">
+        <cov.data calls="323" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="remove(long)">
+        <cov.data calls="313" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="remove()">
+        <cov.data calls="313" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="reallyPoll()">
+        <cov.data calls="363" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="poll()">
+        <cov.data calls="40" hit_lines="0" total_lines="3" />
+      </method>
+    </class>
+  </package>
+  <package name="java.security">
+    <cov.data calls="5554" hit_methods="45" total_methods="149" hit_lines="91" total_lines="1148" />
+    <class name="PrivilegedAction" source="PrivilegedAction.java">
+      <cov.data calls="0" hit_methods="0" total_methods="1" hit_lines="0" total_lines="0" />
+      <method name="run()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="PrivilegedExceptionAction" source="PrivilegedExceptionAction.java">
+      <cov.data calls="0" hit_methods="0" total_methods="1" hit_lines="0" total_lines="0" />
+      <method name="run()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="ProtectionDomain" source="ProtectionDomain.java">
+      <cov.data calls="19" hit_methods="2" total_methods="5" hit_lines="0" total_lines="14" />
+      <method name="&lt;init&gt;(java.security.CodeSource, java.security.PermissionCollection)">
+        <cov.data calls="18" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getCodeSource()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getPermissions()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="implies(java.security.Permission)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+    </class>
+    <class name="Guard" source="Guard.java">
+      <cov.data calls="0" hit_methods="0" total_methods="1" hit_lines="0" total_lines="0" />
+      <method name="checkGuard(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="SecureClassLoader" source="SecureClassLoader.java">
+      <cov.data calls="920" hit_methods="4" total_methods="7" hit_lines="0" total_lines="59" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="&lt;init&gt;(ClassLoader)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getProtectionDomain(java.security.CodeSource)">
+        <cov.data calls="442" hit_lines="0" total_lines="24" />
+      </method>
+      <method name="defineClass(String, byte[], int, int, java.security.CodeSource)">
+        <cov.data calls="442" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="check()">
+        <cov.data calls="18" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getPermissions(java.security.CodeSource)">
+        <cov.data calls="18" hit_lines="0" total_lines="10" />
+      </method>
+    </class>
+    <class name="Permission" source="Permission.java">
+      <cov.data calls="42" hit_methods="2" total_methods="9" hit_lines="0" total_lines="13" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="41" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getName()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="implies(java.security.Permission)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getActions()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="newPermissionCollection()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="checkGuard(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+    </class>
+    <class name="AccessController" source="AccessController.java">
+      <cov.data calls="1083" hit_methods="7" total_methods="9" hit_lines="0" total_lines="27" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="checkPermission(java.security.Permission)">
+        <cov.data calls="0" hit_lines="0" total_lines="19" />
+      </method>
+      <method name="doPrivileged(java.security.PrivilegedExceptionAction)">
+        <cov.data calls="41" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="doPrivileged(java.security.PrivilegedAction)">
+        <cov.data calls="62" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="doPrivileged(java.security.PrivilegedExceptionAction, java.security.AccessControlContext)">
+        <cov.data calls="930" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="doPrivileged(java.security.PrivilegedAction, java.security.AccessControlContext)">
+        <cov.data calls="44" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getInheritedAccessControlContext()">
+        <cov.data calls="2" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getContext()">
+        <cov.data calls="2" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="getStackAccessControlContext()">
+        <cov.data calls="2" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="BasicPermission" source="BasicPermission.java">
+      <cov.data calls="36" hit_methods="2" total_methods="9" hit_lines="0" total_lines="60" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="18" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="implies(java.security.Permission)">
+        <cov.data calls="0" hit_lines="0" total_lines="20" />
+      </method>
+      <method name="init(String)">
+        <cov.data calls="18" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getActions()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="newPermissionCollection()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="AccessControlContext" source="AccessControlContext.java">
+      <cov.data calls="2" hit_methods="1" total_methods="15" hit_lines="0" total_lines="389" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="&lt;init&gt;(java.security.AccessControlContext, java.security.DomainCombiner)">
+        <cov.data calls="0" hit_lines="0" total_lines="23" />
+      </method>
+      <method name="&lt;init&gt;(java.security.ProtectionDomain[], java.security.DomainCombiner)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="&lt;init&gt;(java.security.ProtectionDomain[])">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="&lt;init&gt;(java.security.ProtectionDomain[], boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isPrivileged()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="29" />
+      </method>
+      <method name="checkPermission(java.security.Permission)">
+        <cov.data calls="0" hit_lines="0" total_lines="56" />
+      </method>
+      <method name="getDebug()">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="getDomainCombiner()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="combineWithPrivilegedContext()">
+        <cov.data calls="0" hit_lines="0" total_lines="88" />
+      </method>
+      <method name="optimize()">
+        <cov.data calls="2" hit_lines="0" total_lines="107" />
+      </method>
+      <method name="goCombiner(java.security.ProtectionDomain[], java.security.AccessControlContext, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="containsAllPDs(java.security.AccessControlContext)">
+        <cov.data calls="0" hit_lines="0" total_lines="25" />
+      </method>
+    </class>
+    <class name="PrivilegedActionException" source="PrivilegedActionException.java">
+      <cov.data calls="1002" hit_methods="2" total_methods="6" hit_lines="4" total_lines="21" />
+      <method name="&lt;init&gt;(Exception)">
+        <cov.data calls="501" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="printStackTrace()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="printStackTrace(java.io.PrintStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="printStackTrace(java.io.PrintWriter)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getException()">
+        <cov.data calls="501" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="CodeSource" source="CodeSource.java">
+      <cov.data calls="2246" hit_methods="5" total_methods="11" hit_lines="17" total_lines="138" />
+      <method name="hashCode()">
+        <cov.data calls="478" hit_lines="2" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(java.net.URL, java.security.cert.Certificate[])">
+        <cov.data calls="442" hit_lines="4" total_lines="5" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="424" hit_lines="8" total_lines="27" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="getCertificates()">
+        <cov.data calls="884" hit_lines="2" total_lines="3" />
+      </method>
+      <method name="implies(java.security.CodeSource)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="writeObject(java.io.ObjectOutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="22" />
+      </method>
+      <method name="getLocation()">
+        <cov.data calls="18" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="matchCerts(java.security.CodeSource)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="matchLocation(java.security.CodeSource)">
+        <cov.data calls="0" hit_lines="0" total_lines="41" />
+      </method>
+    </class>
+    <class name="Policy" source="Policy.java">
+      <cov.data calls="20" hit_methods="3" total_methods="8" hit_lines="10" total_lines="21" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="class$(String)">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getPermissions(java.security.CodeSource)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isSet()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getPolicyNoCheck()">
+        <cov.data calls="18" hit_lines="8" total_lines="11" />
+      </method>
+      <method name="getPolicy()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="setPolicy(java.security.Policy)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="refresh()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="Policy$1" source="Policy.java">
+      <cov.data calls="2" hit_methods="2" total_methods="2" hit_lines="2" total_lines="2" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="run()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="Security" source="Security.java">
+      <cov.data calls="6" hit_methods="6" total_methods="38" hit_lines="33" total_lines="334" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="setProperty(String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="check(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="access$000()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="class$(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getProperty(String)">
+        <cov.data calls="1" hit_lines="6" total_lines="7" />
+      </method>
+      <method name="debug(String, Throwable)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="debug(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="initialize()">
+        <cov.data calls="1" hit_lines="12" total_lines="16" />
+      </method>
+      <method name="reloadProviders()">
+        <cov.data calls="0" hit_lines="0" total_lines="17" />
+      </method>
+      <method name="error(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="error(String, Throwable)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="initializeStatic()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="loadProviders()">
+        <cov.data calls="1" hit_lines="10" total_lines="12" />
+      </method>
+      <method name="securityPropFile(String)">
+        <cov.data calls="1" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="getProviderProperty(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="23" />
+      </method>
+      <method name="getProviderProperty(String, java.security.Provider)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="getStandardName(String, String, java.security.Provider)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getAlgorithmProperty(String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getEngineClassName(String, String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="20" />
+      </method>
+      <method name="getEngineClassName(String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="insertProviderAt(java.security.Provider, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="addProvider(java.security.Provider)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="removeProvider(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="getProviders(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="getProviders()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getProviders(java.util.Map)">
+        <cov.data calls="0" hit_lines="0" total_lines="28" />
+      </method>
+      <method name="getProvider(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="checkSuperclass(Class, Class)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getImpl(String, String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="invalidateSMCache(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getAllQualifyingCandidates(String, String, java.security.Provider[])">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="getProvidersNotUsingCache(String, String, String, String, java.util.HashSet, java.security.Provider[])">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="isCriterionSatisfied(java.security.Provider, String, String, String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="isStandardAttr(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="isConstraintSatisfied(String, String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="getFilterComponents(String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="24" />
+      </method>
+    </class>
+    <class name="Security$1" source="Security.java">
+      <cov.data calls="2" hit_methods="2" total_methods="2" hit_lines="3" total_lines="3" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="run()">
+        <cov.data calls="1" hit_lines="2" total_lines="2" />
+      </method>
+    </class>
+    <class name="Provider" source="Provider.java">
+      <cov.data calls="102" hit_methods="4" total_methods="18" hit_lines="18" total_lines="53" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(String, double, String)">
+        <cov.data calls="2" hit_lines="7" total_lines="7" />
+      </method>
+      <method name="put(Object, Object)">
+        <cov.data calls="49" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getName()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="check(String)">
+        <cov.data calls="49" hit_lines="3" total_lines="4" />
+      </method>
+      <method name="values()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="load(java.io.InputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="debug(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="debug(String, Throwable)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="remove(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="keySet()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="entrySet()">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="putAll(java.util.Map)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="loadProvider(String)">
+        <cov.data calls="2" hit_lines="6" total_lines="10" />
+      </method>
+      <method name="getVersion()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getInfo()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="PermissionCollection" source="PermissionCollection.java">
+      <cov.data calls="72" hit_methods="3" total_methods="7" hit_lines="4" total_lines="14" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="18" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="add(java.security.Permission)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="implies(java.security.Permission)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setReadOnly()">
+        <cov.data calls="18" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="elements()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isReadOnly()">
+        <cov.data calls="36" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+  </package>
+  <package name="java.lang.reflect">
+    <cov.data calls="3843" hit_methods="8" total_methods="55" hit_lines="2" total_lines="189" />
+    <class name="ReflectPermission" source="ReflectPermission.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="Constructor" source="Constructor.java">
+      <cov.data calls="115" hit_methods="1" total_methods="10" hit_lines="0" total_lines="49" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="26" />
+      </method>
+      <method name="getName()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="newInstance(Object[])">
+        <cov.data calls="115" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getModifiers()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDeclaringClass()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getParameterTypes()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getExceptionTypes()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="AccessibleObject" source="AccessibleObject.java">
+      <cov.data calls="0" hit_methods="0" total_methods="7" hit_lines="0" total_lines="23" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="class$(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setAccessible(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="setAccessible(reflect.AccessibleObject[], boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="setAccessible0(reflect.AccessibleObject, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="isAccessible()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Method" source="Method.java">
+      <cov.data calls="3226" hit_methods="5" total_methods="12" hit_lines="0" total_lines="59" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="invoke(Object, Object[])">
+        <cov.data calls="1148" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="17" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="27" />
+      </method>
+      <method name="getName()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getModifiers()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDeclaringClass()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getReturnType()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getParameterTypes()">
+        <cov.data calls="1038" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getExceptionTypes()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="copy(Class[])">
+        <cov.data calls="1038" hit_lines="0" total_lines="7" />
+      </method>
+    </class>
+    <class name="Member" source="Member.java">
+      <cov.data calls="0" hit_methods="0" total_methods="3" hit_lines="0" total_lines="0" />
+      <method name="getName()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getModifiers()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getDeclaringClass()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="InvocationTargetException" source="InvocationTargetException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="7" hit_lines="0" total_lines="25" />
+      <method name="&lt;init&gt;(Throwable)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(Throwable, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="printStackTrace()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="printStackTrace(java.io.PrintStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="printStackTrace(java.io.PrintWriter)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="getTargetException()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="Modifier" source="Modifier.java">
+      <cov.data calls="502" hit_methods="2" total_methods="14" hit_lines="2" total_lines="29" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toString(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="isInterface(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isPrivate(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isTransient(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isStatic(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isPublic(int)">
+        <cov.data calls="251" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="isProtected(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isAbstract(int)">
+        <cov.data calls="251" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="isFinal(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isSynchronized(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isVolatile(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isNative(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isStrict(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+  </package>
+  <package name="java.net">
+    <cov.data calls="13717" hit_methods="53" total_methods="135" hit_lines="37" total_lines="1130" />
+    <class name="URLStreamHandler" source="URLStreamHandler.java">
+      <cov.data calls="5198" hit_methods="10" total_methods="12" hit_lines="0" total_lines="256" />
+      <method name="hashCode(java.net.URL)">
+        <cov.data calls="54" hit_lines="0" total_lines="34" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(java.net.URL, java.net.URL)">
+        <cov.data calls="434" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="openConnection(java.net.URL)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="sameFile(java.net.URL, java.net.URL)">
+        <cov.data calls="434" hit_lines="0" total_lines="22" />
+      </method>
+      <method name="toExternalForm(java.net.URL)">
+        <cov.data calls="52" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="parseURL(java.net.URL, String, int, int)">
+        <cov.data calls="511" hit_lines="0" total_lines="122" />
+      </method>
+      <method name="setURL(java.net.URL, String, String, int, String, String)">
+        <cov.data calls="462" hit_lines="0" total_lines="26" />
+      </method>
+      <method name="setURL(java.net.URL, String, String, int, String, String, String, String, String)">
+        <cov.data calls="973" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="getDefaultPort()">
+        <cov.data calls="922" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getHostAddress(java.net.URL)">
+        <cov.data calls="922" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="hostsEqual(java.net.URL, java.net.URL)">
+        <cov.data calls="434" hit_lines="0" total_lines="10" />
+      </method>
+    </class>
+    <class name="URL" source="URL.java">
+      <cov.data calls="4170" hit_methods="23" total_methods="33" hit_lines="0" total_lines="363" />
+      <method name="hashCode()">
+        <cov.data calls="566" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="&lt;init&gt;(String, String, int, String, java.net.URLStreamHandler)">
+        <cov.data calls="45" hit_lines="0" total_lines="32" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="8" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.net.URL, String)">
+        <cov.data calls="973" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.net.URL, String, java.net.URLStreamHandler)">
+        <cov.data calls="973" hit_lines="0" total_lines="100" />
+      </method>
+      <method name="&lt;init&gt;(String, String, String)">
+        <cov.data calls="5" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(String, String, int, String)">
+        <cov.data calls="5" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="434" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="52" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="set(String, String, int, String, String, String, String, String)">
+        <cov.data calls="973" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="set(String, String, int, String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="19" />
+      </method>
+      <method name="openStream()">
+        <cov.data calls="8" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="writeObject(java.io.ObjectOutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="38" />
+      </method>
+      <method name="getPath()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="openConnection()">
+        <cov.data calls="47" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getProtocol()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getFile()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getHost()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isValidProtocol(String)">
+        <cov.data calls="8" hit_lines="0" total_lines="14" />
+      </method>
+      <method name="checkSpecifyHandler(SecurityManager)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getQuery()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getUserInfo()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getAuthority()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getPort()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getRef()">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="sameFile(java.net.URL)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toExternalForm()">
+        <cov.data calls="52" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getContent()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getContent(Class[])">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setURLStreamHandlerFactory(java.net.URLStreamHandlerFactory)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="getURLStreamHandler(String)">
+        <cov.data calls="13" hit_lines="0" total_lines="90" />
+      </method>
+    </class>
+    <class name="URLStreamHandlerFactory" source="URLStreamHandlerFactory.java">
+      <cov.data calls="0" hit_methods="0" total_methods="1" hit_lines="0" total_lines="0" />
+      <method name="createURLStreamHandler(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="URLClassLoader" source="URLClassLoader.java">
+      <cov.data calls="2319" hit_methods="8" total_methods="17" hit_lines="0" total_lines="228" />
+      <method name="&lt;init&gt;(java.net.URL[])">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="&lt;init&gt;(java.net.URL[], ClassLoader, java.net.URLStreamHandlerFactory)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="&lt;init&gt;(java.net.URL[], ClassLoader)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="newInstance(java.net.URL[], ClassLoader)">
+        <cov.data calls="0" hit_lines="0" total_lines="12" />
+      </method>
+      <method name="newInstance(java.net.URL[])">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="findClass(String)">
+        <cov.data calls="930" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="defineClass(String, sun.misc.Resource)">
+        <cov.data calls="442" hit_lines="0" total_lines="34" />
+      </method>
+      <method name="findResources(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="findResource(String)">
+        <cov.data calls="44" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="definePackage(String, java.util.jar.Manifest, java.net.URL)">
+        <cov.data calls="65" hit_lines="0" total_lines="44" />
+      </method>
+      <method name="access$000(java.net.URLClassLoader)">
+        <cov.data calls="1" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$100(java.net.URLClassLoader, String, sun.misc.Resource)">
+        <cov.data calls="442" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getPermissions(java.security.CodeSource)">
+        <cov.data calls="18" hit_lines="0" total_lines="51" />
+      </method>
+      <method name="access$300(java.net.URLClassLoader)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="addURL(java.net.URL)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getURLs()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isSealed(String, java.util.jar.Manifest)">
+        <cov.data calls="377" hit_lines="0" total_lines="12" />
+      </method>
+    </class>
+    <class name="URLClassLoader$1" source="URLClassLoader.java">
+      <cov.data calls="1860" hit_methods="2" total_methods="2" hit_lines="6" total_lines="7" />
+      <method name="&lt;init&gt;(java.net.URLClassLoader, String)">
+        <cov.data calls="930" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="run()">
+        <cov.data calls="930" hit_lines="5" total_lines="6" />
+      </method>
+    </class>
+    <class name="URLConnection" source="URLConnection.java">
+      <cov.data calls="48" hit_methods="2" total_methods="51" hit_lines="14" total_lines="240" />
+      <method name="&lt;init&gt;(java.net.URL)">
+        <cov.data calls="47" hit_lines="9" total_lines="9" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="5" total_lines="5" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$000()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getURL()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getPermission()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getContent(Class[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getContent()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getInputStream()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="connect()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setRequestProperty(String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getDate()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getContentLength()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getHeaderField(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getHeaderField(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getHeaderFieldKey(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getFileNameMap()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getContentType()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="guessContentTypeFromStream(java.io.InputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="47" />
+      </method>
+      <method name="guessContentTypeFromName(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="setFileNameMap(java.net.FileNameMap)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getContentEncoding()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getExpiration()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getLastModified()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getHeaderFieldInt(String, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getHeaderFieldDate(String, long)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getOutputStream()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setDoInput(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getDoInput()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setDoOutput(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getDoOutput()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setAllowUserInteraction(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getAllowUserInteraction()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setDefaultAllowUserInteraction(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getDefaultAllowUserInteraction()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setUseCaches(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getUseCaches()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setIfModifiedSince(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getIfModifiedSince()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDefaultUseCaches()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setDefaultUseCaches(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getRequestProperty(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="setDefaultRequestProperty(String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDefaultRequestProperty(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setContentHandlerFactory(java.net.ContentHandlerFactory)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="getContentHandler()">
+        <cov.data calls="0" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="stripOffParameters(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="lookupContentHandlerClassFor(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="typeToPackageName(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="getContentHandlerPkgPrefixes()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="checkfpx(java.io.InputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="46" />
+      </method>
+    </class>
+    <class name="ContentHandler" source="ContentHandler.java">
+      <cov.data calls="1" hit_methods="1" total_methods="3" hit_lines="1" total_lines="6" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getContent(java.net.URLConnection, Class[])">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getContent(java.net.URLConnection)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="UnknownContentHandler" source="URLConnection.java">
+      <cov.data calls="1" hit_methods="1" total_methods="2" hit_lines="1" total_lines="2" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getContent(java.net.URLConnection)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="URLClassLoader$2" source="URLClassLoader.java">
+      <cov.data calls="88" hit_methods="2" total_methods="2" hit_lines="2" total_lines="2" />
+      <method name="&lt;init&gt;(java.net.URLClassLoader, String)">
+        <cov.data calls="44" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="run()">
+        <cov.data calls="44" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="JarURLConnection" source="JarURLConnection.java">
+      <cov.data calls="32" hit_methods="4" total_methods="10" hit_lines="13" total_lines="22" />
+      <method name="&lt;init&gt;(java.net.URL)">
+        <cov.data calls="8" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="getCertificates()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getManifest()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getAttributes()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getMainAttributes()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getJarFile()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getJarEntry()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getJarFileURL()">
+        <cov.data calls="8" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getEntryName()">
+        <cov.data calls="8" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="parseSpecs(java.net.URL)">
+        <cov.data calls="8" hit_lines="8" total_lines="9" />
+      </method>
+    </class>
+    <class name="MalformedURLException" source="MalformedURLException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+  </package>
+  <package name="java.util.zip">
+    <cov.data calls="86626" hit_methods="104" total_methods="174" hit_lines="285" total_lines="659" />
+    <class name="ZipFile" source="ZipFile.java">
+      <cov.data calls="37851" hit_methods="21" total_methods="33" hit_lines="47" total_lines="84" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.File, int)">
+        <cov.data calls="30" hit_lines="10" total_lines="12" />
+      </method>
+      <method name="&lt;init&gt;(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="finalize()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getName()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getMethod(long)">
+        <cov.data calls="511" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="access$000(java.util.zip.ZipFile, java.util.zip.Inflater)">
+        <cov.data calls="409" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="access$100(java.util.zip.ZipFile)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="size()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="access$200(java.util.zip.ZipFile)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$300(long, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="access$400(long, long)">
+        <cov.data calls="510" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="access$500(long)">
+        <cov.data calls="511" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="read(long, long, int, byte[], int, int)">
+        <cov.data calls="1754" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="access$600(long)">
+        <cov.data calls="511" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="access$700(long, long, int, byte[], int, int)">
+        <cov.data calls="1754" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="open(String, int)">
+        <cov.data calls="30" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="close()">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="close(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="initIDs()">
+        <cov.data calls="1" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getInputStream(String)">
+        <cov.data calls="511" hit_lines="8" total_lines="12" />
+      </method>
+      <method name="getInputStream(java.util.zip.ZipEntry)">
+        <cov.data calls="511" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="entries()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getEntry(long, String)">
+        <cov.data calls="18566" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getEntry(String)">
+        <cov.data calls="9304" hit_lines="10" total_lines="12" />
+      </method>
+      <method name="getSize(long)">
+        <cov.data calls="511" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getTotal(long)">
+        <cov.data calls="30" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="freeEntry(long, long)">
+        <cov.data calls="1063" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getInflater()">
+        <cov.data calls="413" hit_lines="7" total_lines="8" />
+      </method>
+      <method name="releaseInflater(java.util.zip.Inflater)">
+        <cov.data calls="409" hit_lines="3" total_lines="4" />
+      </method>
+      <method name="getNextEntry(long, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getCSize(long)">
+        <cov.data calls="511" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="ZipEntry" source="ZipEntry.java">
+      <cov.data calls="13810" hit_methods="23" total_methods="28" hit_lines="66" total_lines="93" />
+      <method name="hashCode()">
+        <cov.data calls="634" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(java.util.zip.ZipEntry)">
+        <cov.data calls="575" hit_lines="15" total_lines="15" />
+      </method>
+      <method name="&lt;init&gt;(String, long)">
+        <cov.data calls="553" hit_lines="9" total_lines="9" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="317" hit_lines="10" total_lines="12" />
+      </method>
+      <method name="&lt;init&gt;(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getName()">
+        <cov.data calls="667" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getMethod()">
+        <cov.data calls="3181" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="setSize(long)">
+        <cov.data calls="317" hit_lines="3" total_lines="4" />
+      </method>
+      <method name="initIDs()">
+        <cov.data calls="1" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="isDirectory()">
+        <cov.data calls="88" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getSize()">
+        <cov.data calls="1217" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="initFields(long)">
+        <cov.data calls="553" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setTime(long)">
+        <cov.data calls="317" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="getTime()">
+        <cov.data calls="951" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getCompressedSize()">
+        <cov.data calls="911" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="setCompressedSize(long)">
+        <cov.data calls="317" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="setCrc(long)">
+        <cov.data calls="317" hit_lines="3" total_lines="4" />
+      </method>
+      <method name="getCrc()">
+        <cov.data calls="674" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="setMethod(int)">
+        <cov.data calls="317" hit_lines="3" total_lines="4" />
+      </method>
+      <method name="setExtra(byte[])">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getExtra()">
+        <cov.data calls="317" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="setComment(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getComment()">
+        <cov.data calls="317" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="dosToJavaTime(long)">
+        <cov.data calls="951" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="javaToDosTime(long)">
+        <cov.data calls="317" hit_lines="4" total_lines="5" />
+      </method>
+    </class>
+    <class name="ZipFile$ZipFileInputStream" source="ZipFile.java">
+      <cov.data calls="3799" hit_methods="5" total_methods="7" hit_lines="27" total_lines="38" />
+      <method name="&lt;init&gt;(long, long)">
+        <cov.data calls="511" hit_lines="7" total_lines="7" />
+      </method>
+      <method name="read()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="read(byte[], int, int)">
+        <cov.data calls="1755" hit_lines="12" total_lines="13" />
+      </method>
+      <method name="skip(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="available()">
+        <cov.data calls="10" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="close()">
+        <cov.data calls="507" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="cleanup()">
+        <cov.data calls="1016" hit_lines="5" total_lines="5" />
+      </method>
+    </class>
+    <class name="InflaterInputStream" source="InflaterInputStream.java">
+      <cov.data calls="4026" hit_methods="5" total_methods="10" hit_lines="25" total_lines="64" />
+      <method name="&lt;init&gt;(java.io.InputStream, java.util.zip.Inflater, int)">
+        <cov.data calls="413" hit_lines="8" total_lines="10" />
+      </method>
+      <method name="&lt;init&gt;(java.io.InputStream, java.util.zip.Inflater)">
+        <cov.data calls="413" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.InputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="fill()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="read()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="read(byte[], int, int)">
+        <cov.data calls="1591" hit_lines="10" total_lines="14" />
+      </method>
+      <method name="skip(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="available()">
+        <cov.data calls="9" hit_lines="3" total_lines="4" />
+      </method>
+      <method name="close()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="ensureOpen()">
+        <cov.data calls="1600" hit_lines="2" total_lines="3" />
+      </method>
+    </class>
+    <class name="ZipFile$1" source="ZipFile.java">
+      <cov.data calls="2410" hit_methods="4" total_methods="4" hit_lines="14" total_lines="19" />
+      <method name="&lt;init&gt;(java.util.zip.ZipFile, java.io.InputStream, java.util.zip.Inflater)">
+        <cov.data calls="413" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="fill()">
+        <cov.data calls="1579" hit_lines="5" total_lines="9" />
+      </method>
+      <method name="available()">
+        <cov.data calls="9" hit_lines="2" total_lines="3" />
+      </method>
+      <method name="close()">
+        <cov.data calls="409" hit_lines="5" total_lines="5" />
+      </method>
+    </class>
+    <class name="Inflater" source="Inflater.java">
+      <cov.data calls="13508" hit_methods="15" total_methods="28" hit_lines="31" total_lines="60" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(boolean)">
+        <cov.data calls="25" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="finalize()">
+        <cov.data calls="4" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="reset(long)">
+        <cov.data calls="388" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="reset()">
+        <cov.data calls="388" hit_lines="6" total_lines="7" />
+      </method>
+      <method name="init(boolean)">
+        <cov.data calls="25" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="initIDs()">
+        <cov.data calls="1" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="end()">
+        <cov.data calls="4" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="end(long)">
+        <cov.data calls="4" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setInput(byte[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setInput(byte[], int, int)">
+        <cov.data calls="1579" hit_lines="6" total_lines="8" />
+      </method>
+      <method name="finished()">
+        <cov.data calls="1591" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="needsDictionary()">
+        <cov.data calls="1579" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="needsInput()">
+        <cov.data calls="1579" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="inflate(byte[], int, int)">
+        <cov.data calls="3170" hit_lines="3" total_lines="5" />
+      </method>
+      <method name="inflate(byte[])">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setDictionary(long, byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setDictionary(byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="setDictionary(byte[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getRemaining()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getAdler()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getAdler(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getTotalIn()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getTotalIn(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getTotalOut()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getTotalOut(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="inflateBytes(byte[], int, int)">
+        <cov.data calls="3170" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="ZipInputStream" source="ZipInputStream.java">
+      <cov.data calls="0" hit_methods="0" total_methods="15" hit_lines="0" total_lines="165" />
+      <method name="&lt;init&gt;(java.io.InputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="read(byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="28" />
+      </method>
+      <method name="skip(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="15" />
+      </method>
+      <method name="available()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="close()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="ensureOpen()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="readFully(byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="getNextEntry()">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="closeEntry()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="readLOC()">
+        <cov.data calls="0" hit_lines="0" total_lines="28" />
+      </method>
+      <method name="getUTF8String(byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="33" />
+      </method>
+      <method name="createZipEntry(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="readEnd(java.util.zip.ZipEntry)">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="get16(byte[], int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="get32(byte[], int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="DeflaterOutputStream" source="DeflaterOutputStream.java">
+      <cov.data calls="1732" hit_methods="5" total_methods="8" hit_lines="23" total_lines="39" />
+      <method name="&lt;init&gt;(java.io.OutputStream, java.util.zip.Deflater)">
+        <cov.data calls="1" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.OutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.OutputStream, java.util.zip.Deflater, int)">
+        <cov.data calls="1" hit_lines="6" total_lines="8" />
+      </method>
+      <method name="write(byte[], int, int)">
+        <cov.data calls="328" hit_lines="8" total_lines="11" />
+      </method>
+      <method name="write(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="close()">
+        <cov.data calls="1" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="finish()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="deflate()">
+        <cov.data calls="1401" hit_lines="4" total_lines="4" />
+      </method>
+    </class>
+    <class name="CRC32" source="CRC32.java">
+      <cov.data calls="1295" hit_methods="6" total_methods="9" hit_lines="11" total_lines="17" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="3" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="reset()">
+        <cov.data calls="317" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="getValue()">
+        <cov.data calls="318" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="update(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="update(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="update(byte[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="update(byte[], int, int)">
+        <cov.data calls="328" hit_lines="4" total_lines="6" />
+      </method>
+      <method name="updateBytes(int, byte[], int, int)">
+        <cov.data calls="328" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="ZipException" source="ZipException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="Deflater" source="Deflater.java">
+      <cov.data calls="8195" hit_methods="20" total_methods="30" hit_lines="41" total_lines="76" />
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(int, boolean)">
+        <cov.data calls="1" hit_lines="6" total_lines="6" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="finalize()">
+        <cov.data calls="1" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="reset(long)">
+        <cov.data calls="297" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="reset()">
+        <cov.data calls="297" hit_lines="6" total_lines="7" />
+      </method>
+      <method name="init(int, int, boolean)">
+        <cov.data calls="1" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="initIDs()">
+        <cov.data calls="1" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="end()">
+        <cov.data calls="1" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="end(long)">
+        <cov.data calls="1" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="setInput(byte[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setInput(byte[], int, int)">
+        <cov.data calls="328" hit_lines="6" total_lines="8" />
+      </method>
+      <method name="finished()">
+        <cov.data calls="2026" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="needsInput()">
+        <cov.data calls="656" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="setDictionary(byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="setDictionary(byte[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setDictionary(long, byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getAdler()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getAdler(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getTotalIn()">
+        <cov.data calls="297" hit_lines="2" total_lines="3" />
+      </method>
+      <method name="getTotalIn(long)">
+        <cov.data calls="297" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getTotalOut(long)">
+        <cov.data calls="297" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getTotalOut()">
+        <cov.data calls="297" hit_lines="2" total_lines="3" />
+      </method>
+      <method name="setLevel(int)">
+        <cov.data calls="297" hit_lines="3" total_lines="6" />
+      </method>
+      <method name="finish()">
+        <cov.data calls="297" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="deflate(byte[], int, int)">
+        <cov.data calls="1401" hit_lines="3" total_lines="5" />
+      </method>
+      <method name="deflate(byte[])">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setStrategy(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="deflateBytes(byte[], int, int)">
+        <cov.data calls="1401" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+  </package>
+  <package name="java.util.jar">
+    <cov.data calls="76626" hit_methods="55" total_methods="90" hit_lines="260" total_lines="488" />
+    <class name="JarFile" source="JarFile.java">
+      <cov.data calls="20129" hit_methods="10" total_methods="14" hit_lines="57" total_lines="74" />
+      <method name="&lt;init&gt;(String, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.File)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.File, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.io.File, boolean, int)">
+        <cov.data calls="30" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="30" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="getBytes(java.util.zip.ZipEntry)">
+        <cov.data calls="61" hit_lines="5" total_lines="5" />
+      </method>
+      <method name="access$000(java.util.jar.JarFile)">
+        <cov.data calls="442" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getManifest()">
+        <cov.data calls="472" hit_lines="12" total_lines="17" />
+      </method>
+      <method name="getInputStream(java.util.zip.ZipEntry)">
+        <cov.data calls="450" hit_lines="9" total_lines="10" />
+      </method>
+      <method name="getJarEntry(String)">
+        <cov.data calls="9296" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="entries()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getMetaInfEntryNames()">
+        <cov.data calls="23" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getEntry(String)">
+        <cov.data calls="9304" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="initializeVerifier()">
+        <cov.data calls="21" hit_lines="20" total_lines="23" />
+      </method>
+    </class>
+    <class name="JarEntry" source="JarEntry.java">
+      <cov.data calls="597" hit_methods="2" total_methods="5" hit_lines="6" total_lines="10" />
+      <method name="&lt;init&gt;(java.util.zip.ZipEntry)">
+        <cov.data calls="575" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;(java.util.jar.JarEntry)">
+        <cov.data calls="22" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getCertificates()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getAttributes()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="JarFile$JarFileEntry" source="JarFile.java">
+      <cov.data calls="995" hit_methods="2" total_methods="3" hit_lines="5" total_lines="12" />
+      <method name="&lt;init&gt;(java.util.jar.JarFile, java.util.zip.ZipEntry)">
+        <cov.data calls="553" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="getCertificates()">
+        <cov.data calls="442" hit_lines="2" total_lines="5" />
+      </method>
+      <method name="getAttributes()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+    </class>
+    <class name="Manifest" source="Manifest.java">
+      <cov.data calls="4579" hit_methods="8" total_methods="15" hit_lines="44" total_lines="85" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(java.util.jar.Manifest)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="&lt;init&gt;(java.io.InputStream)">
+        <cov.data calls="28" hit_lines="5" total_lines="5" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="3" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="write(java.io.OutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="read(java.io.InputStream)">
+        <cov.data calls="28" hit_lines="29" total_lines="33" />
+      </method>
+      <method name="getAttributes(String)">
+        <cov.data calls="893" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getMainAttributes()">
+        <cov.data calls="473" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getEntries()">
+        <cov.data calls="899" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="make72Safe(StringBuffer)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="parseName(byte[], int)">
+        <cov.data calls="451" hit_lines="2" total_lines="3" />
+      </method>
+      <method name="toLower(int)">
+        <cov.data calls="1804" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="Attributes" source="Attributes.java">
+      <cov.data calls="5785" hit_methods="12" total_methods="24" hit_lines="33" total_lines="82" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(java.util.jar.Attributes)">
+        <cov.data calls="3" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="482" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="31" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="put(Object, Object)">
+        <cov.data calls="1301" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="3" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="get(Object)">
+        <cov.data calls="861" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getValue(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getValue(java.util.jar.Attributes$Name)">
+        <cov.data calls="861" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="values()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="size()">
+        <cov.data calls="454" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="remove(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="write(java.io.DataOutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="keySet()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="entrySet()">
+        <cov.data calls="6" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="isEmpty()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="containsValue(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="containsKey(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="putAll(java.util.Map)">
+        <cov.data calls="3" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="read(java.util.jar.Manifest$FastInputStream, byte[])">
+        <cov.data calls="479" hit_lines="16" total_lines="23" />
+      </method>
+      <method name="writeMain(java.io.DataOutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="20" />
+      </method>
+      <method name="putValue(String, String)">
+        <cov.data calls="1301" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="Manifest$FastInputStream" source="Manifest.java">
+      <cov.data calls="5097" hit_methods="6" total_methods="11" hit_lines="38" total_lines="72" />
+      <method name="&lt;init&gt;(java.io.InputStream, int)">
+        <cov.data calls="28" hit_lines="5" total_lines="5" />
+      </method>
+      <method name="&lt;init&gt;(java.io.InputStream)">
+        <cov.data calls="28" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="readLine(byte[], int, int)">
+        <cov.data calls="2264" hit_lines="23" total_lines="23" />
+      </method>
+      <method name="readLine(byte[])">
+        <cov.data calls="2264" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="fill()">
+        <cov.data calls="62" hit_lines="5" total_lines="5" />
+      </method>
+      <method name="read()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="read(byte[], int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="skip(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="9" />
+      </method>
+      <method name="available()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="close()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="peek()">
+        <cov.data calls="451" hit_lines="2" total_lines="3" />
+      </method>
+    </class>
+    <class name="Attributes$Name" source="Attributes.java">
+      <cov.data calls="39241" hit_methods="8" total_methods="9" hit_lines="36" total_lines="42" />
+      <method name="hashCode()">
+        <cov.data calls="2182" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="1318" hit_lines="6" total_lines="8" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="17" total_lines="17" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="35" hit_lines="2" total_lines="3" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isDigit(char)">
+        <cov.data calls="1699" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="isValid(String)">
+        <cov.data calls="1318" hit_lines="5" total_lines="7" />
+      </method>
+      <method name="isValid(char)">
+        <cov.data calls="16344" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="isAlpha(char)">
+        <cov.data calls="16344" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="JarVerifier" source="JarVerifier.java">
+      <cov.data calls="203" hit_methods="7" total_methods="9" hit_lines="41" total_lines="111" />
+      <method name="&lt;init&gt;(java.util.jar.Manifest, byte[])">
+        <cov.data calls="28" hit_lines="13" total_lines="13" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="beginEntry(java.util.jar.JarEntry, sun.security.util.ManifestEntryVerifier)">
+        <cov.data calls="33" hit_lines="9" total_lines="30" />
+      </method>
+      <method name="update(int, byte[], int, int, sun.security.util.ManifestEntryVerifier)">
+        <cov.data calls="66" hit_lines="5" total_lines="6" />
+      </method>
+      <method name="update(int, sun.security.util.ManifestEntryVerifier)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="doneWithMeta()">
+        <cov.data calls="21" hit_lines="8" total_lines="8" />
+      </method>
+      <method name="nothingToVerify()">
+        <cov.data calls="21" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getCerts(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="processEntry(sun.security.util.ManifestEntryVerifier)">
+        <cov.data calls="33" hit_lines="4" total_lines="45" />
+      </method>
+    </class>
+  </package>
+  <package name="java.security.cert">
+    <cov.data calls="0" hit_methods="0" total_methods="10" hit_lines="0" total_lines="25" />
+    <class name="Certificate" source="Certificate.java">
+      <cov.data calls="0" hit_methods="0" total_methods="10" hit_lines="0" total_lines="25" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getType()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="verify(java.security.PublicKey)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="verify(java.security.PublicKey, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getEncoded()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="getPublicKey()">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="writeReplace()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+  </package>
+  <package name="java.text">
+    <cov.data calls="864" hit_methods="64" total_methods="245" hit_lines="435" total_lines="1620" />
+    <class name="Format" source="Format.java">
+      <cov.data calls="16" hit_methods="1" total_methods="6" hit_lines="1" total_lines="10" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="16" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="format(Object, StringBuffer, java.text.FieldPosition)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="format(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="parseObject(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="parseObject(String, java.text.ParsePosition)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+    </class>
+    <class name="NumberFormat" source="NumberFormat.java">
+      <cov.data calls="295" hit_methods="16" total_methods="39" hit_lines="42" total_lines="95" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="12" hit_lines="12" total_lines="12" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getInstance()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getInstance(java.util.Locale, int)">
+        <cov.data calls="4" hit_lines="6" total_lines="6" />
+      </method>
+      <method name="getInstance(java.util.Locale)">
+        <cov.data calls="4" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="writeObject(java.io.ObjectOutputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="getAvailableLocales()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="format(long)">
+        <cov.data calls="16" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="format(double, StringBuffer, java.text.FieldPosition)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="format(long, StringBuffer, java.text.FieldPosition)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="format(Object, StringBuffer, java.text.FieldPosition)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="format(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="parse(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="parse(String, java.text.ParsePosition)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="parseObject(String, java.text.ParsePosition)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isParseIntegerOnly()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setParseIntegerOnly(boolean)">
+        <cov.data calls="4" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="getNumberInstance()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getNumberInstance(java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getCurrencyInstance(java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getCurrencyInstance()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getPercentInstance()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getPercentInstance(java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getScientificInstance()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getScientificInstance(java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isGroupingUsed()">
+        <cov.data calls="98" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="setGroupingUsed(boolean)">
+        <cov.data calls="16" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="getMaximumIntegerDigits()">
+        <cov.data calls="16" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="setMaximumIntegerDigits(int)">
+        <cov.data calls="20" hit_lines="3" total_lines="4" />
+      </method>
+      <method name="getMinimumIntegerDigits()">
+        <cov.data calls="16" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="setMinimumIntegerDigits(int)">
+        <cov.data calls="20" hit_lines="3" total_lines="4" />
+      </method>
+      <method name="getMaximumFractionDigits()">
+        <cov.data calls="16" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="setMaximumFractionDigits(int)">
+        <cov.data calls="12" hit_lines="3" total_lines="4" />
+      </method>
+      <method name="getMinimumFractionDigits()">
+        <cov.data calls="24" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="setMinimumFractionDigits(int)">
+        <cov.data calls="16" hit_lines="3" total_lines="4" />
+      </method>
+    </class>
+    <class name="DecimalFormat" source="DecimalFormat.java">
+      <cov.data calls="201" hit_methods="13" total_methods="43" hit_lines="196" total_lines="624" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(String, java.text.DecimalFormatSymbols)">
+        <cov.data calls="4" hit_lines="15" total_lines="15" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="8" hit_lines="15" total_lines="15" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="22" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="format(double, StringBuffer, java.text.FieldPosition)">
+        <cov.data calls="0" hit_lines="0" total_lines="25" />
+      </method>
+      <method name="format(long, StringBuffer, java.text.FieldPosition)">
+        <cov.data calls="16" hit_lines="9" total_lines="19" />
+      </method>
+      <method name="parse(String, java.text.ParsePosition)">
+        <cov.data calls="0" hit_lines="0" total_lines="31" />
+      </method>
+      <method name="setMaximumIntegerDigits(int)">
+        <cov.data calls="20" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="setMinimumIntegerDigits(int)">
+        <cov.data calls="20" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="setMaximumFractionDigits(int)">
+        <cov.data calls="12" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="setMinimumFractionDigits(int)">
+        <cov.data calls="16" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="subformat(StringBuffer, java.text.FieldPosition, boolean, boolean)">
+        <cov.data calls="16" hit_lines="31" total_lines="91" />
+      </method>
+      <method name="subparse(String, java.text.ParsePosition, java.text.DigitList, boolean, boolean[])">
+        <cov.data calls="0" hit_lines="0" total_lines="89" />
+      </method>
+      <method name="getDecimalFormatSymbols()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setDecimalFormatSymbols(java.text.DecimalFormatSymbols)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getPositivePrefix()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setPositivePrefix(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getNegativePrefix()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setNegativePrefix(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getPositiveSuffix()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setPositiveSuffix(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getNegativeSuffix()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setNegativeSuffix(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getMultiplier()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setMultiplier(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getGroupingSize()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setGroupingSize(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="isDecimalSeparatorAlwaysShown()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setDecimalSeparatorAlwaysShown(boolean)">
+        <cov.data calls="16" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="toPattern(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="28" />
+      </method>
+      <method name="toPattern()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toLocalizedPattern()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="expandAffixes()">
+        <cov.data calls="12" hit_lines="10" total_lines="10" />
+      </method>
+      <method name="expandAffix(String, StringBuffer)">
+        <cov.data calls="48" hit_lines="9" total_lines="18" />
+      </method>
+      <method name="appendAffix(StringBuffer, String, String, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="25" />
+      </method>
+      <method name="appendAffix(StringBuffer, String, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="applyPattern(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="applyPattern(String, boolean)">
+        <cov.data calls="12" hit_lines="96" total_lines="162" />
+      </method>
+      <method name="applyLocalizedPattern(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="DateFormat" source="DateFormat.java">
+      <cov.data calls="8" hit_methods="2" total_methods="30" hit_lines="2" total_lines="55" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="4" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="get(int, int, int, java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="getInstance()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getAvailableLocales()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="format(java.util.Date)">
+        <cov.data calls="4" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="format(java.util.Date, StringBuffer, java.text.FieldPosition)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="format(Object, StringBuffer, java.text.FieldPosition)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="parse(String, java.text.ParsePosition)">
+        <cov.data calls="0" hit_lines="0" total_lines="0" />
+      </method>
+      <method name="parse(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="parseObject(String, java.text.ParsePosition)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getTimeInstance(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getTimeInstance()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getTimeInstance(int, java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDateInstance(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDateInstance()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDateInstance(int, java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDateTimeInstance(int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDateTimeInstance()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDateTimeInstance(int, int, java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setCalendar(java.util.Calendar)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getCalendar()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setNumberFormat(java.text.NumberFormat)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getNumberFormat()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setTimeZone(java.util.TimeZone)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getTimeZone()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setLenient(boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="isLenient()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="SimpleDateFormat" source="SimpleDateFormat.java">
+      <cov.data calls="41" hit_methods="9" total_methods="30" hit_lines="66" total_lines="423" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(String, java.util.Locale)">
+        <cov.data calls="4" hit_lines="6" total_lines="6" />
+      </method>
+      <method name="&lt;init&gt;(String, java.text.DateFormatSymbols)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="&lt;init&gt;(int, int, java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="3" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="format(java.util.Date, StringBuffer, java.text.FieldPosition)">
+        <cov.data calls="4" hit_lines="18" total_lines="22" />
+      </method>
+      <method name="initialize(java.util.Locale)">
+        <cov.data calls="4" hit_lines="9" total_lines="9" />
+      </method>
+      <method name="parse(String, java.text.ParsePosition)">
+        <cov.data calls="0" hit_lines="0" total_lines="97" />
+      </method>
+      <method name="toPattern()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="toLocalizedPattern()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="applyPattern(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="applyLocalizedPattern(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="initializeDefaultCentury()">
+        <cov.data calls="4" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="parseAmbiguousDatesAsAfter(java.util.Date)">
+        <cov.data calls="4" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="set2DigitYearStart(java.util.Date)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="get2DigitYearStart()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="subFormat(char, int, int, java.text.FieldPosition)">
+        <cov.data calls="9" hit_lines="17" total_lines="60" />
+      </method>
+      <method name="zeroPaddingNumber(long, int, int)">
+        <cov.data calls="8" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="matchString(String, int, int, String[])">
+        <cov.data calls="0" hit_lines="0" total_lines="13" />
+      </method>
+      <method name="matchZoneString(String, int, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="subParseZoneString(String, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="21" />
+      </method>
+      <method name="subParse(String, int, char, int, boolean, boolean[])">
+        <cov.data calls="0" hit_lines="0" total_lines="106" />
+      </method>
+      <method name="translatePattern(String, String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="18" />
+      </method>
+      <method name="getDateFormatSymbols()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setDateFormatSymbols(java.text.DateFormatSymbols)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="ParseException" source="ParseException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="2" hit_lines="0" total_lines="4" />
+      <method name="&lt;init&gt;(String, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getErrorOffset()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="DateFormatSymbols" source="DateFormatSymbols.java">
+      <cov.data calls="21" hit_methods="7" total_methods="31" hit_lines="60" total_lines="138" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="&lt;init&gt;(java.util.Locale)">
+        <cov.data calls="4" hit_lines="11" total_lines="11" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="equals(String[], String[])">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="getZoneIndex(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getEras()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setEras(String[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getMonths()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setMonths(String[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getShortMonths()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setShortMonths(String[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getWeekdays()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setWeekdays(String[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getShortWeekdays()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setShortWeekdays(String[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getAmPmStrings()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setAmPmStrings(String[])">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getZoneStrings()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="setZoneStrings(String[][])">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getLocalPatternChars()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setLocalPatternChars(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="getLocaleElements(java.util.Locale)">
+        <cov.data calls="2" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getZoneData(java.util.Locale)">
+        <cov.data calls="2" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="cacheLookup(java.util.Locale)">
+        <cov.data calls="4" hit_lines="12" total_lines="16" />
+      </method>
+      <method name="loadZoneStrings(java.util.Locale, java.util.ResourceBundle)">
+        <cov.data calls="4" hit_lines="13" total_lines="13" />
+      </method>
+      <method name="initializeData(java.util.Locale)">
+        <cov.data calls="4" hit_lines="20" total_lines="20" />
+      </method>
+      <method name="duplicate(String[])">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="copyMembers(java.text.DateFormatSymbols, java.text.DateFormatSymbols)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+    </class>
+    <class name="DecimalFormatSymbols" source="DecimalFormatSymbols.java">
+      <cov.data calls="89" hit_methods="8" total_methods="36" hit_lines="35" total_lines="94" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="&lt;init&gt;(java.util.Locale)">
+        <cov.data calls="12" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="clone()">
+        <cov.data calls="4" hit_lines="1" total_lines="2" />
+      </method>
+      <method name="readObject(java.io.ObjectInputStream)">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="initialize(java.util.Locale)">
+        <cov.data calls="12" hit_lines="25" total_lines="26" />
+      </method>
+      <method name="getNaN()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getInfinity()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getZeroDigit()">
+        <cov.data calls="16" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getGroupingSeparator()">
+        <cov.data calls="16" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getMonetaryDecimalSeparator()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getDecimalSeparator()">
+        <cov.data calls="16" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getExponentialSymbol()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getInternationalCurrencySymbol()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getCurrencySymbol()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getPercent()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getPerMill()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getMinusSign()">
+        <cov.data calls="12" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getDigit()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getPatternSeparator()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setPercent(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setZeroDigit(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setGroupingSeparator(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setDecimalSeparator(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setPerMill(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setDigit(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setPatternSeparator(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setInfinity(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setNaN(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setMinusSign(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setCurrencySymbol(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setInternationalCurrencySymbol(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setMonetaryDecimalSeparator(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setExponentialSymbol(char)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+    <class name="DigitList" source="DigitList.java">
+      <cov.data calls="45" hit_methods="4" total_methods="19" hit_lines="22" total_lines="157" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="4" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="12" hit_lines="4" total_lines="4" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="6" total_lines="6" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="append(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="set(long, int)">
+        <cov.data calls="16" hit_lines="11" total_lines="15" />
+      </method>
+      <method name="set(double, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="set(double, int, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="37" />
+      </method>
+      <method name="set(long)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="clear()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getLong()">
+        <cov.data calls="0" hit_lines="0" total_lines="6" />
+      </method>
+      <method name="round(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="16" />
+      </method>
+      <method name="isZero()">
+        <cov.data calls="16" hit_lines="1" total_lines="2" />
+      </method>
+      <method name="fitsIntoLong(boolean, boolean)">
+        <cov.data calls="0" hit_lines="0" total_lines="11" />
+      </method>
+      <method name="getDouble()">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="shouldRoundUp(int)">
+        <cov.data calls="0" hit_lines="0" total_lines="10" />
+      </method>
+      <method name="isLongMIN_VALUE()">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="log10(double)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+    </class>
+    <class name="FieldPosition" source="FieldPosition.java">
+      <cov.data calls="148" hit_methods="4" total_methods="9" hit_lines="11" total_lines="20" />
+      <method name="hashCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="20" hit_lines="6" total_lines="6" />
+      </method>
+      <method name="equals(Object)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="toString()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getField()">
+        <cov.data calls="64" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="setBeginIndex(int)">
+        <cov.data calls="32" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="setEndIndex(int)">
+        <cov.data calls="32" hit_lines="2" total_lines="2" />
+      </method>
+      <method name="getEndIndex()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getBeginIndex()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+  </package>
+  <package name="java.sql">
+    <cov.data calls="0" hit_methods="0" total_methods="14" hit_lines="0" total_lines="54" />
+    <class name="SQLException" source="SQLException.java">
+      <cov.data calls="0" hit_methods="0" total_methods="8" hit_lines="0" total_lines="38" />
+      <method name="&lt;init&gt;(String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="7" />
+      </method>
+      <method name="&lt;init&gt;(String, String, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="getNextException()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="setNextException(java.sql.SQLException)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+      <method name="getSQLState()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getErrorCode()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+    </class>
+    <class name="SQLWarning" source="SQLWarning.java">
+      <cov.data calls="0" hit_methods="0" total_methods="6" hit_lines="0" total_lines="16" />
+      <method name="&lt;init&gt;(String, String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(String)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;()">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="&lt;init&gt;(String, String, int)">
+        <cov.data calls="0" hit_lines="0" total_lines="3" />
+      </method>
+      <method name="getNextWarning()">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+      <method name="setNextWarning(java.sql.SQLWarning)">
+        <cov.data calls="0" hit_lines="0" total_lines="2" />
+      </method>
+    </class>
+  </package>
+  <package name="java.text.resources">
+    <cov.data calls="2649" hit_methods="22" total_methods="22" hit_lines="104" total_lines="106" />
+    <class name="LocaleElements" source="LocaleElements.java">
+      <cov.data calls="2" hit_methods="2" total_methods="2" hit_lines="2" total_lines="2" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getContents()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="LocaleElements_fr" source="LocaleElements_fr.java">
+      <cov.data calls="2" hit_methods="2" total_methods="2" hit_lines="2" total_lines="2" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getContents()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="LocaleElements_fr_FR" source="LocaleElements_fr_FR.java">
+      <cov.data calls="2" hit_methods="2" total_methods="2" hit_lines="2" total_lines="2" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getContents()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="DateFormatZoneData" source="DateFormatZoneData.java">
+      <cov.data calls="1319" hit_methods="5" total_methods="5" hit_lines="77" total_lines="79" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="3" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="getKeys()">
+        <cov.data calls="4" hit_lines="9" total_lines="9" />
+      </method>
+      <method name="handleGetObject(String)">
+        <cov.data calls="1308" hit_lines="12" total_lines="13" />
+      </method>
+      <method name="getContents()">
+        <cov.data calls="1" hit_lines="43" total_lines="43" />
+      </method>
+      <method name="loadLookup()">
+        <cov.data calls="3" hit_lines="10" total_lines="11" />
+      </method>
+    </class>
+    <class name="DateFormatZoneData_fr" source="DateFormatZoneData_fr.java">
+      <cov.data calls="2" hit_methods="2" total_methods="2" hit_lines="2" total_lines="2" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getContents()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="DateFormatZoneData$1" source="DateFormatZoneData.java">
+      <cov.data calls="1316" hit_methods="3" total_methods="3" hit_lines="13" total_lines="13" />
+      <method name="&lt;init&gt;(java.text.resources.DateFormatZoneData, java.util.Enumeration, java.util.Enumeration)">
+        <cov.data calls="2" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="nextElement()">
+        <cov.data calls="658" hit_lines="9" total_lines="9" />
+      </method>
+      <method name="hasMoreElements()">
+        <cov.data calls="656" hit_lines="3" total_lines="3" />
+      </method>
+    </class>
+    <class name="LocaleElements_en" source="LocaleElements_en.java">
+      <cov.data calls="2" hit_methods="2" total_methods="2" hit_lines="2" total_lines="2" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getContents()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="LocaleElements_en_US" source="LocaleElements_en_US.java">
+      <cov.data calls="2" hit_methods="2" total_methods="2" hit_lines="2" total_lines="2" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getContents()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+    <class name="DateFormatZoneData_en" source="DateFormatZoneData_en.java">
+      <cov.data calls="2" hit_methods="2" total_methods="2" hit_lines="2" total_lines="2" />
+      <method name="&lt;init&gt;()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+      <method name="getContents()">
+        <cov.data calls="1" hit_lines="1" total_lines="1" />
+      </method>
+    </class>
+  </package>
+  <package name="java.awt">
+    <cov.data calls="4" hit_methods="2" total_methods="6" hit_lines="6" total_lines="21" />
+    <class name="ComponentOrientation" source="ComponentOrientation.java">
+      <cov.data calls="4" hit_methods="2" total_methods="6" hit_lines="6" total_lines="21" />
+      <method name="&lt;init&gt;(int)">
+        <cov.data calls="3" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="&lt;clinit&gt;()">
+        <cov.data calls="1" hit_lines="3" total_lines="3" />
+      </method>
+      <method name="isHorizontal()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="isLeftToRight()">
+        <cov.data calls="0" hit_lines="0" total_lines="1" />
+      </method>
+      <method name="getOrientation(java.util.ResourceBundle)">
+        <cov.data calls="0" hit_lines="0" total_lines="8" />
+      </method>
+      <method name="getOrientation(java.util.Locale)">
+        <cov.data calls="0" hit_lines="0" total_lines="5" />
+      </method>
+    </class>
+  </package>
+</snapshot>
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/sos/sos.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/sos/sos.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/sos/sos.xml	(revision 16139)
@@ -0,0 +1,108 @@
+<?xml version="1.0"?>
+
+<project name="sos-test" basedir="." default="sosget.1">
+
+    <!--
+    ===========================================================================
+     Test SOSGet missing attributes
+    ===========================================================================
+    -->
+    <target name="sosget.1">
+        <sosget/>
+    </target>
+
+    <target name="sosget.2">
+        <sosget sosserverpath="192.168.0.1:8888"/>
+    </target>
+
+    <target name="sosget.3">
+        <sosget sosserverpath="192.168.0.1:8888"
+            username="ant"/>
+    </target>
+
+    <target name="sosget.4">
+        <sosget sosserverpath="192.168.0.1:8888"
+            username="ant"
+            vssserverpath="\\server\vss\srcsafe.ini"/>
+    </target>
+
+    <!--
+    ===========================================================================
+     Test SOSCheckin missing attributes
+    ===========================================================================
+    -->
+    <target name="soscheckin.1">
+        <soscheckin/>
+    </target>
+
+    <target name="soscheckin.2">
+        <soscheckin sosserverpath="192.168.0.1:8888"/>
+    </target>
+
+    <target name="soscheckin.3">
+        <soscheckin sosserverpath="192.168.0.1:8888"
+            username="ant"/>
+    </target>
+
+    <target name="soscheckin.4">
+        <soscheckin sosserverpath="192.168.0.1:8888"
+            username="ant"
+            vssserverpath="\\server\vss\srcsafe.ini"/>
+    </target>
+
+    <!--
+    ===========================================================================
+     Test SOSCheckout missing attributes
+    ===========================================================================
+    -->
+    <target name="soscheckout.1">
+        <soscheckout/>
+    </target>
+
+    <target name="soscheckout.2">
+        <soscheckout sosserverpath="192.168.0.1:8888"/>
+    </target>
+
+    <target name="soscheckout.3">
+        <soscheckout sosserverpath="192.168.0.1:8888"
+            username="ant"/>
+    </target>
+
+    <target name="soscheckout.4">
+        <soscheckout sosserverpath="192.168.0.1:8888"
+            username="ant"
+            vssserverpath="\\server\vss\srcsafe.ini"/>
+    </target>
+
+    <!--
+    ===========================================================================
+     Test SOSLabel missing attributes
+    ===========================================================================
+    -->
+    <target name="soslabel.1">
+        <soslabel/>
+    </target>
+
+    <target name="soslabel.2">
+        <soslabel sosserverpath="192.168.0.1:8888"/>
+    </target>
+
+    <target name="soslabel.3">
+        <soslabel sosserverpath="192.168.0.1:8888"
+            username="ant"/>
+    </target>
+
+    <target name="soslabel.4">
+        <soslabel sosserverpath="192.168.0.1:8888"
+            username="ant"
+            vssserverpath="\\server\vss\srcsafe.ini"/>
+    </target>
+
+    <target name="soslabel.5">
+        <soslabel sosserverpath="192.168.0.1:8888"
+            username="ant"
+            vssserverpath="\\server\vss\srcsafe.ini"
+            projectpath="$/SourceRoot/Project"/>
+    </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/unix/symlink.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/unix/symlink.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/unix/symlink.xml	(revision 16139)
@@ -0,0 +1,324 @@
+<?xml version="1.0"?>
+
+<!--
+
+/*
+ * Since the initial version of this file was deveolped on the clock on
+ * an NSF grant I should say the following boilerplate:
+ *
+ * This material is based upon work supported by the National Science
+ * Foundaton under Grant No. EIA-0196404. Any opinions, findings, and
+ * conclusions or recommendations expressed in this material are those
+ * of the author and do not necessarily reflect the views of the
+ * National Science Foundation.
+ */
+
+-->
+
+<project name="symlink-test" basedir="." default="all">
+
+  <!-- 
+       Since the symlink task and some of these targets rely on
+       calls to exec, it may be possible for the JVM to outrun the 
+       execution of the command line  system calls, so this value is
+       the number of seconds we give the operating system to
+       catch up before executing a task that depends on the 
+       completion of previous tasks. This delay is also added to
+       the end of each target so junit doesn't go testing things
+       before they have finnished (hopefully). Tweak if needed.
+  -->
+
+  <property name="delay" value="3"/>
+ 
+  <property name="tdir" value="${basedir}/test-working"/>
+
+  <target name="setup">
+      <delete dir="${tdir}"/>
+      <mkdir dir="${tdir}"/>
+  </target>
+
+  <!-- test for action = single -->
+  <!-- 
+    Creates:
+         File: ${tdir}/symlink.test
+         Link: ${tdir}/singletest
+  -->
+  <target name="test-single">
+    <touch file="${tdir}/symlink.test"/>
+    <symlink resource="${tdir}/symlink.test" 
+             link="${tdir}/singletest" 
+             failonerror="yes"/>
+    <sleep seconds="${delay}"/> <!-- make sure OS has time to catch up -->
+    <available file="${tdir}/symlink.test" 
+               property="test.single.file.created"/>
+    <available file="${tdir}/singletest" 
+               property="test.single.link.created"/>
+  </target>
+
+
+
+  <!-- test for action = delete  (no calls to command line so no sleep) -->
+
+  <!-- 
+    Creates:
+         (none)
+    Deletes:
+         Link: ${tdir}/singletest
+  -->
+
+  <target name="test-delete">
+    <touch file="${tdir}/symlink.test"/>
+    <symlink resource="${tdir}/symlink.test" 
+             link="${tdir}/singletest" 
+             failonerror="yes"/>
+    <sleep seconds="${delay}"/> <!-- make sure OS has time to catch up -->
+
+    <symlink action="delete" link="${tdir}/singletest" failonerror="yes"/>
+    <symlink action="delete" link="${tdir}/symlink.test" failonerror="no"/>
+    <sleep seconds="${delay}"/> <!-- make sure OS has time to catch up -->
+
+    <available file="${tdir}/symlink.test" 
+               property="test.delete.file.still.there"/>
+    <available file="${tdir}/singletest" 
+               property="test.delete.link.still.there"
+               value="ERROR: link deletion failed"/>
+    
+  </target>
+
+
+
+  <!-- test for action = record -->
+
+  <!-- 
+    Creates:
+         Dir:  ${tdir}/symtest1
+         Dir:  ${tdir}/symtest1/symtest2
+         Dir:  ${tdir}/symtest1/symtest3
+         File: ${tdir}/symtest1/file1
+         File: ${tdir}/symtest1/symtest2/file2
+         File: ${tdir}/symtest1/symtest3/fileA
+         File: ${tdir}/symtest1/symtest3/fileB
+         File: ${tdir}/symtest1/symtest3/fileC
+         Link: ${tdir}/symtest1/link1==>${tdir}/symtest1/file1
+         Link: ${tdir}/symtest1/link2==>${tdir}/symtest1/symtest2/file2
+         Link: ${tdir}/symtest1/symtest2/link3==>
+                                           ${tdir}/symtest1/symtest2/file2
+         Link: ${tdir}/symtest1/dirlink==>${tdir}/symtest1/symtest3
+         Link: ${tdir}/symtest1/dirlink2==>${tdir}/symtest1/symtest3
+         Link: ${tdir}/symtest1/dirlink3==>${tdir}/symtest1/symtest3
+         File: ${tdir}/symtest1/recorded.links
+         File: ${tdir}/symtest1/symtest2/recorded.links
+    Deletes:
+         (none)
+  -->
+
+  <target name="test-record">
+
+    <mkdir dir="${tdir}/symtest1"/>
+    <mkdir dir="${tdir}/symtest1/symtest2"/>
+    <mkdir dir="${tdir}/symtest1/symtest3"/>
+    <touch file="${tdir}/symtest1/file1"/>
+    <touch file="${tdir}/symtest1/symtest2/file2"/>
+
+    <touch file="${tdir}/symtest1/symtest3/fileA"/>
+    <touch file="${tdir}/symtest1/symtest3/fileB"/>
+    <touch file="${tdir}/symtest1/symtest3/fileC"/>
+
+    <symlink resource="${tdir}/symtest1/file1" 
+             link="${tdir}/symtest1/link1" 
+             failonerror="no" />
+    <symlink resource="${tdir}/symtest1/symtest2/file2" 
+             link="${tdir}/symtest1/link2" 
+             failonerror="no" />
+    <symlink resource="${tdir}/symtest1/symtest2/file2" 
+             link="${tdir}/symtest1/symtest2/link3" 
+             failonerror="no" />
+    <symlink resource="${tdir}/symtest1/symtest3"
+             link="${tdir}/symtest1/dirlink"
+             failonerror="no" />
+    <symlink resource="${tdir}/symtest1/symtest3" 
+             link="${tdir}/symtest1/dirlink2"
+             failonerror="no"/>
+    <symlink resource="${tdir}/symtest1/symtest3" 
+             link="${tdir}/symtest1/dirlink3"
+             failonerror="no"/>
+
+    <sleep seconds="${delay}"/> <!-- make sure OS has time to catch up -->
+
+    <symlink action="record" linkfilename="recorded.links">
+       <fileset dir="${tdir}/symtest1" includes="**/**"/>
+    </symlink>
+
+    <sleep seconds="${delay}"/> <!-- make sure OS has time to catch up -->
+
+    <!-- Test to see if the directories were created -->
+
+    <available file="${tdir}/symtest1"
+               type="dir"
+               property="test.record.dir1.created"/>
+
+    <available file="${tdir}/symtest1/symtest2"
+               type="dir"
+               property="test.record.dir2.created"/>
+
+    <available file="${tdir}/symtest1/symtest3"
+               type="dir"
+               property="test.record.dir3.created"/>
+
+    <!-- Test to see if the Files were created -->
+
+    <available file="${tdir}/symtest1/file1"
+               property="test.record.file1.created"/>
+
+    <available file="${tdir}/symtest1/symtest2/file2"
+               property="test.record.file2.created"/>
+
+    <available file="${tdir}/symtest1/symtest3/fileA"
+               property="test.record.fileA.created"/>
+
+    <available file="${tdir}/symtest1/symtest3/fileB"
+               property="test.record.fileB.created"/>
+
+    <available file="${tdir}/symtest1/symtest3/fileC"
+               property="test.record.fileC.created"/>
+
+    <!-- Test to see if the links were created -->
+
+    <available file="${tdir}/symtest1/link1"
+               property="test.record.link1.created"/>
+
+    <available file="${tdir}/symtest1/link2"
+               property="test.record.link2.created"/>
+
+    <available file="${tdir}/symtest1/symtest2/link3"
+               property="test.record.link3.created"/>
+
+    <available file="${tdir}/symtest1/dirlink"
+               property="test.record.dirlink.created"/>
+
+    <!-- this is redundant for this test, but used in the recreate test -->
+
+    <available file="${tdir}/symtest1/dirlink2" 
+               property="test.record.dirlink2.created"/>
+
+    <!-- Test to see if the linkfiles were created -->
+
+    <available file="${tdir}/symtest1/recorded.links"
+               property="test.record.dir1.recorded"/>
+
+    <available file="${tdir}/symtest1/symtest2/recorded.links"
+               property="test.record.dir2.recorded"/>
+
+    <!-- THIS should not be set -->
+
+    <available file="${tdir}/symtest1/symtest3/recorded.links"
+               property="test.record.dir3.recorded"
+               value="ERROR: symtest3/recorded.links should not exist"/>
+
+
+  </target>
+
+  <!-- test for action = recreate -->
+
+  <!-- 
+    Deletes:
+         Link: ${tdir}/symtest1/link1==>${tdir}/symtest1/file1
+         Link: ${tdir}/symtest1/link2==>${tdir}/symtest1/symtest2/file2
+         Link: ${tdir}/symtest1/symtest2/link3==>
+                                           ${tdir}/symtest1/symtest2/file2
+         Link: ${tdir}/symtest1/dirlink==>${tdir}/symtest1/symtest3
+         Link: ${tdir}/symtest1/dirlink3==>${tdir}/symtest1/symtest3
+
+    Creates
+         Link: ${tdir}/symtest1/dirlink3==>${tdir}/symtest1/symtest2
+
+    Recreates:
+         Link: ${tdir}/symtest1/link1==>${tdir}/symtest1/file1
+         Link: ${tdir}/symtest1/link2==>${tdir}/symtest1/symtest2/file2
+         Link: ${tdir}/symtest1/symtest2/link3==>
+                                           ${tdir}/symtest1/symtest2/file2
+         Link: ${tdir}/symtest1/dirlink==>${tdir}/symtest1/symtest3
+
+    Should Change:
+         Link: ${tdir}/symtest1/dirlink3==>${tdir}/symtest1/symtest2
+               to
+               ${tdir}/symtest1/dirlink3==>${tdir}/symtest1/symtest3
+
+    Should Not Create (bug 25181):
+         Link: ${tdir}/symtest1/symtest3/dirlink2==>${tdir}/symtest1/symtest3
+  -->
+
+  <target name="test-recreate" depends="test-record">
+
+    <symlink action="delete" link="${tdir}/symtest1/link1"/>
+    <symlink action="delete" link="${tdir}/symtest1/link2"/>
+    <symlink action="delete" link="${tdir}/symtest1/symtest2/link3"/>
+    <symlink action="delete" link="${tdir}/symtest1/dirlink"/>
+    <!-- dirlink2 intentionally not deleted to test bug 25181 -->
+    <symlink action="delete" link="${tdir}/symtest1/dirlink3"/>
+
+    <sleep seconds="${delay}"/>  <!-- make sure OS has time to catch up -->
+
+    <symlink resource="${tdir}/symtest1/symtest2" 
+             link="${tdir}/symtest1/dirlink3"
+             failonerror="no"/>
+
+    <sleep seconds="${delay}"/>  <!-- make sure OS has time to catch up -->
+
+    <available file="${tdir}/symtest1/link1"
+               property="test.recreate.link1.not.removed"
+               value="ERROR: rm -f symtest1/link1 failed"/>
+
+    <available file="${tdir}/symtest1/link2"
+               property="test.recreate.link2.not.removed"
+               value="ERROR: rm -f symtest1/link2 failed"/>
+
+    <available file="${tdir}/symtest1/symtest2/link3"
+               property="test.recreate.link3.not.removed"
+               value="ERROR: rm -f symtest1/symtest2/link3 failed"/>
+
+     <available file="${tdir}/symtest1/zdirlink"
+               property="test.recreate.zdirlink.not.removed"
+               value="ERROR: rm -f symtest1/zdirlink failed"/>
+
+    <sleep seconds="${delay}"/>  <!-- make sure OS has time to do the execs -->
+
+    <symlink action="recreate">
+      <fileset dir="${tdir}/symtest1" includes="**/recorded.links"/>
+    </symlink>
+
+    <sleep seconds="${delay}"/>  <!-- make sure OS has time to catch up -->
+
+    <available file="${tdir}/symtest1/link1"
+               property="test.recreate.link1.recreated"/>
+
+    <available file="${tdir}/symtest1/link2"
+               property="test.recreate.link2.recreated"/>
+
+    <available file="${tdir}/symtest1/symtest2/link3"
+               property="test.recreate.link3.recreated"/>
+
+    <available file="${tdir}/symtest1/dirlink"
+               property="test.recreate.dirlink.recreated"/>
+
+    <!-- this should not get set -->
+    <available file="${tdir}/symtest1/symtest3/symtest3" 
+               property="test.recreate.dirlink2.recreated.twice"
+               value="ERROR: dirlink2 was created a second time (bug 25181)"/>
+
+    <touch file="${tdir}/symtest1/dirlink3/WhereAmI"/>
+
+    <sleep seconds="${delay}"/>  <!-- make sure OS has time to do the execs -->
+
+    <available file="${tdir}/symtest1/symtest3/WhereAmI" 
+               property="test.recreate.dirlink3.was.altered"/>
+  </target>
+
+
+<!-- CALL THIS to clean things up afterwards -->
+
+  <target name="teardown">
+    <delete dir="${tdir}"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/vss/vss.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/vss/vss.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/vss/vss.xml	(revision 16139)
@@ -0,0 +1,52 @@
+<?xml version="1.0"?>
+
+<project name="vss-test" basedir="." default="sosget.1">
+
+    <!--
+    ===========================================================================
+     Test required attributes
+    ===========================================================================
+    -->
+    <target name="vssget.1">
+        <vssget/>
+    </target>
+
+    <target name="vsslabel.1">
+        <vsslabel/>
+    </target>
+
+   <target name="vsslabel.2">
+        <vsslabel vsspath="$/SourceRoot/Project"/>
+    </target>
+
+    <target name="vsshistory.1">
+        <vsshistory/>
+    </target>
+
+    <target name="vsscheckin.1">
+        <vsscheckin/>
+    </target>
+
+    <target name="vsscheckout.1">
+        <vsscheckout/>
+    </target>
+
+    <target name="vsscheckout.2">
+        <vsscheckout
+            vsspath="$/SourceRoot/Project"
+            filetimestamp="blah"/>
+    </target>
+
+    <target name="vssadd.1">
+        <vssadd/>
+    </target>
+
+    <target name="vsscp.1">
+        <vsscp/>
+    </target>
+
+     <target name="vsscreate.1">
+        <vsscreate/>
+    </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/wsdl/StockQuoteService.wsdl
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/wsdl/StockQuoteService.wsdl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/wsdl/StockQuoteService.wsdl	(revision 16139)
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<wsdl:definitions targetNamespace="http://localhost:8080/axis/StockQuoteService.jws" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:impl="http://localhost:8080/axis/StockQuoteService.jws-impl" xmlns:intf="http://localhost:8080/axis/StockQuoteService.jws" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <wsdl:message name="getQuoteResponse">
+    <wsdl:part name="return" type="xsd:float"/>
+  </wsdl:message>
+  <wsdl:message name="getQuoteRequest">
+    <wsdl:part name="symbol" type="xsd:string"/>
+  </wsdl:message>
+  <wsdl:message name="Exception">
+  </wsdl:message>
+  <wsdl:portType name="StockQuoteService">
+    <wsdl:operation name="getQuote" parameterOrder="symbol">
+      <wsdl:input message="intf:getQuoteRequest"/>
+      <wsdl:output message="intf:getQuoteResponse"/>
+      <wsdl:fault message="intf:Exception" name="Exception"/>
+    </wsdl:operation>
+  </wsdl:portType>
+  <wsdl:binding name="StockQuoteServiceSoapBinding" type="intf:StockQuoteService">
+    <wsdlsoap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
+    <wsdl:operation name="getQuote">
+      <wsdlsoap:operation soapAction=""/>
+      <wsdl:input>
+        <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost:8080/axis/StockQuoteService.jws" use="encoded"/>
+      </wsdl:input>
+      <wsdl:output>
+        <wsdlsoap:body encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" namespace="http://localhost:8080/axis/StockQuoteService.jws" use="encoded"/>
+      </wsdl:output>
+    </wsdl:operation>
+  </wsdl:binding>
+  <wsdl:service name="StockQuoteServiceService">
+    <wsdl:port binding="intf:StockQuoteServiceSoapBinding" name="StockQuoteService">
+      <wsdlsoap:address location="http://localhost:8080/axis/StockQuoteService.jws"/>
+    </wsdl:port>
+  </wsdl:service>
+</wsdl:definitions>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xalan-redirect-in.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xalan-redirect-in.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xalan-redirect-in.xsl	(revision 16139)
@@ -0,0 +1,20 @@
+<xsl:stylesheet	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
+	xmlns:lxslt="http://xml.apache.org/xslt"
+	xmlns:redirect="org.apache.xalan.xslt.extensions.Redirect"
+	extension-element-prefixes="redirect">
+<!--
+This is a test to ensure that systemid is set correctly
+for a xsl...the behavior might be dependent on Xalan1
+and Xalan2...this will be a problem to erase the files :(
+Can take as a systemid the base for the xsl document or
+the base or the JVM working dir just like: new File("xalan-redirect-out.tmp")
+-->	
+<xsl:param name="xalan-version" select="'x'"/>
+
+<xsl:template match="/">
+<redirect:write file="./xalan{$xalan-version}-redirect-out.tmp">
+	<test>This should be written to the file</test>
+</redirect:write>
+</xsl:template>
+
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/about.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/about.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/about.xml	(revision 16139)
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE doc PUBLIC
+  "-//stevo//DTD doc 1.0//EN"
+  "http://chemical/brothers"
+  >
+<doc>
+  <section title="About">
+  in the absence of technology, there is only marketing
+  </section>
+</doc>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/catalog
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/catalog	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/catalog	(revision 16139)
@@ -0,0 +1,2 @@
+PUBLIC "-//stevo//DTD doc 1.0//EN" "doc.dtd"
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/doc.dtd
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/doc.dtd	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/doc.dtd	(revision 16139)
@@ -0,0 +1,8 @@
+<!ELEMENT doc (section) >
+<!ELEMENT section (#PCDATA)>
+<!ATTLIST section title CDATA #IMPLIED>
+
+
+
+
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/doc.xsd
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/doc.xsd	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/doc.xsd	(revision 16139)
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xs:schema 
+  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
+  elementFormDefault="qualified">
+  <xs:element name="doc">
+    <xs:complexType>
+      <xs:sequence>
+        <xs:element ref="section"/>
+      </xs:sequence>
+    </xs:complexType>
+  </xs:element>
+  <xs:element name="section">
+    <xs:complexType>
+      <xs:simpleContent>
+        <xs:extension base="xs:string">
+          <xs:attribute name="title" type="xs:string"/>
+        </xs:extension>
+      </xs:simpleContent>
+    </xs:complexType>
+  </xs:element>
+</xs:schema>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/doc.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/doc.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/doc.xsl	(revision 16139)
@@ -0,0 +1,9 @@
+<xsl:stylesheet 
+    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+    xmlns:template="struts template" 
+    version="1.0">
+<xsl:output method="text"/>
+<xsl:template match="/">
+<xsl:value-of select="/doc/section"/>
+</xsl:template> 
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/endpiece-noSchema-invalid.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/endpiece-noSchema-invalid.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/endpiece-noSchema-invalid.xml	(revision 16139)
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- 
+    Invalid test XML file without any schema refeferences 
+-->
+<doc>
+    <section title="endpiece">
+        With a little luck, the network will pick me up. 
+        This is Ripley - last survivor of The Nostromo - signing off.
+    </section>
+    
+    <invalidelement/>
+    
+</doc>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/endpiece-noSchema.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/endpiece-noSchema.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/endpiece-noSchema.xml	(revision 16139)
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- 
+    Test XML file without any schema refeferences 
+-->
+<doc>
+    <section title="endpiece">
+        With a little luck, the network will pick me up. 
+        This is Ripley - last survivor of The Nostromo - signing off.
+    </section>
+    
+</doc>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/endpiece.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/endpiece.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/endpiece.xml	(revision 16139)
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<doc xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
+    xsi:noNamespaceSchemaLocation="doc.xsd"
+    xmlns="http://Massive/Attack+Mezzanine">
+  <section title="endpiece">
+ With a little luck, the network will pick me up. 
+ This is Ripley - last survivor of The Nostromo - signing off.
+  </section>
+</doc>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/endpiece2.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/endpiece2.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/endpiece2.xml	(revision 16139)
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<doc xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
+    xsi:noNamespaceSchemaLocation="doc.xsd"
+    xmlns="http://Massive/Attack+Mezzanine">
+  <section title="endpiece">
+ With a little luck, the network will pick me up. 
+ This is Ripley - last survivor of The Nostromo - signing off.
+ <illegal-element/>
+  </section>
+</doc>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/iso-2022-jp.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/iso-2022-jp.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/iso-2022-jp.xml	(revision 16139)
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="iso-2022-jp"?>
+<!DOCTYPE test [
+  <!ELEMENT test (#PCDATA)>
+]>
+<test>
+ISO-2022-JP $B$N%U%!%$%k!#(B
+</test>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/test.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/test.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/test.xml	(revision 16139)
@@ -0,0 +1,8 @@
+<root>
+  <a>this is the first line</a>
+  <b><c>not indented</c></b>
+  <b>
+    <c>indented</c>
+  </b>
+</root>
+  
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/test.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/test.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/test.xsl	(revision 16139)
@@ -0,0 +1,8 @@
+<xsl:stylesheet	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+<!-- Copy every node and attributes recursively -->
+  <xsl:template match="node()|@*">
+    <xsl:copy>
+      <xsl:apply-templates select="@*|node()"/>
+    </xsl:copy>
+  </xsl:template>  
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/utf-8.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/utf-8.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/utf-8.xml	(revision 16139)
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE test [
+  <!ELEMENT test (#PCDATA)>
+]>
+<test>
+Liberté, égalité, fraternité!
+</test>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/validate.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/validate.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xml/validate.xml	(revision 16139)
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<project name="validate" default="testValidate" basedir=".">
+
+ <target name="testValidate">
+    <xmlvalidate warn="false">
+      <fileset dir="." includes="about.xml"/>  
+       <dtd publicID="-//stevo//DTD doc 1.0//EN"
+         location="doc.dtd"/>
+    </xmlvalidate>
+  </target>
+  
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xmlvalidate.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xmlvalidate.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xmlvalidate.xml	(revision 16139)
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<project name="validate" default="testValidate" basedir=".">
+
+  <target name="testValidate">
+    <xmlvalidate warn="false">
+      <fileset dir="xml" includes="**/about.xml"/>  
+       <dtd publicID="-//stevo//DTD doc 1.0//EN"
+         location="xml/doc.dtd"/>
+    </xmlvalidate>
+  </target>
+  
+  <target name="testDeepValidate">
+    <ant dir="xml" 
+      antfile="validate.xml"
+      target="testValidate"/>
+  </target>
+
+  <target name="xmlcatalog">
+    <xmlvalidate warn="false">
+      <fileset dir="xml" includes="**/about.xml"/>
+      <xmlcatalog classpath="xml">
+        <dtd publicID="-//stevo//DTD doc 1.0//EN"
+             location="doc.dtd"/>
+      </xmlcatalog>
+    </xmlvalidate>
+  </target>
+
+  <target name="xmlcatalogViaRefid">
+    <xmlcatalog classpath="xml" id="cat">
+      <dtd publicID="-//stevo//DTD doc 1.0//EN" location="doc.dtd"/>
+    </xmlcatalog>
+    <xmlvalidate warn="false">
+      <xmlcatalog refid="cat"/>
+      <fileset dir="xml" includes="**/about.xml"/>
+    </xmlvalidate>
+  </target>
+
+  <target name="xmlcatalognested">
+    <xmlvalidate warn="false">
+      <fileset dir="xml" includes="**/about.xml"/>
+      <xmlcatalog classpath="xml">
+        <entity publicID = "bogusImage"
+                 location = "/i/dont/exist.jpg"/>
+        <xmlcatalog>
+          <dtd publicID="-//stevo//DTD doc 1.0//EN"
+               location="doc.dtd"/>
+        </xmlcatalog>
+      </xmlcatalog>
+    </xmlvalidate>
+  </target>
+
+  <!-- The -override tests should pass without resolver.jar -->
+  <target name="xmlcatalogfiles-override">
+    <xmlvalidate warn="false">
+      <fileset dir="xml" includes="**/about.xml"/>
+      <xmlcatalog classpath="xml">
+        <catalogpath>
+          <fileset dir="xml" includes="catalog"/>
+        </catalogpath>
+        <dtd publicID="-//stevo//DTD doc 1.0//EN"
+             location="doc.dtd"/>
+      </xmlcatalog>
+    </xmlvalidate>
+  </target>
+ 
+  <target name="xmlcatalogpath-override">
+    <xmlvalidate warn="false">
+      <fileset dir="xml" includes="**/about.xml"/>
+      <xmlcatalog classpath="xml">
+        <catalogpath>
+          <pathelement location="xml/catalog"/>
+        </catalogpath>
+        <dtd publicID="-//stevo//DTD doc 1.0//EN"
+          location="doc.dtd"/>
+      </xmlcatalog>
+    </xmlvalidate>
+  </target>
+ 
+  <target name="xmlcatalogfiles">
+    <xmlvalidate warn="false">
+      <fileset dir="xml" includes="**/about.xml"/>
+      <xmlcatalog classpath="xml">
+        <catalogpath>
+          <fileset dir="xml" includes="catalog"/>
+        </catalogpath>
+      </xmlcatalog>
+    </xmlvalidate>
+  </target>
+ 
+  <target name="xmlcatalogpath">
+    <xmlvalidate warn="false">
+      <fileset dir="xml" includes="**/about.xml"/>
+      <xmlcatalog classpath="xml">
+        <catalogpath>
+          <pathelement location="xml/catalog"/>
+        </catalogpath>
+      </xmlcatalog>
+    </xmlvalidate>
+  </target>
+
+  <target name="testSchemaGood">
+    <xmlvalidate warn="false" lenient="no" >
+      <fileset dir="xml" includes="endpiece.xml"/>
+
+        <attribute name="http://xml.org/sax/features/validation"
+                   value="false"/>
+        <attribute name="http://apache.org/xml/features/validation/schema"
+                   value="false"/>
+
+    </xmlvalidate>
+  </target>
+
+  <target name="testSchemaBad">
+    <xmlvalidate warn="false">
+      <fileset dir="xml" includes="endpiece2.xml"/>
+      
+        <attribute name="http://xml.org/sax/features/validation"
+                   value="true"/>
+        <attribute name="http://apache.org/xml/features/validation/schema"
+                   value="true"/>
+    </xmlvalidate>
+  </target>
+    
+  <target name="testIso2022Jp">
+    <xmlvalidate warn="false" file="xml/iso-2022-jp.xml"/>
+  </target>
+
+  <target name="testUtf8">
+    <xmlvalidate warn="false" file="xml/utf-8.xml"/>
+  </target>
+
+
+  <!-- Tests property element with XML file that satisfies schema -->
+  <target name="testProperty.validXML">
+
+    <!-- Converts path to URL format -->
+    <pathconvert dirsep="/" property="xsd.file">
+    <path>
+    <pathelement location="xml/doc.xsd"/>
+    </path>
+    </pathconvert>
+
+    <xmlvalidate file="xml/endpiece-noSchema.xml" lenient="false"
+        failonerror="true" warn="true">
+
+        <attribute name="http://apache.org/xml/features/validation/schema"
+            value="true"/>
+
+        <property
+        name="http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation"
+        value="${xsd.file}"/>
+    </xmlvalidate>
+  </target>
+
+
+  <!-- Tests property element with XML file that fails schema validation -->
+  <target name="testProperty.invalidXML">
+
+      <!-- Converts path to URL format -->
+      <pathconvert dirsep="/" property="xsd.file">
+          <path>
+              <pathelement location="xml/doc.xsd"/>
+          </path>
+      </pathconvert>
+
+      <xmlvalidate file="xml/endpiece-noSchema-invalid.xml" lenient="false"
+        failonerror="true" warn="true">
+
+        <attribute name="http://apache.org/xml/features/validation/schema"
+                   value="true"/>
+
+        <property
+            name="http://apache.org/xml/properties/schema/external-noNamespaceSchemaLocation"
+            value="${xsd.file}"/>
+      </xmlvalidate>
+  </target>
+
+</project>
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xslt.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xslt.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xslt.xml	(revision 16139)
@@ -0,0 +1,65 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<project name="xslt" default="xslt" basedir=".">
+  <target name="init">
+      <mkdir dir="xml/out"/>
+  </target>
+
+  <target name="teardown">
+      <delete dir="xml/out"/>
+  </target>
+  
+  <target name="testCatchNoDtd" depends="init">
+    <style basedir="xml" destdir="xml/out"
+           includes="about.xml"
+           extension=".txt"
+           style="xml/doc.xsl">
+     </style>
+  </target>
+  
+  <xmlcatalog id="xdocs.catalog">
+   <dtd publicID="-//stevo//DTD doc 1.0//EN"
+     location="xml/doc.dtd"/>
+  </xmlcatalog>  
+  
+  <target name="testCatalog" depends="init">
+    <style destdir="xml/out"
+           includes="about.xml"
+           extension=".txt"
+           style="xml/doc.xsl">
+       <xmlcatalog refid="xdocs.catalog"/>
+     </style>
+  </target>
+
+  <target name="testOutputProperty" depends="init">
+    <style in="xml/test.xml"
+           out="xml/out/test-out.xml"
+           style="xml/test.xsl">
+      <outputproperty name="method" value="xml"/>
+      <outputproperty name="standalone" value="yes"/>
+      <outputproperty name="encoding" value="iso8859_1"/>
+      <outputproperty name="indent" value="yes"/>
+    </style>
+  </target>
+  
+  
+  <target name="testFactory" depends="init">
+    <style in="xml/test.xml"
+           out="xml/out/test-out.xml"
+           style="xml/test.xsl">
+           <factory name="org.apache.xalan.processor.TransformerFactoryImpl"/>
+    </style>
+  </target>
+
+  <target name="testAttribute" depends="init">
+    <style in="xml/test.xml"
+           out="xml/out/test-out.xml"
+           style="xml/test.xsl">
+           <factory name="org.apache.xalan.processor.TransformerFactoryImpl">
+            <attribute name="http://xml.apache.org/xalan/features/optimize" value="true"/>
+           </factory>
+    </style>
+  </target>
+
+  
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-encoding-in.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-encoding-in.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-encoding-in.xml	(revision 16139)
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<root>
+    <message>éàèïù</message>
+</root>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-encoding-in.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-encoding-in.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-encoding-in.xsl	(revision 16139)
@@ -0,0 +1,10 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+
+<xsl:template match="/">
+    <root>
+    <xsl:for-each select="/root/message">
+        <message><xsl:value-of select="."/></message>
+    </xsl:for-each>
+    </root>
+</xsl:template>
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-in.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-in.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-in.xml	(revision 16139)
@@ -0,0 +1,7 @@
+<?xml version="1.0"?>
+<!DOCTYPE project [
+    <!ENTITY include SYSTEM "xsltliaison-include.xml">
+]>
+<project>
+    &include;
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-in.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-in.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-in.xsl	(revision 16139)
@@ -0,0 +1,3 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+<xsl:include href="xsltliaison-include.xsl"/>
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-include.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-include.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-include.xml	(revision 16139)
@@ -0,0 +1,2 @@
+<!-- to be included by xsltliaison-include.xsl -->
+<task/>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-include.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-include.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/optional/xsltliaison-include.xsl	(revision 16139)
@@ -0,0 +1,3 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+<!-- to be included by xsltliaison-in.xsl -->
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/parallel.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/parallel.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/parallel.xml	(revision 16139)
@@ -0,0 +1,133 @@
+<?xml version="1.0"?>
+
+<project name="parallel-test" basedir="." default="help">
+  <target name="testBasic">
+    <parallel>
+      <sequential>
+        <sleep seconds="1"/>
+        <echo message="${test.delayed}"/>
+      </sequential>
+      <echo message="${test.direct}"/>
+    </parallel>
+  </target>
+
+  <target name="testFail">
+    <parallel>
+      <sequential>
+        <sleep seconds="1"/>
+        <echo message="${test.delayed}"/>
+      </sequential>
+      <fail message="${test.failure}"/>
+    </parallel>
+  </target>
+
+  <target name="testThreadCount">
+    <echo>|1/</echo>
+    <parallel threadCount='1' pollInterval="60">
+      <sequential>
+        <echo message="+"/>
+        <sleep milliseconds="30"/>
+        <echo message="-"/>
+      </sequential>
+      <sequential>
+        <echo message="+"/>
+        <sleep milliseconds="60"/>
+        <echo message="-"/>
+      </sequential>
+      <sequential>
+        <echo message="+"/>
+        <sleep milliseconds="90"/>
+        <echo message="-"/>
+      </sequential>
+    </parallel>
+    <echo>|2/</echo>
+    <parallel threadCount='2' pollInterval="30">
+      <sequential>
+        <echo message="+"/>
+        <sleep milliseconds="30"/>
+        <echo message="-"/>
+      </sequential>
+      <sequential>
+        <echo message="+"/>
+        <sleep milliseconds="60"/>
+        <echo message="-"/>
+      </sequential>
+      <sequential>
+        <echo message="+"/>
+        <sleep milliseconds="90"/>
+        <echo message="-"/>
+      </sequential>
+    </parallel>
+    <echo>|3/</echo>
+    <parallel threadCount='3' pollInterval="30">
+      <sequential>
+        <echo message="+"/>
+        <sleep milliseconds="30"/>
+        <echo message="-"/>
+      </sequential>
+      <sequential>
+        <echo message="+"/>
+        <sleep milliseconds="60"/>
+        <echo message="-"/>
+      </sequential>
+      <sequential>
+        <echo message="+"/>
+        <sleep milliseconds="90"/>
+        <echo message="-"/>
+      </sequential>
+    </parallel>
+    <echo>|4/</echo>
+    <parallel threadCount='4' pollInterval="30">
+      <sequential>
+        <echo message="+"/>
+        <sleep milliseconds="30"/>
+        <echo message="-"/>
+      </sequential>
+      <sequential>
+        <echo message="+"/>
+        <sleep milliseconds="60"/>
+        <echo message="-"/>
+      </sequential>
+      <sequential>
+        <echo message="+"/>
+        <sleep milliseconds="90"/>
+        <echo message="-"/>
+      </sequential>
+    </parallel>
+    <echo>|4/</echo>
+    <parallel threadsPerProcessor='1' threadcount='4' pollInterval="30">
+      <sequential>
+        <echo message="+"/>
+        <sleep milliseconds="30"/>
+        <echo message="-"/>
+      </sequential>
+      <sequential>
+        <echo message="+"/>
+        <sleep milliseconds="60"/>
+        <echo message="-"/>
+      </sequential>
+      <sequential>
+        <echo message="+"/>
+        <sleep milliseconds="90"/>
+        <echo message="-"/>
+      </sequential>
+    </parallel>
+    <echo>|</echo>
+    
+  </target>
+
+  <target name="testDemux">
+    <parallel>
+      <demuxtest/>
+      <demuxtest/>
+      <demuxtest/>
+      <demuxtest/>
+      <demuxtest/>
+    </parallel>
+  </target>
+  
+  <target name="help">
+    <echo>Test build file for the &lt;parallel&gt; task.</echo> 
+    <echo>Use the various targets to run the tests.</echo>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/pathconvert.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/pathconvert.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/pathconvert.xml	(revision 16139)
@@ -0,0 +1,25 @@
+<project name="pathconvert">
+
+    <path id="testpath">
+        <pathelement path="${ant.file}" />
+    </path>
+
+    <target name="testmap">
+        <pathconvert property="result" dirsep="#">
+            <path refid="testpath" />
+            <map from="${basedir}" to="test" />
+        </pathconvert>
+    </target>
+
+    <target name="testmapper">
+        <pathconvert property="result" dirsep="#">
+            <path refid="testpath" />
+            <mapper type="glob" from="${basedir}" to="test" />
+        </pathconvert>
+    </target>
+
+    <target name="testnotargetos">
+        <pathconvert property="result" refid="testpath" />
+    </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/presetdef.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/presetdef.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/presetdef.xml	(revision 16139)
@@ -0,0 +1,103 @@
+<project>
+  <path id="test-classes">
+    <pathelement location="../../../../build/testcases" />
+    <pathelement path="${java.class.path}" />
+  </path>
+  
+  <target name="simple">
+    <presetdef name="my.echo">
+      <echo message="Hello world"/>
+    </presetdef>
+    <my.echo/>
+  </target>
+
+  <target name="text">
+    <presetdef name="my.echo">
+      <echo>Inner Text</echo>
+    </presetdef>
+    <my.echo/>
+  </target>
+
+  <target name="uri">
+    <presetdef name="echo" uri="abc">
+      <echo message="Hello world"/>
+    </presetdef>
+    <x:echo xmlns:x="abc"/>
+  </target>
+
+  <target name="defaulttest">
+    <taskdef name="defaulttest"
+             classname="org.apache.tools.ant.taskdefs.PreSetDefTest$DefaultTest"
+             classpathref="test-classes"/>
+    <presetdef name="d">
+      <defaulttest attribute="true"/>
+    </presetdef>
+    <d attribute="false"/>
+  </target>
+
+  <target name="doubledefault">
+    <taskdef name="defaulttest"
+             classname="org.apache.tools.ant.taskdefs.PreSetDefTest$DefaultTest"
+             classpathref="test-classes"/>
+    <presetdef name="d">
+      <defaulttest attribute="true"/>
+    </presetdef>
+    <presetdef name="dd">
+      <d attribute="false"/>
+    </presetdef>
+    <dd/>
+    <dd attribute="true"/>
+  </target>
+
+  <target name="antTypeTest">
+    <taskdef name="anttypetest"
+             classname="org.apache.tools.ant.taskdefs.PreSetDefTest$AntTypeTest"
+             classpathref="test-classes"/>
+    <presetdef name="java.fileset">
+      <fileset>
+        <include name="**/*.java"/>
+      </fileset>
+    </presetdef>
+    
+    <anttypetest>
+      <fileset ant-type="java.fileset" dir="."/>
+      <configured ant-type="java.fileset" dir="."/>
+    </anttypetest>
+  </target>
+
+  <target name="text.optional">
+    <presetdef name="echo.mytext">
+      <echo>MyText</echo>
+    </presetdef>
+    <echo.mytext/>
+    <echo.mytext>override text</echo.mytext>
+  </target>
+
+  <target name="element.order">
+    <presetdef name="el.order">
+      <sequential>
+        <echo>Line 1</echo>
+      </sequential>
+    </presetdef>
+    <el.order>
+      <echo>Line 2</echo>
+    </el.order>
+  </target>
+
+  <target name="element.order2">
+    <presetdef name="el.order">
+      <sequential>
+        <echo>Line 1</echo>
+      </sequential>
+    </presetdef>
+    <presetdef name="el.order2">
+      <el.order>
+        <echo>Line 2</echo>
+      </el.order>
+    </presetdef>
+    <el.order2>
+      <echo>Line 3</echo>
+    </el.order2>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/property.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/property.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/property.xml	(revision 16139)
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+
+<project name="property-test" basedir="." default="test1">
+
+  <target name="test1">
+    <property environment="testenv"/>
+  </target>
+
+  <target name="test2">
+    <property name="testprop1" value="aa"/>
+    <property file="property1.properties"/>
+    <echo message="testprop1=${testprop1}, testprop3=${testprop3}, testprop4=${testprop4}"/>
+  </target>
+
+  <target name="test3">
+    <property file="property2.properties"/>
+  </target>
+
+  <target name="test4">
+    <property name="http.port" value="999" />
+    <property file="property3.properties"/>
+    <echo message="http.url is ${http.url}"/>
+  </target>
+
+  <target name="test5">
+    <property name="http.port" value="999" />
+    <property url="${test5.url}"/>
+    <echo message="http.url is ${http.url}"/>
+  </target>
+
+  <target name="prefix.success">
+    <property file="property3.properties" prefix="server1"/>
+  </target>
+
+  <target name="prefix.fail">
+    <property name="someprop" value="value" prefix="prefix"/>
+  </target>
+
+  <!-- caused an endless loop, PR 21825 -->
+  <target name="testCircularReference">
+    <property file="property4.properties"/>
+  </target>
+
+  <target name="thisIsNotACircularReference">
+    <property file="property5.properties"/>
+    <echo>b is ${b}</echo>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/property1.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/property1.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/property1.properties	(revision 16139)
@@ -0,0 +1,3 @@
+testprop2=xx
+testprop3=${testprop2}yy
+testprop4=${testprop1}zz
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/property2.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/property2.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/property2.properties	(revision 16139)
@@ -0,0 +1,3 @@
+testprop1=aa${testprop2}bb
+testprop2=cc${testprop1}dd
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/property3.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/property3.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/property3.properties	(revision 16139)
@@ -0,0 +1,4 @@
+http.port = 80
+http.url = http://localhost:${http.port}
+
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/property4.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/property4.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/property4.properties	(revision 16139)
@@ -0,0 +1,2 @@
+a=${a}
+b=${a}/b
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/property5.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/property5.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/property5.properties	(revision 16139)
@@ -0,0 +1,3 @@
+a=A
+b=${a}/${c}/${a}
+c=${a}
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/rename.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/rename.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/rename.xml	(revision 16139)
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+
+<project name="xxx-test" basedir="." default="test1">
+
+  <target name="test1">
+    <rename/>
+  </target>
+
+  <target name="test2">
+    <rename src=""/>
+  </target>
+
+  <target name="test3">
+    <rename dest=""/>
+  </target>
+
+  <target name="test4">
+    <rename src="testdir" 
+            dest="testdir"/>
+  </target>
+
+  <target name="test5">
+    <rename src="template.xml" 
+            dest="."/>
+  </target>
+
+  <target name="test6">
+    <rename src="template.xml" 
+            dest="template.tmp"/>
+    <rename src="template.tmp" 
+            dest="template.xml"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/replace.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/replace.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/replace.xml	(revision 16139)
@@ -0,0 +1,64 @@
+<?xml version="1.0"?>
+
+<project name="xxx-test" basedir="." default="test1">
+
+  <property name="tmp.dir" location="replace/tmp"/>
+
+  <target name="test1">
+    <replace/>
+  </target>
+
+  <target name="test2">
+    <replace file=""/>
+  </target>
+
+  <target name="test3">
+    <replace file="template.xml"/>
+  </target>
+
+  <target name="test4">
+    <replace file="template.xml" token=""/>
+  </target>
+
+  <target name="test5">
+    <replace file="template.xml" 
+             token="dont_want_to_really_replace_something"/>
+  </target>
+
+  <target name="test6">
+    <replace file="template.xml">
+      <replacefilter />
+    </replace>
+  </target>
+
+  <target name="test7">
+    <replace file="template.xml">
+      <replacefilter token="" />
+    </replace>
+  </target>
+
+  <target name="test8">
+    <replace file="template.xml">
+      <replacefilter token="dont_want_to_really_replace_something" />
+    </replace>
+  </target>
+
+  <target name="test9-setup">
+    <!-- this fixing of line endings is necessary because the replace task is transforming the line endings -->
+    <!-- of the replacement tokens and values to the platform default -->
+    <!-- in certain cases (checkout done with cvs of cygwin, the line endings of the various files do not match-->
+    <!-- the system property line.separator -->
+    <copy file="replace/source.txt" tofile="${tmp.dir}/output.txt"/>
+    <copy file="replace/value.txt" tofile="${tmp.dir}/value.txt" />
+    <copy file="replace/result.txt" tofile="${tmp.dir}/result.txt" />
+    <fixcrlf srcdir="${tmp.dir}" includes="*.txt"/>
+  </target>
+  <target name="test9" depends="test9-setup">
+    <loadfile srcFile="${tmp.dir}/value.txt" property="content"/>
+    <replace file="${tmp.dir}/output.txt" token="@@@Replace this@@@" value="${content}"/>
+  </target>
+
+  <target name="cleanup">
+      <delete dir="${tmp.dir}" quiet="true"/>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/replace/result.txt
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/replace/result.txt	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/replace/result.txt	(revision 16139)
@@ -0,0 +1,7 @@
+This is line one
+This is line two
+This is line three
+This is line four
+This is line five
+This is line six
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/replace/source.txt
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/replace/source.txt	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/replace/source.txt	(revision 16139)
@@ -0,0 +1,4 @@
+This is line one
+This is line two
+This is line three
+@@@Replace this@@@
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/replace/value.txt
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/replace/value.txt	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/replace/value.txt	(revision 16139)
@@ -0,0 +1,3 @@
+This is line four
+This is line five
+This is line six
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/signjar.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/signjar.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/signjar.xml	(revision 16139)
@@ -0,0 +1,44 @@
+<project name="signjartest" default="help" basedir="..">
+  <property name="classes.dir" value="../../../build/classes"/>
+
+  <target name="basic">
+    <jar jarfile="signtest.jar" basedir="${classes.dir}" includes="**/Task.class"/>
+    <signjar jar="signtest.jar" alias="testonly" keystore="testkeystore"
+             storepass="apacheant"/>
+  </target>
+
+  <target name="sigfile">
+    <jar jarfile="signtest.jar" basedir="${classes.dir}" includes="**/Task.class"/>
+    <signjar jar="signtest.jar" alias="testonly" keystore="testkeystore"
+             storepass="apacheant" sigfile="TEST"/>
+  </target>
+
+  <target name="maxmemory">
+    <jar jarfile="signtest.jar" basedir="${classes.dir}" includes="**/Task.class"/>
+    <signjar jar="signtest.jar" alias="testonly" keystore="testkeystore"
+             storepass="apacheant" maxmemory="128m"/>
+  </target>
+
+  <target name="urlKeystoreFile">
+    <jar jarfile="signtest.jar" basedir="${classes.dir}" includes="**/Task.class"/>
+    <signjar jar="signtest.jar" alias="testonly" keystore="file://../testkeystore"
+             storepass="apacheant" maxmemory="128m"/>
+  </target>
+
+  <target name="urlKeystoreHTTP">
+    <jar jarfile="signtest.jar" basedir="${classes.dir}" includes="**/Task.class"/>
+    <signjar jar="signtest.jar" alias="testonly"
+             keystore="http://cvs.apache.org/viewcvs.cgi/*checkout*/ant/src/etc/testcases/testkeystore?rev=HEAD"
+             storepass="apacheant" maxmemory="128m"/>
+  </target>
+
+  <target name="clean">
+    <delete file="signtest.jar"/>
+  </target>
+
+  <target name="help">
+    <echo>This build is for use with Ant's test cases</echo>
+  </target>
+
+</project>
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/skinconfig.dtd
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/skinconfig.dtd	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/skinconfig.dtd	(revision 16139)
@@ -0,0 +1,3 @@
+<!ELEMENT skinconfig (foo, bar?)>
+<!ELEMENT foo (#PCDATA)>
+<!ELEMENT bar (#PCDATA)>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/sleep.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/sleep.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/sleep.xml	(revision 16139)
@@ -0,0 +1,30 @@
+<project name="sleeptest" default="all" >
+
+    <target name="test1">
+        <sleep />
+    </target>
+
+    <target name="test2">
+        <sleep milliseconds="10"/>
+    </target>
+
+    <target name="test3">
+        <sleep seconds="2"/>
+    </target>
+
+    <target name="test4">
+        <sleep hours="1" minutes="-59" seconds="-58"/>
+    </target>
+
+    <target name="test5">
+        <sleep minutes="-59" seconds="-58"/>
+    </target>
+
+    <target name="test6">
+        <sleep minutes="-59" seconds="-58" failonerror="no"/>
+    </target>
+
+    <target name="all"
+        depends="test1,test2,test3,test4,test5" />
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/style/build.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/style/build.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/style/build.xml	(revision 16139)
@@ -0,0 +1,79 @@
+<?xml version="1.0"?>
+
+<project name="style-test" basedir="." default="nothing">
+
+  <property name="out.dir" value="out"/>
+
+
+  <target name="setup">
+  </target>
+
+  <target name="teardown">
+      <delete dir="${out.dir}" failonerror="false" />
+  </target>
+
+  <target name="testStyleIsSet">
+      <style in="data.xml" out="${out.dir}/out.xml"/>
+  </target>
+
+  <target name="testTransferParameterSet">
+      <property name="value" value="myvalue"/>
+      <style in="data.xml" out="${out.dir}/out.xml" style="printParams.xsl">
+          <param name="set" expression="${value}"/>
+      </style>
+  </target>
+
+  <target name="testTransferParameterEmpty">
+      <property name="value" value=""/>
+      <style in="data.xml" out="${out.dir}/out.xml" style="printParams.xsl">
+          <param name="empty" expression="${value}"/>
+      </style>
+  </target>
+
+  <target name="testTransferParameterUnset">
+      <style in="data.xml" out="${out.dir}/out.xml" style="printParams.xsl">
+          <param name="undefined" expression="${value}"/>
+      </style>
+  </target>
+
+  <target name="testTransferParameterUnsetWithIf">
+      <style in="data.xml" out="${out.dir}/out.xml" style="printParams.xsl">
+          <param name="undefined" expression="${value}" if="value" />
+      </style>
+  </target>
+
+  <target name="testDefaultMapper">
+    <property name="value" value="myvalue"/>
+    <style style="printParams.xsl" destDir="${out.dir}" basedir=".">
+      <param name="set" expression="${value}"/>
+    </style>
+  </target>
+
+  <target name="testCustomMapper">
+    <property name="value" value="myvalue"/>
+    <style style="printParams.xsl" destDir="${out.dir}" basedir=".">
+      <param name="set" expression="${value}"/>
+      <mapper type="glob" from="data.*" to="out.*"/>
+    </style>
+  </target>
+
+  <target name="testNewerStylesheet">
+      <antcall target="copyXsl">
+        <param name="xsl.value" value="old-value"/>
+      </antcall>
+      <style in="data.xml" out="${out.dir}/out.xml" style="tmp.xsl"/>
+
+      <antcall target="copyXsl">
+        <param name="xsl.value" value="new-value"/>
+      </antcall>
+      <style in="data.xml" out="${out.dir}/out.xml" style="tmp.xsl"/>
+      <delete file="tmp.xsl"/>
+  </target>
+
+  <target name="copyXsl" if="xsl.value">
+      <copy file="testNewerStylesheet.xsl" tofile="tmp.xsl" overwrite="true">
+          <filterchain><expandproperties/></filterchain>
+      </copy>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/style/data.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/style/data.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/style/data.xml	(revision 16139)
@@ -0,0 +1,1 @@
+<data/>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/style/printParams.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/style/printParams.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/style/printParams.xsl	(revision 16139)
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+
+<xsl:stylesheet
+  version="1.0"
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  xmlns:fo="http://www.w3.org/1999/XSL/Format">
+
+<!-- get the xsl-parameter -->
+<xsl:param name="set">set default value</xsl:param>
+<xsl:param name="empty">empty default value</xsl:param>
+<xsl:param name="undefined">undefined default value</xsl:param>
+
+<!-- use the xsl-parameter -->
+<xsl:template match="/">
+set='<xsl:value-of select="$set"/>'
+empty='<xsl:value-of select="$empty"/>'
+undefined='<xsl:value-of select="$undefined"/>'
+</xsl:template>
+
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/style/testNewerStylesheet.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/style/testNewerStylesheet.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/style/testNewerStylesheet.xsl	(revision 16139)
@@ -0,0 +1,12 @@
+<?xml version="1.0"?>
+<xsl:stylesheet
+  version="1.0"
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<xsl:output method="text"/>
+
+<xsl:template match="/">
+${xsl.value}
+</xsl:template>
+
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/subant.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/subant.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/subant.xml	(revision 16139)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<project name="subant-test" basedir="." default="testgenericantfile">
+    <target name="testnodirs" depends="cleanup">
+        <subant genericantfile="subant/genericsubant.xml">
+            <dirset dir="." includes="subant-test*"/>
+        </subant>
+
+    </target>
+    <target name="testgenericantfile">
+        <subant genericantfile="subant/genericsubant.xml">
+            <dirset dir="subant" includes="subant-test*"/>
+        </subant>
+    </target>
+    <target name="testantfile">
+        <subant antfile="mysubant.xml">
+            <dirset dir="." includes="subant/subant-test*"/>
+        </subant>
+
+    </target>
+    <target name="cleanup">
+        <!-- nothing to do -->
+    </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/subant/genericsubant.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/subant/genericsubant.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/subant/genericsubant.xml	(revision 16139)
@@ -0,0 +1,5 @@
+<project name="genericsubant" basedir=".." default="mysubant">
+    <target name="mysubant">
+        <echo message="${basedir}"/>
+    </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/subant/subant-test1/mysubant.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/subant/subant-test1/mysubant.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/subant/subant-test1/mysubant.xml	(revision 16139)
@@ -0,0 +1,5 @@
+<project name="mysubant" basedir="." default="mysubant">
+    <target name="mysubant">
+        <echo message="${basedir}"/>
+    </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/subant/subant-test2/mysubant.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/subant/subant-test2/mysubant.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/subant/subant-test2/mysubant.xml	(revision 16139)
@@ -0,0 +1,5 @@
+<project name="subant" basedir=".." default="mysubant">
+    <target name="mysubant">
+        <echo message="${basedir}"/>
+    </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/tar.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/tar.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/tar.xml	(revision 16139)
@@ -0,0 +1,111 @@
+<?xml version="1.0"?>
+
+<project name="tar-test" basedir="." default="test1">
+
+  <target name="test1">
+    <tar/>
+  </target>
+
+  <target name="test2">
+    <tar tarfile=""/>
+  </target>
+
+  <target name="test3">
+    <tar basedir=""/>
+  </target>
+
+  <target name="test4">
+    <touch file="test4.tar"/>
+    <tar destfile="test4.tar"
+         basedir="."/>
+  </target>
+
+  <target name="test5">
+    <mkdir dir="test5dir"/>
+    <tar destfile="test5.tar"
+         basedir="."
+         includes="test5dir"/>
+  </target>
+
+  <target name="test6">
+    <tar destfile="blah" longfile="Foo"/>
+  </target>
+
+  <target name="test7">
+    <mkdir dir="test7dir"/>
+    <tar destfile="test7.tar">
+      <tarfileset dir="." prefix="test7-prefix/">
+        <include name="test7dir"/>
+      </tarfileset>
+      <tarfileset dir="." prefix="">
+        <include name="test7dir"/>
+      </tarfileset>
+    </tar>
+    <untar src="test7.tar" dest="."/>
+  </target>
+
+  <target name="test8">
+    <tar destfile="test8.tar">
+      <tarfileset dir="." fullpath="/test8.xml">
+        <include name="tar.xml"/>
+      </tarfileset>
+    </tar>
+    <untar src="test8.tar" dest="."/>
+  </target>
+
+  <target name="test9">
+    <tar destfile="blah" compression="Foo"/>
+  </target>
+
+  <target name="test10">
+    <tar destfile="test10.tar.gz" compression="gzip">
+      <tarfileset dir="." fullpath="/test10.xml">
+        <include name="tar.xml"/>
+      </tarfileset>
+    </tar>
+    <untar src="test10.tar.gz" dest="." compression="gzip"/>
+  </target>
+
+  <target name="test11">
+    <tar destfile="test11.tar.bz2" compression="bzip2">
+      <tarfileset dir="." fullpath="/test11.xml">
+        <include name="tar.xml"/>
+      </tarfileset>
+    </tar>
+    <untar src="test11.tar.bz2" dest="." compression="bzip2"/>
+  </target>
+
+  <target name="cleanup"> 
+    <delete file="test4.tar"/>
+    <delete file="test5.tar"/>
+    <delete file="asf-logo.gif.tar"/>
+    <delete dir="test5dir"/>
+    <delete dir="test7dir"/>
+    <delete dir="test7-prefix"/>
+    <delete file="test7.tar"/>
+    <delete file="test8.tar"/>
+    <delete file="test8.xml"/>
+    <delete file="test10.tar.gz"/>
+    <delete file="test10.xml"/>
+    <delete file="test11.tar.bz2"/>
+    <delete file="test11.xml"/>
+    <delete file="asf-logo.gif.tar.gz"/>
+    <delete file="asf-logo.gif.tar.bz2"/>
+  </target>
+
+  <target name="feather">
+    <tar destfile="asf-logo.gif.tar"
+         basedir=".."
+         includes="asf-logo.gif" />
+    <tar destfile="asf-logo.gif.tar.gz"
+         basedir=".."
+         includes="asf-logo.gif" 
+         compression="gzip"/>
+    <tar destfile="asf-logo.gif.tar.bz2"
+         basedir=".."
+         includes="asf-logo.gif"
+         compression="bzip2" />
+  </target>
+
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/taskdef.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/taskdef.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/taskdef.xml	(revision 16139)
@@ -0,0 +1,73 @@
+<?xml version="1.0"?>
+
+<project name="xxx-test" basedir="." default="test1">
+
+  <path id="testclasses">
+    <pathelement location="../../../../build/testcases" />
+    <pathelement path="${java.class.path}" />
+  </path>
+
+  <taskdef name="global"
+           classname="org.example.tasks.TaskdefTestContainerTask">
+    <classpath refid="testclasses" />
+  </taskdef>
+
+  <target name="test1">
+    <taskdef/>
+  </target>
+
+  <target name="test2">
+    <taskdef name=""/>
+  </target>
+
+  <target name="test3">
+    <taskdef classname=""/>
+  </target>
+
+  <target name="test4">
+    <taskdef name="" classname="oops"/>
+  </target>
+
+  <target name="test5">
+    <taskdef name="test" classname="org.apache.tools.ant.Project" />
+  </target>
+
+  <target name="test5a">
+    <taskdef name="test" classname="org.apache.tools.ant.taskdefs.Copy" />
+  </target>
+
+  <target name="test6">
+    <taskdef name="test6"
+             classname="org.example.tasks.TaskdefTestSimpleTask">
+      <classpath refid="testclasses" />
+    </taskdef>
+    <test6>
+      <echo message="worked" />
+    </test6>
+  </target>
+
+  <target name="test7">
+    <taskdef name="test7"
+             classname="org.example.tasks.TaskdefTestContainerTask">
+      <classpath refid="testclasses" />
+    </taskdef>
+    <test7>
+      <echo message="worked" />
+    </test7>
+  </target>
+
+  <target name="testGlobal">
+    <global>
+      <echo message="worked" />
+    </global>
+  </target>
+
+  <target name="testOverride">
+    <taskdef name="copy" classname="org.apache.tools.ant.taskdefs.Echo" />
+    <copy>In target</copy>
+    <sequential>
+      <copy>In TaskContainer</copy>
+    </sequential>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/test.antlib.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/test.antlib.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/test.antlib.xml	(revision 16139)
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<antlib>
+  <typedef 
+    name="mytask" onerror="ignore"
+    classname="org.apache.tools.ant.taskdefs.AntlibTest$MyTask"/>
+</antlib>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/test2.antlib.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/test2.antlib.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/test2.antlib.xml	(revision 16139)
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<antlib>
+  <typedef 
+    name="mytask2" onerror="ignore"
+    classname="org.apache.tools.ant.taskdefs.AntlibTest$MyTask2"/>
+</antlib>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/toplevelant.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/toplevelant.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/toplevelant.xml	(revision 16139)
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+
+<project name="ant-test" basedir="." default="bar">
+  <ant antfile="toplevelant.xml" target="foo"/>
+
+  <target name="foo"/>
+  <target name="bar"/>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/toplevelantcall.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/toplevelantcall.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/toplevelantcall.xml	(revision 16139)
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+
+<project name="ant-test" basedir="." default="bar">
+  <antcall target="foo"/>
+
+  <target name="foo"/>
+  <target name="bar"/>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/toplevelsubant.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/toplevelsubant.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/toplevelsubant.xml	(revision 16139)
@@ -0,0 +1,10 @@
+<?xml version="1.0"?>
+
+<project name="ant-test" basedir="." default="bar">
+  <subant target="foo">
+    <fileset dir="." includes="toplevelsubant.xml"/>
+  </subant>
+
+  <target name="foo"/>
+  <target name="bar"/>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/touch.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/touch.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/touch.xml	(revision 16139)
@@ -0,0 +1,194 @@
+<?xml version="1.0"?>
+<project default="cleanup" basedir=".">
+
+  <property name="mappermillis" value="1072224000000" />
+  
+  <selector id="map.selector">
+    <date millis="${mappermillis}" />
+  </selector>
+
+  <target name="cleanup">
+    <delete>
+      <fileset dir="." includes="touchtest*" />
+    </delete>
+  </target>
+
+  <target name="noSeconds">
+    <touch file="touchtest" datetime="06/24/2003 2:20 pm"/>
+  </target>
+
+  <target name="seconds">
+    <touch file="touchtest" datetime="06/24/2003 2:20:12 pm"/>
+  </target>
+
+  <target name="testNow">
+    <touch file="touchtest" />
+  </target>
+  
+  <target name="testMillis">
+    <!-- this one is about 21 years after the epoch -->
+    <!-- less than 20 years after the epoch, test does not pass on my Win2K/FAT -->
+    <!-- Antoine February 8, 2004 -->
+    <!-- see http://developer.java.sun.com/developer/bugParade/bugs/4177432.html -->
+    <!-- and http://developer.java.sun.com/developer/bugParade/bugs/4697792.html -->
+    <!-- not sure why -->
+    <touch file="touchtest" millis="662256000000" />
+  </target>
+  
+  <target name="test2000">
+    <!-- this number of milliseconds is 30 * 365 * 24 * 3600 * 1000 -->
+    <!-- so the corresponding time is at the end of 1999 -->
+    <touch file="touchtest" millis="946080000000" />
+  </target>
+  
+  <target name="testFilelist">
+    <touch millis="662256000000" >
+      <filelist dir="." files="touchtest"/>
+    </touch>
+  </target>
+
+  <target name="testFileset" depends="testNow">
+    <touch millis="946080000000" >
+      <fileset dir="." includes="touchtest"/>
+    </touch>
+  </target>
+
+  <target name="testMappedFileset">
+    <touch file="touchtest" millis="${mappermillis}" />
+    <touch>
+      <fileset file="touchtest" />
+      <compositemapper>
+        <globmapper from="*" to="*foo" />
+        <globmapper from="*" to="*bar" />
+      </compositemapper>
+    </touch>
+
+    <fileset id="touchtest" file="touchtest">
+      <selector refid="map.selector" />
+    </fileset>
+
+    <fileset id="touchtestfoo" file="touchtestfoo">
+      <selector refid="map.selector" />
+    </fileset>
+
+    <fileset id="touchtestbar" file="touchtestbar">
+      <selector refid="map.selector" />
+    </fileset>
+
+    <pathconvert property="touchtest" pathsep=" "
+                 refid="touchtest" setonempty="false" />
+
+    <pathconvert property="touchtestfoo" pathsep=" "
+                 refid="touchtestfoo" setonempty="false" />
+
+    <pathconvert property="touchtestbar" pathsep=" "
+                 refid="touchtestbar" setonempty="false" />
+
+    <fail>
+      <condition>
+        <not>
+          <and>
+            <isset property="touchtest" />
+            <isset property="touchtestfoo" />
+            <isset property="touchtestbar" />
+          </and>
+        </not>
+      </condition>
+    </fail>
+
+  </target>
+
+  <target name="testExplicitMappedFileset">
+    <touch file="touchtest" millis="${mappermillis}" />
+    <touch>
+      <fileset file="touchtest" />
+      <mapper>
+        <compositemapper>
+          <globmapper from="*" to="*foo" />
+          <globmapper from="*" to="*bar" />
+        </compositemapper>
+      </mapper>
+    </touch>
+
+    <fileset id="touchtest" file="touchtest">
+      <selector refid="map.selector" />
+    </fileset>
+
+    <fileset id="touchtestfoo" file="touchtestfoo">
+      <selector refid="map.selector" />
+    </fileset>
+
+    <fileset id="touchtestbar" file="touchtestbar">
+      <selector refid="map.selector" />
+    </fileset>
+
+    <pathconvert property="touchtest" pathsep=" "
+                 refid="touchtest" setonempty="false" />
+
+    <pathconvert property="touchtestfoo" pathsep=" "
+                 refid="touchtestfoo" setonempty="false" />
+
+    <pathconvert property="touchtestbar" pathsep=" "
+                 refid="touchtestbar" setonempty="false" />
+
+    <fail>
+      <condition>
+        <not>
+          <and>
+            <isset property="touchtest" />
+            <isset property="touchtestfoo" />
+            <isset property="touchtestbar" />
+          </and>
+        </not>
+      </condition>
+    </fail>
+
+  </target>
+
+  <target name="testMappedFilelist">
+    <touch millis="${mappermillis}">
+      <filelist dir="." files="idonotexist" />
+      <mergemapper to="touchtest" />
+    </touch>
+
+    <fileset id="touchtest" file="touchtest">
+      <selector refid="map.selector" />
+    </fileset>
+
+    <pathconvert property="touchtest" pathsep=" "
+                 refid="touchtest" setonempty="false" />
+
+    <fail>
+      <condition>
+        <not>
+          <isset property="touchtest" />
+        </not>
+      </condition>
+    </fail>
+
+  </target>
+
+  <target name="testGoodPattern">
+    <touch file="touchtest" datetime="06242003142012GMTfoo" pattern="MMddyyyyHHmmssz'foo'" />
+
+    <fileset id="touchtest" file="touchtest">
+      <date millis="1056464412000" />
+    </fileset>
+
+    <pathconvert property="touchtest" pathsep=" "
+                 refid="touchtest" setonempty="false" />
+
+    <fail>
+      <condition>
+        <not>
+          <isset property="touchtest" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="testBadPattern">
+    <touch file="touchtest" datetime="06242003142012GMTfoo" pattern="MMddyyyyHHmmssz'bar'" />
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/typeadapter.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/typeadapter.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/typeadapter.xml	(revision 16139)
@@ -0,0 +1,63 @@
+<?xml version="1.0"?>
+
+<project name="test" basedir="." default="invalid">
+  <property name="testcases.dir" location="../../../../build/testcases"/>
+  <path id="testclasses">
+    <pathelement location="${testcases.dir}" />
+    <pathelement path="${java.class.path}" />
+  </path>
+
+  <target name="taskadapter">
+    <typedef name="myexec"
+             classname="org.apache.tools.ant.taskdefs.TypeAdapterTest$MyExec"
+             classpathref="testclasses"
+             adapter="org.apache.tools.ant.TaskAdapter"/>
+    <myexec/>
+  </target>
+
+  <target name="runadapter">
+    <typedef
+      name="myrunnable"
+      classname="org.apache.tools.ant.taskdefs.TypeAdapterTest$MyRunnable"
+      classpathref="testclasses"
+      adapter="org.apache.tools.ant.taskdefs.TypeAdapterTest$RunnableAdapter"/>
+    <myrunnable/>
+  </target>
+
+  <target name="runadaptererror">
+    <typedef
+      name="myrunnable"
+      classname="org.apache.tools.ant.taskdefs.TypeAdapterTest$MyExec"
+      classpathref="testclasses"
+      adapter="org.apache.tools.ant.taskdefs.TypeAdapterTest$RunnableAdapter"/>
+    <myrunnable/>
+  </target>
+
+  <target name="delay">
+    <typedef
+      name="mytask"
+      classname="org.apache.tools.ant.taskdefs.TypeAdapterTest$MyTask"
+      classpathref="testclasses"
+      onerror="ignore"/>
+    <mytask/>
+  </target>
+
+  <target name="onerror.report">
+    <typedef
+      name="mytask"
+      classname="org.apache.tools.ant.taskdefs.TypeAdapterTest$MyTaskNotPresent"
+      classpathref="testclasses"
+      onerror="report"/>
+  </target>
+
+  <target name="onerror.ignore">
+    <typedef
+      name="mytask"
+      classname="org.apache.tools.ant.taskdefs.TypeAdapterTest$MyTaskNotPresent"
+      classpathref="testclasses"
+      onerror="ignore"/>
+  </target>
+
+
+</project>
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/typedef.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/typedef.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/typedef.xml	(revision 16139)
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+
+<project name="test" basedir="." default="invalid">
+
+  <target name="invalid">
+    <fail>This file should only be run via a testcase</fail>
+  </target>
+
+  <target name="empty">
+    <typedef />
+  </target>
+
+  <target name="noClassname">
+    <typedef name="dummy" />
+  </target>
+
+  <target name="noName">
+    <typedef classname="org.example.types.TypedefTestType">
+      <classpath refid="testclasses" />
+    </typedef>
+  </target>
+
+  <target name="classNotFound">
+    <typedef name="" classname="oops"/>
+  </target>
+
+  <path id="testclasses">
+    <pathelement location="../../../../build/testcases" />
+    <pathelement path="${java.class.path}" />
+  </path>
+
+  <typedef name="global"
+           classname="org.example.types.TypedefTestType">
+    <classpath refid="testclasses" />
+  </typedef>
+
+  <target name="testGlobal">
+    <global id="global" />
+  </target>
+
+  <target name="testLocal">
+    <typedef name="localtype"
+             classname="org.example.types.TypedefTestType">
+      <classpath refid="testclasses" />
+    </typedef>
+    <localtype id="local" />
+  </target>
+
+  <target name="double-notpresent">
+    <typedef name="mytask" classname="notpresent" onerror="ignore"/>
+    <typedef name="mytask" classname="notpresent" onerror="ignore"/>
+    <typedef name="mytask" classname="org.apache.tools.ant.taskdefs.Echo"
+             onerror="ignore"/>
+    <mytask>hi</mytask>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/untar.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/untar.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/untar.xml	(revision 16139)
@@ -0,0 +1,47 @@
+<?xml version="1.0"?>
+
+<project basedir="." default="cleanup">
+
+  <target name="cleanup">
+    <delete file="asf-logo.gif" />
+  </target>
+
+  <target name="testTarTask">
+    <ant antfile="tar.xml" target="feather" />
+    <untar src="asf-logo.gif.tar" dest="." />
+    <ant antfile="tar.xml" target="cleanup" />
+  </target>
+
+  <target name="testGzipTarTask">
+    <ant antfile="tar.xml" target="feather" />
+    <untar src="asf-logo.gif.tar.gz" dest="." compression="gzip" />
+    <ant antfile="tar.xml" target="cleanup" />
+  </target>
+
+  <target name="testBzip2TarTask">
+    <ant antfile="tar.xml" target="feather" />
+    <untar src="asf-logo.gif.tar.bz2" dest="." compression="bzip2"/>
+    <ant antfile="tar.xml" target="cleanup" />
+  </target>
+
+  <target name="realTest">
+    <untar src="expected/asf-logo.gif.tar" dest="." />
+  </target>
+
+  <target name="realGzipTest">
+    <untar src="expected/asf-logo.gif.tar.gz" dest="." compression="gzip" />
+  </target>
+
+  <target name="realBzip2Test">
+    <untar src="expected/asf-logo.gif.tar.bz2" dest="." compression="bzip2"/>
+  </target>
+
+
+  <target name="srcDirTest">
+    <untar src="." dest="." />
+  </target>
+
+  <target name="encoding">
+    <untar src="expected/asf-logo.gif.tar" dest="." encoding="foo"/>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/unzip.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/unzip.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/unzip.xml	(revision 16139)
@@ -0,0 +1,96 @@
+<?xml version="1.0"?>
+
+<project name="xxx-test" basedir="." default="test1">
+
+  <target name="cleanup">
+    <delete file="asf-logo.gif" />
+    <delete file="unziptest.zip"/>
+    <delete dir="unziptestin"/>
+    <delete dir="unziptestout"/>
+  </target>
+
+  <target name="test1">
+    <unzip/>
+  </target>
+
+  <target name="test2">
+    <unzip src=""/>
+  </target>
+
+  <target name="test3">
+    <unzip dest=""/>
+  </target>
+
+  <target name="testZipTask">
+    <ant antfile="zip.xml" target="feather" />
+    <unzip src="asf-logo.gif.zip" dest="." />
+    <ant antfile="zip.xml" target="cleanup" />
+  </target>
+
+  <target name="testUncompressedZipTask">
+    <ant antfile="zip.xml" target="uncompressed-feather" />
+    <unzip src="asf-logo.gif.zip" dest="." />
+    <ant antfile="zip.xml" target="cleanup" />
+  </target>
+
+  <target name="realTest">
+    <unzip src="expected/asf-logo.gif.zip" dest="." />
+  </target>
+
+  <target name="prepareTestZip">
+    <mkdir dir="unziptestin/1"/>
+    <mkdir dir="unziptestin/2"/>
+    <touch file="unziptestin/1/foo"/>
+    <touch file="unziptestin/2/bar"/>
+    <zip destfile="unziptest.zip" basedir="unziptestin"/>
+  </target>
+
+  <target name="testPatternSetExcludeOnly" depends="prepareTestZip">
+    <unzip dest="unziptestout" src="unziptest.zip">
+      <patternset>
+        <exclude name="1/**"/>
+      </patternset>
+    </unzip>
+  </target>
+
+  <target name="testPatternSetIncludeOnly" depends="prepareTestZip">
+    <unzip dest="unziptestout" src="unziptest.zip">
+      <patternset>
+        <include name="2/**"/>
+      </patternset>
+    </unzip>
+  </target>
+
+  <target name="testPatternSetIncludeAndExclude" depends="prepareTestZip">
+    <unzip dest="unziptestout" src="unziptest.zip">
+      <patternset>
+        <include name="2/**"/>
+        <exclude name="2/**"/>
+      </patternset>
+    </unzip>
+  </target>
+
+  <target name="selfExtractingArchive">
+    <mkdir dir="unziptestout"/>
+    <unzip dest="unziptestout" src="zip/test.exe"/>
+  </target>
+
+  <!-- Bugzilla Report 20969 -->
+  <target name="testPatternSetSlashOnly" depends="prepareTestZip">
+    <unzip dest="unziptestout" src="unziptest.zip">
+      <patternset>
+        <include name="2/"/>
+      </patternset>
+    </unzip>
+  </target>
+
+  <!-- Bugzilla Report 10504 -->
+  <target name="encodingTest">
+    <mkdir dir="unziptestin"/>
+    <touch file="unziptestin/foo"/>
+    <zip zipfile="unziptest.zip" basedir="unziptestin" encoding="UnicodeBig"/>
+    <mkdir dir="unziptestout"/>
+    <unzip src="unziptest.zip" dest="unziptestout" encoding="UnicodeBig"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/war.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/war.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/war.xml	(revision 16139)
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<project name="war-test" basedir="." default="help">
+  <property name="working.dir" value="working"/>
+
+  <target name="help">
+    <echo message="Test file for the war task"/>
+  </target>
+
+  <target name="setup">
+    <mkdir dir="${working.dir}"/>
+  </target>
+  
+  <target name="testlibrefs" depends="setup">
+    <fileset id="test" dir="." includes="foobar"/>
+    <war webxml="war.xml" destfile="${working.dir}/test.war">
+      <lib refid="test"/>
+    </war>
+  </target>
+  
+  <target name="clean">
+    <delete dir="${working.dir}"/>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlns.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlns.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlns.xml	(revision 16139)
@@ -0,0 +1,51 @@
+<?xml version="1.0"?>
+<project name="test" xmlns:other="this is the other uri"
+         other:attr="this should be ignored by ant">
+
+  <property name="testcases.dir" location="../../../../build/testcases"/>
+
+  <path id="testclasses">
+    <pathelement location="${testcases.dir}" />
+    <pathelement path="${java.class.path}" />
+  </path>
+
+  <target name="xmlns" xmlns:test="this.is.another.test.uri">
+    <typedef classname="org.apache.tools.ant.taskdefs.XmlnsTest$MyTask"
+             classpathref="testclasses"
+             name="mytask"
+             uri="this.is.another.test.uri" />
+    <test:mytask/>
+  </target>
+
+  <target name="other" other:a="this is another attribute">
+    <echo other:g="abc" message="a message"/>
+  </target>
+
+  <target name="ns.attributes">
+    <taskdef name="my.echo" classname="org.apache.tools.ant.taskdefs.Echo"
+             uri="x-uri"/>
+    <x:my.echo x:message="hello world" xmlns:x="x-uri"/>
+  </target>
+
+  <target name="xmlns.file" xmlns:test="this.is.a.test.uri">
+    <typedef file="test.antlib.xml"
+             classpathref="testclasses"
+             uri="this.is.a.test.uri" />
+    <test:mytask/>
+  </target>
+
+  <target name="core">
+    <typedef file="test.antlib.xml"
+             classpathref="testclasses"
+             uri="antlib:org.apache.tools.ant" />
+    <mytask/>
+  </target>
+
+  <target name="excluded">
+    <typedef file="test.antlib.xml"
+             classpathref="testclasses"
+             uri="ant:notallowed" />
+  </target>
+
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty.xml	(revision 16139)
@@ -0,0 +1,20 @@
+<project name="test" default="test" basedir=".">
+
+  <target name="test">
+    <xmlproperty file="xmlproperty_data.xml"/>
+  </target>
+
+  <target name="testdtd">
+    <xmlproperty file="xmlproperty_withdtd.xml"/>
+  </target>
+
+  <target name="testneedscat">
+    <xmlproperty file="xmlproperty_needscat.xml">
+      <xmlcatalog>
+        <dtd publicId="-//FOO//DTD Skin Configuration V0.1//EN"
+             location="skinconfig.dtd"/>
+      </xmlcatalog>
+    </xmlproperty>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-collapse-input1.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-collapse-input1.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-collapse-input1.properties	(revision 16139)
@@ -0,0 +1,6 @@
+properties.root=foo,bar
+properties.a.b.c=d
+properties.a.b=e
+properties.foo.bar=quux,quux1
+properties.foo.quux=bar
+properties.tag.value=foo
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-collapse-original.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-collapse-original.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-collapse-original.properties	(revision 16139)
@@ -0,0 +1,5 @@
+root-tag.myattr=true
+root-tag.inner-tag=Text
+root-tag.inner-tag.someattr=val
+root-tag.a2.a3.a4=false
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-collapse-override.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-collapse-override.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-collapse-override.properties	(revision 16139)
@@ -0,0 +1,2 @@
+# Match value hardwired in code, NOT in the input...
+override.property.test=foo
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-nocollapse-input1.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-nocollapse-input1.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-nocollapse-input1.properties	(revision 16139)
@@ -0,0 +1,8 @@
+properties.root=foo,bar
+properties.a.b(c)=d
+properties.a.b=e
+properties.foo(bar)=quux
+properties.foo.bar=quux1
+properties.foo.quux=bar
+properties.tag(value)=foo
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-nocollapse-original.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-nocollapse-original.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-nocollapse-original.properties	(revision 16139)
@@ -0,0 +1,5 @@
+root-tag(myattr)=true
+root-tag.inner-tag=Text
+root-tag.inner-tag(someattr)=val
+root-tag.a2.a3.a4=false
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-semantic-include.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-semantic-include.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-semantic-include.properties	(revision 16139)
@@ -0,0 +1,6 @@
+properties.root=foo,bar
+properties.a.b.c=d
+properties.a.b=e
+properties.foo.bar=quux,quux1
+properties.foo.quux=bar
+properties.tag.value=foo
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-semantic-input1.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-semantic-input1.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-semantic-input1.properties	(revision 16139)
@@ -0,0 +1,6 @@
+properties.root=foo,bar
+properties.a.b.c=d
+properties.a.b=e
+properties.foo.bar=quux,quux1
+properties.foo.quux=bar
+properties.tag=foo
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-semantic-override.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-semantic-override.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/keeproot-semantic-override.properties	(revision 16139)
@@ -0,0 +1,2 @@
+# Match value hardwired in code, NOT in the input...
+override.property.test=foo
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-collapse-input1.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-collapse-input1.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-collapse-input1.properties	(revision 16139)
@@ -0,0 +1,6 @@
+root=foo,bar
+a.b.c=d
+a.b=e
+foo.bar=quux,quux1
+foo.quux=bar
+tag.value=foo
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-collapse-original.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-collapse-original.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-collapse-original.properties	(revision 16139)
@@ -0,0 +1,4 @@
+inner-tag=Text
+inner-tag.someattr=val
+a2.a3.a4=false
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-nocollapse-input1.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-nocollapse-input1.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-nocollapse-input1.properties	(revision 16139)
@@ -0,0 +1,7 @@
+root=foo,bar
+a.b(c)=d
+a.b=e
+foo(bar)=quux
+foo.bar=quux1
+foo.quux=bar
+tag(value)=foo
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-nocollapse-multi.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-nocollapse-multi.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-nocollapse-multi.properties	(revision 16139)
@@ -0,0 +1,1 @@
+foo.bar=1,2,3,4
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-nocollapse-original.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-nocollapse-original.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-nocollapse-original.properties	(revision 16139)
@@ -0,0 +1,4 @@
+inner-tag=Text
+inner-tag(someattr)=val
+a2.a3.a4=false
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-include-input1.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-include-input1.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-include-input1.properties	(revision 16139)
@@ -0,0 +1,6 @@
+root=foo,bar
+a.b.c=d
+a.b=e
+foo.bar=quux,quux1
+foo.quux=bar
+tag.value=foo
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-input1.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-input1.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-input1.properties	(revision 16139)
@@ -0,0 +1,6 @@
+root=foo,bar
+a.b.c=d
+a.b=e
+foo.bar=quux,quux1
+foo.quux=bar
+tag=foo
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-locations.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-locations.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-locations.properties	(revision 16139)
@@ -0,0 +1,1 @@
+file=FILE.foo
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-paths.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-paths.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-paths.properties	(revision 16139)
@@ -0,0 +1,1 @@
+foo=ID.path
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-references.properties
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-references.properties	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/goldfiles/nokeeproot-semantic-references.properties	(revision 16139)
@@ -0,0 +1,5 @@
+property=foo
+foo.bar=foo
+foo.quux=foo
+foo.thunk=foo
+foo.property=ID.foo
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/input1.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/input1.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/input1.xml	(revision 16139)
@@ -0,0 +1,10 @@
+<properties>
+  <root>foo</root>
+  <root>bar</root>
+  <a><b c="d">e</b></a>
+  <foo bar="quux">
+    <bar>quux1</bar>
+    <quux>bar</quux>
+  </foo>
+  <tag value="foo"/>
+</properties>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/locations.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/locations.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/locations.xml	(revision 16139)
@@ -0,0 +1,3 @@
+<locations>
+  <file location="foo"/>
+</locations>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/multi.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/multi.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/multi.xml	(revision 16139)
@@ -0,0 +1,8 @@
+<properties>
+  <foo>
+    <bar>1</bar>
+    <bar>2</bar>
+    <bar>3</bar>
+    <bar>4</bar>
+  </foo>
+</properties>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/original.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/original.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/original.xml	(revision 16139)
@@ -0,0 +1,5 @@
+<root-tag myattr="true">
+  <inner-tag someattr="val">Text</inner-tag>
+  <a2><a3><a4>false</a4></a3></a2>
+</root-tag>
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/override.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/override.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/override.xml	(revision 16139)
@@ -0,0 +1,10 @@
+<root>
+  <override>
+    <!-- This property should not get set.  The
+         XmlPropertyTest code explicitly sets
+         override.property.test to foo to make
+         sure that attempts to reset it via
+         property file loads *fail*. -->
+    <property test="bar"/>
+  </override>
+</root>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/paths.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/paths.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/paths.xml	(revision 16139)
@@ -0,0 +1,5 @@
+<paths>
+  <classpath pathid="foo">
+    <path value="bar"/>
+  </classpath>
+</paths>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/references.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/references.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty/inputs/references.xml	(revision 16139)
@@ -0,0 +1,7 @@
+<references>
+  <property value="foo" id="foo.property"/>
+  <foo bar="${property}">
+    <quux refid="foo.property"/>
+    <thunk>${property}</thunk>
+  </foo>
+</references>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty_data.dtd
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty_data.dtd	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty_data.dtd	(revision 16139)
@@ -0,0 +1,14 @@
+<!ELEMENT root-tag (inner-tag, a2, cdatatag)>
+<!ATTLIST root-tag myattr CDATA "">
+
+<!ELEMENT inner-tag (#PCDATA)>
+<!ATTLIST inner-tag someattr CDATA "">
+
+<!ELEMENT a2 (a3)>
+
+<!ELEMENT a3 (a4)>
+
+<!ELEMENT a4 (#PCDATA)>
+
+<!ELEMENT cdatatag (#PCDATA)>
+
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty_data.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty_data.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty_data.xml	(revision 16139)
@@ -0,0 +1,5 @@
+ <root-tag myattr="true">
+   <inner-tag someattr="val">Text</inner-tag>
+   <a2><a3><a4>false</a4></a3></a2>
+   <cdatatag><![CDATA[<test>]]></cdatatag>
+ </root-tag>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty_needscat.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty_needscat.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty_needscat.xml	(revision 16139)
@@ -0,0 +1,6 @@
+<?xml version="1.0"?>
+<!DOCTYPE skinconfig PUBLIC "-//FOO//DTD Skin Configuration V0.1//EN" "http://example-no-dtd.com/dtd/skinconfig.dtd">
+<skinconfig>
+  <foo>true</foo>
+  <bar>false</bar>
+</skinconfig>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty_withdtd.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty_withdtd.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/xmlproperty_withdtd.xml	(revision 16139)
@@ -0,0 +1,7 @@
+<!DOCTYPE root-tag SYSTEM "xmlproperty_data.dtd">
+
+ <root-tag myattr="true">
+   <inner-tag someattr="val">Text</inner-tag>
+   <a2><a3><a4>false</a4></a3></a2>
+   <cdatatag><![CDATA[<test>]]></cdatatag>
+ </root-tag>
Index: /release-kits/shared/ant/src/etc/testcases/taskdefs/zip.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/taskdefs/zip.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/taskdefs/zip.xml	(revision 16139)
@@ -0,0 +1,156 @@
+<?xml version="1.0"?>
+
+<project name="zip-test" basedir="." default="test1">
+
+  <target name="test1">
+    <zip/>
+  </target>
+
+  <target name="test2">
+    <zip destFile="zip.tmp"/>
+  </target>
+
+  <!-- Test when the zip file includes itself
+  when target file exists before the zip task is run -->
+  <target name="test3">
+    <touch file="test3.zip"/>
+    <zip destFile="test3.zip"
+         basedir="."/>
+  </target>
+
+  <!-- Test when the zip file includes itself
+  when target file does not exist before the zip task is run
+  <target name="test4">
+     <zip destFile="test4.zip"
+         basedir="."/>
+  </target>
+  -->
+
+  <target name="test5">
+    <zip zipfile="test5.zip" basedir="." >
+      <exclude name="test5.zip" />
+    </zip>
+  </target>
+
+  <target name="test6">
+    <zip destFile="test6.zip" basedir=".">
+      <include name="*.xml" />
+      <exclude name="zip.*" />
+    </zip>
+  </target>
+
+  <target name="test7">
+    <zip destFile="inner7.zip" basedir="." >
+      <exclude name="inner7.zip" />
+    </zip>
+    <zip destFile="test7.zip" basedir=".">
+      <exclude name="**/*.*" />
+      <zipfileset src="inner7.zip" />
+    </zip>
+  </target>
+
+  <target name="feather">
+    <zip destFile="asf-logo.gif.zip"
+         basedir=".."
+         includes="asf-logo.gif" />
+  </target>
+
+  <target name="uncompressed-feather">
+    <zip destFile="asf-logo.gif.zip"
+         basedir=".."
+         includes="asf-logo.gif" compress="false"/>
+  </target>
+
+  <!-- legacy attribute support -->
+  <target name="test8">
+    <zip zipfile="test8.zip" basedir="." >
+      <exclude name="test8.zip" />
+    </zip>
+  </target>
+
+  <target name="testZipgroupfileset">
+
+    <zip zipfile="zipgroupfileset.zip" basedir=".">
+      <zipgroupfileset dir="zip" 
+        includes="zipgroupfileset*.zip" 
+        excludes="zipgroupfileset3.zip" />
+      <include name="zip/zipgroupfileset3.zip" />
+    </zip>
+  </target>
+
+  <target name="testDuplicateFail">
+
+    <zip zipfile="duplicateFail.zip" basedir="." duplicate="fail">
+      <zipgroupfileset dir="duplicate" includes="duplicate*.zip" />
+    </zip>
+  </target>
+
+  <target name="testUpdateNotNecessary" depends="feather">
+    <zip destFile="asf-logo.gif.zip"
+         basedir=".."
+         includes="asf-logo.gif"
+         update="true" />
+  </target>
+
+  <target name="testUpdateIsNecessary" depends="feather">
+    <sleep seconds="5" />
+    <touch file="../dummyfile" />
+    <zip destFile="asf-logo.gif.zip"
+         basedir=".."
+         includes="asf-logo.gif,dummyfile"
+         update="true" />
+  </target>
+
+  <!-- Bugzilla Report 18403 -->
+  <target name="testPrefixAddsDir">
+    <zip destfile="test3.zip" filesonly="false">
+      <zipfileset dir="." prefix="test" includes="zip.xml"/>
+    </zip>
+  </target>
+
+  <!-- Bugzilla Report 19449 -->
+  <target name="testFilesOnlyDoesntCauseRecreateSetup">
+    <mkdir dir="ziptest"/>
+    <touch file="ziptest/ziptest"/>
+    <zip destfile="test3.zip" basedir="."
+         includes="ziptest/**" filesonly="true"/>
+  </target>
+
+  <!-- Bugzilla Report 19449 -->
+  <target name="testFilesOnlyDoesntCauseRecreate">
+    <zip destfile="test3.zip" basedir="."
+         includes="ziptest/**" filesonly="true"/>
+  </target>
+
+  <!-- Bugzilla Report 22865 -->
+  <target name="testEmptySkip">
+    <mkdir dir="ziptest"/>
+    <zip destfile="test3.zip" basedir="ziptest" whenempty="skip"/>
+  </target>
+
+  <!-- Bugzilla Report 30365 -->
+  <target name="zipEmptyDir">
+        <mkdir dir="empty/empty2"/>
+		<zip destfile="test3.zip" basedir="empty" update="true"/>
+  </target>
+
+  <target name="zipEmptyCreate">
+      <mkdir dir="empty"/>
+      <zip destfile="test3.zip" basedir="empty" whenempty="create" includes="*.xyz"/>
+  </target>
+
+  <target name="cleanup">
+    <delete file="test3.zip"/>
+    <delete file="test4.zip"/>
+    <delete file="test5.zip"/>
+    <delete file="test6.zip"/>
+    <delete file="inner7.zip"/>
+    <delete file="test7.zip"/>
+    <delete file="test8.zip"/>
+    <delete file="asf-logo.gif.zip"/>
+    <delete file="zipgroupfileset.zip"/>
+    <delete file="../dummyfile" />
+    <delete dir="ziptest"/>
+    <delete dir="empty"/>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/types/addtype.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/addtype.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/addtype.xml	(revision 16139)
@@ -0,0 +1,137 @@
+<project name="test" basedir=".">
+
+  <target name="addpath">
+    <typedef name="mypath" classname="org.apache.tools.ant.types.Path"/>
+    <path>
+      <mypath path="build.xml"/>
+    </path>
+  </target>
+
+  <target name="addcondition">
+    <typedef name="mycondition"
+             classname="org.apache.tools.ant.taskdefs.condition.Equals"/>
+    <condition property="mycondition.set">
+      <mycondition arg1="string" arg2="string"/>
+    </condition>
+    <fail unless="mycondition.set"/>
+  </target>
+
+  <target name="addfilter">
+    <typedef name="headfilter2"
+             classname="org.apache.tools.ant.filters.HeadFilter"/>
+    <concat>This is line 1
+      This is line 2
+      This is line 3
+      <filterchain>
+        <headfilter2 lines="2"/>
+      </filterchain>
+    </concat>
+  </target>
+
+  <target name="addselector">
+    <typedef
+      name="myselector"
+      classname="org.apache.tools.ant.types.selectors.ContainsSelector"/>
+    <fileset id="myselector.test" dir="${basedir}" includes="*">
+      <myselector text="myselector"/>
+    </fileset>
+  </target>
+
+  <target name="init">
+    <property name="nested.package" value="org.apache.tools.ant.types."/>
+    <path id="test-classes">
+      <pathelement location="../../../../build/testcases" />
+      <pathelement path="${java.class.path}" />
+    </path>
+    <typedef loaderref="nested.loader" classpathref="test-classes"
+             name = "nested.a"
+             classname="${nested.package}AddTypeTest$AImpl"/>
+    <typedef loaderref="nested.loader"
+             name = "nested.b"
+             classname="${nested.package}AddTypeTest$BImpl"/>
+    <typedef loaderref="nested.loader"
+             name = "nested.c"
+             classname="${nested.package}AddTypeTest$CImpl"/>
+    <typedef loaderref="nested.loader"
+             name = "nested.ab"
+             classname="${nested.package}AddTypeTest$ABImpl"/>
+    <taskdef loaderref="nested.loader"
+             name = "nested.container" 
+             classname="${nested.package}AddTypeTest$NestedContainer"/>
+    <taskdef loaderref="nested.loader"
+             name = "nested.condition.task" 
+             classname="${nested.package}AddTypeTest$MyCondition"/>
+    <typedef loaderref="nested.loader"
+             name = "nested.condition.type" 
+             classname="${nested.package}AddTypeTest$MyCondition"/>
+    <typedef loaderref="nested.loader"
+             name = "myaddconfigured" 
+             classname="${nested.package}AddTypeTest$MyAddConfigured"/>
+    <typedef loaderref="nested.loader"
+             name = "myvalue" 
+             classname="${nested.package}AddTypeTest$MyValue"/>
+  </target>
+
+  <target name="nested.a" depends="init">
+    <nested.container>
+      <nested.a/>
+    </nested.container>
+  </target>
+
+  <target name="nested.b" depends="init">
+    <nested.container>
+      <nested.b/>
+    </nested.container>
+  </target>
+  
+  <target name="nested.c" depends="init">
+    <nested.container>
+      <nested.c/>
+    </nested.container>
+  </target>
+
+  <target name="nested.ab" depends="init">
+    <nested.container>
+      <nested.ab/>
+    </nested.container>
+  </target>
+
+  <!-- tests for task adaptor -->
+  <target name="condition.type" depends="init">
+    <echo>before</echo>
+    <nested.condition.type/>
+    <echo>after</echo>
+  </target>
+
+  <target name="condition.task" depends="init">
+    <echo>before</echo>
+    <nested.condition.task/>
+    <echo>after</echo>
+  </target>
+
+  <target name="condition.condition.type" depends="init">
+    <condition property="condition.condition.type">
+      <nested.condition.type/>
+    </condition>
+  </target>
+
+  <target name="condition.condition.task" depends="init">
+    <condition property="condition.condition.task">
+      <nested.condition.task/>
+    </condition>
+  </target>
+
+  <target name="myaddconfigured" depends="init">
+    <myaddconfigured>
+      <myvalue>Value Set</myvalue>
+    </myaddconfigured>
+  </target>
+
+  <target name="namespacetest" xmlns:prefix="uri">
+    <typedef name="eq" uri="uri"
+             classname="org.apache.tools.ant.taskdefs.condition.Equals"/>
+    <condition property="p">
+      <prefix:eq arg1="a" arg2="b"/>
+    </condition>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/types/assertions.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/assertions.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/assertions.xml	(revision 16139)
@@ -0,0 +1,153 @@
+<?xml version="1.0"?>
+
+<project name="assertions" basedir="." default="init">
+
+  <property name="build.dir" location="assertions/build"/>
+  <property name="src.dir" location="assertions"/>
+  <property name="classname" value="AssertionTest"/>
+  
+  <path id="assert.classpath">
+    <pathelement location="${build.dir}"/>
+  </path>
+  
+  <target name="setup" >
+    <mkdir dir="${build.dir}"/>
+    <javac srcdir="${src.dir}"
+      includes="*.java"
+      source="1.4"
+      debug="true"
+      destdir="${build.dir}"
+      />
+  </target>
+  
+  <target name="teardown" >
+    <delete dir="${build.dir}"/>
+  </target>
+
+  <!-- if per-class assertions work, this run asserts -->
+  <target name="test-classname" depends="setup">
+    <java fork="true" failonerror="true"
+      classname="${classname}"
+      classpathref="assert.classpath">
+      <assertions enablesystemassertions="true">
+        <enable class="${classname}" />
+      </assertions>
+    </java>
+  </target>
+  
+  <!-- if package works, this run asserts -->
+  <target name="test-package" depends="setup">
+    <java fork="true"  failonerror="true"
+      classname="${classname}"
+      classpathref="assert.classpath">
+      <assertions enableSystemAssertions="false" >
+        <enable package="..." />
+      </assertions>
+    </java>
+  </target>
+
+  <!-- this test should run the app successfully -->
+  <target name="test-empty-assertions" depends="setup">
+    <java fork="true"  failonerror="true"
+      classname="${classname}"
+      classpathref="assert.classpath">
+      <assertions/>
+    </java>
+  </target>  
+
+  <!-- this test should run the app successfully -->
+  <target name="test-disable" depends="setup">
+    <java fork="true" failonerror="true"
+      classname="${classname}"
+      classpathref="assert.classpath">
+      <assertions enableSystemAssertions="false" >
+        <enable package="..." />
+        <disable class="${classname}" />
+      </assertions>
+    </java>
+  </target>  
+
+  <!-- repeated settigns result in the last declaration winning
+    except that the rule 'classes win over packages takes priority
+    this run will assert -->
+  <target name="test-override" depends="setup">
+    <java fork="true" failonerror="true"
+      classname="${classname}"
+      classpathref="assert.classpath">
+      <assertions enableSystemAssertions="false" >
+        <enable package="..." />
+        <disable class="${classname}" />
+        <enable class="${classname}" />
+        <disable package="..." />
+      </assertions>
+    </java>
+  </target>  
+
+  <!-- repeated settigns result in the last declaration winning;
+    this run will not assert -->
+  <target name="test-override2" depends="setup">
+    <java fork="true" failonerror="true"
+      classname="${classname}"
+      classpathref="assert.classpath">
+      <assertions enableSystemAssertions="false" >
+        <enable package="..." />
+        <enable class="${classname}" />
+        <disable class="${classname}" />
+      </assertions>
+    </java>
+  </target>  
+  
+  <!-- if references work, this run asserts -->
+  <target name="test-references">
+  <assertions id="project.assertions" >
+    <enable package="org.apache.test" />
+    <disable package="org.apache.log4j"/>
+    <enable package="..."/>
+  </assertions>  
+    <java fork="true" failonerror="true"
+      classname="${classname}"
+      classpathref="assert.classpath">
+      <assertions refid="project.assertions"/>
+    </java>
+  </target>
+  
+  <!-- when fork=false; we need to reject the construct -->
+  <target name="test-nofork" depends="setup">
+    <java fork="false" failonerror="true"
+      classname="${classname}"
+      classpathref="assert.classpath">
+      <assertions enablesystemassertions="true">
+        <enable class="${classname}" />
+      </assertions>
+    </java>
+  </target>  
+
+  <!-- this throws a build error -->
+  <target name="test-multiple-assertions" depends="setup">
+    <java fork="true" failonerror="true"
+      classname="${classname}"
+      classpathref="assert.classpath">
+      <assertions enablesystemassertions="true">
+        <enable class="${classname}" />
+      </assertions>
+      <assertions/>
+    </java>
+  </target>  
+  
+  <!-- should throw a build exception -->
+  <target name="test-reference-abuse" depends="setup">
+  <assertions id="project.assertions2" >
+    <enable package="org.apache.test" />
+    <disable package="org.apache.log4j"/>
+    <enable package="..."/>
+  </assertions>  
+    <java fork="true" failonerror="true"
+      classname="${classname}"
+      classpathref="assert.classpath">
+      <assertions refid="project.assertions2">
+        <disable class="${classname}" />
+      </assertions>      
+    </java>
+  </target>    
+  
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/types/assertions/AssertionTest.java
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/assertions/AssertionTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/assertions/AssertionTest.java	(revision 16139)
@@ -0,0 +1,29 @@
+/* 
+ * Copyright  2003-2004 Apache Software Foundation
+ *
+ *  Licensed 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.
+ * 
+ */
+ 
+
+/**
+ * this is an assertion tester
+ */
+public class AssertionTest {
+    
+    public static void main(String args[]) {
+		assert true == false : "there exist no facts that are both true and false";
+		System.out.println("Assertions are disabled");
+    }
+    
+}
Index: /release-kits/shared/ant/src/etc/testcases/types/description1.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/description1.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/description1.xml	(revision 16139)
@@ -0,0 +1,5 @@
+<project name="test" default="main" basedir=".">
+  <description>Test Project Description</description>
+  <target name="main">
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/types/description2.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/description2.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/description2.xml	(revision 16139)
@@ -0,0 +1,6 @@
+<project name="test" default="main" basedir=".">
+  <description>Multi Line
+Project Description</description>
+  <target name="main">
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/types/description3.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/description3.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/description3.xml	(revision 16139)
@@ -0,0 +1,6 @@
+<project name="test" default="main" basedir=".">
+  <description>Multi Instance </description>
+  <description>Project Description</description>
+  <target name="main">
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/types/description4.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/description4.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/description4.xml	(revision 16139)
@@ -0,0 +1,6 @@
+<project name="test" default="main" basedir=".">
+  <description>Multi Instance </description>
+  <target name="main">
+      <description>Nested Project Description</description>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/types/filterset.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/filterset.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/filterset.xml	(revision 16139)
@@ -0,0 +1,57 @@
+<project name="test" default="new" basedir=".">
+  <filterset id="testset.one">
+    <filter token="aaaa" value="1111"/>
+    <filter token="bbbb" value="2222"/>
+  </filterset>
+  
+  <filterset id="testset.two" beginToken="%" endToken="^">
+    <filter token="cccc" value="3333"/>
+    <filter token="dddd" value="4444"/>
+  </filterset>
+  
+  <target name="test1">
+    <delete file="dest1.txt"/>
+    <copy file="filterseta.txt" tofile="dest1.txt">
+        <filterset refid="testset.one"/> 
+    </copy>
+  </target>
+
+  <target name="test2">
+    <delete file="dest2.txt"/>
+    <copy file="filtersetb.txt" tofile="dest2.txt">
+        <filterset refid="testset.two"/> 
+    </copy>
+  </target>
+
+  <target name="test3">
+    <delete file="dest3.txt"/>
+    <copy file="filtersetc.txt" tofile="dest3.txt">
+        <filterset refid="testset.one"/> 
+        <filterset refid="testset.two"/> 
+    </copy>
+  </target>
+  
+  <target name="test-nested-filtersets">
+    <filterset id="1">
+      <filter token="token1" value="value1"/>
+    </filterset>
+    <filterset id="2">
+      <filterset refid="testset.one"/>
+    </filterset>
+    <filterset id="3">
+      <filterset id="4">
+        <filter token="token4" value="value4"/>
+      </filterset>
+    </filterset>
+    <filterset id="5">
+      <filterset refid="1"/>
+    </filterset>
+  </target>
+
+  <target name="cleanup">
+    <delete file="dest1.txt" quiet="true" />
+    <delete file="dest2.txt" quiet="true" />
+    <delete file="dest3.txt" quiet="true" />
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/types/filterseta.txt
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/filterseta.txt	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/filterseta.txt	(revision 16139)
@@ -0,0 +1,2 @@
+This is a test file for filters @aaaa@
+It has two lines @bbbb@
Index: /release-kits/shared/ant/src/etc/testcases/types/filtersetb.txt
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/filtersetb.txt	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/filtersetb.txt	(revision 16139)
@@ -0,0 +1,5 @@
+This is a test file for filters with non default markers
+@cccc@ - should not change
+%cccc^ - should change
+^dddd% - should not change
+%dddd^ - should change
Index: /release-kits/shared/ant/src/etc/testcases/types/filtersetc.txt
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/filtersetc.txt	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/filtersetc.txt	(revision 16139)
@@ -0,0 +1,7 @@
+Combined filter test
+@aaaa@ - should change
+@bbbb@ - should change
+@cccc@ - should not change
+%cccc^ - should change
+^dddd% - should not change
+%dddd^ - should change
Index: /release-kits/shared/ant/src/etc/testcases/types/flexinteger.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/flexinteger.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/flexinteger.xml	(revision 16139)
@@ -0,0 +1,18 @@
+<project name="test" default="test" basedir=".">
+
+  <path id="testclasses">
+    <pathelement location="../../../../build/testcases" />
+    <pathelement path="${java.class.path}" />
+  </path>
+
+  <target name="test">
+    <taskdef name="flexint"
+             classname="org.apache.tools.ant.types.FlexIntegerTest"
+             classpathref="testclasses"
+    />
+            
+    <flexint propname="flexint.value1" value="0xA"/>
+    <flexint propname="flexint.value2" value="010"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/types/gold/filterset1.txt
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/gold/filterset1.txt	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/gold/filterset1.txt	(revision 16139)
@@ -0,0 +1,2 @@
+This is a test file for filters 1111
+It has two lines 2222
Index: /release-kits/shared/ant/src/etc/testcases/types/gold/filterset2.txt
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/gold/filterset2.txt	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/gold/filterset2.txt	(revision 16139)
@@ -0,0 +1,5 @@
+This is a test file for filters with non default markers
+@cccc@ - should not change
+3333 - should change
+^dddd% - should not change
+4444 - should change
Index: /release-kits/shared/ant/src/etc/testcases/types/gold/filterset3.txt
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/gold/filterset3.txt	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/gold/filterset3.txt	(revision 16139)
@@ -0,0 +1,7 @@
+Combined filter test
+1111 - should change
+2222 - should change
+@cccc@ - should not change
+3333 - should change
+^dddd% - should not change
+4444 - should change
Index: /release-kits/shared/ant/src/etc/testcases/types/mapper.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/mapper.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/mapper.xml	(revision 16139)
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+
+<project name="copy-test" basedir="." default="test1">
+
+  <target name="test1">
+    <mkdir dir="copytest" />
+    <copy todir="copytest">
+      <fileset dir="../../../main">
+        <include name="**/taskdefs/*.java" />
+      </fileset>
+      <fileset dir="../../../testcases">
+        <include name="**/taskdefs/*.java" />
+      </fileset>
+      <mapper type="flatten" />
+    </copy>
+  </target>
+
+  <target name="cleanup">
+    <delete dir="copytest" />
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/types/poly.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/poly.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/poly.xml	(revision 16139)
@@ -0,0 +1,45 @@
+<project name="test" basedir=".">
+  
+  <property name="c" value="org.apache.tools.ant.types.PolyTest"/>
+  
+  <path id="test-c">
+    <pathelement location="../../../../build/testcases" />
+    <pathelement path="${java.class.path}" />
+  </path>
+
+  <target name="init">
+    <typedef loaderref="poly"   classpathref="test-c"
+             name = "myfileset" classname="${c}$MyFileSet"/>
+
+    <typedef loaderref="poly" classpathref="test-c"
+             name = "mypath"  classname="${c}$MyPath"/>
+
+    <typedef loaderref="poly" classpathref="test-c"
+             name = "mytask"  classname="${c}$MyTask"/>
+  </target>
+
+  <target name="fileset" depends="init">
+    <mytask>
+      <fileset dir="."/>
+    </mytask>
+  </target>
+
+  <target name="fileset-ant-type" depends="init">
+    <mytask>
+      <fileset ant-type="myfileset" dir="."/>
+    </mytask>
+  </target>
+
+  <target name="path" depends="init">
+    <mytask>
+      <path path="."/>
+    </mytask>
+  </target>
+
+  <target name="path-ant-type" depends="init">
+    <mytask>
+      <path ant-type="mypath" path="."/>
+    </mytask>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/types/propertyset.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/propertyset.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/propertyset.xml	(revision 16139)
@@ -0,0 +1,94 @@
+<!--
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+-->
+
+<project>
+  <property name="fooA" value="FooA"/>
+  <property name="barB" value="BarB"/>
+
+  <propertyset id="properties-starting-with-foo">
+    <propertyref prefix="foo"/>
+  </propertyset>
+  <propertyset id="properties-starting-with-bar">
+    <propertyref prefix="bar"/>
+  </propertyset>
+  <propertyset id="my-set">
+    <propertyset refid="properties-starting-with-foo"/>
+    <propertyset refid="properties-starting-with-bar"/>
+  </propertyset>
+  
+  <macrodef name="expect.equals">
+    <attribute name="test"/>
+    <attribute name="exp"/>
+    <attribute name="got"/>
+    <sequential>
+      <fail message=
+          "@{test} failed: expected &quot;@{exp}&quot; got &quot;@{got}&quot;">
+        <condition>
+          <not>
+            <equals arg1="@{exp}" arg2="@{got}"/>
+          </not>
+        </condition>
+      </fail>
+    </sequential>
+  </macrodef>
+
+  <target name="reference-to-two-references">
+    <expect.equals
+      test="reference to two references"
+      exp="barB=BarB, fooA=FooA"
+      got="${toString:my-set}"/>
+  </target>
+
+  <target name="nested-mapped">
+    <propertyset id="nested-mapped">
+      <propertyset>
+        <propertyset refid="properties-starting-with-foo"/>
+        <globmapper from="foo*" to="boo*" />
+      </propertyset>
+      <propertyset>
+        <propertyset refid="properties-starting-with-bar"/>
+        <globmapper from="bar*" to="far*" />
+      </propertyset>
+    </propertyset>
+    <expect.equals
+      test="nested mapped propertysets"
+      exp="booA=FooA, farB=BarB"
+      got="${toString:nested-mapped}"/>
+  </target>
+
+  <target name="nested-mapped-mapped">
+    <propertyset id="nested-mapped-mapped">
+      <propertyset>
+        <propertyset refid="properties-starting-with-foo"/>
+        <globmapper from="foo*" to="boo*" />
+      </propertyset>
+      <propertyset>
+        <propertyset refid="properties-starting-with-bar"/>
+        <globmapper from="bar*" to="far*" />
+      </propertyset>
+      <mapper>
+        <globmapper from="boo*" to="hoo*" />
+        <globmapper from="far*" to="near*" />
+      </mapper>
+    </propertyset>
+    <expect.equals
+      test="nested mapped propertysets"
+      exp="hooA=FooA, nearB=BarB"
+      got="${toString:nested-mapped-mapped}"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/types/quote1.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/quote1.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/quote1.xml	(revision 16139)
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+  <!-- I belong to:
+       org.apache.tools.ant.types.XMLCatalogBuildFileTest.java 
+       -->
+
+<para>
+  A stitch in time saves nine
+</para>
Index: /release-kits/shared/ant/src/etc/testcases/types/quote2.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/quote2.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/quote2.xml	(revision 16139)
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+  <!-- I belong to:
+       org.apache.tools.ant.types.XMLCatalogBuildFileTest.java 
+       -->
+
+<para>
+  No news is good news
+</para>
+
Index: /release-kits/shared/ant/src/etc/testcases/types/redirector.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/redirector.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/redirector.xml	(revision 16139)
@@ -0,0 +1,54 @@
+<!-- Copyright 2004-2005 The Apache Software Foundation. -->
+<project name="redirector" basedir=".">
+
+  <target name="test1" description="create ref">
+    <redirector id="test1" output="out" />
+  </target>
+
+  <target name="test2" depends="test1" description="fail">
+    <redirector refid="test1" output="out" />
+  </target>
+
+  <target name="test3" depends="test1" description="fail">
+    <redirector refid="test1">
+      <outputmapper type="flatten" />
+    </redirector>
+  </target>
+
+  <target name="test4" depends="test1" description="pass">
+    <redirector>
+      <outputmapper type="flatten" />
+    </redirector>
+  </target>
+
+  <target name="testRefid" depends="cat-check" if="can-cat">
+    <fail message="Property testRefid.out is already set!">
+      <condition>
+        <isset property="testRefid.out" />
+      </condition>
+    </fail>
+    <redirector id="r" outputproperty="testRefid.out" inputstring="foo" />
+    <exec executable="cat">
+      <redirector refid="r" />
+    </exec>
+    <fail>
+      <condition>
+        <not>
+          <equals arg1="${testRefid.out}" arg2="foo" />
+        </not>
+      </condition>
+    </fail>
+  </target>
+
+  <target name="cat-check">
+    <property environment="env" />
+    <condition property="can-cat">
+      <or>
+        <available file="cat" filepath="${env.PATH}" property="can-cat" />
+        <available file="cat.exe" filepath="${env.PATH}" property="can-cat" />
+        <available file="cat.exe" filepath="${env.Path}" property="can-cat" />
+      </or>
+    </condition>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/types/selectors.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/selectors.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/selectors.xml	(revision 16139)
@@ -0,0 +1,245 @@
+<?xml version="1.0"?>
+
+<project name="selectors-test" basedir="." default="setupfiles">
+
+  <property name="etc.dir" value=".."/>
+  <property name="test.dir"
+          value="selectortest"/>
+  <property name="testregexpsrc.dir"
+          value="regexpseltestsrc"/>
+  <property name="testregexpdest.dir"
+          value="regexpseltestdest"/>
+  <property name="mirror.dir"
+          value="selectortest2"/>
+
+  <target name="setupfiles">
+    <mkdir dir="${test.dir}" />
+    <mkdir dir="${test.dir}/zip" />
+    <mkdir dir="${test.dir}/tar" />
+    <mkdir dir="${test.dir}/tar/gz" />
+    <mkdir dir="${test.dir}/tar/bz2" />
+    <copy file="${etc.dir}/taskdefs/expected/asf-logo.gif.md5"
+            tofile="${test.dir}/asf-logo.gif.md5" />
+    <copy file="${etc.dir}/taskdefs/expected/asf-logo.gif.bz2"
+            tofile="${test.dir}/asf-logo.gif.bz2" />
+    <copy file="${etc.dir}/taskdefs/expected/asf-logo.gif.gz"
+            tofile="${test.dir}/asf-logo.gif.gz" />
+    <copy file="${etc.dir}/taskdefs/expected/copy.filterset.filtered"
+            tofile="${test.dir}/copy.filterset.filtered" />
+    <copy file="${etc.dir}/taskdefs/expected/asf-logo.gif.zip"
+            tofile="${test.dir}/zip/asf-logo.gif.zip" />
+    <copy file="${etc.dir}/taskdefs/expected/asf-logo.gif.tar"
+            tofile="${test.dir}/tar/asf-logo.gif.tar" />
+    <copy file="${etc.dir}/taskdefs/expected/asf-logo-huge.tar.gz"
+            tofile="${test.dir}/tar/asf-logo-huge.tar.gz" />
+    <copy file="${etc.dir}/taskdefs/expected/asf-logo.gif.tar.gz"
+            tofile="${test.dir}/tar/gz/asf-logo.gif.tar.gz" />
+    <copy file="${etc.dir}/taskdefs/expected/asf-logo.gif.tar.bz2"
+            tofile="${test.dir}/tar/bz2/asf-logo.gif.tar.bz2" />
+    <copy file="${etc.dir}/taskdefs/expected/asf-logo-huge.tar.bz2"
+            tofile="${test.dir}/tar/bz2/asf-logo-huge.tar.bz2" />
+    <!-- Make linefeeds consistent between platforms -->
+    <fixcrlf srcdir="${test.dir}" includes="*.filtered" eol="lf"/>
+    <!-- Set a known base time for all files -->
+    <touch datetime="11/21/2001 4:55 AM">
+        <fileset dir="${test.dir}">
+            <include name="**/*"/>
+        </fileset>
+    </touch>
+    <!-- Then adjust individual ones -->
+    <touch file="${test.dir}/asf-logo.gif.bz2"
+            datetime="01/01/2001 12:00 AM"/>
+    <touch file="${test.dir}/asf-logo.gif.gz"
+            datetime="04/15/2002 2:30 PM"/>
+    <touch file="${test.dir}/zip/asf-logo.gif.zip"
+            datetime="05/10/2002 2:30 PM"/>
+    <touch file="${test.dir}/tar/asf-logo.gif.tar"
+            datetime="05/10/2002 2:29 PM"/>
+    <touch file="${test.dir}/tar/asf-logo-huge.tar.gz"
+            datetime="05/10/2002 2:29 AM"/>
+  </target>
+
+  <target name="cleanup">
+    <delete dir="${test.dir}" />
+  </target>
+
+  <target name="mirrorfiles">
+    <mkdir dir="${mirror.dir}" />
+    <mkdir dir="${mirror.dir}/zip" />
+    <mkdir dir="${mirror.dir}/tar" />
+    <mkdir dir="${mirror.dir}/tar/gz" />
+    <mkdir dir="${mirror.dir}/tar/bz2" />
+    <touch file="${mirror.dir}/asf-logo.gif.md5"/>
+    <touch file="${mirror.dir}/asf-logo.gif.bz2"/>
+    <sleep seconds="2"/>
+    <touch file="${mirror.dir}/zip/asf-logo.gif.zip"/>
+    <sleep seconds="3"/>
+    <touch file="${mirror.dir}/tar/asf-logo.gif.tar"/>
+    <sleep seconds="2"/>
+    <touch file="${mirror.dir}/tar/asf-logo-huge.tar.gz"/>
+    <touch file="${mirror.dir}/tar/gz/asf-logo.gif.tar.gz"/>
+    <touch file="${mirror.dir}/tar/bz2/asf-logo.gif.tar.bz2"/>
+    <touch file="${mirror.dir}/tar/bz2/asf-logo-huge.tar.bz2"/>
+  </target>
+
+  <target name="cleanup.mirrorfiles">
+    <delete dir="${mirror.dir}" />
+  </target>
+
+  <target name="cleanupregexp">
+    <delete dir="${testregexpsrc.dir}" />
+    <delete dir="${testregexpdest.dir}" />
+  </target>
+
+  <target name="containsregexp">
+    <mkdir dir="${testregexpsrc.dir}" />
+    <mkdir dir="${testregexpdest.dir}" />
+    <!-- Make two test files, shouldcopy.txt will get selected if everything works
+         shouldnotcopy.txt will not get selected for copy.  The test looks to see
+         that only one file is copied
+    -->
+    <echo message="Some testregexp text 2.0" file="${testregexpsrc.dir}/shouldcopy.txt" />
+    <echo message="Some testregexp text 20" file="${testregexpsrc.dir}/shouldnotcopy.txt" />
+    <copy todir="${testregexpdest.dir}">
+      <fileset dir="${testregexpsrc.dir}">
+        <include name="*.txt" />
+        <containsregexp expression="[0-9]\.[0,1,2]" />
+      </fileset>
+    </copy>
+  </target>
+
+  <!-- ==========  Test for ModifiedSelector  ========== -->
+
+  <target name="modifiedselectortest-makeDirty">
+      <!-- Load propertyfile generated by SelectorTest-class -->
+      <property file="ModifiedSelectorTest.properties"/>
+
+      <!-- Modify only timestamp -->
+      <touch file="${test.dir}/${f2name}" datetime="02/28/2003 9:55 AM"/>
+      <!-- Change content but keep timestamp -->
+      <echo file="${test.dir}/${f3name}" append="true" message="new content"/>
+      <touch file="${test.dir}/${f3name}" datetime="11/21/2001 4:55 AM"/>
+      <!-- Change content and timestamp -->
+      <echo file="${test.dir}/${f4name}" append="true" message="new content"/>
+  </target>
+
+  <target name="modifiedselectortest-scenario-clean">
+      <delete dir="${test.dir}"/>
+  </target>
+
+  <target name="modifiedselectortest-scenario-prepare">
+      <mkdir dir="${test.dir}/src"/>
+      <copy todir="${test.dir}/src">
+          <fileset dir="${ant.home}/lib" includes="ant.jar">
+              <type type="file"/>
+          </fileset>
+          <fileset dir="${ant.home}/bin">
+                <type type="file"/>
+          </fileset>
+      </copy>
+      <touch datetime="12/24/2002 4:00 pm">
+          <fileset dir="${test.dir}"/>
+      </touch>
+      <mkdir dir="${test.dir}/to-1"/>
+      <mkdir dir="${test.dir}/to-2"/>
+      <mkdir dir="${test.dir}/to-3"/>
+  </target>
+
+  <target name="modifiedselectortest-scenario-makeDirty">
+      <touch file="${test.dir}/src/ant.jar"/>
+      <echo file="${test.dir}/src/ant.bat" append="true" message="new-content"/>
+      <echo file="${test.dir}/src/antRun.pl" append="true" message="new-content"/>
+      <touch file="${test.dir}/src/antRun.pl" datetime="12/24/2002 4:00 pm"/>
+  </target>
+
+  <target name="modifiedselectortest-scenario-coreselector-defaults" depends="modifiedselectortest-scenario-prepare">
+      <!-- copy first time and create cachefile -->
+      <copy todir="${test.dir}/to-1">
+          <fileset dir="${test.dir}/src">
+              <modified/>
+          </fileset>
+      </copy>
+      <!-- copy second time: nothing should be copied -->
+      <copy todir="${test.dir}/to-2">
+          <fileset dir="${test.dir}/src">
+              <modified/>
+          </fileset>
+      </copy>
+      <!-- 'modify' the source files -->
+      <antcall target="modifiedselectortest-scenario-makeDirty"/>
+      <!-- copy third time: only the files with new CONTENT should be copied -->
+      <copy todir="${test.dir}/to-3">
+          <fileset dir="${test.dir}/src">
+              <modified/>
+          </fileset>
+      </copy>
+  </target>
+
+  <target name="modifiedselectortest-scenario-coreselector-settings" depends="modifiedselectortest-scenario-prepare">
+      <!-- copy first time and create cachefile -->
+      <copy todir="${test.dir}/to-1">
+          <fileset dir="${test.dir}/src">
+              <modified cache="propertyfile" algorithm="hashvalue" update="true">
+                  <param name="cache.cachefile" value="core.cache.properties" />
+              </modified>
+          </fileset>
+      </copy>
+      <!-- copy second time: nothing should be copied -->
+      <copy todir="${test.dir}/to-2">
+          <fileset dir="${test.dir}/src">
+              <modified cache="propertyfile" algorithm="hashvalue" update="true">
+                  <param name="cache.cachefile" value="core.cache.properties" />
+              </modified>
+          </fileset>
+      </copy>
+      <!-- 'modify' the source files -->
+      <antcall target="modifiedselectortest-scenario-makeDirty"/>
+      <!-- copy third time: only the files with new CONTENT should be copied -->
+      <copy todir="${test.dir}/to-3">
+          <fileset dir="${test.dir}/src">
+              <modified cache="propertyfile" algorithm="hashvalue" update="true">
+                  <param name="cache.cachefile" value="core.cache.properties" />
+              </modified>
+          </fileset>
+      </copy>
+  </target>
+
+  <target name="modifiedselectortest-scenario-customselector-settings" depends="modifiedselectortest-scenario-prepare">
+      <!-- copy first time and create cachefile -->
+      <copy todir="${test.dir}/to-1">
+          <fileset dir="${test.dir}/src">
+              <custom classname="org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector">
+                  <param name="cache"      value="propertyfile"/>
+                  <param name="algorithm"  value="hashvalue"/>
+                  <param name="update"     value="true"/>
+                  <param name="cache.cachefile" value="core.cache.properties"/>
+              </custom>
+          </fileset>
+      </copy>
+      <!-- copy second time: nothing should be copied -->
+      <copy todir="${test.dir}/to-2">
+          <fileset dir="${test.dir}/src">
+              <custom classname="org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector">
+                  <param name="cache"      value="propertyfile"/>
+                  <param name="algorithm"  value="hashvalue"/>
+                  <param name="update"     value="true"/>
+                  <param name="cache.cachefile" value="core.cache.properties"/>
+              </custom>
+          </fileset>
+      </copy>
+      <!-- 'modify' the source files -->
+      <antcall target="modifiedselectortest-scenario-makeDirty"/>
+      <!-- copy third time: only the files with new CONTENT should be copied -->
+      <copy todir="${test.dir}/to-3">
+          <fileset dir="${test.dir}/src">
+              <custom classname="org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector">
+                  <param name="cache"      value="propertyfile"/>
+                  <param name="algorithm"  value="hashvalue"/>
+                  <param name="update"     value="true"/>
+                  <param name="cache.cachefile" value="core.cache.properties"/>
+              </custom>
+          </fileset>
+      </copy>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/types/xmlcatalog.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/xmlcatalog.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/xmlcatalog.xml	(revision 16139)
@@ -0,0 +1,134 @@
+<?xml version='1.0'?>
+<!DOCTYPE project>
+
+<project name="xmlcatalog" default="all" basedir=".">
+
+  <description>
+    This is to test XMLCatalog for external entity resolution from the
+    xslt task, both the simple case and using the document() function
+    to refer to a second file (which refers to the entity). This
+    buildfile is called by
+    org.apache.tools.ant.types.XMLCatalogBuildFileTest.java
+    Alternatively, you may try it out by hand by first removing the
+    comment symbols around the echo statements and then calling 
+    ant -buildfile xmlcatalog.xml
+  </description>
+
+  <property name="transformer" value = "xmlcatalog.xsl"/>
+
+  <target 
+    name    = "all" 
+    depends = "testentitynocatalog, testentitywithcatalog, 
+    testdocumentnocatalog, testdocumentwithcatalog"/>
+
+  <target 
+    name        = "testentitynocatalog"
+    description = "Test external entity resolver for simple XML
+document without using XMLCatalog">
+    <delete
+      quiet = "yes"
+      file  = "result.out"/>
+    <xslt
+      basedir   = "${basedir}"
+      destdir   = "${basedir}"
+      extension = ".text"
+      style     = "${transformer}"
+      in        = "xmlcatalog1.xml"
+      out       = "result.out">
+      <param
+        name       = "outprop"
+        expression = "val1"/>
+    </xslt>
+    <property file = "result.out"/>
+    <!-- <echo message = "${val1}"/> -->
+    <delete
+      quiet = "yes"
+      file  = "result.out"/>
+  </target>
+
+  <target 
+    name        = "testentitywithcatalog"
+    description = "Test external entity resolver for simple
+XML document using XMLCatalog">
+    <delete
+      quiet = "yes"
+      file  = "result.out"/>
+    <xslt
+      basedir   = "${basedir}"
+      destdir   = "${basedir}"
+      extension = ".text"
+      style     = "${transformer}"
+      in        = "xmlcatalog1.xml"
+      out       = "result.out">
+      <param
+        name       = "outprop"
+        expression = "val2"/>
+      <xmlcatalog>
+        <entity 
+          publicId = "myquote"
+          location = "quote2.xml"/>
+      </xmlcatalog>
+    </xslt>
+    <property file = "result.out"/>
+    <!-- <echo message = "${val2}"/> -->
+    <delete
+      quiet = "yes"
+      file  = "result.out"/>
+  </target>
+
+  <target 
+    name        = "testdocumentnocatalog"
+    description = "Test entity resolution in XML document called
+from XSLT document() function without using XMLCatalog">
+    <delete
+      quiet = "yes"
+      file  = "result.out"/>
+    <xslt
+      basedir   = "${basedir}"
+      destdir   = "${basedir}"
+      extension = ".text"
+      style     = "${transformer}"
+      in        = "xmlcatalog2.xml"
+      out       = "result.out">
+      <param
+        name       = "outprop"
+        expression = "val3"/>
+    </xslt>
+    <property file = "result.out"/>
+    <!-- <echo message = "${val3}"/> -->
+    <delete
+      quiet = "yes"
+      file  = "result.out"/>
+  </target>
+
+  <target 
+    name        = "testdocumentwithcatalog" 
+    description = "Test entity resolution in XML document called
+from XSLT document() function using XMLCatalog">
+    <delete
+      quiet = "yes"
+      file  = "result.out"/>
+    <xslt
+      basedir   = "${basedir}"
+      destdir   = "${basedir}"
+      extension = ".text"
+      style     = "${transformer}"
+      in        = "xmlcatalog2.xml"
+      out       = "result.out">
+      <param
+        name       = "outprop"
+        expression = "val4"/>
+      <xmlcatalog>
+        <entity 
+          publicId = "myquote"
+          location = "quote2.xml"/>
+      </xmlcatalog>
+    </xslt>
+    <property file = "result.out"/>
+    <!-- <echo message = "${val4}"/> -->
+    <delete
+      quiet = "yes"
+      file  = "result.out"/>
+  </target>
+
+</project>
Index: /release-kits/shared/ant/src/etc/testcases/types/xmlcatalog.xsl
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/xmlcatalog.xsl	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/xmlcatalog.xsl	(revision 16139)
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<xsl:stylesheet 
+  xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+  version="1.0">
+
+  <!-- I belong to:
+       org.apache.tools.ant.types.XMLCatalogBuildFileTest.java 
+       -->
+
+  <xsl:output method="text"/>
+
+  <!-- name of the output parameter to write -->
+  <xsl:param name="outprop">value</xsl:param>
+
+  <xsl:strip-space elements="*"/>
+
+  <xsl:template match="/">
+    <xsl:value-of select="$outprop"/>: <xsl:apply-templates select="/fragment/para"/>
+  </xsl:template>
+
+  <!-- This will only be matched in doc2.xml -->
+  <xsl:template match="Ref">
+    <xsl:apply-templates select="document(@file)/fragment/para"/>
+  </xsl:template>
+
+  <!-- This will only be matched in doc1.xml -->
+  <xsl:template match="text()">
+    <xsl:value-of select="normalize-space(.)"/>
+  </xsl:template>
+
+</xsl:stylesheet>
Index: /release-kits/shared/ant/src/etc/testcases/types/xmlcatalog1.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/xmlcatalog1.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/xmlcatalog1.xml	(revision 16139)
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE fragment [
+  <!ENTITY quote PUBLIC "myquote" "quote1.xml">
+]>
+
+  <!-- I belong to:
+       org.apache.tools.ant.types.XMLCatalogBuildFileTest.java 
+       -->
+
+<fragment>
+  <para>
+      &quote;
+  </para>
+</fragment>
Index: /release-kits/shared/ant/src/etc/testcases/types/xmlcatalog2.xml
===================================================================
--- /release-kits/shared/ant/src/etc/testcases/types/xmlcatalog2.xml	(revision 16139)
+++ /release-kits/shared/ant/src/etc/testcases/types/xmlcatalog2.xml	(revision 16139)
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+
+  <!-- I belong to:
+       org.apache.tools.ant.types.XMLCatalogBuildFileTest.java 
+       -->
+
+<fragment>
+  <para>
+    <Ref file="xmlcatalog1.xml"/>
+  </para>
+</fragment>
Index: /release-kits/shared/ant/src/etc/yearcheck.sh
===================================================================
--- /release-kits/shared/ant/src/etc/yearcheck.sh	(revision 16139)
+++ /release-kits/shared/ant/src/etc/yearcheck.sh	(revision 16139)
@@ -0,0 +1,79 @@
+#
+#  Copyright  2002-2004 The Apache Software Foundation
+# 
+#   Licensed 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.
+# 
+#
+
+#
+# Simple shell script that checks whether changed files contain a copyright
+# statement for a given year.
+#
+# Rename (or symlink) this script to the year you want to check, i.e. name it
+# 2002 if you want to check for copyright statements that do not contain#
+# the year 2002.
+#
+# Use this script instead of your usual cvs update command.
+#
+# Usage YEAR [all]
+#
+# If the optional all argument has been omitted, the proposal directory will
+# be skipped.
+#
+
+if [ -n "$TMP" ]; then
+  TEMP_FILE="$TMP"/changed-files
+else
+  if [ -n "$TEMP" ]; then
+    TEMP_FILE="$TEMP"/changed-files
+  else
+    TEMP_FILE=/tmp/changed-files
+  fi
+fi
+
+YEAR=`basename $0`
+
+if [ $YEAR = yearcheck.sh ]; then
+    YEAR=`date -R | cut -d ' ' -f 4`
+fi
+
+if [ -z "$1" ]; then
+  cvs -z3 update -dP | fgrep -v proposal | cut -f 2 -d ' ' > $TEMP_FILE
+else
+  if [ "all" == "$1" ]; then
+    cvs -z3 update -dP | cut -f 2 -d ' ' > $TEMP_FILE
+  else
+    echo "Usage: $YEAR [all]"
+    exit
+  fi
+fi
+
+echo "Changed:"
+echo "========"
+cat $TEMP_FILE
+echo
+
+xargs fgrep -L Copyright < $TEMP_FILE > /tmp/no-copyright
+
+echo "No Copyright line"
+echo "================="
+cat /tmp/no-copyright
+echo
+
+xargs egrep -L "Copyright.*$YEAR" < $TEMP_FILE | cut -f 1 -d : > /tmp/no-$YEAR
+
+echo "No Copyright line for year $YEAR"
+echo "================================"
+cat /tmp/no-$YEAR
+
+rm $TEMP_FILE
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/AntClassLoader.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/AntClassLoader.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/AntClassLoader.java	(revision 16139)
@@ -0,0 +1,1317 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.CollectionUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.LoaderUtils;
+
+/**
+ * Used to load classes within ant with a different classpath from
+ * that used to start ant. Note that it is possible to force a class
+ * into this loader even when that class is on the system classpath by
+ * using the forceLoadClass method. Any subsequent classes loaded by that
+ * class will then use this loader rather than the system class loader.
+ *
+ */
+public class AntClassLoader extends ClassLoader implements SubBuildListener {
+
+    private static final FileUtils FILE_UTILS = FileUtils.newFileUtils();
+
+    /**
+     * An enumeration of all resources of a given name found within the
+     * classpath of this class loader. This enumeration is used by the
+     * ClassLoader.findResources method, which is in
+     * turn used by the ClassLoader.getResources method.
+     *
+     * @see AntClassLoader#findResources(String)
+     * @see java.lang.ClassLoader#getResources(String)
+     */
+    private class ResourceEnumeration implements Enumeration {
+        /**
+         * The name of the resource being searched for.
+         */
+        private String resourceName;
+
+        /**
+         * The index of the next classpath element to search.
+         */
+        private int pathElementsIndex;
+
+        /**
+         * The URL of the next resource to return in the enumeration. If this
+         * field is <code>null</code> then the enumeration has been completed,
+         * i.e., there are no more elements to return.
+         */
+        private URL nextResource;
+
+        /**
+         * Constructs a new enumeration of resources of the given name found
+         * within this class loader's classpath.
+         *
+         * @param name the name of the resource to search for.
+         */
+        ResourceEnumeration(String name) {
+            this.resourceName = name;
+            this.pathElementsIndex = 0;
+            findNextResource();
+        }
+
+        /**
+         * Indicates whether there are more elements in the enumeration to
+         * return.
+         *
+         * @return <code>true</code> if there are more elements in the
+         *         enumeration; <code>false</code> otherwise.
+         */
+        public boolean hasMoreElements() {
+            return (this.nextResource != null);
+        }
+
+        /**
+         * Returns the next resource in the enumeration.
+         *
+         * @return the next resource in the enumeration
+         */
+        public Object nextElement() {
+            URL ret = this.nextResource;
+            findNextResource();
+            return ret;
+        }
+
+        /**
+         * Locates the next resource of the correct name in the classpath and
+         * sets <code>nextResource</code> to the URL of that resource. If no
+         * more resources can be found, <code>nextResource</code> is set to
+         * <code>null</code>.
+         */
+        private void findNextResource() {
+            URL url = null;
+            while ((pathElementsIndex < pathComponents.size())
+                    && (url == null)) {
+                try {
+                    File pathComponent
+                        = (File) pathComponents.elementAt(pathElementsIndex);
+                    url = getResourceURL(pathComponent, this.resourceName);
+                    pathElementsIndex++;
+                } catch (BuildException e) {
+                    // ignore path elements which are not valid relative to the
+                    // project
+                }
+            }
+            this.nextResource = url;
+        }
+    }
+
+    /**
+     * The size of buffers to be used in this classloader.
+     */
+    private static final int BUFFER_SIZE = 8192;
+    /**
+     * Number of array elements in a test array of strings
+     */
+    private static final int NUMBER_OF_STRINGS = 256;
+
+    /**
+     * The components of the classpath that the classloader searches
+     * for classes.
+     */
+    private Vector pathComponents  = new Vector();
+
+    /**
+     * The project to which this class loader belongs.
+     */
+    private Project project;
+
+    /**
+     * Indicates whether the parent class loader should be
+     * consulted before trying to load with this class loader.
+     */
+    private boolean parentFirst = true;
+
+    /**
+     * These are the package roots that are to be loaded by the parent class
+     * loader regardless of whether the parent class loader is being searched
+     * first or not.
+     */
+    private Vector systemPackages = new Vector();
+
+    /**
+     * These are the package roots that are to be loaded by this class loader
+     * regardless of whether the parent class loader is being searched first
+     * or not.
+     */
+    private Vector loaderPackages = new Vector();
+
+    /**
+     * Whether or not this classloader will ignore the base
+     * classloader if it can't find a class.
+     *
+     * @see #setIsolated(boolean)
+     */
+    private boolean ignoreBase = false;
+
+    /**
+     * The parent class loader, if one is given or can be determined.
+     */
+    private ClassLoader parent = null;
+
+    /**
+     * A hashtable of zip files opened by the classloader (File to ZipFile).
+     */
+    private Hashtable zipFiles = new Hashtable();
+
+    /**
+     * The context loader saved when setting the thread's current
+     * context loader.
+     */
+    private ClassLoader savedContextLoader = null;
+    /**
+     * Whether or not the context loader is currently saved.
+     */
+    private boolean isContextLoaderSaved = false;
+
+    /**
+     * Reflection method reference for getProtectionDomain;
+     * used to avoid 1.1-compatibility problems.
+     */
+    private static Method getProtectionDomain = null;
+
+    /**
+     * Reflection method reference for defineClassProtectionDomain;
+     * used to avoid 1.1-compatibility problems.
+     */
+    private static Method defineClassProtectionDomain = null;
+
+
+    // Set up the reflection-based Java2 methods if possible
+    static {
+        try {
+            getProtectionDomain
+                = Class.class.getMethod("getProtectionDomain", new Class[0]);
+            Class protectionDomain
+                = Class.forName("java.security.ProtectionDomain");
+            Class[] args = new Class[] {String.class, byte[].class,
+                Integer.TYPE, Integer.TYPE, protectionDomain};
+            defineClassProtectionDomain
+                = ClassLoader.class.getDeclaredMethod("defineClass", args);
+        } catch (Exception e) {
+            // ignore failure to get access to 1.2+ methods
+        }
+    }
+
+
+    /**
+     * Create an Ant Class Loader
+     */
+    public AntClassLoader() {
+        setParent(null);
+    }
+
+    /**
+     * Creates a classloader for the given project using the classpath given.
+     *
+     * @param project The project to which this classloader is to belong.
+     *                Must not be <code>null</code>.
+     * @param classpath The classpath to use to load the classes.  This
+     *                is combined with the system classpath in a manner
+     *                determined by the value of ${build.sysclasspath}.
+     *                May be <code>null</code>, in which case no path
+     *                elements are set up to start with.
+     */
+    public AntClassLoader(Project project, Path classpath) {
+        setParent(null);
+        setProject(project);
+        setClassPath(classpath);
+    }
+
+    /**
+     * Creates a classloader for the given project using the classpath given.
+     *
+     * @param parent The parent classloader to which unsatisfied loading
+     *               attempts are delegated. May be <code>null</code>,
+     *               in which case the classloader which loaded this
+     *               class is used as the parent.
+     * @param project The project to which this classloader is to belong.
+     *                Must not be <code>null</code>.
+     * @param classpath the classpath to use to load the classes.
+     *                  May be <code>null</code>, in which case no path
+     *                  elements are set up to start with.
+     * @param parentFirst If <code>true</code>, indicates that the parent
+     *                    classloader should be consulted  before trying to
+     *                    load the a class through this loader.
+     */
+    public AntClassLoader(ClassLoader parent, Project project, Path classpath,
+                          boolean parentFirst) {
+        this(project, classpath);
+        if (parent != null) {
+            setParent(parent);
+        }
+        setParentFirst(parentFirst);
+        addJavaLibraries();
+    }
+
+
+    /**
+     * Creates a classloader for the given project using the classpath given.
+     *
+     * @param project The project to which this classloader is to belong.
+     *                Must not be <code>null</code>.
+     * @param classpath The classpath to use to load the classes. May be
+     *                  <code>null</code>, in which case no path
+     *                  elements are set up to start with.
+     * @param parentFirst If <code>true</code>, indicates that the parent
+     *                    classloader should be consulted before trying to
+     *                    load the a class through this loader.
+     */
+    public AntClassLoader(Project project, Path classpath,
+                          boolean parentFirst) {
+        this(null, project, classpath, parentFirst);
+    }
+
+    /**
+     * Creates an empty class loader. The classloader should be configured
+     * with path elements to specify where the loader is to look for
+     * classes.
+     *
+     * @param parent The parent classloader to which unsatisfied loading
+     *               attempts are delegated. May be <code>null</code>,
+     *               in which case the classloader which loaded this
+     *               class is used as the parent.
+     * @param parentFirst If <code>true</code>, indicates that the parent
+     *                    classloader should be consulted before trying to
+     *                    load the a class through this loader.
+     */
+    public AntClassLoader(ClassLoader parent, boolean parentFirst) {
+        setParent(parent);
+        project = null;
+        this.parentFirst = parentFirst;
+    }
+
+    /**
+     * Set the project associated with this class loader
+     *
+     * @param project the project instance
+     */
+    public void setProject(Project project) {
+        this.project = project;
+        if (project != null) {
+            project.addBuildListener(this);
+        }
+    }
+
+    /**
+     * Set the classpath to search for classes to load. This should not be
+     * changed once the classloader starts to server classes
+     *
+     * @param classpath the search classpath consisting of directories and
+     *        jar/zip files.
+     */
+    public void setClassPath(Path classpath) {
+        pathComponents.removeAllElements();
+        if (classpath != null) {
+            Path actualClasspath = classpath.concatSystemClasspath("ignore");
+            String[] pathElements = actualClasspath.list();
+            for (int i = 0; i < pathElements.length; ++i) {
+                try {
+                    addPathElement(pathElements[i]);
+                } catch (BuildException e) {
+                    // ignore path elements which are invalid
+                    // relative to the project
+                }
+            }
+        }
+    }
+
+    /**
+     * Set the parent for this class loader. This is the class loader to which
+     * this class loader will delegate to load classes
+     *
+     * @param parent the parent class loader.
+     */
+    public void setParent(ClassLoader parent) {
+        if (parent == null) {
+            this.parent = AntClassLoader.class.getClassLoader();
+        } else {
+            this.parent = parent;
+        }
+    }
+
+    /**
+     * Control whether class lookup is delegated to the parent loader first
+     * or after this loader. Use with extreme caution. Setting this to
+     * false violates the class loader hierarchy and can lead to Linkage errors
+     *
+     * @param parentFirst if true, delegate initial class search to the parent
+     *                    classloader.
+     */
+    public void setParentFirst(boolean parentFirst) {
+        this.parentFirst = parentFirst;
+    }
+
+
+    /**
+     * Logs a message through the project object if one has been provided.
+     *
+     * @param message The message to log.
+     *                Should not be <code>null</code>.
+     *
+     * @param priority The logging priority of the message.
+     */
+    protected void log(String message, int priority) {
+        if (project != null) {
+            project.log(message, priority);
+        }
+//         else {
+//             System.out.println(message);
+//         }
+    }
+
+    /**
+     * Sets the current thread's context loader to this classloader, storing
+     * the current loader value for later resetting.
+     */
+    public void setThreadContextLoader() {
+        if (isContextLoaderSaved) {
+            throw new BuildException("Context loader has not been reset");
+        }
+        if (LoaderUtils.isContextLoaderAvailable()) {
+            savedContextLoader = LoaderUtils.getContextClassLoader();
+            ClassLoader loader = this;
+            if (project != null
+                && "only".equals(project.getProperty("build.sysclasspath"))) {
+                loader = this.getClass().getClassLoader();
+            }
+            LoaderUtils.setContextClassLoader(loader);
+            isContextLoaderSaved = true;
+        }
+    }
+
+    /**
+     * Resets the current thread's context loader to its original value.
+     */
+    public void resetThreadContextLoader() {
+        if (LoaderUtils.isContextLoaderAvailable()
+            && isContextLoaderSaved) {
+            LoaderUtils.setContextClassLoader(savedContextLoader);
+            savedContextLoader = null;
+            isContextLoaderSaved = false;
+        }
+    }
+
+
+    /**
+     * Adds an element to the classpath to be searched.
+     *
+     * @param pathElement The path element to add. Must not be
+     *                    <code>null</code>.
+     *
+     * @exception BuildException if the given path element cannot be resolved
+     *                           against the project.
+     */
+    public void addPathElement(String pathElement) throws BuildException {
+        File pathComponent
+            = project != null ? project.resolveFile(pathElement)
+                              : new File(pathElement);
+        try {
+            addPathFile(pathComponent);
+        } catch (IOException e) {
+            throw new BuildException(e);
+        }
+    }
+
+    /**
+     * Add a file to the path
+     *
+     * @param pathComponent the file which is to be added to the path for
+     *                      this class loader
+     *
+     * @throws IOException if data needed from the file cannot be read.
+     */
+    protected void addPathFile(File pathComponent) throws IOException {
+        pathComponents.addElement(pathComponent);
+    }
+
+    /**
+     * Returns the classpath this classloader will consult.
+     *
+     * @return the classpath used for this classloader, with elements
+     *         separated by the path separator for the system.
+     */
+    public String getClasspath() {
+        StringBuffer sb = new StringBuffer();
+        boolean firstPass = true;
+        Enumeration componentEnum = pathComponents.elements();
+        while (componentEnum.hasMoreElements()) {
+            if (!firstPass) {
+                sb.append(System.getProperty("path.separator"));
+            } else {
+                firstPass = false;
+            }
+            sb.append(((File) componentEnum.nextElement()).getAbsolutePath());
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Sets whether this classloader should run in isolated mode. In
+     * isolated mode, classes not found on the given classpath will
+     * not be referred to the parent class loader but will cause a
+     * ClassNotFoundException.
+     *
+     * @param isolated Whether or not this classloader should run in
+     *                 isolated mode.
+     */
+    public synchronized void setIsolated(boolean isolated) {
+        ignoreBase = isolated;
+    }
+
+    /**
+     * Forces initialization of a class in a JDK 1.1 compatible, albeit hacky
+     * way.
+     *
+     * @param theClass The class to initialize.
+     *                 Must not be <code>null</code>.
+     *
+     * @deprecated use Class.forName with initialize=true instead.
+     */
+    public static void initializeClass(Class theClass) {
+        // ***HACK*** We ask the VM to create an instance
+        // by voluntarily providing illegal arguments to force
+        // the VM to run the class' static initializer, while
+        // at the same time not running a valid constructor.
+
+        final Constructor[] cons = theClass.getDeclaredConstructors();
+        //At least one constructor is guaranteed to be there, but check anyway.
+        if (cons != null) {
+            if (cons.length > 0 && cons[0] != null) {
+                final String[] strs = new String[NUMBER_OF_STRINGS];
+                try {
+                    cons[0].newInstance(strs);
+                    // Expecting an exception to be thrown by this call:
+                    // IllegalArgumentException: wrong number of Arguments
+                } catch (Throwable t) {
+                    // Ignore - we are interested only in the side
+                    // effect - that of getting the static initializers
+                    // invoked.  As we do not want to call a valid
+                    // constructor to get this side effect, an
+                    // attempt is made to call a hopefully
+                    // invalid constructor - come on, nobody
+                    // would have a constructor that takes in
+                    // 256 String arguments ;-)
+                    // (In fact, they can't - according to JVM spec
+                    // section 4.10, the number of method parameters is limited
+                    // to 255 by the definition of a method descriptor.
+                    // Constructors count as methods here.)
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds a package root to the list of packages which must be loaded on the
+     * parent loader.
+     *
+     * All subpackages are also included.
+     *
+     * @param packageRoot The root of all packages to be included.
+     *                    Should not be <code>null</code>.
+     */
+    public void addSystemPackageRoot(String packageRoot) {
+        systemPackages.addElement(packageRoot
+                                  + (packageRoot.endsWith(".") ? "" : "."));
+    }
+
+    /**
+     * Adds a package root to the list of packages which must be loaded using
+     * this loader.
+     *
+     * All subpackages are also included.
+     *
+     * @param packageRoot The root of all packages to be included.
+     *                    Should not be <code>null</code>.
+     */
+    public void addLoaderPackageRoot(String packageRoot) {
+        loaderPackages.addElement(packageRoot
+                                  + (packageRoot.endsWith(".") ? "" : "."));
+    }
+
+    /**
+     * Loads a class through this class loader even if that class is available
+     * on the parent classpath.
+     *
+     * This ensures that any classes which are loaded by the returned class
+     * will use this classloader.
+     *
+     * @param classname The name of the class to be loaded.
+     *                  Must not be <code>null</code>.
+     *
+     * @return the required Class object
+     *
+     * @exception ClassNotFoundException if the requested class does not exist
+     *                                   on this loader's classpath.
+     */
+    public Class forceLoadClass(String classname)
+         throws ClassNotFoundException {
+        log("force loading " + classname, Project.MSG_DEBUG);
+
+        Class theClass = findLoadedClass(classname);
+
+        if (theClass == null) {
+            theClass = findClass(classname);
+        }
+
+        return theClass;
+    }
+
+    /**
+     * Loads a class through this class loader but defer to the parent class
+     * loader.
+     *
+     * This ensures that instances of the returned class will be compatible
+     * with instances which have already been loaded on the parent
+     * loader.
+     *
+     * @param classname The name of the class to be loaded.
+     *                  Must not be <code>null</code>.
+     *
+     * @return the required Class object
+     *
+     * @exception ClassNotFoundException if the requested class does not exist
+     * on this loader's classpath.
+     */
+    public Class forceLoadSystemClass(String classname)
+         throws ClassNotFoundException {
+        log("force system loading " + classname, Project.MSG_DEBUG);
+
+        Class theClass = findLoadedClass(classname);
+
+        if (theClass == null) {
+            theClass = findBaseClass(classname);
+        }
+
+        return theClass;
+    }
+
+    /**
+     * Returns a stream to read the requested resource name.
+     *
+     * @param name The name of the resource for which a stream is required.
+     *             Must not be <code>null</code>.
+     *
+     * @return a stream to the required resource or <code>null</code> if the
+     *         resource cannot be found on the loader's classpath.
+     */
+    public InputStream getResourceAsStream(String name) {
+
+        InputStream resourceStream = null;
+        if (isParentFirst(name)) {
+            resourceStream = loadBaseResource(name);
+            if (resourceStream != null) {
+                log("ResourceStream for " + name
+                    + " loaded from parent loader", Project.MSG_DEBUG);
+
+            } else {
+                resourceStream = loadResource(name);
+                if (resourceStream != null) {
+                    log("ResourceStream for " + name
+                        + " loaded from ant loader", Project.MSG_DEBUG);
+                }
+            }
+        } else {
+            resourceStream = loadResource(name);
+            if (resourceStream != null) {
+                log("ResourceStream for " + name
+                    + " loaded from ant loader", Project.MSG_DEBUG);
+
+            } else {
+                resourceStream = loadBaseResource(name);
+                if (resourceStream != null) {
+                    log("ResourceStream for " + name
+                        + " loaded from parent loader", Project.MSG_DEBUG);
+                }
+            }
+        }
+
+        if (resourceStream == null) {
+            log("Couldn't load ResourceStream for " + name,
+                Project.MSG_DEBUG);
+        }
+
+        return resourceStream;
+    }
+
+    /**
+     * Returns a stream to read the requested resource name from this loader.
+     *
+     * @param name The name of the resource for which a stream is required.
+     *             Must not be <code>null</code>.
+     *
+     * @return a stream to the required resource or <code>null</code> if
+     *         the resource cannot be found on the loader's classpath.
+     */
+    private InputStream loadResource(String name) {
+        // we need to search the components of the path to see if we can
+        // find the class we want.
+        InputStream stream = null;
+
+        Enumeration e = pathComponents.elements();
+        while (e.hasMoreElements() && stream == null) {
+            File pathComponent = (File) e.nextElement();
+            stream = getResourceStream(pathComponent, name);
+        }
+        return stream;
+    }
+
+    /**
+     * Finds a system resource (which should be loaded from the parent
+     * classloader).
+     *
+     * @param name The name of the system resource to load.
+     *             Must not be <code>null</code>.
+     *
+     * @return a stream to the named resource, or <code>null</code> if
+     *         the resource cannot be found.
+     */
+    private InputStream loadBaseResource(String name) {
+        if (parent == null) {
+            return getSystemResourceAsStream(name);
+        } else {
+            return parent.getResourceAsStream(name);
+        }
+    }
+
+    /**
+     * Returns an inputstream to a given resource in the given file which may
+     * either be a directory or a zip file.
+     *
+     * @param file the file (directory or jar) in which to search for the
+     *             resource. Must not be <code>null</code>.
+     * @param resourceName The name of the resource for which a stream is
+     *                     required. Must not be <code>null</code>.
+     *
+     * @return a stream to the required resource or <code>null</code> if
+     *         the resource cannot be found in the given file.
+     */
+    private InputStream getResourceStream(File file, String resourceName) {
+        try {
+            if (!file.exists()) {
+                return null;
+            }
+
+            if (file.isDirectory()) {
+                File resource = new File(file, resourceName);
+
+                if (resource.exists()) {
+                    return new FileInputStream(resource);
+                }
+            } else {
+                // is the zip file in the cache
+                ZipFile zipFile = (ZipFile) zipFiles.get(file);
+                if (zipFile == null) {
+                    zipFile = new ZipFile(file);
+                    zipFiles.put(file, zipFile);
+                }
+                ZipEntry entry = zipFile.getEntry(resourceName);
+                if (entry != null) {
+                    return zipFile.getInputStream(entry);
+                }
+            }
+        } catch (Exception e) {
+            log("Ignoring Exception " + e.getClass().getName()
+                + ": " + e.getMessage() + " reading resource " + resourceName
+                + " from " + file, Project.MSG_VERBOSE);
+        }
+
+        return null;
+    }
+
+    /**
+     * Tests whether or not the parent classloader should be checked for
+     * a resource before this one. If the resource matches both the
+     * "use parent classloader first" and the "use this classloader first"
+     * lists, the latter takes priority.
+     *
+     * @param resourceName The name of the resource to check.
+     *                     Must not be <code>null</code>.
+     *
+     * @return whether or not the parent classloader should be checked for a
+     *         resource before this one is.
+     */
+    private boolean isParentFirst(String resourceName) {
+        // default to the global setting and then see
+        // if this class belongs to a package which has been
+        // designated to use a specific loader first
+        // (this one or the parent one)
+
+        // XXX - shouldn't this always return false in isolated mode?
+
+        boolean useParentFirst = parentFirst;
+
+        for (Enumeration e = systemPackages.elements(); e.hasMoreElements();) {
+            String packageName = (String) e.nextElement();
+            if (resourceName.startsWith(packageName)) {
+                useParentFirst = true;
+                break;
+            }
+        }
+
+        for (Enumeration e = loaderPackages.elements(); e.hasMoreElements();) {
+            String packageName = (String) e.nextElement();
+            if (resourceName.startsWith(packageName)) {
+                useParentFirst = false;
+                break;
+            }
+        }
+
+        return useParentFirst;
+    }
+
+    /**
+     * Finds the resource with the given name. A resource is
+     * some data (images, audio, text, etc) that can be accessed by class
+     * code in a way that is independent of the location of the code.
+     *
+     * @param name The name of the resource for which a stream is required.
+     *             Must not be <code>null</code>.
+     *
+     * @return a URL for reading the resource, or <code>null</code> if the
+     *         resource could not be found or the caller doesn't have
+     *         adequate privileges to get the resource.
+     */
+    public URL getResource(String name) {
+        // we need to search the components of the path to see if
+        // we can find the class we want.
+        URL url = null;
+        if (isParentFirst(name)) {
+            url = (parent == null) ? super.getResource(name)
+                                   : parent.getResource(name);
+        }
+
+        if (url != null) {
+            log("Resource " + name + " loaded from parent loader",
+                Project.MSG_DEBUG);
+
+        } else {
+            // try and load from this loader if the parent either didn't find
+            // it or wasn't consulted.
+            Enumeration e = pathComponents.elements();
+            while (e.hasMoreElements() && url == null) {
+                File pathComponent = (File) e.nextElement();
+                url = getResourceURL(pathComponent, name);
+                if (url != null) {
+                    log("Resource " + name
+                        + " loaded from ant loader",
+                        Project.MSG_DEBUG);
+                }
+            }
+        }
+
+        if (url == null && !isParentFirst(name)) {
+            // this loader was first but it didn't find it - try the parent
+
+            url = (parent == null) ? super.getResource(name)
+                : parent.getResource(name);
+            if (url != null) {
+                log("Resource " + name + " loaded from parent loader",
+                    Project.MSG_DEBUG);
+            }
+        }
+
+        if (url == null) {
+            log("Couldn't load Resource " + name, Project.MSG_DEBUG);
+        }
+
+        return url;
+    }
+
+    /**
+     * Returns an enumeration of URLs representing all the resources with the
+     * given name by searching the class loader's classpath.
+     *
+     * @param name The resource name to search for.
+     *             Must not be <code>null</code>.
+     * @return an enumeration of URLs for the resources
+     * @exception IOException if I/O errors occurs (can't happen)
+     */
+    protected Enumeration/*<URL>*/ findResources(String name) throws IOException {
+        Enumeration/*<URL>*/ mine = new ResourceEnumeration(name);
+        Enumeration/*<URL>*/ base;
+        if (parent != null && parent != getParent()) {
+            // Delegate to the parent:
+            base = parent.getResources(name);
+            // Note: could cause overlaps in case ClassLoader.this.parent has matches.
+        } else {
+            // ClassLoader.this.parent is already delegated to from ClassLoader.getResources, no need:
+            base = new CollectionUtils.EmptyEnumeration();
+        }
+        if (isParentFirst(name)) {
+            // Normal case.
+            return CollectionUtils.append(base, mine);
+        } else {
+            // Inverted.
+            return CollectionUtils.append(mine, base);
+        }
+    }
+
+    /**
+     * Returns the URL of a given resource in the given file which may
+     * either be a directory or a zip file.
+     *
+     * @param file The file (directory or jar) in which to search for
+     *             the resource. Must not be <code>null</code>.
+     * @param resourceName The name of the resource for which a stream
+     *                     is required. Must not be <code>null</code>.
+     *
+     * @return a stream to the required resource or <code>null</code> if the
+     *         resource cannot be found in the given file object.
+     */
+    protected URL getResourceURL(File file, String resourceName) {
+        try {
+            if (!file.exists()) {
+                return null;
+            }
+
+            if (file.isDirectory()) {
+                File resource = new File(file, resourceName);
+
+                if (resource.exists()) {
+                    try {
+                        return FILE_UTILS.getFileURL(resource);
+                    } catch (MalformedURLException ex) {
+                        return null;
+                    }
+                }
+            } else {
+                ZipFile zipFile = (ZipFile) zipFiles.get(file);
+                if (zipFile == null) {
+                    zipFile = new ZipFile(file);
+                    zipFiles.put(file, zipFile);
+                }
+
+                ZipEntry entry = zipFile.getEntry(resourceName);
+                if (entry != null) {
+                    try {
+                        return new URL("jar:" + FILE_UTILS.getFileURL(file)
+                            + "!/" + entry);
+                    } catch (MalformedURLException ex) {
+                        return null;
+                    }
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        return null;
+    }
+
+    /**
+     * Loads a class with this class loader.
+     *
+     * This class attempts to load the class in an order determined by whether
+     * or not the class matches the system/loader package lists, with the
+     * loader package list taking priority. If the classloader is in isolated
+     * mode, failure to load the class in this loader will result in a
+     * ClassNotFoundException.
+     *
+     * @param classname The name of the class to be loaded.
+     *                  Must not be <code>null</code>.
+     * @param resolve <code>true</code> if all classes upon which this class
+     *                depends are to be loaded.
+     *
+     * @return the required Class object
+     *
+     * @exception ClassNotFoundException if the requested class does not exist
+     * on the system classpath (when not in isolated mode) or this loader's
+     * classpath.
+     */
+    protected synchronized Class loadClass(String classname, boolean resolve)
+         throws ClassNotFoundException {
+        // 'sync' is needed - otherwise 2 threads can load the same class
+        // twice, resulting in LinkageError: duplicated class definition.
+        // findLoadedClass avoids that, but without sync it won't work.
+
+        Class theClass = findLoadedClass(classname);
+        if (theClass != null) {
+            return theClass;
+        }
+
+        if (isParentFirst(classname)) {
+            try {
+                theClass = findBaseClass(classname);
+                log("Class " + classname + " loaded from parent loader "
+                    + "(parentFirst)", Project.MSG_DEBUG);
+            } catch (ClassNotFoundException cnfe) {
+                theClass = findClass(classname);
+                log("Class " + classname + " loaded from ant loader "
+                    + "(parentFirst)", Project.MSG_DEBUG);
+            }
+        } else {
+            try {
+                theClass = findClass(classname);
+                log("Class " + classname + " loaded from ant loader",
+                    Project.MSG_DEBUG);
+            } catch (ClassNotFoundException cnfe) {
+                if (ignoreBase) {
+                    throw cnfe;
+                }
+                theClass = findBaseClass(classname);
+                log("Class " + classname + " loaded from parent loader",
+                    Project.MSG_DEBUG);
+            }
+        }
+
+        if (resolve) {
+            resolveClass(theClass);
+        }
+
+        return theClass;
+    }
+
+    /**
+     * Converts the class dot notation to a filesystem equivalent for
+     * searching purposes.
+     *
+     * @param classname The class name in dot format (eg java.lang.Integer).
+     *                  Must not be <code>null</code>.
+     *
+     * @return the classname in filesystem format (eg java/lang/Integer.class)
+     */
+    private String getClassFilename(String classname) {
+        return classname.replace('.', '/') + ".class";
+    }
+
+    /**
+     * Define a class given its bytes
+     *
+     * @param container the container from which the class data has been read
+     *                  may be a directory or a jar/zip file.
+     *
+     * @param classData the bytecode data for the class
+     * @param classname the name of the class
+     *
+     * @return the Class instance created from the given data
+     *
+     * @throws IOException if the class data cannot be read.
+     */
+    protected Class defineClassFromData(File container, byte[] classData,
+                                        String classname) throws IOException {
+        // Simply put:
+        // defineClass(classname, classData, 0, classData.length,
+        //             Project.class.getProtectionDomain());
+        // Made more elaborate to be 1.1-safe.
+        if (defineClassProtectionDomain != null) {
+            try {
+                Object domain
+                    = getProtectionDomain.invoke(Project.class, new Object[0]);
+                Object[] args
+                    = new Object[] {classname, classData, new Integer(0),
+                                    new Integer(classData.length), domain};
+                return (Class) defineClassProtectionDomain.invoke(this, args);
+            } catch (InvocationTargetException ite) {
+                Throwable t = ite.getTargetException();
+                if (t instanceof ClassFormatError) {
+                    throw (ClassFormatError) t;
+                } else if (t instanceof NoClassDefFoundError) {
+                    throw (NoClassDefFoundError) t;
+                } else if (t instanceof SecurityException) {
+                    throw (SecurityException) t;
+                } else {
+                    throw new IOException(t.toString());
+                }
+            } catch (Exception e) {
+                throw new IOException(e.toString());
+            }
+        } else {
+            return defineClass(classname, classData, 0, classData.length);
+        }
+    }
+
+    /**
+     * Reads a class definition from a stream.
+     *
+     * @param stream The stream from which the class is to be read.
+     *               Must not be <code>null</code>.
+     * @param classname The name of the class in the stream.
+     *                  Must not be <code>null</code>.
+     * @param container the file or directory containing the class.
+     *
+     * @return the Class object read from the stream.
+     *
+     * @exception IOException if there is a problem reading the class from the
+     * stream.
+     * @exception SecurityException if there is a security problem while
+     * reading the class from the stream.
+     */
+    private Class getClassFromStream(InputStream stream, String classname,
+                                       File container)
+                throws IOException, SecurityException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        int bytesRead = -1;
+        byte[] buffer = new byte[BUFFER_SIZE];
+
+        while ((bytesRead = stream.read(buffer, 0, BUFFER_SIZE)) != -1) {
+            baos.write(buffer, 0, bytesRead);
+        }
+
+        byte[] classData = baos.toByteArray();
+        return defineClassFromData(container, classData, classname);
+    }
+
+    /**
+     * Searches for and load a class on the classpath of this class loader.
+     *
+     * @param name The name of the class to be loaded. Must not be
+     *             <code>null</code>.
+     *
+     * @return the required Class object
+     *
+     * @exception ClassNotFoundException if the requested class does not exist
+     *                                   on this loader's classpath.
+     */
+    public Class findClass(String name) throws ClassNotFoundException {
+        log("Finding class " + name, Project.MSG_DEBUG);
+
+        return findClassInComponents(name);
+    }
+
+    /**
+     * Indicate if the given file is in this loader's path
+     *
+     * @param component the file which is to be checked
+     *
+     * @return true if the file is in the class path
+     */
+    protected boolean isInPath(File component) {
+        for (Enumeration e = pathComponents.elements(); e.hasMoreElements();) {
+            File pathComponent = (File) e.nextElement();
+            if (pathComponent.equals(component)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * Finds a class on the given classpath.
+     *
+     * @param name The name of the class to be loaded. Must not be
+     *             <code>null</code>.
+     *
+     * @return the required Class object
+     *
+     * @exception ClassNotFoundException if the requested class does not exist
+     * on this loader's classpath.
+     */
+    private Class findClassInComponents(String name)
+         throws ClassNotFoundException {
+        // we need to search the components of the path to see if
+        // we can find the class we want.
+        InputStream stream = null;
+        String classFilename = getClassFilename(name);
+        try {
+            Enumeration e = pathComponents.elements();
+            while (e.hasMoreElements()) {
+                File pathComponent = (File) e.nextElement();
+                try {
+                    stream = getResourceStream(pathComponent, classFilename);
+                    if (stream != null) {
+                        log("Loaded from " + pathComponent + " "
+                            + classFilename, Project.MSG_DEBUG);
+                        return getClassFromStream(stream, name, pathComponent);
+                    }
+                } catch (SecurityException se) {
+                    throw se;
+                } catch (IOException ioe) {
+                    // ioe.printStackTrace();
+                    log("Exception reading component " + pathComponent
+                        + " (reason: " + ioe.getMessage() + ")",
+                        Project.MSG_VERBOSE);
+                }
+            }
+
+            throw new ClassNotFoundException(name);
+        } finally {
+            try {
+                if (stream != null) {
+                    stream.close();
+                }
+            } catch (IOException e) {
+                //ignore
+            }
+        }
+    }
+
+    /**
+     * Finds a system class (which should be loaded from the same classloader
+     * as the Ant core).
+     *
+     * For JDK 1.1 compatibility, this uses the findSystemClass method if
+     * no parent classloader has been specified.
+     *
+     * @param name The name of the class to be loaded.
+     *             Must not be <code>null</code>.
+     *
+     * @return the required Class object
+     *
+     * @exception ClassNotFoundException if the requested class does not exist
+     * on this loader's classpath.
+     */
+    private Class findBaseClass(String name) throws ClassNotFoundException {
+        if (parent == null) {
+            return findSystemClass(name);
+        } else {
+            return parent.loadClass(name);
+        }
+    }
+
+    /**
+     * Cleans up any resources held by this classloader. Any open archive
+     * files are closed.
+     */
+    public synchronized void cleanup() {
+        for (Enumeration e = zipFiles.elements(); e.hasMoreElements();) {
+            ZipFile zipFile = (ZipFile) e.nextElement();
+            try {
+                zipFile.close();
+            } catch (IOException ioe) {
+                // ignore
+            }
+        }
+        zipFiles = new Hashtable();
+        if (project != null) {
+            project.removeBuildListener(this);
+        }
+        project = null;
+    }
+
+    /**
+     * Empty implementation to satisfy the BuildListener interface.
+     *
+     * @param event the buildStarted event
+     */
+    public void buildStarted(BuildEvent event) {
+    }
+
+    /**
+     * Cleans up any resources held by this classloader at the end
+     * of a build.
+     *
+     * @param event the buildFinished event
+     */
+    public void buildFinished(BuildEvent event) {
+        cleanup();
+    }
+
+    /**
+     * Cleans up any resources held by this classloader at the end of
+     * a subbuild if it has been created for the subbuild's project
+     * instance.
+     *
+     * @param event the buildFinished event
+     *
+     * @since Ant 1.6.2
+     */
+    public void subBuildFinished(BuildEvent event) {
+        if (event.getProject() == project) {
+            cleanup();
+        }
+    }
+
+    /**
+     * Empty implementation to satisfy the BuildListener interface.
+     *
+     * @param event the buildStarted event
+     *
+     * @since Ant 1.6.2
+     */
+    public void subBuildStarted(BuildEvent event) {
+    }
+
+    /**
+     * Empty implementation to satisfy the BuildListener interface.
+     *
+     * @param event the targetStarted event
+     */
+    public void targetStarted(BuildEvent event) {
+    }
+
+    /**
+     * Empty implementation to satisfy the BuildListener interface.
+     *
+     * @param event the targetFinished event
+     */
+    public void targetFinished(BuildEvent event) {
+    }
+
+    /**
+     * Empty implementation to satisfy the BuildListener interface.
+     *
+     * @param event the taskStarted event
+     */
+    public void taskStarted(BuildEvent event) {
+    }
+
+    /**
+     * Empty implementation to satisfy the BuildListener interface.
+     *
+     * @param event the taskFinished event
+     */
+    public void taskFinished(BuildEvent event) {
+    }
+
+    /**
+     * Empty implementation to satisfy the BuildListener interface.
+     *
+     * @param event the messageLogged event
+     */
+    public void messageLogged(BuildEvent event) {
+    }
+
+    /**
+     * add any libraries that come with different java versions
+     * here
+     */
+    public void addJavaLibraries() {
+        Vector packages = JavaEnvUtils.getJrePackages();
+        Enumeration e = packages.elements();
+        while (e.hasMoreElements()) {
+            String packageName = (String) e.nextElement();
+            addSystemPackageRoot(packageName);
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/AntTypeDefinition.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/AntTypeDefinition.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/AntTypeDefinition.java	(revision 16139)
@@ -0,0 +1,374 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+
+/**
+ * This class contains all the information
+ * on a particular ant type,
+ * the classname, adaptor and the class
+ * it should be assignable from.
+ * This type replaces the task/datatype split
+ * of pre ant 1.6.
+ *
+ */
+public class AntTypeDefinition {
+    private String      name;
+    private Class       clazz;
+    private Class       adapterClass;
+    private Class       adaptToClass;
+    private String      className;
+    private ClassLoader classLoader;
+
+    /**
+     * set the definition's name
+     * @param name the name of the definition
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * return the definition's name
+     * @return the name of the definition
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * set the class of the definition.
+     * as a side-effect may set the classloader and classname
+     * @param clazz the class of this definition
+     */
+    public void setClass(Class clazz) {
+        this.clazz = clazz;
+        if (clazz == null) {
+            return;
+        }
+        if (classLoader == null) {
+            this.classLoader = clazz.getClassLoader();
+        }
+        if (className == null) {
+            this.className = clazz.getName();
+        }
+    }
+
+    /**
+     * set the classname of the definition
+     * @param className the classname of this definition
+     */
+    public void setClassName(String className) {
+        this.className = className;
+    }
+
+    /**
+     * get the classname of the definition
+     * @return the name of the class of this definition
+     */
+    public String getClassName() {
+        return className;
+    }
+
+    /**
+     * set the adapter class for this definition.
+     * this class is used to adapt the definitions class if
+     * required.
+     * @param adapterClass the adapterClass
+     */
+    public void setAdapterClass(Class adapterClass) {
+        this.adapterClass = adapterClass;
+    }
+
+    /**
+     * set the assignable class for this definition.
+     * @param adaptToClass the assignable class
+     */
+
+    public void setAdaptToClass(Class adaptToClass) {
+        this.adaptToClass = adaptToClass;
+    }
+
+    /**
+     * set the classloader to use to create an instance
+     * of the definition
+     * @param classLoader the classLoader
+     */
+    public void setClassLoader(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    /**
+     * get the classloader for this definition
+     * @return the classloader for this definition
+     */
+    public ClassLoader getClassLoader() {
+        return classLoader;
+    }
+
+    /**
+     * get the exposed class for this
+     * definition. This will be a proxy class
+     * (adapted class) if there is an adapter
+     * class and the definition class is not
+     * assignable from the assignable class.
+     * @param project the current project
+     * @return the exposed class
+     */
+    public Class getExposedClass(Project project) {
+        if (adaptToClass != null) {
+            Class z = getTypeClass(project);
+            if (z == null) {
+                return null;
+            }
+            if (adaptToClass.isAssignableFrom(z)) {
+                return z;
+            }
+        }
+        if (adapterClass != null) {
+            return adapterClass;
+        }
+        return getTypeClass(project);
+    }
+
+    /**
+     * get the definition class
+     * @param project the current project
+     * @return the type of the definition
+     */
+    public Class getTypeClass(Project project) {
+        if (clazz != null) {
+            return clazz;
+        }
+
+        try {
+            if (classLoader == null) {
+                clazz = Class.forName(className);
+            } else {
+                clazz = classLoader.loadClass(className);
+            }
+        } catch (NoClassDefFoundError ncdfe) {
+            project.log("Could not load a dependent class ("
+                        + ncdfe.getMessage() + ") for type "
+                        + name, Project.MSG_DEBUG);
+        } catch (ClassNotFoundException cnfe) {
+            project.log("Could not load class (" + className
+                        + ") for type " + name, Project.MSG_DEBUG);
+        }
+        return clazz;
+    }
+
+    /**
+     * create an instance of the definition.
+     * The instance may be wrapped in a proxy class.
+     * @param project the current project
+     * @return the created object
+     */
+    public Object create(Project project) {
+        return icreate(project);
+    }
+
+    /**
+     * Create a component object based on
+     * its definition
+     */
+    private Object icreate(Project project) {
+        Class c = getTypeClass(project);
+        if (c == null) {
+            return null;
+        }
+
+        Object o = createAndSet(project, c);
+        if (o == null || adapterClass == null) {
+            return o;
+        }
+
+        if (adaptToClass != null) {
+            if (adaptToClass.isAssignableFrom(o.getClass())) {
+                return o;
+            }
+        }
+
+        TypeAdapter adapterObject = (TypeAdapter) createAndSet(
+            project, adapterClass);
+        if (adapterObject == null) {
+            return null;
+        }
+
+        adapterObject.setProxy(o);
+        return adapterObject;
+    }
+
+    /**
+     * Checks if the attributes are correct.
+     * <dl>
+     *   <li>if the class can be created.</li>
+     *   <li>if an adapter class can be created</li>
+     *   <li>if the type is assignable from adapto</li>
+     *   <li>if the type can be used with the adapter class</li>
+     * </dl>
+     * @param project the current project
+     */
+    public void checkClass(Project project) {
+        if (clazz == null) {
+            clazz = getTypeClass(project);
+            if (clazz == null) {
+                throw new BuildException(
+                    "Unable to create class for " + getName());
+            }
+        }
+        // check adapter
+        if (adapterClass != null) {
+            boolean needToCheck = true;
+            if (adaptToClass != null
+                && adaptToClass.isAssignableFrom(clazz)) {
+                needToCheck = false;
+            }
+            if (needToCheck) {
+                TypeAdapter adapter = (TypeAdapter) createAndSet(
+                    project, adapterClass);
+                if (adapter == null) {
+                    throw new BuildException("Unable to create adapter object");
+                }
+                adapter.checkProxyClass(clazz);
+            }
+        }
+    }
+
+    /**
+     * get the constructor of the definition
+     * and invoke it.
+     */
+    private Object createAndSet(Project project, Class c) {
+        try {
+            java.lang.reflect.Constructor ctor = null;
+            boolean noArg = false;
+            // DataType can have a "no arg" constructor or take a single
+            // Project argument.
+            try {
+                ctor = c.getConstructor(new Class[0]);
+                noArg = true;
+            } catch (NoSuchMethodException nse) {
+                ctor = c.getConstructor(new Class[] {Project.class});
+                noArg = false;
+            }
+
+            Object o = null;
+            if (noArg) {
+                o = ctor.newInstance(new Object[0]);
+            } else {
+                o = ctor.newInstance(new Object[] {project});
+            }
+            project.setProjectReference(o);
+            return o;
+        } catch (java.lang.reflect.InvocationTargetException ex) {
+            Throwable t = ex.getTargetException();
+            throw new BuildException(
+                "Could not create type " + name + " due to " + t, t);
+        } catch (NoClassDefFoundError ncdfe) {
+            String msg = "Type " + name + ": A class needed by class "
+                + c + " cannot be found: " + ncdfe.getMessage();
+            throw new BuildException(msg, ncdfe);
+       } catch (Throwable t) {
+            throw new BuildException(
+                "Could not create type " + name + " due to " + t, t);
+        }
+    }
+
+    /**
+     * Equality method for this definition (assumes the names are the same)
+     *
+     * @param other another definition
+     * @param project the project the definition
+     * @return true if the definitions are the same
+     */
+    public boolean sameDefinition(AntTypeDefinition other, Project project) {
+        if (other == null) {
+            return false;
+        }
+        if (other.getClass() != this.getClass()) {
+            return false;
+        }
+        if (!(other.getTypeClass(project).equals(getTypeClass(project)))) {
+            return false;
+        }
+        if (!other.getExposedClass(project).equals(getExposedClass(project))) {
+            return false;
+        }
+        if (other.adapterClass != adapterClass) {
+            return false;
+        }
+        if (other.adaptToClass != adaptToClass) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Similar definition
+     * used to compare two definitions defined twice with the same
+     * name and the same types.
+     * the classloader may be different but have the same
+     * path so #sameDefinition cannot
+     * be used.
+     * @param other the definition to compare to
+     * @param project the current project
+     * @return true if the definitions are the same
+     */
+    public boolean similarDefinition(AntTypeDefinition other, Project project) {
+        if (other == null) {
+            return false;
+        }
+        if (getClass() != other.getClass()) {
+            return false;
+        }
+        if (!getClassName().equals(other.getClassName())) {
+            return false;
+        }
+        if (!extractClassname(adapterClass).equals(
+                extractClassname(other.adapterClass))) {
+            return false;
+        }
+        if (!extractClassname(adaptToClass).equals(
+                extractClassname(other.adaptToClass))) {
+            return false;
+        }
+        // all the names are the same: check if the class path of the loader
+        // is the same
+        ClassLoader oldLoader = other.getClassLoader();
+        ClassLoader newLoader = this.getClassLoader();
+        if (oldLoader == newLoader) {
+            return true;
+        }
+        return
+            newLoader != null
+            && oldLoader != null
+            && oldLoader instanceof AntClassLoader
+            && newLoader instanceof AntClassLoader
+            && ((AntClassLoader) oldLoader).getClasspath()
+            .equals(((AntClassLoader) newLoader).getClasspath());
+    }
+
+    private String extractClassname(Class c) {
+        if (c == null) {
+            return "<null>";
+        } else {
+            return c.getClass().getName();
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/BuildEvent.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/BuildEvent.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/BuildEvent.java	(revision 16139)
@@ -0,0 +1,199 @@
+/*
+ * Copyright  2000,2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant;
+
+import java.util.EventObject;
+
+/**
+ * Class representing an event occurring during a build. An
+ * event is built by specifying either a project, a task or a target.
+ * A project level event will only have a project reference;
+ * a target level event will have project and target references;
+ * a task level event will have project, target and task references.
+ *
+ */
+public class BuildEvent extends EventObject {
+
+    /** Project which emitted the event. */
+    private Project project;
+    /** Target which emitted the event, if specified. */
+    private Target target;
+    /** Task which emitted the event, if specified. */
+    private Task task;
+    /**
+     * Message associated with the event. This is only used for
+     * "messageLogged" events.
+     */
+    private String message;
+    /**
+     * The priority of the message, for "messageLogged" events.
+     */
+    private int priority = Project.MSG_VERBOSE;
+    /**
+     * The exception associated with this event, if any.
+     * This is only used for "taskFinished", "targetFinished",
+     * and "buildFinished" events.
+     */
+    private Throwable exception;
+
+    /**
+     * Construct a BuildEvent for a project level event.
+     *
+     * @param project the project that emitted the event.
+     *                Should not be <code>null</code>.
+     */
+    public BuildEvent(Project project) {
+        super(project);
+        this.project = project;
+        this.target = null;
+        this.task = null;
+    }
+
+    /**
+     * Construct a BuildEvent for a target level event.
+     * The project associated with the event is derived
+     * from the given target.
+     *
+     * @param target the target that emitted the event.
+     *               Must not be <code>null</code>.
+     */
+    public BuildEvent(Target target) {
+        super(target);
+        this.project = target.getProject();
+        this.target = target;
+        this.task = null;
+    }
+
+    /**
+     * Construct a BuildEvent for a task level event.
+     * The project and target associated with the event
+     * are derived from the given task.
+     *
+     * @param task the task that emitted the event.
+     *             Must not be <code>null</code>.
+     */
+    public BuildEvent(Task task) {
+        super(task);
+        this.project = task.getProject();
+        this.target = task.getOwningTarget();
+        this.task = task;
+    }
+
+    /**
+     * Sets the message and priority associated with this event.
+     * This is used for "messageLogged" events.
+     *
+     * @param message the message to be associated with this event.
+     *                Should not be <code>null</code>.
+     * @param priority the priority to be associated with this event,
+     *                 as defined in the {@link Project Project} class.
+     *
+     * @see BuildListener#messageLogged(BuildEvent)
+     */
+    public void setMessage(String message, int priority) {
+        this.message = message;
+        this.priority = priority;
+    }
+
+    /**
+     * Sets the exception associated with this event. This is used
+     * for "taskFinished", "targetFinished", and "buildFinished"
+     * events.
+     *
+     * @param exception The exception to be associated with this event.
+     *                  May be <code>null</code>.
+     *
+     * @see BuildListener#taskFinished(BuildEvent)
+     * @see BuildListener#targetFinished(BuildEvent)
+     * @see BuildListener#buildFinished(BuildEvent)
+     */
+    public void setException(Throwable exception) {
+        this.exception = exception;
+    }
+
+    /**
+     * Returns the project that fired this event.
+     *
+     * @return the project that fired this event
+     */
+    public Project getProject() {
+        return project;
+    }
+
+    /**
+     * Returns the target that fired this event.
+     *
+     * @return the project that fired this event, or <code>null</code>
+     *          if this event is a project level event.
+     */
+    public Target getTarget() {
+
+        return target;
+    }
+
+    /**
+     * Returns the task that fired this event.
+     *
+     * @return the task that fired this event, or <code>null</code>
+     *         if this event is a project or target level event.
+     */
+    public Task getTask() {
+        return task;
+    }
+
+    /**
+     * Returns the logging message. This field will only be set
+     * for "messageLogged" events.
+     *
+     * @return the message associated with this event, or <code>null</code>
+     *         if no message has been set.
+     *
+     * @see BuildListener#messageLogged(BuildEvent)
+     */
+    public String getMessage() {
+        return message;
+    }
+
+    /**
+     * Returns the priority of the logging message. This field will only
+     * be set for "messageLogged" events. The meaning of this priority
+     * is as specified by the constants in the {@link Project Project} class.
+     *
+     * @return the priority associated with this event.
+     *
+     * @see BuildListener#messageLogged(BuildEvent)
+     */
+    public int getPriority() {
+        return priority;
+    }
+
+    /**
+     * Returns the exception that was thrown, if any. This field will only
+     * be set for "taskFinished", "targetFinished", and "buildFinished"
+     * events.
+     *
+     * @return the exception associated with this exception, or
+     *         <code>null</code> if no exception has been set.
+     *
+     * @see BuildListener#taskFinished(BuildEvent)
+     * @see BuildListener#targetFinished(BuildEvent)
+     * @see BuildListener#buildFinished(BuildEvent)
+     */
+    public Throwable getException() {
+        return exception;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/BuildException.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/BuildException.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/BuildException.java	(revision 16139)
@@ -0,0 +1,209 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * Signals an error condition during a build
+ *
+ */
+public class BuildException extends RuntimeException {
+
+    /** Exception that might have caused this one. */
+    private Throwable cause;
+
+    /** Location in the build file where the exception occurred */
+    private Location location = Location.UNKNOWN_LOCATION;
+
+    /**
+     * Constructs a build exception with no descriptive information.
+     */
+    public BuildException() {
+        super();
+    }
+
+    /**
+     * Constructs an exception with the given descriptive message.
+     *
+     * @param message A description of or information about the exception.
+     *            Should not be <code>null</code>.
+     */
+    public BuildException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs an exception with the given message and exception as
+     * a root cause.
+     *
+     * @param message A description of or information about the exception.
+     *            Should not be <code>null</code> unless a cause is specified.
+     * @param cause The exception that might have caused this one.
+     *              May be <code>null</code>.
+     */
+    public BuildException(String message, Throwable cause) {
+        super(message);
+        this.cause = cause;
+    }
+
+    /**
+     * Constructs an exception with the given message and exception as
+     * a root cause and a location in a file.
+     *
+     * @param msg A description of or information about the exception.
+     *            Should not be <code>null</code> unless a cause is specified.
+     * @param cause The exception that might have caused this one.
+     *              May be <code>null</code>.
+     * @param location The location in the project file where the error
+     *                 occurred. Must not be <code>null</code>.
+     */
+    public BuildException(String msg, Throwable cause, Location location) {
+        this(msg, cause);
+        this.location = location;
+    }
+
+    /**
+     * Constructs an exception with the given exception as a root cause.
+     *
+     * @param cause The exception that might have caused this one.
+     *              Should not be <code>null</code>.
+     */
+    public BuildException(Throwable cause) {
+        super(cause.toString());
+        this.cause = cause;
+    }
+
+    /**
+     * Constructs an exception with the given descriptive message and a
+     * location in a file.
+     *
+     * @param message A description of or information about the exception.
+     *            Should not be <code>null</code>.
+     * @param location The location in the project file where the error
+     *                 occurred. Must not be <code>null</code>.
+     */
+    public BuildException(String message, Location location) {
+        super(message);
+        this.location = location;
+    }
+
+    /**
+     * Constructs an exception with the given exception as
+     * a root cause and a location in a file.
+     *
+     * @param cause The exception that might have caused this one.
+     *              Should not be <code>null</code>.
+     * @param location The location in the project file where the error
+     *                 occurred. Must not be <code>null</code>.
+     */
+    public BuildException(Throwable cause, Location location) {
+        this(cause);
+        this.location = location;
+    }
+
+    /**
+     * Returns the nested exception, if any.
+     *
+     * @return the nested exception, or <code>null</code> if no
+     *         exception is associated with this one
+     */
+    public Throwable getException() {
+        return cause;
+    }
+
+    /**
+     * Returns the nested exception, if any.
+     *
+     * @return the nested exception, or <code>null</code> if no
+     *         exception is associated with this one
+     */
+    public Throwable getCause() {
+        return getException();
+    }
+
+    /**
+     * Returns the location of the error and the error message.
+     *
+     * @return the location of the error and the error message
+     */
+    public String toString() {
+        return location.toString() + getMessage();
+    }
+
+    /**
+     * Sets the file location where the error occurred.
+     *
+     * @param location The file location where the error occurred.
+     *                 Must not be <code>null</code>.
+     */
+    public void setLocation(Location location) {
+        this.location = location;
+    }
+
+    /**
+     * Returns the file location where the error occurred.
+     *
+     * @return the file location where the error occurred.
+     */
+    public Location getLocation() {
+        return location;
+    }
+
+    /**
+     * Prints the stack trace for this exception and any
+     * nested exception to <code>System.err</code>.
+     */
+    public void printStackTrace() {
+        printStackTrace(System.err);
+    }
+
+    /**
+     * Prints the stack trace of this exception and any nested
+     * exception to the specified PrintStream.
+     *
+     * @param ps The PrintStream to print the stack trace to.
+     *           Must not be <code>null</code>.
+     */
+    public void printStackTrace(PrintStream ps) {
+        synchronized (ps) {
+            super.printStackTrace(ps);
+            if (cause != null) {
+                ps.println("--- Nested Exception ---");
+                cause.printStackTrace(ps);
+            }
+        }
+    }
+
+    /**
+     * Prints the stack trace of this exception and any nested
+     * exception to the specified PrintWriter.
+     *
+     * @param pw The PrintWriter to print the stack trace to.
+     *           Must not be <code>null</code>.
+     */
+    public void printStackTrace(PrintWriter pw) {
+        synchronized (pw) {
+            super.printStackTrace(pw);
+            if (cause != null) {
+                pw.println("--- Nested Exception ---");
+                cause.printStackTrace(pw);
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/BuildListener.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/BuildListener.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/BuildListener.java	(revision 16139)
@@ -0,0 +1,104 @@
+/*
+ * Copyright  2000,2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.util.EventListener;
+
+/**
+ * Instances of classes that implement this interface can register
+ * to be notified when things happened during a build.
+ *
+ * @see BuildEvent
+ * @see Project#addBuildListener(BuildListener)
+ *
+ */
+public interface BuildListener extends EventListener {
+
+    /**
+     * Signals that a build has started. This event
+     * is fired before any targets have started.
+     *
+     * @param event An event with any relevant extra information.
+     *              Must not be <code>null</code>.
+     */
+    void buildStarted(BuildEvent event);
+
+    /**
+     * Signals that the last target has finished. This event
+     * will still be fired if an error occurred during the build.
+     *
+     * @param event An event with any relevant extra information.
+     *              Must not be <code>null</code>.
+     *
+     * @see BuildEvent#getException()
+     */
+    void buildFinished(BuildEvent event);
+
+    /**
+     * Signals that a target is starting.
+     *
+     * @param event An event with any relevant extra information.
+     *              Must not be <code>null</code>.
+     *
+     * @see BuildEvent#getTarget()
+     */
+    void targetStarted(BuildEvent event);
+
+    /**
+     * Signals that a target has finished. This event will
+     * still be fired if an error occurred during the build.
+     *
+     * @param event An event with any relevant extra information.
+     *              Must not be <code>null</code>.
+     *
+     * @see BuildEvent#getException()
+     */
+    void targetFinished(BuildEvent event);
+
+    /**
+     * Signals that a task is starting.
+     *
+     * @param event An event with any relevant extra information.
+     *              Must not be <code>null</code>.
+     *
+     * @see BuildEvent#getTask()
+     */
+    void taskStarted(BuildEvent event);
+
+    /**
+     * Signals that a task has finished. This event will still
+     * be fired if an error occurred during the build.
+     *
+     * @param event An event with any relevant extra information.
+     *              Must not be <code>null</code>.
+     *
+     * @see BuildEvent#getException()
+     */
+    void taskFinished(BuildEvent event);
+
+    /**
+     * Signals a message logging event.
+     *
+     * @param event An event with any relevant extra information.
+     *              Must not be <code>null</code>.
+     *
+     * @see BuildEvent#getMessage()
+     * @see BuildEvent#getPriority()
+     */
+    void messageLogged(BuildEvent event);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/BuildLogger.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/BuildLogger.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/BuildLogger.java	(revision 16139)
@@ -0,0 +1,71 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.io.PrintStream;
+
+/**
+ * Interface used by Ant to log the build output.
+ *
+ * A build logger is a build listener which has the 'right' to send output to
+ * the ant log, which is usually <code>System.out</code> unless redirected by
+ * the <code>-logfile</code> option.
+ *
+ */
+public interface BuildLogger extends BuildListener {
+
+    /**
+     * Sets the highest level of message this logger should respond to.
+     *
+     * Only messages with a message level lower than or equal to the
+     * given level should be written to the log.
+     * <P>
+     * Constants for the message levels are in the
+     * {@link Project Project} class. The order of the levels, from least
+     * to most verbose, is <code>MSG_ERR</code>, <code>MSG_WARN</code>,
+     * <code>MSG_INFO</code>, <code>MSG_VERBOSE</code>,
+     * <code>MSG_DEBUG</code>.
+     *
+     * @param level the logging level for the logger.
+     */
+    void setMessageOutputLevel(int level);
+
+    /**
+     * Sets the output stream to which this logger is to send its output.
+     *
+     * @param output The output stream for the logger.
+     *               Must not be <code>null</code>.
+     */
+    void setOutputPrintStream(PrintStream output);
+
+    /**
+     * Sets this logger to produce emacs (and other editor) friendly output.
+     *
+     * @param emacsMode <code>true</code> if output is to be unadorned so that
+     *                  emacs and other editors can parse files names, etc.
+     */
+    void setEmacsMode(boolean emacsMode);
+
+    /**
+     * Sets the output stream to which this logger is to send error messages.
+     *
+     * @param err The error stream for the logger.
+     *            Must not be <code>null</code>.
+     */
+    void setErrorPrintStream(PrintStream err);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/ComponentHelper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/ComponentHelper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/ComponentHelper.java	(revision 16139)
@@ -0,0 +1,814 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Set;
+import java.util.Stack;
+
+import java.util.Vector;
+import java.io.InputStream;
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.lang.reflect.Modifier;
+
+import org.apache.tools.ant.taskdefs.Typedef;
+
+/**
+ * Component creation and configuration.
+ *
+ * The class is based around handing component
+ * definitions in an AntTypeTable.
+ *
+ * The old task/type methods have been kept
+ * for backward compatibly.
+ * Project will just delegate its calls to this class.
+ *
+ * A very simple hook mechanism is provided that allows users to plug
+ * in custom code. It is also possible to replace the default behavior
+ * ( for example in an app embedding ant )
+ *
+ * @since Ant1.6
+ */
+public class ComponentHelper  {
+    /** Map from component name to anttypedefinition */
+    private AntTypeTable antTypeTable;
+
+    /** Map of tasks generated from antTypeTable */
+    private Hashtable taskClassDefinitions = new Hashtable();
+    /** flag to rebuild taskClassDefinitions */
+    private boolean rebuildTaskClassDefinitions = true;
+
+    /** Map of types generated from antTypeTable */
+    private Hashtable typeClassDefinitions = new Hashtable();
+    /** flag to rebuild typeClassDefinitions */
+    private boolean rebuildTypeClassDefinitions = true;
+
+    /** Set of namespaces that have been checked for antlibs */
+    private Set checkedNamespaces = new HashSet();
+
+    /**
+     * Stack of antlib contexts used to resolve definitions while
+     *   processing antlib
+     */
+    private Stack antLibStack = new Stack();
+    /** current antlib uri */
+    private String antLibCurrentUri = null;
+
+    /**
+     * Map from task names to vectors of created tasks
+     * (String to Vector of Task). This is used to invalidate tasks if
+     * the task definition changes.
+     */
+    private Hashtable createdTasks = new Hashtable();
+
+    private ComponentHelper next;
+    private Project project;
+
+    /**
+     * Find a project component for a specific project, creating
+     * it if it does not exist.
+     * @param project the project.
+     * @return the project component for a specific project.
+     */
+    public static ComponentHelper getComponentHelper(Project project) {
+        // Singleton for now, it may change ( per/classloader )
+        ComponentHelper ph = (ComponentHelper) project.getReference(
+            "ant.ComponentHelper");
+        if (ph != null) {
+            return ph;
+        }
+        ph = new ComponentHelper();
+        ph.setProject(project);
+
+        project.addReference("ant.ComponentHelper", ph);
+        return ph;
+    }
+
+    /**
+     * Creates a new ComponentHelper instance.
+     */
+    protected ComponentHelper() {
+    }
+
+    /**
+     * Set the next chained component helper.
+     *
+     * @param next the next chained component helper.
+     */
+    public void setNext(ComponentHelper next) {
+        this.next = next;
+    }
+
+    /**
+     * Get the next chained component helper.
+     *
+     * @return the next chained component helper.
+     */
+    public ComponentHelper getNext() {
+        return next;
+    }
+
+    /**
+     * Sets the project for this component helper.
+     *
+     * @param project the project for this helper.
+     */
+    public void setProject(Project project) {
+        this.project = project;
+        antTypeTable = new AntTypeTable(project);
+    }
+
+    /**
+     * Used with creating child projects. Each child
+     * project inherits the component definitions
+     * from its parent.
+     * @param helper the component helper of the parent project.
+     */
+    public void initSubProject(ComponentHelper helper) {
+        // add the types of the parent project
+        AntTypeTable typeTable = helper.antTypeTable;
+        for (Iterator i = typeTable.values().iterator(); i.hasNext();) {
+            AntTypeDefinition def = (AntTypeDefinition) i.next();
+            antTypeTable.put(def.getName(), def);
+        }
+        // add the parsed namespaces of the parent project
+        for (Iterator i = helper.checkedNamespaces.iterator(); i.hasNext();) {
+            checkedNamespaces.add(i.next());
+        }
+    }
+
+    /**
+     * Factory method to create the components.
+     *
+     * This should be called by UnknownElement.
+     *
+     * @param ue The Unknown Element creating this component.
+     * @param ns Namespace URI. Also available as ue.getNamespace().
+     * @param componentType The component type,
+     *                       Also available as ue.getComponentName().
+     * @return the created component.
+     * @throws BuildException if an error occurs.
+     */
+    public Object createComponent(UnknownElement ue,
+                                  String ns,
+                                  String componentType)
+        throws BuildException {
+        Object component = createComponent(componentType);
+        if (component instanceof Task) {
+            Task task = (Task) component;
+            task.setLocation(ue.getLocation());
+            task.setTaskType(componentType);
+            task.setTaskName(ue.getTaskName());
+            task.setOwningTarget(ue.getOwningTarget());
+            task.init();
+            addCreatedTask(componentType, task);
+        }
+        return component;
+    }
+
+    /**
+     * Create an object for a component.
+     *
+     * @param componentName the name of the component, if
+     *                      the component is in a namespace, the
+     *                      name is prefixed with the namespace uri and ":".
+     * @return the class if found or null if not.
+     */
+    public Object createComponent(String componentName) {
+        AntTypeDefinition def = getDefinition(componentName);
+        return (def == null) ? null : def.create(project);
+    }
+
+    /**
+     * Return the class of the component name.
+     *
+     * @param componentName the name of the component, if
+     *                      the component is in a namespace, the
+     *                      name is prefixed with the namespace uri and ":".
+     * @return the class if found or null if not.
+     */
+    public Class getComponentClass(String componentName) {
+        AntTypeDefinition def = getDefinition(componentName);
+        return (def == null) ? null : def.getExposedClass(project);
+    }
+
+    /**
+     * Return the antTypeDefinition for a componentName.
+     * @param componentName the name of the component.
+     * @return the ant definition or null if not present.
+     */
+    public AntTypeDefinition getDefinition(String componentName) {
+        checkNamespace(componentName);
+        return antTypeTable.getDefinition(componentName);
+    }
+
+    /**
+     * This method is initialization code implementing the original ant component
+     * loading from /org/apache/tools/ant/taskdefs/default.properties
+     * and /org/apache/tools/ant/types/default.properties.
+     */
+    public void initDefaultDefinitions() {
+        initTasks();
+        initTypes();
+    }
+
+    /**
+     * Adds a new task definition to the project.
+     * Attempting to override an existing definition with an
+     * equivalent one (i.e. with the same classname) results in
+     * a verbose log message. Attempting to override an existing definition
+     * with a different one results in a warning log message and
+     * invalidates any tasks which have already been created with the
+     * old definition.
+     *
+     * @param taskName The name of the task to add.
+     *                 Must not be <code>null</code>.
+     * @param taskClass The full name of the class implementing the task.
+     *                  Must not be <code>null</code>.
+     *
+     * @exception BuildException if the class is unsuitable for being an Ant
+     *                           task. An error level message is logged before
+     *                           this exception is thrown.
+     *
+     * @see #checkTaskClass(Class)
+     */
+    public void addTaskDefinition(String taskName, Class taskClass) {
+        checkTaskClass(taskClass);
+        AntTypeDefinition def = new AntTypeDefinition();
+        def.setName(taskName);
+        def.setClassLoader(taskClass.getClassLoader());
+        def.setClass(taskClass);
+        def.setAdapterClass(TaskAdapter.class);
+        def.setClassName(taskClass.getName());
+        def.setAdaptToClass(Task.class);
+        updateDataTypeDefinition(def);
+    }
+
+    /**
+     * Checks whether or not a class is suitable for serving as Ant task.
+     * Ant task implementation classes must be public, concrete, and have
+     * a no-arg constructor.
+     *
+     * @param taskClass The class to be checked.
+     *                  Must not be <code>null</code>.
+     *
+     * @exception BuildException if the class is unsuitable for being an Ant
+     *                           task. An error level message is logged before
+     *                           this exception is thrown.
+     */
+    public void checkTaskClass(final Class taskClass) throws BuildException {
+        if (!Modifier.isPublic(taskClass.getModifiers())) {
+            final String message = taskClass + " is not public";
+            project.log(message, Project.MSG_ERR);
+            throw new BuildException(message);
+        }
+        if (Modifier.isAbstract(taskClass.getModifiers())) {
+            final String message = taskClass + " is abstract";
+            project.log(message, Project.MSG_ERR);
+            throw new BuildException(message);
+        }
+        try {
+            taskClass.getConstructor((Class[]) null);
+            // don't have to check for public, since
+            // getConstructor finds public constructors only.
+        } catch (NoSuchMethodException e) {
+            final String message = "No public no-arg constructor in "
+                    + taskClass;
+            project.log(message, Project.MSG_ERR);
+            throw new BuildException(message);
+        }
+        if (!Task.class.isAssignableFrom(taskClass)) {
+            TaskAdapter.checkTaskClass(taskClass, project);
+        }
+    }
+
+    /**
+     * Returns the current task definition hashtable. The returned hashtable is
+     * "live" and so should not be modified.
+     *
+     * @return a map of from task name to implementing class
+     *         (String to Class).
+     */
+    public Hashtable getTaskDefinitions() {
+        synchronized (taskClassDefinitions) {
+            synchronized (antTypeTable) {
+                if (rebuildTaskClassDefinitions) {
+                    taskClassDefinitions.clear();
+                    for (Iterator i = antTypeTable.keySet().iterator();
+                         i.hasNext();) {
+                        String name = (String) i.next();
+                        Class clazz =
+                            (Class) antTypeTable.getExposedClass(name);
+                        if (clazz == null) {
+                            continue;
+                        }
+                        if (Task.class.isAssignableFrom(clazz)) {
+                            taskClassDefinitions.put(
+                                name, antTypeTable.getTypeClass(name));
+                        }
+                    }
+                    rebuildTaskClassDefinitions = false;
+                }
+            }
+        }
+        return taskClassDefinitions;
+    }
+
+
+    /**
+     * Returns the current type definition hashtable. The returned hashtable is
+     * "live" and so should not be modified.
+     *
+     * @return a map of from type name to implementing class
+     *         (String to Class).
+     */
+    public Hashtable getDataTypeDefinitions() {
+        synchronized (typeClassDefinitions) {
+            synchronized (antTypeTable) {
+                if (rebuildTypeClassDefinitions) {
+                    typeClassDefinitions.clear();
+                    for (Iterator i = antTypeTable.keySet().iterator();
+                         i.hasNext();) {
+                        String name = (String) i.next();
+                        Class clazz =
+                            (Class) antTypeTable.getExposedClass(name);
+                        if (clazz == null) {
+                            continue;
+                        }
+                        if (!(Task.class.isAssignableFrom(clazz))) {
+                            typeClassDefinitions.put(
+                                name, antTypeTable.getTypeClass(name));
+                        }
+                    }
+                    rebuildTypeClassDefinitions = false;
+                }
+            }
+        }
+        return typeClassDefinitions;
+    }
+
+    /**
+     * Adds a new datatype definition.
+     * Attempting to override an existing definition with an
+     * equivalent one (i.e. with the same classname) results in
+     * a verbose log message. Attempting to override an existing definition
+     * with a different one results in a warning log message, but the
+     * definition is changed.
+     *
+     * @param typeName The name of the datatype.
+     *                 Must not be <code>null</code>.
+     * @param typeClass The full name of the class implementing the datatype.
+     *                  Must not be <code>null</code>.
+     */
+    public void addDataTypeDefinition(String typeName, Class typeClass) {
+        AntTypeDefinition def = new AntTypeDefinition();
+        def.setName(typeName);
+        def.setClass(typeClass);
+        updateDataTypeDefinition(def);
+        project.log(" +User datatype: " + typeName + "     "
+                + typeClass.getName(), Project.MSG_DEBUG);
+    }
+
+    /**
+     * Describe <code>addDataTypeDefinition</code> method here.
+     *
+     * @param def an <code>AntTypeDefinition</code> value.
+     */
+    public void addDataTypeDefinition(AntTypeDefinition def) {
+        updateDataTypeDefinition(def);
+    }
+
+    /**
+     * Returns the current datatype definition hashtable. The returned
+     * hashtable is "live" and so should not be modified.
+     *
+     * @return a map of from datatype name to implementing class
+     *         (String to Class).
+     */
+    public Hashtable getAntTypeTable() {
+        return antTypeTable;
+    }
+
+    /**
+     * Creates a new instance of a task, adding it to a list of
+     * created tasks for later invalidation. This causes all tasks
+     * to be remembered until the containing project is removed
+     *
+     *  Called from Project.createTask(), which can be called by tasks.
+     *  The method should be deprecated, as it doesn't support ns and libs.
+     *
+     * @param taskType The name of the task to create an instance of.
+     *                 Must not be <code>null</code>.
+     *
+     * @return an instance of the specified task, or <code>null</code> if
+     *         the task name is not recognised.
+     *
+     * @exception BuildException if the task name is recognised but task
+     *                           creation fails.
+     */
+    public Task createTask(String taskType) throws BuildException {
+        Task task = createNewTask(taskType);
+        if (task == null && taskType.equals("property")) {
+            // quick fix for Ant.java use of property before
+            // initializing the project
+            addTaskDefinition("property",
+                              org.apache.tools.ant.taskdefs.Property.class);
+            task = createNewTask(taskType);
+        }
+        if (task != null) {
+            addCreatedTask(taskType, task);
+        }
+        return task;
+    }
+
+    /**
+     * Creates a new instance of a task. This task is not
+     * cached in the createdTasks list.
+     * @since ant1.6
+     * @param taskType The name of the task to create an instance of.
+     *                 Must not be <code>null</code>.
+     *
+     * @return an instance of the specified task, or <code>null</code> if
+     *         the task name is not recognised.
+     *
+     * @exception BuildException if the task name is recognised but task
+     *                           creation fails.
+     */
+    private Task createNewTask(String taskType) throws BuildException {
+        Class c = getComponentClass(taskType);
+        if (c == null || !(Task.class.isAssignableFrom(c))) {
+            return null;
+        }
+        Task task = (Task) createComponent(taskType);
+        if (task == null) {
+            return null;
+        }
+        task.setTaskType(taskType);
+
+        // set default value, can be changed by the user
+        task.setTaskName(taskType);
+
+        project.log("   +Task: " + taskType, Project.MSG_DEBUG);
+        return task;
+    }
+
+    /**
+     * Keeps a record of all tasks that have been created so that they
+     * can be invalidated if a new task definition overrides the current one.
+     *
+     * @param type The name of the type of task which has been created.
+     *             Must not be <code>null</code>.
+     *
+     * @param task The freshly created task instance.
+     *             Must not be <code>null</code>.
+     */
+    private void addCreatedTask(String type, Task task) {
+        synchronized (createdTasks) {
+            Vector v = (Vector) createdTasks.get(type);
+            if (v == null) {
+                v = new Vector();
+                createdTasks.put(type, v);
+            }
+            v.addElement(new WeakReference(task));
+        }
+    }
+
+    /**
+     * Mark tasks as invalid which no longer are of the correct type
+     * for a given taskname.
+     *
+     * @param type The name of the type of task to invalidate.
+     *             Must not be <code>null</code>.
+     */
+    private void invalidateCreatedTasks(String type) {
+        synchronized (createdTasks) {
+            Vector v = (Vector) createdTasks.get(type);
+            if (v != null) {
+                Enumeration taskEnum = v.elements();
+                while (taskEnum.hasMoreElements()) {
+                    WeakReference ref = (WeakReference) taskEnum.nextElement();
+                    Task t = (Task) ref.get();
+                    //being a weak ref, it may be null by this point
+                    if (t != null) {
+                        t.markInvalid();
+                    }
+                }
+                v.removeAllElements();
+                createdTasks.remove(type);
+            }
+        }
+    }
+
+    /**
+     * Creates a new instance of a data type.
+     *
+     * @param typeName The name of the data type to create an instance of.
+     *                 Must not be <code>null</code>.
+     *
+     * @return an instance of the specified data type, or <code>null</code> if
+     *         the data type name is not recognised.
+     *
+     * @exception BuildException if the data type name is recognised but
+     *                           instance creation fails.
+     */
+    public Object createDataType(String typeName) throws BuildException {
+        return createComponent(typeName);
+    }
+
+    /**
+     * Returns a description of the type of the given element.
+     * <p>
+     * This is useful for logging purposes.
+     *
+     * @param element The element to describe.
+     *                Must not be <code>null</code>.
+     *
+     * @return a description of the element type.
+     *
+     * @since Ant 1.6
+     */
+    public String getElementName(Object element) {
+        //  PR: I do not know what to do if the object class
+        //      has multiple defines
+        //      but this is for logging only...
+        Class elementClass = element.getClass();
+        for (Iterator i = antTypeTable.values().iterator(); i.hasNext();) {
+            AntTypeDefinition def = (AntTypeDefinition) i.next();
+            if (elementClass == def.getExposedClass(project)) {
+                return "The <" + def.getName() + "> type";
+            }
+        }
+        return "Class " + elementClass.getName();
+    }
+
+
+    /**
+     * Check if definition is a valid definition - it
+     * may be a definition of an optional task that
+     * does not exist.
+     * @param def the definition to test.
+     * @return true if exposed type of definition is present.
+     */
+    private boolean validDefinition(AntTypeDefinition def) {
+        return !(def.getTypeClass(project) == null
+            || def.getExposedClass(project) == null);
+    }
+
+    /**
+     * Check if two definitions are the same.
+     * @param def  the new definition.
+     * @param old the old definition.
+     * @return true if the two definitions are the same.
+     */
+    private boolean sameDefinition(
+        AntTypeDefinition def, AntTypeDefinition old) {
+        boolean defValid = validDefinition(def);
+        boolean sameValidity = (defValid == validDefinition(old));
+        //must have same validity; then if they are valid they must also be the same:
+        return sameValidity && (!defValid || def.sameDefinition(old, project));
+    }
+
+    /**
+     * Update the component definition table with a new or
+     * modified definition.
+     * @param def the definition to update or insert.
+     */
+    private void updateDataTypeDefinition(AntTypeDefinition def) {
+        String name = def.getName();
+        synchronized (antTypeTable) {
+            rebuildTaskClassDefinitions = true;
+            rebuildTypeClassDefinitions = true;
+            AntTypeDefinition old = antTypeTable.getDefinition(name);
+            if (old != null) {
+                if (sameDefinition(def, old)) {
+                    return;
+                }
+                Class oldClass = antTypeTable.getExposedClass(name);
+                boolean isTask =
+                    (oldClass != null && Task.class.isAssignableFrom(oldClass));
+                project.log("Trying to override old definition of "
+                    + (isTask ? "task " : "datatype ") + name,
+                    (def.similarDefinition(old, project))
+                    ? Project.MSG_VERBOSE : Project.MSG_WARN);
+                if (isTask) {
+                    invalidateCreatedTasks(name);
+                }
+            }
+            project.log(" +Datatype " + name + " " + def.getClassName(),
+                        Project.MSG_DEBUG);
+            antTypeTable.put(name, def);
+        }
+    }
+
+    /**
+     * Called at the start of processing an antlib.
+     * @param uri the uri that is associated with this antlib.
+     */
+    public void enterAntLib(String uri) {
+        antLibCurrentUri = uri;
+        antLibStack.push(uri);
+    }
+
+    /**
+     * @return the current antlib uri.
+     */
+    public String getCurrentAntlibUri() {
+        return antLibCurrentUri;
+    }
+
+    /**
+     * Called at the end of processing an antlib.
+     */
+    public void exitAntLib() {
+        antLibStack.pop();
+        antLibCurrentUri = (antLibStack.size() == 0)
+            ? null : (String) antLibStack.peek();
+    }
+
+    /**
+     * Load ant's tasks.
+     */
+    private void initTasks() {
+        ClassLoader classLoader = null;
+        if (project.getCoreLoader() != null
+            && !("only".equals(project.getProperty("build.sysclasspath")))) {
+            classLoader = project.getCoreLoader();
+        }
+        String dataDefs = "/org/apache/tools/ant/taskdefs/defaults.properties";
+
+        InputStream in = null;
+        try {
+            Properties props = new Properties();
+            in = this.getClass().getResourceAsStream(dataDefs);
+            if (in == null) {
+                throw new BuildException("Can't load default task list");
+            }
+            props.load(in);
+
+            Enumeration e = props.propertyNames();
+            while (e.hasMoreElements()) {
+                String name = (String) e.nextElement();
+                String className = props.getProperty(name);
+                AntTypeDefinition def = new AntTypeDefinition();
+                def.setName(name);
+                def.setClassName(className);
+                def.setClassLoader(classLoader);
+                def.setAdaptToClass(Task.class);
+                def.setAdapterClass(TaskAdapter.class);
+                antTypeTable.put(name, def);
+            }
+        } catch (IOException ex) {
+            throw new BuildException("Can't load default type list");
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (Exception ignore) {
+                    // Ignore
+                }
+            }
+        }
+    }
+
+    /**
+     * Load ant's datatypes.
+     */
+    private void initTypes() {
+        ClassLoader classLoader = null;
+        if (project.getCoreLoader() != null
+            && !("only".equals(project.getProperty("build.sysclasspath")))) {
+            classLoader = project.getCoreLoader();
+        }
+        String dataDefs = "/org/apache/tools/ant/types/defaults.properties";
+
+        InputStream in = null;
+        try {
+            Properties props = new Properties();
+            in = this.getClass().getResourceAsStream(dataDefs);
+            if (in == null) {
+                throw new BuildException("Can't load default datatype list");
+            }
+            props.load(in);
+
+            Enumeration e = props.propertyNames();
+            while (e.hasMoreElements()) {
+                String name = (String) e.nextElement();
+                String className = props.getProperty(name);
+                AntTypeDefinition def = new AntTypeDefinition();
+                def.setName(name);
+                def.setClassName(className);
+                def.setClassLoader(classLoader);
+                antTypeTable.put(name, def);
+            }
+        } catch (IOException ex) {
+            throw new BuildException("Can't load default type list");
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (Exception ignore) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    /**
+     * Called for each component name, check if the
+     * associated URI has been examined for antlibs.
+     */
+    private synchronized void checkNamespace(String componentName) {
+        String uri = ProjectHelper.extractUriFromComponentName(componentName);
+        if ("".equals(uri)) {
+            uri = ProjectHelper.ANT_CORE_URI;
+        }
+        if (!uri.startsWith(ProjectHelper.ANTLIB_URI)) {
+            return; // namespace that does not contain antlib
+        }
+        if (checkedNamespaces.contains(uri)) {
+            return; // Already processed
+        }
+        checkedNamespaces.add(uri);
+        Typedef definer = new Typedef();
+        definer.setProject(project);
+        definer.setURI(uri);
+        definer.setResource(
+            uri.substring("antlib:".length()).replace('.', '/')
+            + "/antlib.xml");
+        // a fishing expedition :- ignore errors if antlib not present
+        definer.setOnError(new Typedef.OnError("ignore"));
+        definer.init();
+        definer.execute();
+    }
+
+    /**
+     * Map that contains the component definitions.
+     */
+    private static class AntTypeTable extends Hashtable {
+        private Project project;
+
+        public AntTypeTable(Project project) {
+            this.project = project;
+        }
+
+        public AntTypeDefinition getDefinition(String key) {
+            return (AntTypeDefinition) (super.get(key));
+        }
+
+        /** Equivalent to getTypeType */
+        public Object get(Object key) {
+            return getTypeClass((String) key);
+        }
+
+        public Object create(String name) {
+            AntTypeDefinition def = getDefinition(name);
+            return (def == null) ? null : def.create(project);
+        }
+
+        public Class getTypeClass(String name) {
+            AntTypeDefinition def = getDefinition(name);
+            return (def == null) ? null : def.getTypeClass(project);
+        }
+
+        public Class getExposedClass(String name) {
+            AntTypeDefinition def = getDefinition(name);
+            return (def == null) ? null : def.getExposedClass(project);
+        }
+
+        public boolean contains(Object clazz) {
+            boolean found = false;
+            if (clazz instanceof Class) {
+                for (Iterator i = values().iterator(); i.hasNext() && !found;) {
+                    found |= (((AntTypeDefinition) (i.next())).getExposedClass(
+                        project) == clazz);
+                }
+            }
+            return found;
+        }
+
+        public boolean containsValue(Object value) {
+            return contains(value);
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/DefaultLogger.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/DefaultLogger.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/DefaultLogger.java	(revision 16139)
@@ -0,0 +1,314 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.StringReader;
+import org.apache.tools.ant.util.DateUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Writes build events to a PrintStream. Currently, it
+ * only writes which targets are being executed, and
+ * any messages that get logged.
+ *
+ */
+public class DefaultLogger implements BuildLogger {
+    /**
+     * Size of left-hand column for right-justified task name.
+     * @see #messageLogged(BuildEvent)
+     */
+    public static final int LEFT_COLUMN_SIZE = 12;
+
+    /** PrintStream to write non-error messages to */
+    protected PrintStream out;
+
+    /** PrintStream to write error messages to */
+    protected PrintStream err;
+
+    /** Lowest level of message to write out */
+    protected int msgOutputLevel = Project.MSG_ERR;
+
+    /** Time of the start of the build */
+    private long startTime = System.currentTimeMillis();
+
+    /** Line separator */
+    protected static final String lSep = StringUtils.LINE_SEP;
+
+    /** Whether or not to use emacs-style output */
+    protected boolean emacsMode = false;
+
+    /**
+     * Sole constructor.
+     */
+    public DefaultLogger() {
+    }
+
+    /**
+     * Sets the highest level of message this logger should respond to.
+     *
+     * Only messages with a message level lower than or equal to the
+     * given level should be written to the log.
+     * <P>
+     * Constants for the message levels are in the
+     * {@link Project Project} class. The order of the levels, from least
+     * to most verbose, is <code>MSG_ERR</code>, <code>MSG_WARN</code>,
+     * <code>MSG_INFO</code>, <code>MSG_VERBOSE</code>,
+     * <code>MSG_DEBUG</code>.
+     * <P>
+     * The default message level for DefaultLogger is Project.MSG_ERR.
+     *
+     * @param level the logging level for the logger.
+     */
+    public void setMessageOutputLevel(int level) {
+        this.msgOutputLevel = level;
+    }
+
+    /**
+     * Sets the output stream to which this logger is to send its output.
+     *
+     * @param output The output stream for the logger.
+     *               Must not be <code>null</code>.
+     */
+    public void setOutputPrintStream(PrintStream output) {
+        this.out = new PrintStream(output, true);
+    }
+
+    /**
+     * Sets the output stream to which this logger is to send error messages.
+     *
+     * @param err The error stream for the logger.
+     *            Must not be <code>null</code>.
+     */
+    public void setErrorPrintStream(PrintStream err) {
+        this.err = new PrintStream(err, true);
+    }
+
+    /**
+     * Sets this logger to produce emacs (and other editor) friendly output.
+     *
+     * @param emacsMode <code>true</code> if output is to be unadorned so that
+     *                  emacs and other editors can parse files names, etc.
+     */
+    public void setEmacsMode(boolean emacsMode) {
+        this.emacsMode = emacsMode;
+    }
+
+    /**
+     * Responds to a build being started by just remembering the current time.
+     *
+     * @param event Ignored.
+     */
+    public void buildStarted(BuildEvent event) {
+        startTime = System.currentTimeMillis();
+    }
+
+    /**
+     * Prints whether the build succeeded or failed,
+     * any errors the occurred during the build, and
+     * how long the build took.
+     *
+     * @param event An event with any relevant extra information.
+     *              Must not be <code>null</code>.
+     */
+    public void buildFinished(BuildEvent event) {
+        Throwable error = event.getException();
+        StringBuffer message = new StringBuffer();
+
+        if (error == null) {
+            message.append(StringUtils.LINE_SEP);
+            message.append("BUILD SUCCESSFUL");
+        } else {
+            message.append(StringUtils.LINE_SEP);
+            message.append("BUILD FAILED");
+            message.append(StringUtils.LINE_SEP);
+
+            if (Project.MSG_VERBOSE <= msgOutputLevel
+                || !(error instanceof BuildException)) {
+                message.append(StringUtils.getStackTrace(error));
+            } else {
+                if (error instanceof BuildException) {
+                    message.append(error.toString()).append(lSep);
+                } else {
+                    message.append(error.getMessage()).append(lSep);
+                }
+            }
+        }
+        message.append(StringUtils.LINE_SEP);
+        message.append("Total time: ");
+        message.append(formatTime(System.currentTimeMillis() - startTime));
+
+        String msg = message.toString();
+        if (error == null) {
+            printMessage(msg, out, Project.MSG_VERBOSE);
+        } else {
+            printMessage(msg, err, Project.MSG_ERR);
+        }
+        log(msg);
+    }
+
+    /**
+     * Logs a message to say that the target has started if this
+     * logger allows information-level messages.
+     *
+     * @param event An event with any relevant extra information.
+     *              Must not be <code>null</code>.
+      */
+    public void targetStarted(BuildEvent event) {
+        if (Project.MSG_INFO <= msgOutputLevel
+            && !event.getTarget().getName().equals("")) {
+				//count number of .s in address
+				String address=event.getTarget().getAddress();
+				int depth=0;
+				if ( address != null ) for ( int i=0; i<address.length(); i++ ) {
+					if ( address.charAt(i) == '.' ) depth++;
+				}
+
+				//construct message
+            String msg = StringUtils.LINE_SEP;
+				for ( int i=0; i<depth; i++ ) msg += "     ";
+				if ( event.getTarget().getAddress()!=null )
+					msg += event.getTarget().getAddress() + " ";
+				msg += event.getTarget().getName() + ":";
+            printMessage(msg, out, event.getPriority());
+            log(msg);
+        }
+    }
+
+    /**
+     * No-op implementation.
+     *
+     * @param event Ignored.
+     */
+    public void targetFinished(BuildEvent event) {
+    }
+
+    /**
+     * No-op implementation.
+     *
+     * @param event Ignored.
+     */
+    public void taskStarted(BuildEvent event) {
+    }
+
+    /**
+     * No-op implementation.
+     *
+     * @param event Ignored.
+     */
+    public void taskFinished(BuildEvent event) {
+    }
+
+    /**
+     * Logs a message, if the priority is suitable.
+     * In non-emacs mode, task level messages are prefixed by the
+     * task name which is right-justified.
+     *
+     * @param event A BuildEvent containing message information.
+     *              Must not be <code>null</code>.
+     */
+    public void messageLogged(BuildEvent event) {
+        int priority = event.getPriority();
+        // Filter out messages based on priority
+        if (priority <= msgOutputLevel) {
+
+            StringBuffer message = new StringBuffer();
+            if (event.getTask() != null && !emacsMode) {
+                // Print out the name of the task if we're in one
+                String name = event.getTask().getTaskName();
+                String label = "[" + name + "] ";
+                int size = LEFT_COLUMN_SIZE - label.length();
+                StringBuffer tmp = new StringBuffer();
+                for (int i = 0; i < size; i++) {
+                    tmp.append(" ");
+                }
+                tmp.append(label);
+                label = tmp.toString();
+
+                try {
+                    BufferedReader r =
+                        new BufferedReader(
+                            new StringReader(event.getMessage()));
+                    String line = r.readLine();
+                    boolean first = true;
+                    while (line != null) {
+                        if (!first) {
+                            message.append(StringUtils.LINE_SEP);
+                        }
+                        first = false;
+                        message.append(label).append(line);
+                        line = r.readLine();
+                    }
+                } catch (IOException e) {
+                    // shouldn't be possible
+                    message.append(label).append(event.getMessage());
+                }
+            } else {
+                message.append(event.getMessage());
+            }
+
+            String msg = message.toString();
+            if (priority != Project.MSG_ERR) {
+                printMessage(msg, out, priority);
+            } else {
+                printMessage(msg, err, priority);
+            }
+            log(msg);
+        }
+    }
+
+    /**
+     * Convenience method to format a specified length of time.
+     *
+     * @param millis Length of time to format, in milliseconds.
+     *
+     * @return the time as a formatted string.
+     *
+     * @see DateUtils#formatElapsedTime(long)
+     */
+    protected static String formatTime(final long millis) {
+        return DateUtils.formatElapsedTime(millis);
+    }
+
+    /**
+     * Prints a message to a PrintStream.
+     *
+     * @param message  The message to print.
+     *                 Should not be <code>null</code>.
+     * @param stream   A PrintStream to print the message to.
+     *                 Must not be <code>null</code>.
+     * @param priority The priority of the message.
+     *                 (Ignored in this implementation.)
+     */
+    protected void printMessage(final String message,
+                                final PrintStream stream,
+                                final int priority) {
+        stream.println(message);
+    }
+
+    /**
+     * Empty implementation which allows subclasses to receive the
+     * same output that is generated here.
+     *
+     * @param message Message being logged. Should not be <code>null</code>.
+     */
+    protected void log(String message) {
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/DemuxInputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/DemuxInputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/DemuxInputStream.java	(revision 16139)
@@ -0,0 +1,59 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ *
+ * Passes input requests to the project object for demuxing into
+ * individual tasks and threads.
+ *
+ * @since Ant 1.6
+ */
+public class DemuxInputStream extends InputStream {
+
+    /**
+     * The project to from which to get input.
+     */
+    private Project project;
+
+    /**
+     * Create a DemuxInputStream for the given project
+     *
+     * @param project the project instance
+     */
+    public DemuxInputStream(Project project) {
+        this.project = project;
+    }
+
+    public int read() throws IOException {
+        byte[] buffer = new byte[1];
+        if (project.demuxInput(buffer, 0, 1) == -1) {
+            return -1;
+        }
+        return buffer[0];
+    }
+
+
+    public int read(byte[] buffer, int offset, int length) throws IOException {
+        return project.demuxInput(buffer, offset, length);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/DemuxOutputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/DemuxOutputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/DemuxOutputStream.java	(revision 16139)
@@ -0,0 +1,247 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Hashtable;
+
+/**
+ * Logs content written by a thread and forwards the buffers onto the
+ * project object which will forward the content to the appropriate
+ * task.
+ *
+ * @since 1.4
+ */
+public class DemuxOutputStream extends OutputStream {
+
+    /**
+     * A data class to store information about a buffer. Such information
+     * is stored on a per-thread basis.
+     */
+    private static class BufferInfo {
+        /**
+         * The per-thread output stream.
+         */
+        private ByteArrayOutputStream buffer;
+
+        /**
+         * Indicates we have just seen a carriage return. It may be part of
+         * a crlf pair or a single cr invoking processBuffer twice.
+         */
+         private boolean crSeen = false;
+    }
+
+    /** Maximum buffer size. */
+    private static final int MAX_SIZE = 1024;
+
+    /** Initial buffer size. */
+    private static final int INTIAL_SIZE = 132;
+
+    /** Carriage return */
+    private static final int CR = 0x0d;
+
+    /** Linefeed */
+    private static final int LF = 0x0a;
+
+    /** Mapping from thread to buffer (Thread to BufferInfo). */
+    private Hashtable buffers = new Hashtable();
+
+    /**
+     * The project to send output to.
+     */
+    private Project project;
+
+    /**
+     * Whether or not this stream represents an error stream.
+     */
+    private boolean isErrorStream;
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param project The project instance for which output is being
+     *                demultiplexed. Must not be <code>null</code>.
+     * @param isErrorStream <code>true</code> if this is the error string,
+     *                      otherwise a normal output stream. This is
+     *                      passed to the project so it knows
+     *                      which stream it is receiving.
+     */
+    public DemuxOutputStream(Project project, boolean isErrorStream) {
+        this.project = project;
+        this.isErrorStream = isErrorStream;
+    }
+
+    /**
+     * Returns the buffer associated with the current thread.
+     *
+     * @return a BufferInfo for the current thread to write data to
+     */
+    private BufferInfo getBufferInfo() {
+        Thread current = Thread.currentThread();
+        BufferInfo bufferInfo = (BufferInfo) buffers.get(current);
+        if (bufferInfo == null) {
+            bufferInfo = new BufferInfo();
+            bufferInfo.buffer = new ByteArrayOutputStream(INTIAL_SIZE);
+            bufferInfo.crSeen = false;
+            buffers.put(current, bufferInfo);
+        }
+        return bufferInfo;
+    }
+
+    /**
+     * Resets the buffer for the current thread.
+     */
+    private void resetBufferInfo() {
+        Thread current = Thread.currentThread();
+        BufferInfo bufferInfo = (BufferInfo) buffers.get(current);
+        try {
+            bufferInfo.buffer.close();
+        } catch (IOException e) {
+            // Shouldn't happen
+        }
+        bufferInfo.buffer = new ByteArrayOutputStream();
+        bufferInfo.crSeen = false;
+    }
+
+    /**
+     * Removes the buffer for the current thread.
+     */
+    private void removeBuffer() {
+        Thread current = Thread.currentThread();
+        buffers.remove (current);
+    }
+
+    /**
+     * Writes the data to the buffer and flushes the buffer if a line
+     * separator is detected or if the buffer has reached its maximum size.
+     *
+     * @param cc data to log (byte).
+     * @exception IOException if the data cannot be written to the stream
+     */
+    public void write(int cc) throws IOException {
+        final byte c = (byte) cc;
+
+        BufferInfo bufferInfo = getBufferInfo();
+
+        if (c == '\n') {
+            // LF is always end of line (i.e. CRLF or single LF)
+            bufferInfo.buffer.write(cc);
+            processBuffer(bufferInfo.buffer);
+        } else {
+            if (bufferInfo.crSeen) {
+                // CR without LF - send buffer then add char
+                processBuffer(bufferInfo.buffer);
+            }
+            // add into buffer
+            bufferInfo.buffer.write(cc);
+        }
+        bufferInfo.crSeen = (c == '\r');
+        if (!bufferInfo.crSeen && bufferInfo.buffer.size() > MAX_SIZE) {
+            processBuffer(bufferInfo.buffer);
+        }
+    }
+
+    /**
+     * Converts the buffer to a string and sends it to the project.
+     *
+     * @param buffer the ByteArrayOutputStream used to collect the output
+     * until a line separator is seen.
+     *
+     * @see Project#demuxOutput(String,boolean)
+     */
+    protected void processBuffer(ByteArrayOutputStream buffer) {
+        String output = buffer.toString();
+        project.demuxOutput(output, isErrorStream);
+        resetBufferInfo();
+    }
+
+    /**
+     * Converts the buffer to a string and sends it to the project.
+     *
+     * @param buffer the ByteArrayOutputStream used to collect the output
+     * until a line separator is seen.
+     *
+     * @see Project#demuxOutput(String,boolean)
+     */
+    protected void processFlush(ByteArrayOutputStream buffer) {
+        String output = buffer.toString();
+        project.demuxFlush(output, isErrorStream);
+        resetBufferInfo();
+    }
+
+    /**
+     * Equivalent to flushing the stream.
+     *
+     * @exception IOException if there is a problem closing the stream.
+     *
+     * @see #flush
+     */
+    public void close() throws IOException {
+        flush();
+        removeBuffer();
+    }
+
+    /**
+     * Writes all remaining data in the buffer associated
+     * with the current thread to the project.
+     *
+     * @exception IOException if there is a problem flushing the stream.
+     */
+    public void flush() throws IOException {
+        BufferInfo bufferInfo = getBufferInfo();
+        if (bufferInfo.buffer.size() > 0) {
+            processFlush(bufferInfo.buffer);
+        }
+    }
+
+    /**
+     * Write a block of characters to the output stream
+     *
+     * @param b the array containing the data
+     * @param off the offset into the array where data starts
+     * @param len the length of block
+     *
+     * @throws IOException if the data cannot be written into the stream.
+     */
+    public void write(byte[] b, int off, int len) throws IOException {
+        // find the line breaks and pass other chars through in blocks
+        int offset = off;
+        int blockStartOffset = offset;
+        int remaining = len;
+        BufferInfo bufferInfo = getBufferInfo();
+        while (remaining > 0) {
+            while (remaining > 0 && b[offset] != LF && b[offset] != CR) {
+                offset++;
+                remaining--;
+            }
+            // either end of buffer or a line separator char
+            int blockLength = offset - blockStartOffset;
+            if (blockLength > 0) {
+                bufferInfo.buffer.write(b, blockStartOffset, blockLength);
+            }
+            while (remaining > 0 && (b[offset] == LF || b[offset] == CR)) {
+                write(b[offset]);
+                offset++;
+                remaining--;
+            }
+            blockStartOffset = offset;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/Diagnostics.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/Diagnostics.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/Diagnostics.java	(revision 16139)
@@ -0,0 +1,378 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant;
+
+import org.apache.tools.ant.util.LoaderUtils;
+
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.parsers.SAXParser;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.PrintStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * A little diagnostic helper that output some information that may help
+ * in support. It should quickly give correct information about the
+ * jar existing in ant.home/lib and the jar versions...
+ *
+ * @since Ant 1.5
+ */
+public final class Diagnostics {
+
+    private static final String TEST_CLASS
+        = "org.apache.tools.ant.taskdefs.optional.Test";
+
+    /** utility class */
+    private Diagnostics() {
+    }
+
+    /**
+     * Check if optional tasks are available. Not that it does not check
+     * for implementation version. Use <tt>validateVersion()</tt> for this.
+     * @return <tt>true</tt> if optional tasks are available.
+     */
+    public static boolean isOptionalAvailable() {
+        try {
+            Class.forName(TEST_CLASS);
+        } catch (ClassNotFoundException e) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Check if core and optional implementation version do match.
+     * @throws BuildException if the implementation version of optional tasks
+     * does not match the core implementation version.
+     */
+    public static void validateVersion() throws BuildException {
+        try {
+            Class optional
+                = Class.forName("org.apache.tools.ant.taskdefs.optional.Test");
+            String coreVersion = getImplementationVersion(Main.class);
+            String optionalVersion = getImplementationVersion(optional);
+
+            if (coreVersion != null && !coreVersion.equals(optionalVersion)) {
+                throw new BuildException("Invalid implementation version "
+                    + "between Ant core and Ant optional tasks.\n"
+                    + " core    : " + coreVersion + "\n"
+                    + " optional: " + optionalVersion);
+            }
+        } catch (ClassNotFoundException e) {
+            // ignore
+        }
+    }
+
+    /**
+     * return the list of jar files existing in ANT_HOME/lib
+     * and that must have been picked up by Ant script.
+     * @return the list of jar files existing in ant.home/lib or
+     * <tt>null</tt> if an error occurs.
+     */
+    public static File[] listLibraries() {
+        String home = System.getProperty("ant.home");
+        if (home == null) {
+            return null;
+        }
+        File libDir = new File(home, "lib");
+        FilenameFilter filter = new FilenameFilter() {
+            public boolean accept(File dir, String name) {
+                return name.endsWith(".jar");
+            }
+        };
+        // listFiles is JDK 1.2+ method...
+        String[] filenames = libDir.list(filter);
+        if (filenames == null) {
+            return null;
+        }
+        File[] files = new File[filenames.length];
+        for (int i = 0; i < filenames.length; i++) {
+            files[i] = new File(libDir, filenames[i]);
+        }
+        return files;
+    }
+
+    /**
+     * main entry point for command line
+     * @param args command line arguments.
+     */
+    public static void main(String[] args) {
+        doReport(System.out);
+    }
+
+
+    /**
+     * Helper method to get the implementation version.
+     * @param clazz the class to get the information from.
+     * @return null if there is no package or implementation version.
+     * '?.?' for JDK 1.0 or 1.1.
+     */
+    private static String getImplementationVersion(Class clazz) {
+        try {
+          // Package pkg = clazz.getPackage();
+          Method method = Class.class.getMethod("getPackage", new Class[0]);
+          Object pkg = method.invoke(clazz, null);
+          if (pkg != null) {
+              // pkg.getImplementationVersion();
+              method = pkg.getClass().getMethod("getImplementationVersion", new Class[0]);
+              Object version = method.invoke(pkg, null);
+              return (String) version;
+          }
+        } catch (Exception e) {
+          // JDK < 1.2 should land here because the methods above don't exist.
+          return "?.?";
+        }
+        return null;
+    }
+
+    /**
+     * what parser are we using.
+     * @return the classname of the parser
+     */
+    private static String getXmlParserName() {
+        SAXParser saxParser = getSAXParser();
+        if (saxParser == null) {
+            return "Could not create an XML Parser";
+        }
+
+        // check to what is in the classname
+        String saxParserName = saxParser.getClass().getName();
+        return saxParserName;
+    }
+
+    /**
+     * Create a JAXP SAXParser
+     * @return parser or null for trouble
+     */
+    private static SAXParser getSAXParser() {
+        SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+        if (saxParserFactory == null) {
+            return null;
+        }
+        SAXParser saxParser = null;
+        try {
+            saxParser = saxParserFactory.newSAXParser();
+        } catch (Exception e) {
+            // ignore
+        }
+        return saxParser;
+    }
+
+    /**
+     * get the location of the parser
+     * @return path or null for trouble in tracking it down
+     */
+
+    private static String getXMLParserLocation() {
+        SAXParser saxParser = getSAXParser();
+        if (saxParser == null) {
+            return null;
+        }
+        String location = getClassLocation(saxParser.getClass());
+        return location;
+    }
+
+    /**
+     * get the location of a class. Stolen from axis/webapps/happyaxis.jsp
+     * @param clazz
+     * @return the jar file or path where a class was found, or null
+     */
+
+    private static String getClassLocation(Class clazz) {
+        File f = LoaderUtils.getClassSource(clazz);
+        return f == null ? null : f.getAbsolutePath();
+    }
+
+
+    /**
+     * Print a report to the given stream.
+     * @param out the stream to print the report to.
+     */
+    public static void doReport(PrintStream out) {
+        out.println("------- Ant diagnostics report -------");
+        out.println(Main.getAntVersion());
+        out.println();
+        out.println("-------------------------------------------");
+        out.println(" Implementation Version (JDK1.2+ only)");
+        out.println("-------------------------------------------");
+        out.println("core tasks     : " + getImplementationVersion(Main.class));
+
+        Class optional = null;
+        try {
+            optional = Class.forName(
+                    "org.apache.tools.ant.taskdefs.optional.Test");
+            out.println("optional tasks : "
+                + getImplementationVersion(optional));
+        } catch (ClassNotFoundException e) {
+            out.println("optional tasks : not available");
+        }
+
+        out.println();
+        out.println("-------------------------------------------");
+        out.println(" ANT_HOME/lib jar listing");
+        out.println("-------------------------------------------");
+        doReportLibraries(out);
+
+        out.println();
+        out.println("-------------------------------------------");
+        out.println(" Tasks availability");
+        out.println("-------------------------------------------");
+        doReportTasksAvailability(out);
+
+        out.println();
+        out.println("-------------------------------------------");
+        out.println(" org.apache.env.Which diagnostics");
+        out.println("-------------------------------------------");
+        doReportWhich(out);
+
+
+        out.println();
+        out.println("-------------------------------------------");
+        out.println(" XML Parser information");
+        out.println("-------------------------------------------");
+        doReportParserInfo(out);
+
+        out.println();
+        out.println("-------------------------------------------");
+        out.println(" System properties");
+        out.println("-------------------------------------------");
+        doReportSystemProperties(out);
+
+        out.println();
+    }
+
+    /**
+     * Report a listing of system properties existing in the current vm.
+     * @param out the stream to print the properties to.
+     */
+    private static void doReportSystemProperties(PrintStream out) {
+        for (Enumeration keys = System.getProperties().propertyNames();
+            keys.hasMoreElements();) {
+            String key = (String) keys.nextElement();
+            out.println(key + " : " + System.getProperty(key));
+        }
+    }
+
+
+    /**
+     * Report the content of ANT_HOME/lib directory
+     * @param out the stream to print the content to
+     */
+    private static void doReportLibraries(PrintStream out) {
+        out.println("ant.home: " + System.getProperty("ant.home"));
+        File[] libs = listLibraries();
+        if (libs == null) {
+            out.println("Unable to list libraries.");
+            return;
+        }
+        for (int i = 0; i < libs.length; i++) {
+            out.println(libs[i].getName()
+                    + " (" + libs[i].length() + " bytes)");
+        }
+    }
+
+
+    /**
+     * Call org.apache.env.Which if available
+     * @param out the stream to print the content to.
+     */
+    private static void doReportWhich(PrintStream out) {
+        Throwable error = null;
+        try {
+            Class which = Class.forName("org.apache.env.Which");
+            Method method
+                = which.getMethod("main", new Class[]{String[].class});
+            method.invoke(null, new Object[]{new String[]{}});
+        } catch (ClassNotFoundException e) {
+            out.println("Not available.");
+            out.println("Download it at http://xml.apache.org/commons/");
+        } catch (InvocationTargetException e) {
+            error = e.getTargetException() == null ? e : e.getTargetException();
+        } catch (Throwable e) {
+            error = e;
+        }
+        // report error if something weird happens...this is diagnostic.
+        if (error != null) {
+            out.println("Error while running org.apache.env.Which");
+            error.printStackTrace();
+        }
+    }
+
+    /**
+     * Create a report about non-available tasks that are defined in the
+     * mapping but could not be found via lookup. It might generally happen
+     * because Ant requires multiple libraries to compile and one of them
+     * was missing when compiling Ant.
+     * @param out the stream to print the tasks report to
+     * <tt>null</tt> for a missing stream (ie mapping).
+     */
+    private static void doReportTasksAvailability(PrintStream out) {
+        InputStream is = Main.class.getResourceAsStream(
+                "/org/apache/tools/ant/taskdefs/defaults.properties");
+        if (is == null) {
+            out.println("None available");
+        } else {
+            Properties props = new Properties();
+            try {
+                props.load(is);
+                for (Enumeration keys = props.keys(); keys.hasMoreElements();) {
+                    String key = (String) keys.nextElement();
+                    String classname = props.getProperty(key);
+                    try {
+                        Class.forName(classname);
+                        props.remove(key);
+                    } catch (ClassNotFoundException e) {
+                        out.println(key + " : Not Available");
+                    } catch (NoClassDefFoundError e) {
+                        String pkg = e.getMessage().replace('/', '.');
+                        out.println(key + " : Missing dependency " + pkg);
+                    } catch (Error e) {
+                        out.println(key + " : Initialization error");
+                    }
+                }
+                if (props.size() == 0) {
+                    out.println("All defined tasks are available");
+                }
+            } catch (IOException e) {
+                out.println(e.getMessage());
+            }
+        }
+    }
+
+    /**
+     * tell the user about the XML parser
+     * @param out
+     */
+    private static void doReportParserInfo(PrintStream out) {
+        String parserName = getXmlParserName();
+        String parserLocation = getXMLParserLocation();
+        if (parserName == null) {
+            parserName = "unknown";
+        }
+        if (parserLocation == null) {
+            parserLocation = "unknown";
+        }
+        out.println("XML Parser : " + parserName);
+        out.println("XML Parser Location: " + parserLocation);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/DirectoryScanner.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/DirectoryScanner.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/DirectoryScanner.java	(revision 16139)
@@ -0,0 +1,1658 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+import java.util.Arrays;
+import java.util.Vector;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.Enumeration;
+
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceFactory;
+import org.apache.tools.ant.types.selectors.FileSelector;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+import org.apache.tools.ant.types.selectors.SelectorScanner;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Class for scanning a directory for files/directories which match certain
+ * criteria.
+ * <p>
+ * These criteria consist of selectors and patterns which have been specified.
+ * With the selectors you can select which files you want to have included.
+ * Files which are not selected are excluded. With patterns you can include
+ * or exclude files based on their filename.
+ * <p>
+ * The idea is simple. A given directory is recursively scanned for all files
+ * and directories. Each file/directory is matched against a set of selectors,
+ * including special support for matching against filenames with include and
+ * and exclude patterns. Only files/directories which match at least one
+ * pattern of the include pattern list or other file selector, and don't match
+ * any pattern of the exclude pattern list or fail to match against a required
+ * selector will be placed in the list of files/directories found.
+ * <p>
+ * When no list of include patterns is supplied, "**" will be used, which
+ * means that everything will be matched. When no list of exclude patterns is
+ * supplied, an empty list is used, such that nothing will be excluded. When
+ * no selectors are supplied, none are applied.
+ * <p>
+ * The filename pattern matching is done as follows:
+ * The name to be matched is split up in path segments. A path segment is the
+ * name of a directory or file, which is bounded by
+ * <code>File.separator</code> ('/' under UNIX, '\' under Windows).
+ * For example, "abc/def/ghi/xyz.java" is split up in the segments "abc",
+ * "def","ghi" and "xyz.java".
+ * The same is done for the pattern against which should be matched.
+ * <p>
+ * The segments of the name and the pattern are then matched against each
+ * other. When '**' is used for a path segment in the pattern, it matches
+ * zero or more path segments of the name.
+ * <p>
+ * There is a special case regarding the use of <code>File.separator</code>s
+ * at the beginning of the pattern and the string to match:<br>
+ * When a pattern starts with a <code>File.separator</code>, the string
+ * to match must also start with a <code>File.separator</code>.
+ * When a pattern does not start with a <code>File.separator</code>, the
+ * string to match may not start with a <code>File.separator</code>.
+ * When one of these rules is not obeyed, the string will not
+ * match.
+ * <p>
+ * When a name path segment is matched against a pattern path segment, the
+ * following special characters can be used:<br>
+ * '*' matches zero or more characters<br>
+ * '?' matches one character.
+ * <p>
+ * Examples:
+ * <p>
+ * "**\*.class" matches all .class files/dirs in a directory tree.
+ * <p>
+ * "test\a??.java" matches all files/dirs which start with an 'a', then two
+ * more characters and then ".java", in a directory called test.
+ * <p>
+ * "**" matches everything in a directory tree.
+ * <p>
+ * "**\test\**\XYZ*" matches all files/dirs which start with "XYZ" and where
+ * there is a parent directory called test (e.g. "abc\test\def\ghi\XYZ123").
+ * <p>
+ * Case sensitivity may be turned off if necessary. By default, it is
+ * turned on.
+ * <p>
+ * Example of usage:
+ * <pre>
+ *   String[] includes = {"**\\*.class"};
+ *   String[] excludes = {"modules\\*\\**"};
+ *   ds.setIncludes(includes);
+ *   ds.setExcludes(excludes);
+ *   ds.setBasedir(new File("test"));
+ *   ds.setCaseSensitive(true);
+ *   ds.scan();
+ *
+ *   System.out.println("FILES:");
+ *   String[] files = ds.getIncludedFiles();
+ *   for (int i = 0; i < files.length; i++) {
+ *     System.out.println(files[i]);
+ *   }
+ * </pre>
+ * This will scan a directory called test for .class files, but excludes all
+ * files in all proper subdirectories of a directory called "modules"
+ *
+ */
+public class DirectoryScanner
+       implements FileScanner, SelectorScanner, ResourceFactory {
+
+    /** Is OpenVMS the operating system we're running on? */
+    private static final boolean ON_VMS = Os.isFamily("openvms");
+
+    /**
+     * Patterns which should be excluded by default.
+     *
+     * <p>Note that you can now add patterns to the list of default
+     * excludes.  Added patterns will not become part of this array
+     * that has only been kept around for backwards compatibility
+     * reasons.</p>
+     *
+     * @deprecated use the {@link #getDefaultExcludes
+     * getDefaultExcludes} method instead.
+     */
+    protected static final String[] DEFAULTEXCLUDES = {
+        // Miscellaneous typical temporary files
+        "**/*~",
+        "**/#*#",
+        "**/.#*",
+        "**/%*%",
+        "**/._*",
+
+        // CVS
+        "**/CVS",
+        "**/CVS/**",
+        "**/.cvsignore",
+
+        // SCCS
+        "**/SCCS",
+        "**/SCCS/**",
+
+        // Visual SourceSafe
+        "**/vssver.scc",
+
+        // Subversion
+        "**/.svn",
+        "**/.svn/**",
+
+        // Mac
+        "**/.DS_Store"
+    };
+
+    /** Helper. */
+    private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+    /** iterations for case-sensitive scanning. */
+    private static final boolean[] CS_SCAN_ONLY = new boolean[] {true};
+
+    /** iterations for non-case-sensitive scanning. */
+    private static final boolean[] CS_THEN_NON_CS = new boolean[] {true, false};
+
+    /**
+     * Patterns which should be excluded by default.
+     *
+     * @see #addDefaultExcludes()
+     */
+    private static Vector defaultExcludes = new Vector();
+    static {
+        resetDefaultExcludes();
+    }
+
+    /** The base directory to be scanned. */
+    protected File basedir;
+
+    /** The patterns for the files to be included. */
+    protected String[] includes;
+
+    /** The patterns for the files to be excluded. */
+    protected String[] excludes;
+
+    /** Selectors that will filter which files are in our candidate list. */
+    protected FileSelector[] selectors = null;
+
+    /**
+     * The files which matched at least one include and no excludes
+     * and were selected.
+     */
+    protected Vector filesIncluded;
+
+    /** The files which did not match any includes or selectors. */
+    protected Vector filesNotIncluded;
+
+    /**
+     * The files which matched at least one include and at least
+     * one exclude.
+     */
+    protected Vector filesExcluded;
+
+    /**
+     * The directories which matched at least one include and no excludes
+     * and were selected.
+     */
+    protected Vector dirsIncluded;
+
+    /** The directories which were found and did not match any includes. */
+    protected Vector dirsNotIncluded;
+
+    /**
+     * The directories which matched at least one include and at least one
+     * exclude.
+     */
+    protected Vector dirsExcluded;
+
+    /**
+     * The files which matched at least one include and no excludes and
+     * which a selector discarded.
+     */
+    protected Vector filesDeselected;
+
+    /**
+     * The directories which matched at least one include and no excludes
+     * but which a selector discarded.
+     */
+    protected Vector dirsDeselected;
+
+    /** Whether or not our results were built by a slow scan. */
+    protected boolean haveSlowResults = false;
+
+    /**
+     * Whether or not the file system should be treated as a case sensitive
+     * one.
+     */
+    protected boolean isCaseSensitive = true;
+
+    /**
+     * Whether or not symbolic links should be followed.
+     *
+     * @since Ant 1.5
+     */
+    private boolean followSymlinks = true;
+
+    /** Whether or not everything tested so far has been included. */
+    protected boolean everythingIncluded = true;
+
+    /**
+     * Temporary table to speed up the various scanning methods.
+     *
+     * @since Ant 1.6
+     */
+    private Map fileListMap = new HashMap();
+
+    /**
+     * List of all scanned directories.
+     *
+     * @since Ant 1.6
+     */
+    private Set scannedDirs = new HashSet();
+
+    /**
+     * Set of all include patterns that are full file names and don't
+     * contain any wildcards.
+     *
+     * <p>If this instance is not case sensitive, the file names get
+     * turned to lower case.</p>
+     *
+     * <p>Gets lazily initialized on the first invocation of
+     * isIncluded or isExcluded and cleared at the end of the scan
+     * method (cleared in clearCaches, actually).</p>
+     *
+     * @since Ant 1.6.3
+     */
+    private Set includeNonPatterns = new HashSet();
+
+    /**
+     * Set of all include patterns that are full file names and don't
+     * contain any wildcards.
+     *
+     * <p>If this instance is not case sensitive, the file names get
+     * turned to lower case.</p>
+     *
+     * <p>Gets lazily initialized on the first invocation of
+     * isIncluded or isExcluded and cleared at the end of the scan
+     * method (cleared in clearCaches, actually).</p>
+     *
+     * @since Ant 1.6.3
+     */
+    private Set excludeNonPatterns = new HashSet();
+
+    /**
+     * Array of all include patterns that contain wildcards.
+     *
+     * <p>Gets lazily initialized on the first invocation of
+     * isIncluded or isExcluded and cleared at the end of the scan
+     * method (cleared in clearCaches, actually).</p>
+     *
+     * @since Ant 1.6.3
+     */
+    private String[] includePatterns;
+
+    /**
+     * Array of all exclude patterns that contain wildcards.
+     *
+     * <p>Gets lazily initialized on the first invocation of
+     * isIncluded or isExcluded and cleared at the end of the scan
+     * method (cleared in clearCaches, actually).</p>
+     *
+     * @since Ant 1.6.3
+     */
+    private String[] excludePatterns;
+
+    /**
+     * Have the non-pattern sets and pattern arrays for in- and
+     * excludes been initialized?
+     *
+     * @since Ant 1.6.3
+     */
+    private boolean areNonPatternSetsReady = false;
+
+    /**
+     * Scanning flag.
+     *
+     * @since Ant 1.6.3
+     */
+    private boolean scanning = false;
+
+    /**
+     * Scanning lock.
+     *
+     * @since Ant 1.6.3
+     */
+    private Object scanLock = new Object();
+
+    /**
+     * Slow scanning flag.
+     *
+     * @since Ant 1.6.3
+     */
+    private boolean slowScanning = false;
+
+    /**
+     * Slow scanning lock.
+     *
+     * @since Ant 1.6.3
+     */
+    private Object slowScanLock = new Object();
+
+    /**
+     * Exception thrown during scan.
+     *
+     * @since Ant 1.6.3
+     */
+    private IllegalStateException illegal = null;
+
+    /**
+     * Sole constructor.
+     */
+    public DirectoryScanner() {
+    }
+
+    /**
+     * Test whether or not a given path matches the start of a given
+     * pattern up to the first "**".
+     * <p>
+     * This is not a general purpose test and should only be used if you
+     * can live with false positives. For example, <code>pattern=**\a</code>
+     * and <code>str=b</code> will yield <code>true</code>.
+     *
+     * @param pattern The pattern to match against. Must not be
+     *                <code>null</code>.
+     * @param str     The path to match, as a String. Must not be
+     *                <code>null</code>.
+     *
+     * @return whether or not a given path matches the start of a given
+     * pattern up to the first "**".
+     */
+    protected static boolean matchPatternStart(String pattern, String str) {
+        return SelectorUtils.matchPatternStart(pattern, str);
+    }
+
+    /**
+     * Test whether or not a given path matches the start of a given
+     * pattern up to the first "**".
+     * <p>
+     * This is not a general purpose test and should only be used if you
+     * can live with false positives. For example, <code>pattern=**\a</code>
+     * and <code>str=b</code> will yield <code>true</code>.
+     *
+     * @param pattern The pattern to match against. Must not be
+     *                <code>null</code>.
+     * @param str     The path to match, as a String. Must not be
+     *                <code>null</code>.
+     * @param isCaseSensitive Whether or not matching should be performed
+     *                        case sensitively.
+     *
+     * @return whether or not a given path matches the start of a given
+     * pattern up to the first "**".
+     */
+    protected static boolean matchPatternStart(String pattern, String str,
+                                               boolean isCaseSensitive) {
+        return SelectorUtils.matchPatternStart(pattern, str, isCaseSensitive);
+    }
+
+    /**
+     * Test whether or not a given path matches a given pattern.
+     *
+     * @param pattern The pattern to match against. Must not be
+     *                <code>null</code>.
+     * @param str     The path to match, as a String. Must not be
+     *                <code>null</code>.
+     *
+     * @return <code>true</code> if the pattern matches against the string,
+     *         or <code>false</code> otherwise.
+     */
+    protected static boolean matchPath(String pattern, String str) {
+        return SelectorUtils.matchPath(pattern, str);
+    }
+
+    /**
+     * Test whether or not a given path matches a given pattern.
+     *
+     * @param pattern The pattern to match against. Must not be
+     *                <code>null</code>.
+     * @param str     The path to match, as a String. Must not be
+     *                <code>null</code>.
+     * @param isCaseSensitive Whether or not matching should be performed
+     *                        case sensitively.
+     *
+     * @return <code>true</code> if the pattern matches against the string,
+     *         or <code>false</code> otherwise.
+     */
+    protected static boolean matchPath(String pattern, String str,
+                                       boolean isCaseSensitive) {
+        return SelectorUtils.matchPath(pattern, str, isCaseSensitive);
+    }
+
+    /**
+     * Test whether or not a string matches against a pattern.
+     * The pattern may contain two special characters:<br>
+     * '*' means zero or more characters<br>
+     * '?' means one and only one character
+     *
+     * @param pattern The pattern to match against.
+     *                Must not be <code>null</code>.
+     * @param str     The string which must be matched against the pattern.
+     *                Must not be <code>null</code>.
+     *
+     * @return <code>true</code> if the string matches against the pattern,
+     *         or <code>false</code> otherwise.
+     */
+    public static boolean match(String pattern, String str) {
+        return SelectorUtils.match(pattern, str);
+    }
+
+    /**
+     * Test whether or not a string matches against a pattern.
+     * The pattern may contain two special characters:<br>
+     * '*' means zero or more characters<br>
+     * '?' means one and only one character
+     *
+     * @param pattern The pattern to match against.
+     *                Must not be <code>null</code>.
+     * @param str     The string which must be matched against the pattern.
+     *                Must not be <code>null</code>.
+     * @param isCaseSensitive Whether or not matching should be performed
+     *                        case sensitively.
+     *
+     *
+     * @return <code>true</code> if the string matches against the pattern,
+     *         or <code>false</code> otherwise.
+     */
+    protected static boolean match(String pattern, String str,
+                                   boolean isCaseSensitive) {
+        return SelectorUtils.match(pattern, str, isCaseSensitive);
+    }
+
+
+    /**
+     * Get the list of patterns that should be excluded by default.
+     *
+     * @return An array of <code>String</code> based on the current
+     *         contents of the <code>defaultExcludes</code>
+     *         <code>Vector</code>.
+     *
+     * @since Ant 1.6
+     */
+    public static String[] getDefaultExcludes() {
+        return (String[]) defaultExcludes.toArray(new String[defaultExcludes
+                                                             .size()]);
+    }
+
+    /**
+     * Add a pattern to the default excludes unless it is already a
+     * default exclude.
+     *
+     * @param s   A string to add as an exclude pattern.
+     * @return    <code>true</code> if the string was added;
+     *            <code>false</code> if it already existed.
+     *
+     * @since Ant 1.6
+     */
+    public static boolean addDefaultExclude(String s) {
+        if (defaultExcludes.indexOf(s) == -1) {
+            defaultExcludes.add(s);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Remove a string if it is a default exclude.
+     *
+     * @param s   The string to attempt to remove.
+     * @return    <code>true</code> if <code>s</code> was a default
+     *            exclude (and thus was removed);
+     *            <code>false</code> if <code>s</code> was not
+     *            in the default excludes list to begin with.
+     *
+     * @since Ant 1.6
+     */
+    public static boolean removeDefaultExclude(String s) {
+        return defaultExcludes.remove(s);
+    }
+
+    /**
+     * Go back to the hardwired default exclude patterns.
+     *
+     * @since Ant 1.6
+     */
+    public static void resetDefaultExcludes() {
+        defaultExcludes = new Vector();
+        for (int i = 0; i < DEFAULTEXCLUDES.length; i++) {
+            defaultExcludes.add(DEFAULTEXCLUDES[i]);
+        }
+    }
+
+    /**
+     * Set the base directory to be scanned. This is the directory which is
+     * scanned recursively. All '/' and '\' characters are replaced by
+     * <code>File.separatorChar</code>, so the separator used need not match
+     * <code>File.separatorChar</code>.
+     *
+     * @param basedir The base directory to scan.
+     *                Must not be <code>null</code>.
+     */
+    public void setBasedir(String basedir) {
+        setBasedir(new File(basedir.replace('/', File.separatorChar).replace(
+                '\\', File.separatorChar)));
+    }
+
+    /**
+     * Set the base directory to be scanned. This is the directory which is
+     * scanned recursively.
+     *
+     * @param basedir The base directory for scanning.
+     *                Should not be <code>null</code>.
+     */
+    public synchronized void setBasedir(File basedir) {
+        this.basedir = basedir;
+    }
+
+    /**
+     * Return the base directory to be scanned.
+     * This is the directory which is scanned recursively.
+     *
+     * @return the base directory to be scanned
+     */
+    public synchronized File getBasedir() {
+        return basedir;
+    }
+
+    /**
+     * Find out whether include exclude patterns are matched in a
+     * case sensitive way.
+     * @return whether or not the scanning is case sensitive.
+     * @since Ant 1.6
+     */
+    public synchronized boolean isCaseSensitive() {
+        return isCaseSensitive;
+    }
+
+    /**
+     * Set whether or not include and exclude patterns are matched
+     * in a case sensitive way.
+     *
+     * @param isCaseSensitive whether or not the file system should be
+     *                        regarded as a case sensitive one.
+     */
+    public synchronized void setCaseSensitive(boolean isCaseSensitive) {
+        this.isCaseSensitive = isCaseSensitive;
+    }
+
+    /**
+     * Get whether or not a DirectoryScanner follows symbolic links.
+     *
+     * @return flag indicating whether symbolic links should be followed.
+     *
+     * @since Ant 1.6
+     */
+    public synchronized boolean isFollowSymlinks() {
+        return followSymlinks;
+    }
+
+    /**
+     * Set whether or not symbolic links should be followed.
+     *
+     * @param followSymlinks whether or not symbolic links should be followed.
+     */
+    public synchronized void setFollowSymlinks(boolean followSymlinks) {
+        this.followSymlinks = followSymlinks;
+    }
+
+    /**
+     * Set the list of include patterns to use. All '/' and '\' characters
+     * are replaced by <code>File.separatorChar</code>, so the separator used
+     * need not match <code>File.separatorChar</code>.
+     * <p>
+     * When a pattern ends with a '/' or '\', "**" is appended.
+     *
+     * @param includes A list of include patterns.
+     *                 May be <code>null</code>, indicating that all files
+     *                 should be included. If a non-<code>null</code>
+     *                 list is given, all elements must be
+     *                 non-<code>null</code>.
+     */
+    public synchronized void setIncludes(String[] includes) {
+        if (includes == null) {
+            this.includes = null;
+        } else {
+            this.includes = new String[includes.length];
+            for (int i = 0; i < includes.length; i++) {
+                this.includes[i] = normalizePattern(includes[i]);
+            }
+        }
+    }
+
+    /**
+     * Set the list of exclude patterns to use. All '/' and '\' characters
+     * are replaced by <code>File.separatorChar</code>, so the separator used
+     * need not match <code>File.separatorChar</code>.
+     * <p>
+     * When a pattern ends with a '/' or '\', "**" is appended.
+     *
+     * @param excludes A list of exclude patterns.
+     *                 May be <code>null</code>, indicating that no files
+     *                 should be excluded. If a non-<code>null</code> list is
+     *                 given, all elements must be non-<code>null</code>.
+     */
+    public synchronized void setExcludes(String[] excludes) {
+        if (excludes == null) {
+            this.excludes = null;
+        } else {
+            this.excludes = new String[excludes.length];
+            for (int i = 0; i < excludes.length; i++) {
+                this.excludes[i] = normalizePattern(excludes[i]);
+            }
+        }
+    }
+
+    /**
+     * Add to the list of exclude patterns to use. All '/' and '\'
+     * characters are replaced by <code>File.separatorChar</code>, so
+     * the separator used need not match <code>File.separatorChar</code>.
+     * <p>
+     * When a pattern ends with a '/' or '\', "**" is appended.
+     *
+     * @param excludes A list of exclude patterns.
+     *                 May be <code>null</code>, in which case the
+     *                 exclude patterns don't get changed at all.
+     *
+     * @since Ant 1.6.3
+     */
+    public synchronized void addExcludes(String[] excludes) {
+        if (excludes != null && excludes.length > 0) {
+            if (this.excludes != null && this.excludes.length > 0) {
+                String[] tmp = new String[excludes.length
+                                          + this.excludes.length];
+                System.arraycopy(this.excludes, 0, tmp, 0,
+                                 this.excludes.length);
+                for (int i = 0; i < excludes.length; i++) {
+                    tmp[this.excludes.length + i] =
+                        normalizePattern(excludes[i]);
+                }
+                this.excludes = tmp;
+            } else {
+                setExcludes(excludes);
+            }
+        }
+    }
+
+    /**
+     * All '/' and '\' characters are replaced by
+     * <code>File.separatorChar</code>, so the separator used need not
+     * match <code>File.separatorChar</code>.
+     *
+     * <p> When a pattern ends with a '/' or '\', "**" is appended.
+     *
+     * @since Ant 1.6.3
+     */
+    private static String normalizePattern(String p) {
+        String pattern = p.replace('/', File.separatorChar)
+            .replace('\\', File.separatorChar);
+        if (pattern.endsWith(File.separator)) {
+            pattern += "**";
+        }
+        return pattern;
+    }
+
+    /**
+     * Set the selectors that will select the filelist.
+     *
+     * @param selectors specifies the selectors to be invoked on a scan.
+     */
+    public synchronized void setSelectors(FileSelector[] selectors) {
+        this.selectors = selectors;
+    }
+
+    /**
+     * Return whether or not the scanner has included all the files or
+     * directories it has come across so far.
+     *
+     * @return <code>true</code> if all files and directories which have
+     *         been found so far have been included.
+     */
+    public synchronized boolean isEverythingIncluded() {
+        return everythingIncluded;
+    }
+
+    /**
+     * Scan the base directory for files which match at least one include
+     * pattern and don't match any exclude patterns. If there are selectors
+     * then the files must pass muster there, as well.
+     *
+     * @exception IllegalStateException if the base directory was set
+     *            incorrectly (i.e. if it is <code>null</code>, doesn't exist,
+     *            or isn't a directory).
+     */
+    public void scan() throws IllegalStateException {
+        synchronized (scanLock) {
+            if (scanning) {
+                while (scanning) {
+                    try {
+                        scanLock.wait();
+                    } catch (InterruptedException e) {
+                        continue;
+                    }
+                }
+                if (illegal != null) {
+                    throw illegal;
+                }
+                return;
+            }
+            scanning = true;
+        }
+        try {
+            synchronized (this) {
+                illegal = null;
+                clearResults();
+
+                // set in/excludes to reasonable defaults if needed:
+                boolean nullIncludes = (includes == null);
+                includes = nullIncludes ? new String[] {"**"} : includes;
+                boolean nullExcludes = (excludes == null);
+                excludes = nullExcludes ? new String[0] : excludes;
+
+                if (basedir == null) {
+                    illegal = new IllegalStateException("No basedir set");
+                } else {
+                    if (!basedir.exists()) {
+                        illegal = new IllegalStateException("basedir " + basedir
+                                                        + " does not exist");
+                    }
+                    if (!basedir.isDirectory()) {
+                        illegal = new IllegalStateException("basedir " + basedir
+                                                        + " is not a directory");
+                    }
+                }
+                if (illegal != null) {
+                    throw illegal;
+                }
+                if (isIncluded("")) {
+                    if (!isExcluded("")) {
+                        if (isSelected("", basedir)) {
+                            dirsIncluded.addElement("");
+                        } else {
+                            dirsDeselected.addElement("");
+                        }
+                    } else {
+                        dirsExcluded.addElement("");
+                    }
+                } else {
+                    dirsNotIncluded.addElement("");
+                }
+                checkIncludePatterns();
+                clearCaches();
+                includes = nullIncludes ? null : includes;
+                excludes = nullExcludes ? null : excludes;
+            }
+        } finally {
+            synchronized (scanLock) {
+                scanning = false;
+                scanLock.notifyAll();
+            }
+        }
+    }
+
+    /**
+     * This routine is actually checking all the include patterns in
+     * order to avoid scanning everything under base dir.
+     * @since Ant 1.6
+     */
+    private void checkIncludePatterns() {
+        Hashtable newroots = new Hashtable();
+        // put in the newroots vector the include patterns without
+        // wildcard tokens
+        for (int icounter = 0; icounter < includes.length; icounter++) {
+            newroots.put(SelectorUtils.rtrimWildcardTokens(
+                includes[icounter]), includes[icounter]);
+        }
+        if (newroots.containsKey("")) {
+            // we are going to scan everything anyway
+            scandir(basedir, "", true);
+        } else {
+            // only scan directories that can include matched files or
+            // directories
+            Enumeration enum2 = newroots.keys();
+
+            File canonBase = null;
+            try {
+                canonBase = basedir.getCanonicalFile();
+            } catch (IOException ex) {
+                throw new BuildException(ex);
+            }
+            while (enum2.hasMoreElements()) {
+                String currentelement = (String) enum2.nextElement();
+                String originalpattern = (String) newroots.get(currentelement);
+                File myfile = new File(basedir, currentelement);
+
+                if (myfile.exists()) {
+                    // may be on a case insensitive file system.  We want
+                    // the results to show what's really on the disk, so
+                    // we need to double check.
+                    try {
+                        File canonFile = myfile.getCanonicalFile();
+                        String path = FILE_UTILS.removeLeadingPath(canonBase,
+                                                                  canonFile);
+                        if (!path.equals(currentelement) || ON_VMS) {
+                            myfile = findFile(basedir, currentelement, true);
+                            if (myfile != null) {
+                                currentelement =
+                                    FILE_UTILS.removeLeadingPath(basedir,
+                                                                 myfile);
+                            }
+                        }
+                    } catch (IOException ex) {
+                        throw new BuildException(ex);
+                    }
+                }
+                if ((myfile == null || !myfile.exists()) && !isCaseSensitive()) {
+                    File f = findFile(basedir, currentelement, false);
+                    if (f != null && f.exists()) {
+                        // adapt currentelement to the case we've
+                        // actually found
+                        currentelement = FILE_UTILS.removeLeadingPath(basedir,
+                                                                     f);
+                        myfile = f;
+                    }
+                }
+                if (myfile != null && myfile.exists()) {
+                    if (!followSymlinks
+                        && isSymlink(basedir, currentelement)) {
+                        continue;
+                    }
+                    if (myfile.isDirectory()) {
+                        if (isIncluded(currentelement)
+                            && currentelement.length() > 0) {
+                            accountForIncludedDir(currentelement, myfile, true);
+                        }  else {
+                            if (currentelement.length() > 0) {
+                                if (currentelement.charAt(currentelement
+                                                          .length() - 1)
+                                    != File.separatorChar) {
+                                    currentelement =
+                                        currentelement + File.separatorChar;
+                                }
+                            }
+                            scandir(myfile, currentelement, true);
+                        }
+                    } else {
+                        boolean included = isCaseSensitive()
+                            ? originalpattern.equals(currentelement)
+                            : originalpattern.equalsIgnoreCase(currentelement);
+                        if (included) {
+                            accountForIncludedFile(currentelement, myfile);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Clear the result caches for a scan.
+     */
+    protected synchronized void clearResults() {
+        filesIncluded    = new Vector();
+        filesNotIncluded = new Vector();
+        filesExcluded    = new Vector();
+        filesDeselected  = new Vector();
+        dirsIncluded     = new Vector();
+        dirsNotIncluded  = new Vector();
+        dirsExcluded     = new Vector();
+        dirsDeselected   = new Vector();
+        everythingIncluded = (basedir != null);
+        scannedDirs.clear();
+    }
+
+    /**
+     * Top level invocation for a slow scan. A slow scan builds up a full
+     * list of excluded/included files/directories, whereas a fast scan
+     * will only have full results for included files, as it ignores
+     * directories which can't possibly hold any included files/directories.
+     * <p>
+     * Returns immediately if a slow scan has already been completed.
+     */
+    protected void slowScan() {
+        synchronized (slowScanLock) {
+            if (haveSlowResults) {
+                return;
+            }
+            if (slowScanning) {
+                while (slowScanning) {
+                    try {
+                        slowScanLock.wait();
+                    } catch (InterruptedException e) {
+                    }
+                }
+                return;
+            }
+            slowScanning = true;
+        }
+        try {
+            synchronized (this) {
+
+                // set in/excludes to reasonable defaults if needed:
+                boolean nullIncludes = (includes == null);
+                includes = nullIncludes ? new String[] {"**"} : includes;
+                boolean nullExcludes = (excludes == null);
+                excludes = nullExcludes ? new String[0] : excludes;
+
+                String[] excl = new String[dirsExcluded.size()];
+                dirsExcluded.copyInto(excl);
+        
+                String[] notIncl = new String[dirsNotIncluded.size()];
+                dirsNotIncluded.copyInto(notIncl);
+        
+                for (int i = 0; i < excl.length; i++) {
+                    if (!couldHoldIncluded(excl[i])) {
+                        scandir(new File(basedir, excl[i]),
+                                excl[i] + File.separator, false);
+                    }
+                }
+                for (int i = 0; i < notIncl.length; i++) {
+                    if (!couldHoldIncluded(notIncl[i])) {
+                        scandir(new File(basedir, notIncl[i]),
+                                notIncl[i] + File.separator, false);
+                    }
+                }
+                clearCaches();
+                includes = nullIncludes ? null : includes;
+                excludes = nullExcludes ? null : excludes;
+            }
+        } finally {
+            synchronized (slowScanLock) {
+                haveSlowResults = true;
+                slowScanning = false;
+                slowScanLock.notifyAll();
+            }
+        }
+    }
+
+    /**
+     * Scan the given directory for files and directories. Found files and
+     * directories are placed in their respective collections, based on the
+     * matching of includes, excludes, and the selectors.  When a directory
+     * is found, it is scanned recursively.
+     *
+     * @param dir   The directory to scan. Must not be <code>null</code>.
+     * @param vpath The path relative to the base directory (needed to
+     *              prevent problems with an absolute path when using
+     *              dir). Must not be <code>null</code>.
+     * @param fast  Whether or not this call is part of a fast scan.
+     *
+     * @see #filesIncluded
+     * @see #filesNotIncluded
+     * @see #filesExcluded
+     * @see #dirsIncluded
+     * @see #dirsNotIncluded
+     * @see #dirsExcluded
+     * @see #slowScan
+     */
+    protected void scandir(File dir, String vpath, boolean fast) {
+        if (dir == null) {
+            throw new BuildException("dir must not be null.");
+        } else if (!dir.exists()) {
+            throw new BuildException(dir + " doesn't exists.");
+        } else if (!dir.isDirectory()) {
+            throw new BuildException(dir + " is not a directory.");
+        }
+        // avoid double scanning of directories, can only happen in fast mode
+        if (fast && hasBeenScanned(vpath)) {
+            return;
+        }
+        String[] newfiles = dir.list();
+
+        if (newfiles == null) {
+            /*
+             * two reasons are mentioned in the API docs for File.list
+             * (1) dir is not a directory. This is impossible as
+             *     we wouldn't get here in this case.
+             * (2) an IO error occurred (why doesn't it throw an exception
+             *     then???)
+             */
+            throw new BuildException("IO error scanning directory "
+                                     + dir.getAbsolutePath());
+        }
+        if (!followSymlinks) {
+            Vector noLinks = new Vector();
+            for (int i = 0; i < newfiles.length; i++) {
+                try {
+                    if (FILE_UTILS.isSymbolicLink(dir, newfiles[i])) {
+                        String name = vpath + newfiles[i];
+                        File file = new File(dir, newfiles[i]);
+                        (file.isDirectory()
+                            ? dirsExcluded : filesExcluded).addElement(name);
+                    } else {
+                        noLinks.addElement(newfiles[i]);
+                    }
+                } catch (IOException ioe) {
+                    String msg = "IOException caught while checking "
+                        + "for links, couldn't get canonical path!";
+                    // will be caught and redirected to Ant's logging system
+                    System.err.println(msg);
+                    noLinks.addElement(newfiles[i]);
+                }
+            }
+            newfiles = new String[noLinks.size()];
+            noLinks.copyInto(newfiles);
+        }
+        for (int i = 0; i < newfiles.length; i++) {
+            String name = vpath + newfiles[i];
+            File file = new File(dir, newfiles[i]);
+            if (file.isDirectory()) {
+                if (isIncluded(name)) {
+                    accountForIncludedDir(name, file, fast);
+                } else {
+                    everythingIncluded = false;
+                    dirsNotIncluded.addElement(name);
+                    if (fast && couldHoldIncluded(name)) {
+                        scandir(file, name + File.separator, fast);
+                    }
+                }
+                if (!fast) {
+                    scandir(file, name + File.separator, fast);
+                }
+            } else if (file.isFile()) {
+                if (isIncluded(name)) {
+                    accountForIncludedFile(name, file);
+                } else {
+                    everythingIncluded = false;
+                    filesNotIncluded.addElement(name);
+                }
+            }
+        }
+    }
+
+    /**
+     * Process included file.
+     * @param name  path of the file relative to the directory of the FileSet.
+     * @param file  included File.
+     */
+    private void accountForIncludedFile(String name, File file) {
+        if (filesIncluded.contains(name)
+            || filesExcluded.contains(name)
+            || filesDeselected.contains(name)) {
+            return;
+        }
+        boolean included = false;
+        if (isExcluded(name)) {
+            filesExcluded.addElement(name);
+        } else if (isSelected(name, file)) {
+            included = true;
+            filesIncluded.addElement(name);
+        } else {
+            filesDeselected.addElement(name);
+        }
+        everythingIncluded &= included;
+    }
+
+    /**
+     * Process included directory.
+     * @param name path of the directory relative to the directory of
+     *             the FileSet.
+     * @param file directory as File.
+     * @param fast whether to perform fast scans.
+     */
+    private void accountForIncludedDir(String name, File file, boolean fast) {
+        if (dirsIncluded.contains(name)
+            || dirsExcluded.contains(name)
+            || dirsDeselected.contains(name)) {
+            return;
+        }
+        boolean included = false;
+        if (isExcluded(name)) {
+            dirsExcluded.addElement(name);
+        } else if (isSelected(name, file)) {
+            included = true;
+            dirsIncluded.addElement(name);
+        } else {
+            dirsDeselected.addElement(name);
+        }
+        everythingIncluded &= included;
+        if (fast && couldHoldIncluded(name) && !contentsExcluded(name)) {
+            scandir(file, name + File.separator, fast);
+        }
+    }
+
+    /**
+     * Test whether or not a name matches against at least one include
+     * pattern.
+     *
+     * @param name The name to match. Must not be <code>null</code>.
+     * @return <code>true</code> when the name matches against at least one
+     *         include pattern, or <code>false</code> otherwise.
+     */
+    protected boolean isIncluded(String name) {
+        ensureNonPatternSetsReady();
+
+        if (isCaseSensitive()
+            ? includeNonPatterns.contains(name)
+            : includeNonPatterns.contains(name.toUpperCase())) {
+            return true;
+        }
+        for (int i = 0; i < includePatterns.length; i++) {
+            if (matchPath(includePatterns[i], name, isCaseSensitive())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Test whether or not a name matches the start of at least one include
+     * pattern.
+     *
+     * @param name The name to match. Must not be <code>null</code>.
+     * @return <code>true</code> when the name matches against the start of at
+     *         least one include pattern, or <code>false</code> otherwise.
+     */
+    protected boolean couldHoldIncluded(String name) {
+        for (int i = 0; i < includes.length; i++) {
+            if (matchPatternStart(includes[i], name, isCaseSensitive())
+                && isMorePowerfulThanExcludes(name, includes[i])
+                && isDeeper(includes[i], name)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Verify that a pattern specifies files deeper
+     * than the level of the specified file.
+     * @param pattern the pattern to check.
+     * @param name the name to check.
+     * @return whether the pattern is deeper than the name.
+     * @since Ant 1.6.3
+     */
+    private boolean isDeeper(String pattern, String name) {
+        Vector p = SelectorUtils.tokenizePath(pattern);
+        Vector n = SelectorUtils.tokenizePath(name);
+        return p.contains("**") || p.size() > n.size();
+    }
+
+    /**
+     *  Find out whether one particular include pattern is more powerful
+     *  than all the excludes.
+     *  Note:  the power comparison is based on the length of the include pattern
+     *  and of the exclude patterns without the wildcards.
+     *  Ideally the comparison should be done based on the depth
+     *  of the match; that is to say how many file separators have been matched
+     *  before the first ** or the end of the pattern.
+     *
+     *  IMPORTANT : this function should return false "with care".
+     *
+     *  @param name the relative path to test.
+     *  @param includepattern one include pattern.
+     *  @return true if there is no exclude pattern more powerful than this include pattern.
+     *  @since Ant 1.6
+     */
+    private boolean isMorePowerfulThanExcludes(String name, String includepattern) {
+        String soughtexclude = name + File.separator + "**";
+        for (int counter = 0; counter < excludes.length; counter++) {
+            if (excludes[counter].equals(soughtexclude))  {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Test whether all contents of the specified directory must be excluded.
+     * @param name the directory name to check.
+     * @return whether all the specified directory's contents are excluded.
+     */
+    private boolean contentsExcluded(String name) {
+        name = (name.endsWith(File.separator)) ? name : name + File.separator;
+        for (int i = 0; i < excludes.length; i++) {
+            String e = excludes[i];
+            if (e.endsWith("**") && SelectorUtils.matchPath(
+                e.substring(0, e.length() - 2), name, isCaseSensitive())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Test whether or not a name matches against at least one exclude
+     * pattern.
+     *
+     * @param name The name to match. Must not be <code>null</code>.
+     * @return <code>true</code> when the name matches against at least one
+     *         exclude pattern, or <code>false</code> otherwise.
+     */
+    protected boolean isExcluded(String name) {
+        ensureNonPatternSetsReady();
+
+        if (isCaseSensitive()
+            ? excludeNonPatterns.contains(name)
+            : excludeNonPatterns.contains(name.toUpperCase())) {
+            return true;
+        }
+        for (int i = 0; i < excludePatterns.length; i++) {
+            if (matchPath(excludePatterns[i], name, isCaseSensitive())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Test whether a file should be selected.
+     *
+     * @param name the filename to check for selecting.
+     * @param file the java.io.File object for this filename.
+     * @return <code>false</code> when the selectors says that the file
+     *         should not be selected, <code>true</code> otherwise.
+     */
+    protected boolean isSelected(String name, File file) {
+        if (selectors != null) {
+            for (int i = 0; i < selectors.length; i++) {
+                if (!selectors[i].isSelected(basedir, name, file)) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Return the names of the files which matched at least one of the
+     * include patterns and none of the exclude patterns.
+     * The names are relative to the base directory.
+     *
+     * @return the names of the files which matched at least one of the
+     *         include patterns and none of the exclude patterns.
+     */
+    public synchronized String[] getIncludedFiles() {
+        if (filesIncluded == null) {
+            throw new IllegalStateException();
+        }
+        String[] files = new String[filesIncluded.size()];
+        filesIncluded.copyInto(files);
+        Arrays.sort(files);
+        return files;
+    }
+
+    /**
+     * Return the count of included files.
+     * @return <code>int</code>.
+     * @since Ant 1.6.3
+     */
+    public synchronized int getIncludedFilesCount() {
+        if (filesIncluded == null) {
+            throw new IllegalStateException();
+        }
+        return filesIncluded.size();
+    }
+
+    /**
+     * Return the names of the files which matched none of the include
+     * patterns. The names are relative to the base directory. This involves
+     * performing a slow scan if one has not already been completed.
+     *
+     * @return the names of the files which matched none of the include
+     *         patterns.
+     *
+     * @see #slowScan
+     */
+    public synchronized String[] getNotIncludedFiles() {
+        slowScan();
+        String[] files = new String[filesNotIncluded.size()];
+        filesNotIncluded.copyInto(files);
+        return files;
+    }
+
+    /**
+     * Return the names of the files which matched at least one of the
+     * include patterns and at least one of the exclude patterns.
+     * The names are relative to the base directory. This involves
+     * performing a slow scan if one has not already been completed.
+     *
+     * @return the names of the files which matched at least one of the
+     *         include patterns and at least one of the exclude patterns.
+     *
+     * @see #slowScan
+     */
+    public synchronized String[] getExcludedFiles() {
+        slowScan();
+        String[] files = new String[filesExcluded.size()];
+        filesExcluded.copyInto(files);
+        return files;
+    }
+
+    /**
+     * <p>Return the names of the files which were selected out and
+     * therefore not ultimately included.</p>
+     *
+     * <p>The names are relative to the base directory. This involves
+     * performing a slow scan if one has not already been completed.</p>
+     *
+     * @return the names of the files which were deselected.
+     *
+     * @see #slowScan
+     */
+    public synchronized String[] getDeselectedFiles() {
+        slowScan();
+        String[] files = new String[filesDeselected.size()];
+        filesDeselected.copyInto(files);
+        return files;
+    }
+
+    /**
+     * Return the names of the directories which matched at least one of the
+     * include patterns and none of the exclude patterns.
+     * The names are relative to the base directory.
+     *
+     * @return the names of the directories which matched at least one of the
+     * include patterns and none of the exclude patterns.
+     */
+    public synchronized String[] getIncludedDirectories() {
+        if (dirsIncluded == null) {
+            throw new IllegalStateException();
+        }
+        String[] directories = new String[dirsIncluded.size()];
+        dirsIncluded.copyInto(directories);
+        Arrays.sort(directories);
+        return directories;
+    }
+
+    /**
+     * Return the count of included directories.
+     * @return <code>int</code>.
+     * @since Ant 1.6.3
+     */
+    public synchronized int getIncludedDirsCount() {
+        if (dirsIncluded == null) {
+            throw new IllegalStateException();
+        }
+        return dirsIncluded.size();
+    }
+
+    /**
+     * Return the names of the directories which matched none of the include
+     * patterns. The names are relative to the base directory. This involves
+     * performing a slow scan if one has not already been completed.
+     *
+     * @return the names of the directories which matched none of the include
+     * patterns.
+     *
+     * @see #slowScan
+     */
+    public synchronized String[] getNotIncludedDirectories() {
+        slowScan();
+        String[] directories = new String[dirsNotIncluded.size()];
+        dirsNotIncluded.copyInto(directories);
+        return directories;
+    }
+
+    /**
+     * Return the names of the directories which matched at least one of the
+     * include patterns and at least one of the exclude patterns.
+     * The names are relative to the base directory. This involves
+     * performing a slow scan if one has not already been completed.
+     *
+     * @return the names of the directories which matched at least one of the
+     * include patterns and at least one of the exclude patterns.
+     *
+     * @see #slowScan
+     */
+    public synchronized String[] getExcludedDirectories() {
+        slowScan();
+        String[] directories = new String[dirsExcluded.size()];
+        dirsExcluded.copyInto(directories);
+        return directories;
+    }
+
+    /**
+     * <p>Return the names of the directories which were selected out and
+     * therefore not ultimately included.</p>
+     *
+     * <p>The names are relative to the base directory. This involves
+     * performing a slow scan if one has not already been completed.</p>
+     *
+     * @return the names of the directories which were deselected.
+     *
+     * @see #slowScan
+     */
+    public synchronized String[] getDeselectedDirectories() {
+        slowScan();
+        String[] directories = new String[dirsDeselected.size()];
+        dirsDeselected.copyInto(directories);
+        return directories;
+    }
+
+    /**
+     * Add default exclusions to the current exclusions set.
+     */
+    public synchronized void addDefaultExcludes() {
+        int excludesLength = excludes == null ? 0 : excludes.length;
+        String[] newExcludes;
+        newExcludes = new String[excludesLength + defaultExcludes.size()];
+        if (excludesLength > 0) {
+            System.arraycopy(excludes, 0, newExcludes, 0, excludesLength);
+        }
+        String[] defaultExcludesTemp = getDefaultExcludes();
+        for (int i = 0; i < defaultExcludesTemp.length; i++) {
+            newExcludes[i + excludesLength] =
+                defaultExcludesTemp[i].replace('/', File.separatorChar)
+                .replace('\\', File.separatorChar);
+        }
+        excludes = newExcludes;
+    }
+
+    /**
+     * Get the named resource.
+     * @param name path name of the file relative to the dir attribute.
+     *
+     * @return the resource with the given name.
+     * @since Ant 1.5.2
+     */
+    public synchronized Resource getResource(String name) {
+        File f = FILE_UTILS.resolveFile(basedir, name);
+        return new Resource(name, f.exists(), f.lastModified(),
+                            f.isDirectory(), f.length());
+    }
+
+    /**
+     * Return a cached result of list performed on file, if
+     * available.  Invokes the method and caches the result otherwise.
+     *
+     * @param file File (dir) to list.
+     * @since Ant 1.6
+     */
+    private String[] list(File file) {
+        String[] files = (String[]) fileListMap.get(file);
+        if (files == null) {
+            files = file.list();
+            if (files != null) {
+                fileListMap.put(file, files);
+            }
+        }
+        return files;
+    }
+
+    /**
+     * From <code>base</code> traverse the filesystem in order to find
+     * a file that matches the given name.
+     *
+     * @param base base File (dir).
+     * @param path file path.
+     * @param cs whether to scan case-sensitively.
+     * @return File object that points to the file in question or null.
+     *
+     * @since Ant 1.6.3
+     */
+    private File findFile(File base, String path, boolean cs) {
+        return findFile(base, SelectorUtils.tokenizePath(path), cs);
+    }
+
+    /**
+     * From <code>base</code> traverse the filesystem in order to find
+     * a file that matches the given stack of names.
+     *
+     * @param base base File (dir).
+     * @param pathElements Vector of path elements (dirs...file).
+     * @param cs whether to scan case-sensitively.
+     * @return File object that points to the file in question or null.
+     *
+     * @since Ant 1.6.3
+     */
+    private File findFile(File base, Vector pathElements, boolean cs) {
+        if (pathElements.size() == 0) {
+            return base;
+        }
+        if (!base.isDirectory()) {
+            return null;
+        }
+        String[] files = list(base);
+        if (files == null) {
+            throw new BuildException("IO error scanning directory "
+                                     + base.getAbsolutePath());
+        }
+        String current = (String) pathElements.remove(0);
+
+        boolean[] matchCase = cs ? CS_SCAN_ONLY : CS_THEN_NON_CS;
+        for (int i = 0; i < matchCase.length; i++) {
+            for (int j = 0; j < files.length; j++) {
+                if (matchCase[i] ? files[j].equals(current)
+                                 : files[j].equalsIgnoreCase(current)) {
+                    return findFile(new File(base, files[j]), pathElements, cs);
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Do we have to traverse a symlink when trying to reach path from
+     * basedir?
+     * @param base base File (dir).
+     * @param path file path.
+     * @since Ant 1.6
+     */
+    private boolean isSymlink(File base, String path) {
+        return isSymlink(base, SelectorUtils.tokenizePath(path));
+    }
+
+    /**
+     * Do we have to traverse a symlink when trying to reach path from
+     * basedir?
+     * @param base base File (dir).
+     * @param pathElements Vector of path elements (dirs...file).
+     * @since Ant 1.6
+     */
+    private boolean isSymlink(File base, Vector pathElements) {
+        if (pathElements.size() > 0) {
+            String current = (String) pathElements.remove(0);
+            try {
+                return FILE_UTILS.isSymbolicLink(base, current)
+                    || isSymlink(new File(base, current), pathElements);
+            } catch (IOException ioe) {
+                String msg = "IOException caught while checking "
+                    + "for links, couldn't get canonical path!";
+                // will be caught and redirected to Ant's logging system
+                System.err.println(msg);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Has the directory with the given path relative to the base
+     * directory already been scanned?
+     *
+     * <p>Registers the given directory as scanned as a side effect.</p>
+     *
+     * @since Ant 1.6
+     */
+    private boolean hasBeenScanned(String vpath) {
+        return !scannedDirs.add(vpath);
+    }
+
+    /**
+     * This method is of interest for testing purposes.  The returned
+     * Set is live and should not be modified.
+     * @return the Set of relative directory names that have been scanned.
+     */
+    /* package-private */ Set getScannedDirs() {
+        return scannedDirs;
+    }
+
+    /**
+     * Clear internal caches.
+     *
+     * @since Ant 1.6
+     */
+    private synchronized void clearCaches() {
+        fileListMap.clear();
+        includeNonPatterns.clear();
+        excludeNonPatterns.clear();
+        includePatterns = null;
+        excludePatterns = null;
+        areNonPatternSetsReady = false;
+    }
+
+    /**
+     * Ensure that the in|exclude &quot;patterns&quot;
+     * have been properly divided up.
+     *
+     * @since Ant 1.6.3
+     */
+    private synchronized void ensureNonPatternSetsReady() {
+        if (!areNonPatternSetsReady) {
+            includePatterns = fillNonPatternSet(includeNonPatterns, includes);
+            excludePatterns = fillNonPatternSet(excludeNonPatterns, excludes);
+            areNonPatternSetsReady = true;
+        }
+    }
+
+    /**
+     * Add all patterns that are not real patterns (do not contain
+     * wildcards) to the set and returns the real patterns.
+     *
+     * @param set Set to populate.
+     * @param patterns String[] of patterns.
+     * @since Ant 1.6.3
+     */
+    private String[] fillNonPatternSet(Set set, String[] patterns) {
+        ArrayList al = new ArrayList(patterns.length);
+        for (int i = 0; i < patterns.length; i++) {
+            if (!SelectorUtils.hasWildcards(patterns[i])) {
+                set.add(isCaseSensitive() ? patterns[i]
+                    : patterns[i].toUpperCase());
+            } else {
+                al.add(patterns[i]);
+            }
+        }
+        return set.size() == 0 ? patterns
+            : (String[]) al.toArray(new String[al.size()]);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicAttribute.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicAttribute.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicAttribute.java	(revision 16139)
@@ -0,0 +1,37 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+/**
+ * Enables a task to control unknown attributes
+ *
+ * @since Ant 1.5
+ */
+public interface DynamicAttribute {
+
+    /**
+     * Set a named attribute to the given value
+     *
+     * @param name the name of the attribute
+     * @param value the new value of the attribute
+     * @throws BuildException when any error occurs
+     */
+    void setDynamicAttribute(String name, String value)
+            throws BuildException;
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicAttributeNS.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicAttributeNS.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicAttributeNS.java	(revision 16139)
@@ -0,0 +1,40 @@
+/*
+ * Copyright  2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant;
+
+/**
+ * Enables a task to control unknown attributes.
+ *
+ * @since Ant 1.7
+ */
+public interface DynamicAttributeNS {
+
+    /**
+     * Set a named attribute to the given value
+     *
+     * @param uri The namespace uri for this attribute, "" is
+     *            used if there is no namespace uri.
+     * @param localName The localname of this attribute.
+     * @param qName The qualified name for this attribute
+     * @param value The value of this attribute.
+     * @throws BuildException when any error occurs
+     */
+    void setDynamicAttribute(
+        String uri, String localName, String qName, String value)
+            throws BuildException;
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicConfigurator.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicConfigurator.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicConfigurator.java	(revision 16139)
@@ -0,0 +1,28 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+/**
+ * Enables a task to control unknown attributes and elements.
+ *
+ * @since Ant 1.5
+ */
+public interface DynamicConfigurator
+    extends DynamicAttribute, DynamicElement {
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicConfiguratorNS.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicConfiguratorNS.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicConfiguratorNS.java	(revision 16139)
@@ -0,0 +1,26 @@
+/*
+ * Copyright  2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant;
+
+/**
+ * Enables a task to control unknown attributes and elements.
+ *
+ * @since Ant 1.7
+ */
+public interface DynamicConfiguratorNS
+    extends DynamicAttributeNS, DynamicElementNS {
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicElement.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicElement.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicElement.java	(revision 16139)
@@ -0,0 +1,35 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+/**
+ * Enables a task to control unknown elements.
+ *
+ * @since Ant 1.5
+ */
+public interface DynamicElement {
+
+    /**
+     * Create an element with the given name
+     *
+     * @param name the element nbame
+     * @throws BuildException when any error occurs
+     * @return the element created
+     */
+    Object createDynamicElement(String name) throws BuildException;
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicElementNS.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicElementNS.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/DynamicElementNS.java	(revision 16139)
@@ -0,0 +1,36 @@
+/*
+ * Copyright  2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant;
+
+/**
+ * Enables a task to control unknown elements.
+ *
+ * @since Ant 1.7
+ */
+public interface DynamicElementNS {
+    /**
+     * Create an element with the given name
+     *
+     * @param uri The namespace uri for this attribute.
+     * @param localName The localname of this attribute.
+     * @param qName The qualified name for this element.
+     * @throws BuildException when any error occurs
+     * @return the element created for this element.
+     */
+    Object createDynamicElement(
+        String uri, String localName, String qName) throws BuildException;
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/Executor.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/Executor.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/Executor.java	(revision 16139)
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+/**
+ * Target executor abstraction.
+ * @since Ant 1.6.3
+ */
+public interface Executor {
+
+    /**
+     * Execute the specified Targets for the specified Project.
+     * @param project       the Ant Project.
+     * @param targetNames   String[] of Target names.
+     * @throws BuildException on error
+     */
+    void executeTargets(Project project, String[] targetNames)
+        throws BuildException;
+
+    /**
+     * Get the appropriate subproject Executor instance.
+     * @return an Executor instance.
+     */
+    Executor getSubProjectExecutor();
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/ExitException.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/ExitException.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/ExitException.java	(revision 16139)
@@ -0,0 +1,58 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant;
+
+/**
+ * Used to report exit status of classes which call System.exit().
+ *
+ * @see org.apache.tools.ant.util.optional.NoExitSecurityManager
+ * @see org.apache.tools.ant.types.Permissions
+ *
+ */
+public class ExitException extends SecurityException {
+
+    /** Status code */
+    private int status;
+
+    /**
+     * Constructs an exit exception.
+     * @param status the status code returned via System.exit()
+     */
+    public ExitException(int status) {
+        super("ExitException: status " + status);
+        this.status = status;
+    }
+
+    /**
+     * Constructs an exit exception.
+     * @param msg the message to be displayed.
+     * @param status the status code returned via System.exit()
+     */
+    public ExitException(String msg, int status) {
+        super(msg);
+        this.status = status;
+    }
+
+    /**
+     * The status code returned by System.exit()
+     *
+     * @return the status code returned by System.exit()
+     */
+    public int getStatus() {
+        return status;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/ExitStatusException.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/ExitStatusException.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/ExitStatusException.java	(revision 16139)
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant;
+
+/**
+ * BuildException + exit status.
+ *
+ * @since Ant 1.7
+ */
+public class ExitStatusException extends BuildException {
+
+    /** Status code */
+    private int status;
+
+    /**
+     * Constructs an <CODE>ExitStatusException</CODE>.
+     * @param status the associated status code
+     */
+    public ExitStatusException(int status) {
+        super();
+        this.status = status;
+    }
+
+    /**
+     * Constructs an <CODE>ExitStatusException</CODE>.
+     * @param msg the associated message
+     * @param status the associated status code
+     */
+    public ExitStatusException(String msg, int status) {
+        super(msg);
+        this.status = status;
+    }
+
+    /**
+     * Get the status code.
+     * @return <CODE>int</CODE>
+     */
+    public int getStatus() {
+        return status;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/FileScanner.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/FileScanner.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/FileScanner.java	(revision 16139)
@@ -0,0 +1,157 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant;
+
+import java.io.File;
+
+/**
+ * An interface used to describe the actions required of any type of
+ * directory scanner.
+ *
+ */
+public interface FileScanner {
+    /**
+     * Adds default exclusions to the current exclusions set.
+     */
+    void addDefaultExcludes();
+
+    /**
+     * Returns the base directory to be scanned.
+     * This is the directory which is scanned recursively.
+     *
+     * @return the base directory to be scanned
+     */
+    File getBasedir();
+
+    /**
+     * Returns the names of the directories which matched at least one of the
+     * include patterns and at least one of the exclude patterns.
+     * The names are relative to the base directory.
+     *
+     * @return the names of the directories which matched at least one of the
+     * include patterns and at least one of the exclude patterns.
+     */
+    String[] getExcludedDirectories();
+
+    /**
+     * Returns the names of the files which matched at least one of the
+     * include patterns and at least one of the exclude patterns.
+     * The names are relative to the base directory.
+     *
+     * @return the names of the files which matched at least one of the
+     *         include patterns and at least one of the exclude patterns.
+     *
+     */
+    String[] getExcludedFiles();
+
+    /**
+     * Returns the names of the directories which matched at least one of the
+     * include patterns and none of the exclude patterns.
+     * The names are relative to the base directory.
+     *
+     * @return the names of the directories which matched at least one of the
+     * include patterns and none of the exclude patterns.
+     */
+    String[] getIncludedDirectories();
+
+    /**
+     * Returns the names of the files which matched at least one of the
+     * include patterns and none of the exclude patterns.
+     * The names are relative to the base directory.
+     *
+     * @return the names of the files which matched at least one of the
+     *         include patterns and none of the exclude patterns.
+     */
+    String[] getIncludedFiles();
+
+    /**
+     * Returns the names of the directories which matched none of the include
+     * patterns. The names are relative to the base directory.
+     *
+     * @return the names of the directories which matched none of the include
+     * patterns.
+     */
+    String[] getNotIncludedDirectories();
+
+    /**
+     * Returns the names of the files which matched none of the include
+     * patterns. The names are relative to the base directory.
+     *
+     * @return the names of the files which matched none of the include
+     *         patterns.
+     */
+    String[] getNotIncludedFiles();
+
+    /**
+     * Scans the base directory for files which match at least one include
+     * pattern and don't match any exclude patterns.
+     *
+     * @exception IllegalStateException if the base directory was set
+     *            incorrectly (i.e. if it is <code>null</code>, doesn't exist,
+     *            or isn't a directory).
+     */
+    void scan() throws IllegalStateException;
+
+    /**
+     * Sets the base directory to be scanned. This is the directory which is
+     * scanned recursively. All '/' and '\' characters should be replaced by
+     * <code>File.separatorChar</code>, so the separator used need not match
+     * <code>File.separatorChar</code>.
+     *
+     * @param basedir The base directory to scan.
+     *                Must not be <code>null</code>.
+     */
+    void setBasedir(String basedir);
+
+    /**
+     * Sets the base directory to be scanned. This is the directory which is
+     * scanned recursively.
+     *
+     * @param basedir The base directory for scanning.
+     *                Should not be <code>null</code>.
+     */
+    void setBasedir(File basedir);
+
+    /**
+     * Sets the list of exclude patterns to use.
+     *
+     * @param excludes A list of exclude patterns.
+     *                 May be <code>null</code>, indicating that no files
+     *                 should be excluded. If a non-<code>null</code> list is
+     *                 given, all elements must be non-<code>null</code>.
+     */
+    void setExcludes(String[] excludes);
+
+    /**
+     * Sets the list of include patterns to use.
+     *
+     * @param includes A list of include patterns.
+     *                 May be <code>null</code>, indicating that all files
+     *                 should be included. If a non-<code>null</code>
+     *                 list is given, all elements must be
+     * non-<code>null</code>.
+     */
+    void setIncludes(String[] includes);
+
+    /**
+     * Sets whether or not the file system should be regarded as case sensitive.
+     *
+     * @param isCaseSensitive whether or not the file system should be
+     *                        regarded as a case sensitive one
+     */
+    void setCaseSensitive(boolean isCaseSensitive);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/IntrospectionHelper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/IntrospectionHelper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/IntrospectionHelper.java	(revision 16139)
@@ -0,0 +1,1493 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.taskdefs.PreSetDef;
+
+/**
+ * Helper class that collects the methods a task or nested element
+ * holds to set attributes, create nested elements or hold PCDATA
+ * elements.
+ * The class is final as it has a private constructor.
+ */
+public final class IntrospectionHelper implements BuildListener {
+
+    /**
+     * EMPTY_MAP was added in java 1.3 (EMTPY_SET and EMPTY_LIST
+     * is in java 1.2!)
+     */
+    private static final Map EMPTY_MAP
+        = Collections.unmodifiableMap(new HashMap(0));
+
+    /**
+     * Map from attribute names to attribute types
+     * (String to Class).
+     */
+    private Hashtable attributeTypes = new Hashtable();
+
+    /**
+     * Map from attribute names to attribute setter methods
+     * (String to AttributeSetter).
+     */
+    private Hashtable attributeSetters = new Hashtable();
+
+    /**
+     * Map from attribute names to nested types
+     * (String to Class).
+     */
+    private Hashtable nestedTypes = new Hashtable();
+
+    /**
+     * Map from attribute names to methods to create nested types
+     * (String to NestedCreator).
+     */
+    private Hashtable nestedCreators = new Hashtable();
+
+    /**
+     * Vector of methods matching add[Configured](Class) pattern.
+     */
+    private List addTypeMethods = new ArrayList();
+
+    /**
+     * The method to invoke to add PCDATA.
+     */
+    private Method addText = null;
+
+    /**
+     * The class introspected by this instance.
+     */
+    private Class bean;
+
+    /**
+     * Helper instances we've already created (Class to IntrospectionHelper).
+     */
+    private static Hashtable helpers = new Hashtable();
+
+    /**
+     * Map from primitive types to wrapper classes for use in
+     * createAttributeSetter (Class to Class). Note that char
+     * and boolean are in here even though they get special treatment
+     * - this way we only need to test for the wrapper class.
+     */
+    private static final Hashtable PRIMITIVE_TYPE_MAP = new Hashtable(8);
+
+    // Set up PRIMITIVE_TYPE_MAP
+    static {
+        Class[] primitives = {Boolean.TYPE, Byte.TYPE, Character.TYPE,
+                              Short.TYPE, Integer.TYPE, Long.TYPE,
+                              Float.TYPE, Double.TYPE};
+        Class[] wrappers = {Boolean.class, Byte.class, Character.class,
+                            Short.class, Integer.class, Long.class,
+                            Float.class, Double.class};
+        for (int i = 0; i < primitives.length; i++) {
+            PRIMITIVE_TYPE_MAP.put (primitives[i], wrappers[i]);
+        }
+    }
+
+    // XXX: (Jon Skeet) The documentation below doesn't draw a clear
+    // distinction between addConfigured and add. It's obvious what the
+    // code *here* does (addConfigured sets both a creator method which
+    // calls a no-arg constructor and a storer method which calls the
+    // method we're looking at, while add just sets a creator method
+    // which calls the method we're looking at) but it's not at all
+    // obvious what the difference in actual *effect* will be later
+    // on. I can't see any mention of addConfiguredXXX in "Developing
+    // with Ant" (at least in the version on the web site). Someone
+    // who understands should update this documentation
+    // (and preferably the manual too) at some stage.
+    /**
+     * Sole constructor, which is private to ensure that all
+     * IntrospectionHelpers are created via {@link #getHelper(Class) getHelper}.
+     * Introspects the given class for bean-like methods.
+     * Each method is examined in turn, and the following rules are applied:
+     * <p>
+     * <ul>
+     * <li>If the method is <code>Task.setLocation(Location)</code>,
+     * <code>Task.setTaskType(String)</code>
+     * or <code>TaskContainer.addTask(Task)</code>, it is ignored. These
+     * methods are handled differently elsewhere.
+     * <li><code>void addText(String)</code> is recognised as the method for
+     * adding PCDATA to a bean.
+     * <li><code>void setFoo(Bar)</code> is recognised as a method for
+     * setting the value of attribute <code>foo</code>, so long as
+     * <code>Bar</code> is non-void and is not an array type. Non-String
+     * parameter types always overload String parameter types, but that is
+     * the only guarantee made in terms of priority.
+     * <li><code>Foo createBar()</code> is recognised as a method for
+     * creating a nested element called <code>bar</code> of type
+     * <code>Foo</code>, so long as <code>Foo</code> is not a primitive or
+     * array type.
+     * <li><code>void addConfiguredFoo(Bar)</code> is recognised as a
+     * method for storing a pre-configured element called
+     * <code>foo</code> and of type <code>Bar</code>, so long as
+     * <code>Bar</code> is not an array, primitive or String type.
+     * <code>Bar</code> must have an accessible constructor taking no
+     * arguments.
+     * <li><code>void addFoo(Bar)</code> is recognised as a
+     * method for storing an element called <code>foobar</code>
+     * and of type <code>Baz</code>, so long as
+     * <code>Baz</code> is not an array, primitive or String type.
+     * <code>Baz</code> must have an accessible constructor taking no
+     * arguments.
+     * </ul>
+     * Note that only one method is retained to create/set/addConfigured/add
+     * any element or attribute.
+     *
+     * @param bean The bean type to introspect.
+     *             Must not be <code>null</code>.
+     *
+     * @see #getHelper(Class)
+     */
+    private IntrospectionHelper(final Class bean) {
+        this.bean = bean;
+
+        Method[] methods = bean.getMethods();
+        for (int i = 0; i < methods.length; i++) {
+            final Method m = methods[i];
+            final String name = m.getName();
+            Class returnType = m.getReturnType();
+            Class[] args = m.getParameterTypes();
+
+            // check of add[Configured](Class) pattern
+            if (args.length == 1 && java.lang.Void.TYPE.equals(returnType)
+                && ("add".equals(name) || "addConfigured".equals(name))) {
+                insertAddTypeMethod(m);
+                continue;
+            }
+            // not really user settable properties on tasks
+            if (org.apache.tools.ant.Task.class.isAssignableFrom(bean)
+                 && args.length == 1 && isHiddenSetMethod(name, args[0])) {
+                continue;
+            }
+            // hide addTask for TaskContainers
+            if (isContainer() && args.length == 1 && "addTask".equals(name)
+                && org.apache.tools.ant.Task.class.equals(args[0])) {
+                continue;
+            }
+            if ("addText".equals(name) && java.lang.Void.TYPE.equals(returnType)
+                && args.length == 1 && java.lang.String.class.equals(args[0])) {
+
+                addText = methods[i];
+            } else if (name.startsWith("set")
+                       && java.lang.Void.TYPE.equals(returnType)
+                       && args.length == 1 && !args[0].isArray()) {
+
+                String propName = getPropertyName(name, "set");
+                if (attributeSetters.get(propName) != null) {
+                    if (java.lang.String.class.equals(args[0])) {
+                        /*
+                            Ignore method m, as there is an overloaded
+                            form of this method that takes in a
+                            non-string argument, which gains higher
+                            priority.
+                        */
+                        continue;
+                    }
+                    /*
+                        If the argument is not a String, and if there
+                        is an overloaded form of this method already defined,
+                        we just override that with the new one.
+                        This mechanism does not guarantee any specific order
+                        in which the methods will be selected: so any code
+                        that depends on the order in which "set" methods have
+                        been defined, is not guaranteed to be selected in any
+                        particular order.
+                    */
+                }
+                AttributeSetter as = createAttributeSetter(m, args[0], propName);
+                if (as != null) {
+                    attributeTypes.put(propName, args[0]);
+                    attributeSetters.put(propName, as);
+                }
+            } else if (name.startsWith("create") && !returnType.isArray()
+                       && !returnType.isPrimitive() && args.length == 0) {
+
+                String propName = getPropertyName(name, "create");
+                // Check if a create of this property is already present
+                // add takes preference over create for CB purposes
+                if (nestedCreators.get(propName) == null) {
+                    nestedTypes.put(propName, returnType);
+                    nestedCreators.put(propName, new CreateNestedCreator(m));
+                }
+            } else if (name.startsWith("addConfigured")
+                && java.lang.Void.TYPE.equals(returnType) && args.length == 1
+                && !java.lang.String.class.equals(args[0])
+                && !args[0].isArray() && !args[0].isPrimitive()) {
+                try {
+                    Constructor constructor = null;
+                    try {
+                        constructor = args[0].getConstructor(new Class[] {});
+                    } catch (NoSuchMethodException ex) {
+                        constructor =
+                            args[0].getConstructor(new Class[] {Project.class});
+                    }
+                    String propName = getPropertyName(name, "addConfigured");
+                    nestedTypes.put(propName, args[0]);
+                    nestedCreators.put(propName, new AddNestedCreator(m,
+                        constructor, AddNestedCreator.ADD_CONFIGURED));
+                } catch (NoSuchMethodException nse) {
+                    // ignore
+                }
+            } else if (name.startsWith("add")
+                && java.lang.Void.TYPE.equals(returnType) && args.length == 1
+                && !java.lang.String.class.equals(args[0])
+                && !args[0].isArray() && !args[0].isPrimitive()) {
+                try {
+                    Constructor constructor = null;
+                    try {
+                        constructor = args[0].getConstructor(new Class[] {});
+                    } catch (NoSuchMethodException ex) {
+                        constructor =
+                            args[0].getConstructor(new Class[] {Project.class});
+                    }
+                    String propName = getPropertyName(name, "add");
+                    nestedTypes.put(propName, args[0]);
+                    nestedCreators.put(propName, new AddNestedCreator(m,
+                        constructor, AddNestedCreator.ADD));
+                } catch (NoSuchMethodException nse) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    /**
+     * Certain set methods are part of the Ant core interface to tasks and
+     * therefore not to be considered for introspection
+     *
+     * @param name the name of the set method
+     * @param type the type of the set method's parameter
+     * @return true if the given set method is to be hidden.
+     */
+    private boolean isHiddenSetMethod(String name, Class type) {
+        if ("setLocation".equals(name)
+             && org.apache.tools.ant.Location.class.equals(type)) {
+            return true;
+        }
+
+        if ("setTaskType".equals(name)
+             && java.lang.String.class.equals(type)) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns a helper for the given class, either from the cache
+     * or by creating a new instance.
+     *
+     * @param c The class for which a helper is required.
+     *          Must not be <code>null</code>.
+     *
+     * @return a helper for the specified class
+     */
+    public static synchronized IntrospectionHelper getHelper(Class c) {
+        IntrospectionHelper ih = (IntrospectionHelper) helpers.get(c);
+        if (ih == null) {
+            ih = new IntrospectionHelper(c);
+            helpers.put(c, ih);
+        }
+        return ih;
+    }
+
+    /**
+     * Returns a helper for the given class, either from the cache
+     * or by creating a new instance.
+     *
+     * The method will make sure the helper will be cleaned up at the end of
+     * the project, and only one instance will be created for each class.
+     *
+     * @param p the project instance.
+     * @param c The class for which a helper is required.
+     *          Must not be <code>null</code>.
+     *
+     * @return a helper for the specified class
+     */
+    public static IntrospectionHelper getHelper(Project p, Class c) {
+        IntrospectionHelper ih = getHelper(c);
+        // Cleanup at end of project
+        p.addBuildListener(ih);
+        return ih;
+    }
+
+    /**
+     * Sets the named attribute in the given element, which is part of the
+     * given project.
+     *
+     * @param p The project containing the element. This is used when files
+     *          need to be resolved. Must not be <code>null</code>.
+     * @param element The element to set the attribute in. Must not be
+     *                <code>null</code>.
+     * @param attributeName The name of the attribute to set. Must not be
+     *                      <code>null</code>.
+     * @param value The value to set the attribute to. This may be interpreted
+     *              or converted to the necessary type if the setter method
+     *              doesn't just take a string. Must not be <code>null</code>.
+     *
+     * @exception BuildException if the introspected class doesn't support
+     *                           the given attribute, or if the setting
+     *                           method fails.
+     */
+    public void setAttribute(Project p, Object element, String attributeName,
+                             String value) throws BuildException {
+        AttributeSetter as
+            = (AttributeSetter) attributeSetters.get(
+                attributeName.toLowerCase(Locale.US));
+        if (as == null) {
+            if (element instanceof DynamicAttributeNS) {
+                DynamicAttributeNS dc = (DynamicAttributeNS) element;
+                String uriPlusPrefix =
+                    ProjectHelper.extractUriFromComponentName(attributeName);
+                String uri =
+                    ProjectHelper.extractUriFromComponentName(uriPlusPrefix);
+                String localName =
+                    ProjectHelper.extractNameFromComponentName(attributeName);
+                String qName = ("".equals(uri)
+                                ? localName : (uri + ":" + localName));
+
+                dc.setDynamicAttribute(uri, localName, qName, value);
+                return;
+            } else if (element instanceof DynamicAttribute) {
+                DynamicAttribute dc = (DynamicAttribute) element;
+                dc.setDynamicAttribute(attributeName.toLowerCase(Locale.US), value);
+                return;
+            } else {
+                if (attributeName.indexOf(':') != -1) {
+                    return; // Ignore attribute from unknown uri's
+                }
+                String msg = getElementName(p, element)
+                    + " doesn't support the \"" + attributeName
+                    + "\" attribute.";
+                throw new UnsupportedAttributeException(msg, attributeName);
+            }
+        }
+        try {
+            as.set(p, element, value);
+        } catch (IllegalAccessException ie) {
+            // impossible as getMethods should only return public methods
+            throw new BuildException(ie);
+        } catch (InvocationTargetException ite) {
+            Throwable t = ite.getTargetException();
+            if (t instanceof BuildException) {
+                throw (BuildException) t;
+            }
+            throw new BuildException(t);
+        }
+    }
+
+
+    /**
+     * Adds PCDATA to an element, using the element's
+     * <code>void addText(String)</code> method, if it has one. If no
+     * such method is present, a BuildException is thrown if the
+     * given text contains non-whitespace.
+     *
+     * @param project The project which the element is part of.
+     *                Must not be <code>null</code>.
+     * @param element The element to add the text to.
+     *                Must not be <code>null</code>.
+     * @param text    The text to add.
+     *                Must not be <code>null</code>.
+     *
+     * @exception BuildException if non-whitespace text is provided and no
+     *                           method is available to handle it, or if
+     *                           the handling method fails.
+     */
+    public void addText(Project project, Object element, String text)
+        throws BuildException {
+        if (addText == null) {
+            // Element doesn't handle text content
+            if (text.trim().length() == 0) {
+                // Only whitespace - ignore
+                return;
+            } else {
+                // Not whitespace - fail
+                String msg = project.getElementName(element)
+                    + " doesn't support nested text data.";
+                throw new BuildException(msg);
+            }
+        }
+        try {
+            addText.invoke(element, new Object[] {text});
+        } catch (IllegalAccessException ie) {
+            // impossible as getMethods should only return public methods
+            throw new BuildException(ie);
+        } catch (InvocationTargetException ite) {
+            Throwable t = ite.getTargetException();
+            if (t instanceof BuildException) {
+                throw (BuildException) t;
+            }
+            throw new BuildException(t);
+        }
+    }
+
+    /**
+     * Utility method to throw a NotSupported exception
+     *
+     * @param project the Project instance.
+     * @param parent the object which doesn't support a requested element
+     * @param elementName the name of the Element which is trying to be created.
+     */
+    public void throwNotSupported(Project project, Object parent,
+        String elementName) {
+        String msg = project.getElementName(parent)
+            + " doesn't support the nested \"" + elementName + "\" element.";
+        throw new UnsupportedElementException(msg, elementName);
+    }
+
+    private NestedCreator getNestedCreator(
+        Project project, String parentUri, Object parent,
+        String elementName, UnknownElement child) throws BuildException {
+
+        String uri = ProjectHelper.extractUriFromComponentName(elementName);
+        String name = ProjectHelper.extractNameFromComponentName(elementName);
+
+        if (uri.equals(ProjectHelper.ANT_CORE_URI)) {
+            uri = "";
+        }
+        if (parentUri.equals(ProjectHelper.ANT_CORE_URI)) {
+            parentUri = "";
+        }
+        NestedCreator nc = null;
+        if (uri.equals(parentUri) || uri.equals("")) {
+            nc = (NestedCreator) nestedCreators.get(
+                name.toLowerCase(Locale.US));
+        }
+        if (nc == null) {
+            nc = createAddTypeCreator(project, parent, elementName);
+        }
+        if (nc == null && parent instanceof DynamicElementNS) {
+            DynamicElementNS dc = (DynamicElementNS) parent;
+            String qName = (child == null ? name : child.getQName());
+            final Object nestedElement =
+                dc.createDynamicElement(
+                    (child == null ? "" : child.getNamespace()),
+                    name, qName);
+            if (nestedElement != null) {
+                nc = new NestedCreator(null) {
+                    Object create(
+                        Project project, Object parent, Object ignore) {
+                        return nestedElement;
+                    }
+                };
+            }
+        }
+        if (nc == null && parent instanceof DynamicElement) {
+            DynamicElement dc = (DynamicElement) parent;
+            final Object nestedElement =
+                dc.createDynamicElement(name.toLowerCase(Locale.US));
+            if (nestedElement != null) {
+                nc = new NestedCreator(null) {
+                    Object create(
+                        Project project, Object parent, Object ignore) {
+                        return nestedElement;
+                    }
+                };
+            }
+        }
+        if (nc == null) {
+            throwNotSupported(project, parent, elementName);
+        }
+        return nc;
+    }
+
+    /**
+     * Creates a named nested element. Depending on the results of the
+     * initial introspection, either a method in the given parent instance
+     * or a simple no-arg constructor is used to create an instance of the
+     * specified element type.
+     *
+     * @param project Project to which the parent object belongs.
+     *                Must not be <code>null</code>. If the resulting
+     *                object is an instance of ProjectComponent, its
+     *                Project reference is set to this parameter value.
+     * @param parent  Parent object used to create the instance.
+     *                Must not be <code>null</code>.
+     * @param elementName Name of the element to create an instance of.
+     *                    Must not be <code>null</code>.
+     *
+     * @return an instance of the specified element type
+     * @deprecated This is not a namespace aware method.
+     *
+     * @exception BuildException if no method is available to create the
+     *                           element instance, or if the creating method
+     *                           fails.
+     */
+    public Object createElement(Project project, Object parent,
+        String elementName) throws BuildException {
+        NestedCreator nc = getNestedCreator(project, "", parent, elementName, null);
+        try {
+            Object nestedElement = nc.create(project, parent, null);
+            if (project != null) {
+                project.setProjectReference(nestedElement);
+            }
+            return nestedElement;
+        } catch (IllegalAccessException ie) {
+            // impossible as getMethods should only return public methods
+            throw new BuildException(ie);
+        } catch (InstantiationException ine) {
+            // impossible as getMethods should only return public methods
+            throw new BuildException(ine);
+        } catch (InvocationTargetException ite) {
+            Throwable t = ite.getTargetException();
+            if (t instanceof BuildException) {
+                throw (BuildException) t;
+            }
+            throw new BuildException(t);
+        }
+    }
+
+    /**
+     * returns an object that creates and stores an object
+     * for an element of a parent.
+     *
+     * @param project      Project to which the parent object belongs.
+     * @param parentUri    The namespace uri of the parent object.
+     * @param parent       Parent object used to create the creator object to
+     *                     create and store and instance of a subelement.
+     * @param elementName  Name of the element to create an instance of.
+     * @param ue           The unknown element associated with the element.
+     * @return a creator object to create and store the element instance.
+     */
+    public Creator getElementCreator(
+        Project project, String parentUri, Object parent, String elementName,
+        UnknownElement ue) {
+        NestedCreator nc = getNestedCreator(
+            project, parentUri, parent, elementName, ue);
+        return new Creator(project, parent, nc);
+    }
+
+    /**
+     * Indicates whether the introspected class is a dynamic one,
+     * supporting arbitrary nested elements and/or attributes.
+     *
+     * @return <code>true<code> if the introspected class is dynamic;
+     *         <code>false<code> otherwise.
+     * @since  Ant 1.6.3
+     *
+     * @see DynamicElement
+     * @see DynamicElementNS
+     */
+    public boolean isDynamic() {
+        return DynamicElement.class.isAssignableFrom(bean)
+            || DynamicElementNS.class.isAssignableFrom(bean);
+    }
+
+    /**
+     * Indicates whether the introspected class is a task container,
+     * supporting arbitrary nested tasks/types.
+     *
+     * @return <code>true<code> if the introspected class is a container;
+     *         <code>false<code> otherwise.
+     * @since  Ant 1.6.3
+     *
+     * @see TaskContainer
+     */
+    public boolean isContainer() {
+        return TaskContainer.class.isAssignableFrom(bean);
+    }
+
+    /**
+     * Indicates if this element supports a nested element of the
+     * given name.
+     *
+     * @param elementName the name of the nested element being checked
+     *
+     * @return true if the given nested element is supported
+     */
+    public boolean supportsNestedElement(String elementName) {
+        return nestedCreators.containsKey(elementName.toLowerCase(Locale.US))
+            || isDynamic()
+            || addTypeMethods.size() != 0;
+    }
+
+    /**
+     * Indicate if this element supports a nested element of the
+     * given name.
+     *
+     * @param parentUri   the uri of the parent
+     * @param elementName the name of the nested element being checked
+     *
+     * @return true if the given nested element is supported
+     */
+    public boolean supportsNestedElement(String parentUri, String elementName) {
+        if (parentUri.equals(ProjectHelper.ANT_CORE_URI)) {
+            parentUri = "";
+        }
+        String uri = ProjectHelper.extractUriFromComponentName(elementName);
+        if (uri.equals(ProjectHelper.ANT_CORE_URI)) {
+            uri = "";
+        }
+        String name = ProjectHelper.extractNameFromComponentName(elementName);
+
+        return (
+            nestedCreators.containsKey(name.toLowerCase(Locale.US))
+            && (uri.equals(parentUri) || "".equals(uri)))
+            || isDynamic() || addTypeMethods.size() != 0;
+    }
+
+    /**
+     * Stores a named nested element using a storage method determined
+     * by the initial introspection. If no appropriate storage method
+     * is available, this method returns immediately.
+     *
+     * @param project Ignored in this implementation.
+     *                May be <code>null</code>.
+     *
+     * @param parent  Parent instance to store the child in.
+     *                Must not be <code>null</code>.
+     *
+     * @param child   Child instance to store in the parent.
+     *                Should not be <code>null</code>.
+     *
+     * @param elementName  Name of the child element to store.
+     *                     May be <code>null</code>, in which case
+     *                     this method returns immediately.
+     *
+     * @exception BuildException if the storage method fails.
+     */
+    public void storeElement(Project project, Object parent, Object child,
+        String elementName) throws BuildException {
+        if (elementName == null) {
+            return;
+        }
+        NestedCreator ns = (NestedCreator) nestedCreators.get(
+            elementName.toLowerCase(Locale.US));
+        if (ns == null) {
+            return;
+        }
+        try {
+            ns.store(parent, child);
+        } catch (IllegalAccessException ie) {
+            // impossible as getMethods should only return public methods
+            throw new BuildException(ie);
+        } catch (InstantiationException ine) {
+            // impossible as getMethods should only return public methods
+            throw new BuildException(ine);
+        } catch (InvocationTargetException ite) {
+            Throwable t = ite.getTargetException();
+            if (t instanceof BuildException) {
+                throw (BuildException) t;
+            }
+            throw new BuildException(t);
+        }
+    }
+
+    /**
+     * Returns the type of a named nested element.
+     *
+     * @param elementName The name of the element to find the type of.
+     *                    Must not be <code>null</code>.
+     *
+     * @return the type of the nested element with the specified name.
+     *         This will never be <code>null</code>.
+     *
+     * @exception BuildException if the introspected class does not
+     *                           support the named nested element.
+     */
+    public Class getElementType(String elementName)
+        throws BuildException {
+        Class nt = (Class) nestedTypes.get(elementName);
+        if (nt == null) {
+            throw new UnsupportedElementException("Class "
+                + bean.getName() + " doesn't support the nested \""
+                + elementName + "\" element.", elementName);
+        }
+        return nt;
+    }
+
+    /**
+     * Returns the type of a named attribute.
+     *
+     * @param attributeName The name of the attribute to find the type of.
+     *                      Must not be <code>null</code>.
+     *
+     * @return the type of the attribute with the specified name.
+     *         This will never be <code>null</code>.
+     *
+     * @exception BuildException if the introspected class does not
+     *                           support the named attribute.
+     */
+    public Class getAttributeType(String attributeName)
+        throws BuildException {
+        Class at = (Class) attributeTypes.get(attributeName);
+        if (at == null) {
+            throw new UnsupportedAttributeException("Class "
+                + bean.getName() + " doesn't support the \""
+                + attributeName + "\" attribute.", attributeName);
+        }
+        return at;
+    }
+
+    /**
+     * Returns the addText method when the introspected
+     * class supports nested text.
+     *
+     * @return the method on this introspected class that adds nested text.
+     *         Cannot be <code>null</code>.
+     * @throws BuildException if the introspected class does not
+     *         support the nested text.
+     * @since  Ant 1.6.3
+     */
+    public Method getAddTextMethod()
+                  throws BuildException {
+        if (!supportsCharacters()) {
+            throw new BuildException("Class " + bean.getName()
+                + " doesn't support nested text data.");
+        }
+        return addText;
+    }
+
+    /**
+     * Returns the adder or creator method of a named nested element.
+     *
+     * @param  elementName The name of the attribute to find the setter
+     *         method of. Must not be <code>null</code>.
+     * @return the method on this introspected class that adds or creates this
+     *         nested element. Can be <code>null</code> when the introspected
+     *         class is a dynamic configurator!
+     * @throws BuildException if the introspected class does not
+     *         support the named nested element.
+     * @since  Ant 1.6.3
+     */
+    public Method getElementMethod(String elementName)
+                  throws BuildException {
+        Object creator = nestedCreators.get(elementName);
+        if (creator == null) {
+            throw new UnsupportedElementException("Class "
+                + bean.getName() + " doesn't support the nested \""
+                + elementName + "\" element.", elementName);
+        }
+        return ((NestedCreator) creator).method;
+    }
+
+    /**
+     * Returns the setter method of a named attribute.
+     *
+     * @param  attributeName The name of the attribute to find the setter
+     *         method of. Must not be <code>null</code>.
+     * @return the method on this introspected class that sets this attribute.
+     *         This will never be <code>null</code>.
+     * @throws BuildException if the introspected class does not
+     *         support the named attribute.
+     * @since  Ant 1.6.3
+     */
+    public Method getAttributeMethod(String attributeName)
+                  throws BuildException {
+        Object setter = attributeSetters.get(attributeName);
+        if (setter == null) {
+            throw new UnsupportedAttributeException("Class "
+                + bean.getName() + " doesn't support the \""
+                + attributeName + "\" attribute.", attributeName);
+        }
+        return ((AttributeSetter) setter).method;
+    }
+
+    /**
+     * Returns whether or not the introspected class supports PCDATA.
+     *
+     * @return whether or not the introspected class supports PCDATA.
+     */
+    public boolean supportsCharacters() {
+        return addText != null;
+    }
+
+    /**
+     * Returns an enumeration of the names of the attributes supported
+     * by the introspected class.
+     *
+     * @return an enumeration of the names of the attributes supported
+     *         by the introspected class.
+     * @see #getAttributeMap
+     */
+    public Enumeration getAttributes() {
+        return attributeSetters.keys();
+    }
+
+    /**
+     * Returns a read-only map of attributes supported
+     * by the introspected class.
+     *
+     * @return an attribute name to attribute <code>Class</code>
+     *         unmodifiable map. Can be empty, but never <code>null</code>.
+     * @since  Ant 1.6.3
+     */
+    public Map getAttributeMap() {
+        return (attributeTypes.size() < 1)
+            ? EMPTY_MAP : Collections.unmodifiableMap(attributeTypes);
+    }
+
+    /**
+     * Returns an enumeration of the names of the nested elements supported
+     * by the introspected class.
+     *
+     * @return an enumeration of the names of the nested elements supported
+     *         by the introspected class.
+     * @see #getNestedElementMap
+     */
+    public Enumeration getNestedElements() {
+        return nestedTypes.keys();
+    }
+
+    /**
+     * Returns a read-only map of nested elements supported
+     * by the introspected class.
+     *
+     * @return a nested-element name to nested-element <code>Class</code>
+     *         unmodifiable map. Can be empty, but never <code>null</code>.
+     * @since  Ant 1.6.3
+     */
+    public Map getNestedElementMap() {
+        return (nestedTypes.size() < 1)
+            ? EMPTY_MAP : Collections.unmodifiableMap(nestedTypes);
+    }
+
+    /**
+     * Returns a read-only list of extension points supported
+     * by the introspected class.
+     * <p>
+     * A task/type or nested element with void methods named <code>add()<code>
+     * or <code>addConfigured()</code>, taking a single class or interface
+     * argument, supports extensions point. This method returns the list of
+     * all these <em>void add[Configured](type)</em> methods.
+     *
+     * @return a list of void, single argument add() or addConfigured()
+     *         <code>Method<code>s of all supported extension points.
+     *         These methods are sorted such that if the argument type of a
+     *         method derives from another type also an argument of a method
+     *         of this list, the method with the most derived argument will
+     *         always appear first. Can be empty, but never <code>null</code>.
+     * @since  Ant 1.6.3
+     */
+    public List getExtensionPoints() {
+        return (addTypeMethods.size() < 1) ? Collections.EMPTY_LIST
+            : Collections.unmodifiableList(addTypeMethods);
+    }
+
+    /**
+     * Creates an implementation of AttributeSetter for the given
+     * attribute type. Conversions (where necessary) are automatically
+     * made for the following types:
+     * <ul>
+     * <li>String (left as it is)
+     * <li>Character/char (first character is used)
+     * <li>Boolean/boolean
+     * ({@link Project#toBoolean(String) Project.toBoolean(String)} is used)
+     * <li>Class (Class.forName is used)
+     * <li>File (resolved relative to the appropriate project)
+     * <li>Path (resolve relative to the appropriate project)
+     * <li>EnumeratedAttribute (uses its own
+     * {@link EnumeratedAttribute#setValue(String) setValue} method)
+     * <li>Other primitive types (wrapper classes are used with constructors
+     * taking String)
+     * </ul>
+     *
+     * If none of the above covers the given parameters, a constructor for the
+     * appropriate class taking a String parameter is used if it is available.
+     *
+     * @param m The method to invoke on the bean when the setter is invoked.
+     *          Must not be <code>null</code>.
+     * @param arg The type of the single argument of the bean's method.
+     *            Must not be <code>null</code>.
+     * @param attrName the name of the attribute for which the setter is being
+     *                 created.
+     *
+     * @return an appropriate AttributeSetter instance, or <code>null</code>
+     *         if no appropriate conversion is available.
+     */
+    private AttributeSetter createAttributeSetter(final Method m,
+                                                  Class arg,
+                                                  final String attrName) {
+        // use wrappers for primitive classes, e.g. int and
+        // Integer are treated identically
+        final Class reflectedArg = PRIMITIVE_TYPE_MAP.containsKey(arg)
+            ? (Class) PRIMITIVE_TYPE_MAP.get(arg) : arg;
+
+        // simplest case - setAttribute expects String
+        if (java.lang.String.class.equals(reflectedArg)) {
+            return new AttributeSetter(m) {
+                public void set(Project p, Object parent, String value)
+                        throws InvocationTargetException, IllegalAccessException {
+                    m.invoke(parent, (Object[]) (new String[] {value}));
+                }
+            };
+        // char and Character get special treatment - take the first character
+        } else if (java.lang.Character.class.equals(reflectedArg)) {
+            return new AttributeSetter(m) {
+                public void set(Project p, Object parent, String value)
+                        throws InvocationTargetException, IllegalAccessException {
+                    if (value.length() == 0) {
+                        throw new BuildException("The value \"\" is not a "
+                            + "legal value for attribute \"" + attrName + "\"");
+                    }
+                    m.invoke(parent, (Object[])
+                        (new Character[] {new Character(value.charAt(0))}));
+                }
+            };
+        // boolean and Boolean get special treatment because we
+        // have a nice method in Project
+        } else if (java.lang.Boolean.class.equals(reflectedArg)) {
+            return new AttributeSetter(m) {
+                public void set(Project p, Object parent, String value)
+                        throws InvocationTargetException, IllegalAccessException {
+                    m.invoke(parent, (Object[]) (
+                         new Boolean[] {Project.toBoolean(value)
+                                        ? Boolean.TRUE : Boolean.FALSE}));
+                }
+            };
+        // Class doesn't have a String constructor but a decent factory method
+        } else if (java.lang.Class.class.equals(reflectedArg)) {
+            return new AttributeSetter(m) {
+                public void set(Project p, Object parent, String value)
+                        throws InvocationTargetException, IllegalAccessException, BuildException {
+                    try {
+                        m.invoke(parent, new Object[] {Class.forName(value)});
+                    } catch (ClassNotFoundException ce) {
+                        throw new BuildException(ce);
+                    }
+                }
+            };
+        // resolve relative paths through Project
+        } else if (java.io.File.class.equals(reflectedArg)) {
+            return new AttributeSetter(m) {
+                public void set(Project p, Object parent, String value)
+                        throws InvocationTargetException, IllegalAccessException {
+                    m.invoke(parent, new Object[] {p.resolveFile(value)});
+                }
+            };
+        // EnumeratedAttributes have their own helper class
+        } else if (EnumeratedAttribute.class.isAssignableFrom(reflectedArg)) {
+            return new AttributeSetter(m) {
+                public void set(Project p, Object parent, String value)
+                        throws InvocationTargetException, IllegalAccessException, BuildException {
+                    try {
+                        EnumeratedAttribute ea =
+                            (EnumeratedAttribute) reflectedArg.newInstance();
+                        ea.setValue(value);
+                        m.invoke(parent, new Object[] {ea});
+                    } catch (InstantiationException ie) {
+                        throw new BuildException(ie);
+                    }
+                }
+            };
+        // worst case. look for a public String constructor and use it
+        // also supports new Whatever(Project, String) as for Path or Reference
+        // This is used (deliberately) for all primitives/wrappers other than
+        // char and boolean
+        } else {
+            boolean includeProject;
+            Constructor c;
+            try {
+                // First try with Project.
+                c = reflectedArg.getConstructor(new Class[] {Project.class, String.class});
+                includeProject = true;
+            } catch (NoSuchMethodException nme) {
+                // OK, try without.
+                try {
+                    c = reflectedArg.getConstructor(new Class[] {String.class});
+                    includeProject = false;
+                } catch (NoSuchMethodException nme2) {
+                    // Well, no matching constructor.
+                    return null;
+                }
+            }
+            final boolean finalIncludeProject = includeProject;
+            final Constructor finalConstructor = c;
+
+            return new AttributeSetter(m) {
+                public void set(Project p, Object parent, String value)
+                        throws InvocationTargetException, IllegalAccessException, BuildException {
+                    try {
+                        Object[] args = (finalIncludeProject)
+                            ? new Object[] {p, value} : new Object[] {value};
+
+                        Object attribute = finalConstructor.newInstance(args);
+                        if (p != null) {
+                            p.setProjectReference(attribute);
+                        }
+                        m.invoke(parent, new Object[] {attribute});
+                    } catch (InstantiationException ie) {
+                        throw new BuildException(ie);
+                    }
+                }
+            };
+        }
+    }
+
+    /**
+     * Returns a description of the type of the given element in
+     * relation to a given project. This is used for logging purposes
+     * when the element is asked to cope with some data it has no
+     * way of handling.
+     *
+     * @param project The project the element is defined in.
+     *                Must not be <code>null</code>.
+     *
+     * @param element The element to describe.
+     *                Must not be <code>null</code>.
+     *
+     * @return a description of the element type
+     */
+    protected String getElementName(Project project, Object element) {
+        return project.getElementName(element);
+    }
+
+    /**
+     * Extracts the name of a property from a method name by subtracting
+     * a given prefix and converting into lower case. It is up to calling
+     * code to make sure the method name does actually begin with the
+     * specified prefix - no checking is done in this method.
+     *
+     * @param methodName The name of the method in question.
+     *                   Must not be <code>null</code>.
+     * @param prefix     The prefix to remove.
+     *                   Must not be <code>null</code>.
+     *
+     * @return the lower-cased method name with the prefix removed.
+     */
+    private String getPropertyName(String methodName, String prefix) {
+        return methodName.substring(prefix.length()).toLowerCase(Locale.US);
+    }
+
+    /**
+     * creator - allows use of create/store external
+     * to IntrospectionHelper.
+     * The class is final as it has a private constructor.
+     */
+    public static final class Creator {
+        private NestedCreator nestedCreator;
+        private Object parent;
+        private Project project;
+        private Object nestedObject;
+        private String polyType;
+
+        /**
+         * Creates a new Creator instance.
+         * This object is given to the UnknownElement to create
+         * objects for sub-elements. UnknownElement calls
+         * create to create an object, the object then gets
+         * configured and then UnknownElement calls store.
+         * SetPolyType may be used to override the type used
+         * to create the object with. SetPolyType gets called
+         * before create.
+         *
+         * @param project the current project
+         * @param parent  the parent object to create the object in
+         * @param nestedCreator the nested creator object to use
+         */
+        private Creator(
+            Project project, Object parent, NestedCreator nestedCreator) {
+            this.project = project;
+            this.parent = parent;
+            this.nestedCreator = nestedCreator;
+        }
+
+        /**
+         * Used to override the class used to create the object.
+         *
+         * @param polyType a ant component type name
+         */
+        public void setPolyType(String polyType) {
+            this.polyType = polyType;
+        }
+
+        /**
+         * Create an object using this creator, which is determined
+         * by introspection.
+         *
+         * @return the created object
+         */
+        public Object create() {
+            if (polyType != null) {
+                if (!nestedCreator.isPolyMorphic()) {
+                    throw new BuildException(
+                        "Not allowed to use the polymorphic form"
+                        + " for this element");
+                }
+                Class elementClass = nestedCreator.getElementClass();
+                ComponentHelper helper =
+                    ComponentHelper.getComponentHelper(project);
+                nestedObject = helper.createComponent(polyType);
+                if (nestedObject == null) {
+                    throw new BuildException(
+                        "Unable to create object of type " + polyType);
+                }
+            }
+            try {
+                nestedObject = nestedCreator.create(
+                    project, parent, nestedObject);
+                if (project != null) {
+                    project.setProjectReference(nestedObject);
+                }
+                return nestedObject;
+            } catch (IllegalAccessException ex) {
+                throw new BuildException(ex);
+            } catch (InstantiationException ex) {
+                throw new BuildException(ex);
+            } catch (IllegalArgumentException ex) {
+                if (polyType != null) {
+                    throw new BuildException(
+                        "Invalid type used " + polyType);
+                }
+                throw ex;
+            } catch (InvocationTargetException ex) {
+                Throwable t = ex.getTargetException();
+                if (t instanceof BuildException) {
+                    throw (BuildException) t;
+                }
+                throw new BuildException(t);
+            }
+        }
+
+        /**
+         * @return the real object (used currently only
+         *         for preset def).
+         */
+        public Object getRealObject() {
+            return nestedCreator.getRealObject();
+        }
+
+        /**
+         * Stores the nested element object using a storage method
+         * determined by introspection.
+         *
+         */
+        public void store() {
+            try {
+                nestedCreator.store(parent, nestedObject);
+            } catch (IllegalAccessException ex) {
+                throw new BuildException(ex);
+            } catch (InstantiationException ex) {
+                throw new BuildException(ex);
+            } catch (IllegalArgumentException ex) {
+                if (polyType != null) {
+                    throw new BuildException(
+                        "Invalid type used " + polyType);
+                }
+                throw ex;
+            } catch (InvocationTargetException ex) {
+                Throwable t = ex.getTargetException();
+                if (t instanceof BuildException) {
+                    throw (BuildException) t;
+                }
+                throw new BuildException(t);
+            }
+        }
+    }
+
+    /**
+     * Internal interface used to create nested elements. Not documented
+     * in detail for reasons of source code readability.
+     */
+    private abstract static class NestedCreator {
+        Method method; // the method called to add/create the nested element
+
+        NestedCreator(Method m) {
+            this.method = m;
+        }
+        boolean isPolyMorphic() {
+            return false;
+        }
+        Class getElementClass() {
+            return null;
+        }
+        Object getRealObject() {
+            return null;
+        }
+        abstract Object create(Project project, Object parent, Object child)
+                        throws InvocationTargetException,
+                               IllegalAccessException,
+                               InstantiationException;
+        void store(Object parent, Object child)
+             throws InvocationTargetException,
+                    IllegalAccessException,
+                    InstantiationException {
+            // DO NOTHING
+        }
+    }
+
+    private class CreateNestedCreator extends NestedCreator {
+        CreateNestedCreator(Method m) {
+            super(m);
+        }
+
+        Object create(Project project, Object parent, Object ignore)
+            throws InvocationTargetException, IllegalAccessException {
+            return method.invoke(parent, new Object[] {});
+        }
+    }
+
+    /** Version to use for addXXX and addConfiguredXXX */
+    private class AddNestedCreator extends NestedCreator {
+
+        static final int ADD = 1;
+        static final int ADD_CONFIGURED = 2;
+
+        protected Constructor constructor;
+        protected int behavior;
+
+        AddNestedCreator(Method m, Constructor c, int behavior) {
+            super(m);
+            this.constructor = c;
+            this.behavior = behavior;
+        }
+
+        boolean isPolyMorphic() {
+            return true;
+        }
+
+        Class getElementClass() {
+            return constructor.getDeclaringClass();
+        }
+
+        Object create(Project project, Object parent, Object child)
+                throws InvocationTargetException,
+                IllegalAccessException, InstantiationException {
+            if (child != null) {
+                // Empty
+            } else {
+                child = constructor.newInstance(
+                    (constructor.getParameterTypes().length == 0)
+                    ? new Object[] {} : new Object[] {project});
+            }
+            if (child instanceof PreSetDef.PreSetDefinition) {
+                child = ((PreSetDef.PreSetDefinition) child)
+                    .createObject(project);
+            }
+            if (behavior == ADD) {
+                istore(parent, child);
+            }
+            return child;
+        }
+
+        void store(Object parent, Object child)
+                throws InvocationTargetException,
+                IllegalAccessException, InstantiationException {
+            if (behavior == ADD_CONFIGURED) {
+                istore(parent, child);
+            }
+        }
+
+        private void istore(Object parent, Object child)
+                throws InvocationTargetException,
+                IllegalAccessException, InstantiationException {
+            method.invoke(parent, new Object[] {child});
+        }
+    }
+
+    /**
+     * Internal interface used to setting element attributes. Not documented
+     * in detail for reasons of source code readability.
+     */
+    private abstract static class AttributeSetter {
+        Method method; // the method called to set the attribute
+        AttributeSetter(Method m) {
+            this.method = m;
+        }
+        abstract void set(Project p, Object parent, String value)
+                      throws InvocationTargetException,
+                             IllegalAccessException,
+                             BuildException;
+    }
+
+    /**
+     * Clears all storage used by this class, including the static cache of
+     * helpers.
+     *
+     * @param event Ignored in this implementation.
+     */
+    public void buildFinished(BuildEvent event) {
+        attributeTypes.clear();
+        attributeSetters.clear();
+        nestedTypes.clear();
+        nestedCreators.clear();
+        addText = null;
+        helpers.clear();
+    }
+
+    /**
+     * Empty implementation to satisfy the BuildListener interface.
+     * @param event Ignored in this implementation.
+     */
+    public void buildStarted(BuildEvent event) {
+    }
+
+    /**
+     * Empty implementation to satisfy the BuildListener interface.
+     *
+     * @param event Ignored in this implementation.
+     */
+    public void targetStarted(BuildEvent event) {
+    }
+
+    /**
+     * Empty implementation to satisfy the BuildListener interface.
+     *
+     * @param event Ignored in this implementation.
+     */
+    public void targetFinished(BuildEvent event) {
+    }
+
+    /**
+     * Empty implementation to satisfy the BuildListener interface.
+     *
+     * @param event Ignored in this implementation.
+     */
+    public void taskStarted(BuildEvent event) {
+    }
+
+    /**
+     * Empty implementation to satisfy the BuildListener interface.
+     *
+     * @param event Ignored in this implementation.
+     */
+    public void taskFinished(BuildEvent event) {
+    }
+
+    /**
+     * Empty implementation to satisfy the BuildListener interface.
+     *
+     * @param event Ignored in this implementation.
+     */
+    public void messageLogged(BuildEvent event) {
+    }
+
+    /**
+     *
+     */
+    private NestedCreator createAddTypeCreator(
+        Project project, Object parent, String elementName)
+        throws BuildException {
+        if (addTypeMethods.size() == 0) {
+            return null;
+        }
+        ComponentHelper helper = ComponentHelper.getComponentHelper(project);
+
+        Object addedObject = null;
+        Method addMethod = null;
+        Class clazz = helper.getComponentClass(elementName);
+        if (clazz == null) {
+            return null;
+        }
+        addMethod = findMatchingMethod(clazz, addTypeMethods);
+        if (addMethod == null) {
+            return null;
+        }
+        addedObject = helper.createComponent(elementName);
+        if (addedObject == null) {
+            return null;
+        }
+        Object rObject = addedObject;
+        if (addedObject instanceof PreSetDef.PreSetDefinition) {
+            rObject = ((PreSetDef.PreSetDefinition) addedObject).createObject(
+                project);
+        }
+        final Object nestedObject = addedObject;
+        final Object realObject = rObject;
+
+        return new NestedCreator(addMethod) {
+            Object create(Project project, Object parent, Object ignore)
+                    throws InvocationTargetException, IllegalAccessException {
+                if (!method.getName().endsWith("Configured")) {
+                    method.invoke(parent, new Object[]{realObject});
+                }
+                return nestedObject;
+            }
+
+            Object getRealObject() {
+                return realObject;
+            }
+
+            void store(Object parent, Object child)
+                    throws InvocationTargetException, IllegalAccessException,
+                    InstantiationException {
+                if (method.getName().endsWith("Configured")) {
+                    method.invoke(parent, new Object[]{realObject});
+                }
+            }
+        };
+    }
+
+    /**
+     * Inserts an add or addConfigured method into
+     * the addTypeMethods array. The array is
+     * ordered so that the more derived classes
+     * are first.
+     * @param method the <code>Method</code> to insert.
+     */
+    private void insertAddTypeMethod(Method method) {
+        Class argClass = method.getParameterTypes()[0];
+        for (int c = 0; c < addTypeMethods.size(); ++c) {
+            Method current = (Method) addTypeMethods.get(c);
+            if (current.getParameterTypes()[0].equals(argClass)) {
+                return; // Already present
+            }
+            if (current.getParameterTypes()[0].isAssignableFrom(
+                            argClass)) {
+                addTypeMethods.add(c, method);
+                return; // higher derived
+            }
+        }
+        addTypeMethods.add(method);
+    }
+
+    /**
+     * Search the list of methods to find the first method
+     * that has a parameter that accepts the nested element object.
+     * @param paramClass the <code>Class</code> type to search for.
+     * @param methods the <code>List</code> of methods to search.
+     * @return a matching <code>Method</code>; null if none found.
+     */
+    private Method findMatchingMethod(Class paramClass, List methods) {
+        Class matchedClass = null;
+        Method matchedMethod = null;
+
+        for (int i = 0; i < methods.size(); ++i) {
+            Method method = (Method) methods.get(i);
+            Class  methodClass = method.getParameterTypes()[0];
+            if (methodClass.isAssignableFrom(paramClass)) {
+                if (matchedClass == null) {
+                    matchedClass = methodClass;
+                    matchedMethod = method;
+                } else {
+                    if (!methodClass.isAssignableFrom(matchedClass)) {
+                        throw new BuildException("ambiguous: types "
+                            + matchedClass.getName() + " and "
+                            + methodClass.getName() + " match "
+                            + paramClass.getName());
+                    }
+                }
+            }
+        }
+        return matchedMethod;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/Location.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/Location.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/Location.java	(revision 16139)
@@ -0,0 +1,166 @@
+/*
+ * Copyright  2000,2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.io.Serializable;
+import org.apache.tools.ant.util.FileUtils;
+import org.xml.sax.Locator;
+
+/**
+ * Stores the location of a piece of text within a file (file name,
+ * line number and column number). Note that the column number is
+ * currently ignored.
+ *
+ */
+public class Location implements Serializable {
+
+    /** Name of the file. */
+    private String fileName;
+    /** Line number within the file. */
+    private int lineNumber;
+    /** Column number within the file. */
+    private int columnNumber;
+
+    /** Location to use when one is needed but no information is available */
+    public static final Location UNKNOWN_LOCATION = new Location();
+
+    /**
+     * Creates an "unknown" location.
+     */
+    private Location() {
+        this(null, 0, 0);
+    }
+
+    /**
+     * Creates a location consisting of a file name but no line number or
+     * column number.
+     *
+     * @param fileName The name of the file. May be <code>null</code>,
+     *                 in which case the location is equivalent to
+     *                 {@link #UNKNOWN_LOCATION UNKNOWN_LOCATION}.
+     */
+    public Location(String fileName) {
+        this(fileName, 0, 0);
+    }
+
+    /**
+     * Creates a location from the SAX locator using the system ID as
+     * the filename.
+     *
+     * @param loc Must not be <code>null</code>.
+     *
+     * @since Ant 1.6
+     */
+    public Location(Locator loc) {
+        this(loc.getSystemId(), loc.getLineNumber(), loc.getColumnNumber());
+    }
+
+    /**
+     * Creates a location consisting of a file name, line number and
+     * column number.
+     *
+     * @param fileName The name of the file. May be <code>null</code>,
+     *                 in which case the location is equivalent to
+     *                 {@link #UNKNOWN_LOCATION UNKNOWN_LOCATION}.
+     *
+     * @param lineNumber Line number within the file. Use 0 for unknown
+     *                   positions within a file.
+     * @param columnNumber Column number within the line.
+     */
+    public Location(String fileName, int lineNumber, int columnNumber) {
+        if (fileName != null && fileName.startsWith("file:")) {
+            this.fileName = FileUtils.newFileUtils().fromURI(fileName);
+        } else {
+            this.fileName = fileName;
+        }
+        this.lineNumber = lineNumber;
+        this.columnNumber = columnNumber;
+    }
+
+    /**
+     * @return the filename portion of the location
+     * @since Ant 1.6
+     */
+    public String getFileName() {
+        return fileName;
+    }
+
+    /**
+     * @return the line number
+     * @since Ant 1.6
+     */
+    public int getLineNumber() {
+        return lineNumber;
+    }
+
+    /**
+     * Returns the file name, line number, a colon and a trailing space.
+     * An error message can be appended easily. For unknown locations, an
+     * empty string is returned.
+     *
+     * @return a String of the form <code>"fileName:lineNumber: "</code>
+     *         if both file name and line number are known,
+     *         <code>"fileName: "</code> if only the file name is known,
+     *         and the empty string for unknown locations.
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+
+        if (fileName != null) {
+            buf.append(fileName);
+
+            if (lineNumber != 0) {
+                buf.append(":");
+                buf.append(lineNumber);
+            }
+
+            buf.append(": ");
+        }
+
+        return buf.toString();
+    }
+
+    /**
+     * Equality operation.
+     * @param other the object to compare to.
+     * @return true if the other object contains the same information
+     *              as this object.
+     * @since Ant 1.6.3
+     */
+    public boolean equals(Object other) {
+        if (this == other) {
+            return true;
+        }
+        if (other == null) {
+            return false;
+        }
+        if (!(other.getClass() == getClass())) {
+            return false;
+        }
+        return toString().equals(other.toString());
+    }
+
+    /**
+     * Hash operation.
+     * @return a hash code value for this location.
+     * @since Ant 1.6.3
+     */
+    public int hashCode() {
+        return toString().hashCode();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/MagicNames.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/MagicNames.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/MagicNames.java	(revision 16139)
@@ -0,0 +1,34 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant;
+
+/**
+ * Magic names used within Ant.
+ *
+ * Not all magic names are here yet.
+ *
+ * @since Ant 1.6
+ */
+public class MagicNames {
+    /** The name of the script repository used by the script repo task */
+    public static final String SCRIPT_REPOSITORY = "org.apache.ant.scriptrepo";
+
+    /** The name of the reference to the System Class Loader */
+    public static final String SYSTEM_LOADER_REF = "ant.coreLoader";
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/Main.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/Main.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/Main.java	(revision 16139)
@@ -0,0 +1,1029 @@
+/*
+ * Copyright 2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Vector;
+import org.apache.tools.ant.input.DefaultInputHandler;
+import org.apache.tools.ant.input.InputHandler;
+import org.apache.tools.ant.launch.AntMain;
+
+
+/**
+ * Command line entry point into Ant. This class is entered via the
+ * canonical `public static void main` entry point and reads the
+ * command line arguments. It then assembles and executes an Ant
+ * project.
+ * <p>
+ * If you integrating Ant into some other tool, this is not the class
+ * to use as an entry point. Please see the source code of this
+ * class to see how it manipulates the Ant project classes.
+ *
+ */
+public class Main implements AntMain {
+
+    /** The default build file name. */
+    public static final String DEFAULT_BUILD_FILENAME = "build.xml";
+
+    /** Our current message output status. Follows Project.MSG_XXX. */
+    private int msgOutputLevel = Project.MSG_INFO;
+
+    /** File that we are using for configuration. */
+    private File buildFile; /* null */
+
+    /** Stream to use for logging. */
+    private static PrintStream out = System.out;
+
+    /** Stream that we are using for logging error messages. */
+    private static PrintStream err = System.err;
+
+    /** The build targets. */
+    private Vector targets = new Vector();
+
+    /** Set of properties that can be used by tasks. */
+    private Properties definedProps = new Properties();
+
+    /** Names of classes to add as listeners to project. */
+    private Vector listeners = new Vector(1);
+
+    /** File names of property files to load on startup. */
+    private Vector propertyFiles = new Vector(1);
+
+    /** Indicates whether this build is to support interactive input */
+    private boolean allowInput = true;
+
+    /** keep going mode */
+    private boolean keepGoingMode = false;
+
+    /**
+     * The Ant logger class. There may be only one logger. It will have
+     * the right to use the 'out' PrintStream. The class must implements the
+     * BuildLogger interface.
+     */
+    private String loggerClassname = null;
+
+    /**
+     * The Ant InputHandler class.  There may be only one input
+     * handler.
+     */
+    private String inputHandlerClassname = null;
+
+    /**
+     * Whether or not output to the log is to be unadorned.
+     */
+    private boolean emacsMode = false;
+
+    /**
+     * Whether or not this instance has successfully been
+     * constructed and is ready to run.
+     */
+    private boolean readyToRun = false;
+
+    /**
+     * Whether or not we should only parse and display the project help
+     * information.
+     */
+    private boolean projectHelp = false;
+
+    /**
+     * Whether or not a logfile is being used. This is used to
+     * check if the output streams must be closed.
+     */
+    private static boolean isLogFileUsed = false;
+
+    /**
+     * optional thread priority
+     */
+    private Integer threadPriority = null;
+
+    /**
+     * Prints the message of the Throwable if it (the message) is not
+     * <code>null</code>.
+     *
+     * @param t Throwable to print the message of.
+     *          Must not be <code>null</code>.
+     */
+    private static void printMessage(Throwable t) {
+        String message = t.getMessage();
+        if (message != null) {
+            System.err.println(message);
+        }
+    }
+
+    /**
+     * Creates a new instance of this class using the
+     * arguments specified, gives it any extra user properties which have been
+     * specified, and then runs the build using the classloader provided.
+     *
+     * @param args Command line arguments. Must not be <code>null</code>.
+     * @param additionalUserProperties Any extra properties to use in this
+     *        build. May be <code>null</code>, which is the equivalent to
+     *        passing in an empty set of properties.
+     * @param coreLoader Classloader used for core classes. May be
+     *        <code>null</code> in which case the system classloader is used.
+     */
+    public static void start(String[] args, Properties additionalUserProperties,
+                             ClassLoader coreLoader) {
+        Main m = new Main();
+        m.startAnt(args, additionalUserProperties, coreLoader);
+    }
+
+    /**
+     * Start Ant
+     * @param args command line args
+     * @param additionalUserProperties properties to set beyond those that
+     *        may be specified on the args list
+     * @param coreLoader - not used
+     *
+     * @since Ant 1.6
+     */
+    public void startAnt(String[] args, Properties additionalUserProperties,
+                         ClassLoader coreLoader) {
+
+        try {
+            Diagnostics.validateVersion();
+            processArgs(args);
+        } catch (Throwable exc) {
+            handleLogfile();
+            printMessage(exc);
+            System.exit(1);
+        }
+
+        if (additionalUserProperties != null) {
+            for (Enumeration e = additionalUserProperties.keys();
+                    e.hasMoreElements();) {
+                String key = (String) e.nextElement();
+                String property = additionalUserProperties.getProperty(key);
+                definedProps.put(key, property);
+            }
+        }
+
+        // expect the worst
+        int exitCode = 1;
+        try {
+            try {
+                runBuild(coreLoader);
+                exitCode = 0;
+            } catch (ExitStatusException ese) {
+                exitCode = ese.getStatus();
+                if (exitCode != 0) {
+                    throw ese;
+                }
+            }
+        } catch (BuildException be) {
+            if (err != System.err) {
+                printMessage(be);
+            }
+        } catch (Throwable exc) {
+            exc.printStackTrace();
+            printMessage(exc);
+        } finally {
+            handleLogfile();
+        }
+        System.exit(exitCode);
+    }
+
+    /**
+     * Close logfiles, if we have been writing to them.
+     *
+     * @since Ant 1.6
+     */
+    private static void handleLogfile() {
+        if (isLogFileUsed) {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (final Exception e) {
+                    //ignore
+                }
+            }
+            if (err != null) {
+                try {
+                    err.close();
+                } catch (final Exception e) {
+                    //ignore
+                }
+            }
+        }
+    }
+
+    /**
+     * Command line entry point. This method kicks off the building
+     * of a project object and executes a build using either a given
+     * target or the default target.
+     *
+     * @param args Command line arguments. Must not be <code>null</code>.
+     */
+    public static void main(String[] args) {
+        start(args, null, null);
+    }
+
+    /**
+     * Constructor used when creating Main for later arg processing
+     * and startup
+     */
+    public Main() {
+    }
+
+    /**
+     * Sole constructor, which parses and deals with command line
+     * arguments.
+     *
+     * @param args Command line arguments. Must not be <code>null</code>.
+     *
+     * @exception BuildException if the specified build file doesn't exist
+     *                           or is a directory.
+     *
+     * @deprecated
+     */
+    protected Main(String[] args) throws BuildException {
+        processArgs(args);
+    }
+
+    /**
+     * Process command line arguments.
+     * When ant is started from Launcher, the -lib argument does not get
+     * passed through to this routine.
+     *
+     * @param args the command line arguments.
+     *
+     * @since Ant 1.6
+     */
+    private void processArgs(String[] args) {
+        String searchForThis = null;
+        PrintStream logTo = null;
+
+        // cycle through given args
+
+        for (int i = 0; i < args.length; i++) {
+            String arg = args[i];
+
+            if (arg.equals("-help") || arg.equals("-h")) {
+                printUsage();
+                return;
+            } else if (arg.equals("-version")) {
+                printVersion();
+                return;
+            } else if (arg.equals("-diagnostics")) {
+                Diagnostics.doReport(System.out);
+                return;
+            } else if (arg.equals("-quiet") || arg.equals("-q")) {
+                msgOutputLevel = Project.MSG_WARN;
+            } else if (arg.equals("-verbose") || arg.equals("-v")) {
+                printVersion();
+                msgOutputLevel = Project.MSG_VERBOSE;
+            } else if (arg.equals("-debug") || arg.equals("-d")) {
+                printVersion();
+                msgOutputLevel = Project.MSG_DEBUG;
+            } else if (arg.equals("-noinput")) {
+                allowInput = false;
+            } else if (arg.equals("-logfile") || arg.equals("-l")) {
+                try {
+                    File logFile = new File(args[i + 1]);
+                    i++;
+                    logTo = new PrintStream(new FileOutputStream(logFile));
+                    isLogFileUsed = true;
+                } catch (IOException ioe) {
+                    String msg = "Cannot write on the specified log file. "
+                        + "Make sure the path exists and you have write "
+                        + "permissions.";
+                    throw new BuildException(msg);
+                } catch (ArrayIndexOutOfBoundsException aioobe) {
+                    String msg = "You must specify a log file when "
+                        + "using the -log argument";
+                    throw new BuildException(msg);
+                }
+            } else if (arg.equals("-buildfile") || arg.equals("-file")
+                       || arg.equals("-f")) {
+                try {
+                    buildFile = new File(args[i + 1].replace('/', File.separatorChar));
+                    i++;
+                } catch (ArrayIndexOutOfBoundsException aioobe) {
+                    String msg = "You must specify a buildfile when "
+                        + "using the -buildfile argument";
+                    throw new BuildException(msg);
+                }
+            } else if (arg.equals("-listener")) {
+                try {
+                    listeners.addElement(args[i + 1]);
+                    i++;
+                } catch (ArrayIndexOutOfBoundsException aioobe) {
+                    String msg = "You must specify a classname when "
+                        + "using the -listener argument";
+                    throw new BuildException(msg);
+                }
+            } else if (arg.startsWith("-D")) {
+
+                /* Interestingly enough, we get to here when a user
+                 * uses -Dname=value. However, in some cases, the OS
+                 * goes ahead and parses this out to args
+                 *   {"-Dname", "value"}
+                 * so instead of parsing on "=", we just make the "-D"
+                 * characters go away and skip one argument forward.
+                 *
+                 * I don't know how to predict when the JDK is going
+                 * to help or not, so we simply look for the equals sign.
+                 */
+
+                String name = arg.substring(2, arg.length());
+                String value = null;
+                int posEq = name.indexOf("=");
+                if (posEq > 0) {
+                    value = name.substring(posEq + 1);
+                    name = name.substring(0, posEq);
+                } else if (i < args.length - 1) {
+                    value = args[++i];
+                } else {
+                    throw new BuildException("Missing value for property "
+                                             + name);
+                }
+
+                definedProps.put(name, value);
+            } else if (arg.equals("-logger")) {
+                if (loggerClassname != null) {
+                    throw new BuildException("Only one logger class may "
+                        + " be specified.");
+                }
+                try {
+                    loggerClassname = args[++i];
+                } catch (ArrayIndexOutOfBoundsException aioobe) {
+                    throw new BuildException("You must specify a classname when"
+                                             + " using the -logger argument");
+                }
+            } else if (arg.equals("-inputhandler")) {
+                if (inputHandlerClassname != null) {
+                    throw new BuildException("Only one input handler class may "
+                                             + "be specified.");
+                }
+                try {
+                    inputHandlerClassname = args[++i];
+                } catch (ArrayIndexOutOfBoundsException aioobe) {
+                    throw new BuildException("You must specify a classname when"
+                                             + " using the -inputhandler"
+                                             + " argument");
+                }
+            } else if (arg.equals("-emacs") || arg.equals("-e")) {
+                emacsMode = true;
+            } else if (arg.equals("-projecthelp") || arg.equals("-p")) {
+                // set the flag to display the targets and quit
+                projectHelp = true;
+            } else if (arg.equals("-find") || arg.equals("-s")) {
+                // eat up next arg if present, default to build.xml
+                if (i < args.length - 1) {
+                    searchForThis = args[++i];
+                } else {
+                    searchForThis = DEFAULT_BUILD_FILENAME;
+                }
+            } else if (arg.startsWith("-propertyfile")) {
+                try {
+                    propertyFiles.addElement(args[i + 1]);
+                    i++;
+                } catch (ArrayIndexOutOfBoundsException aioobe) {
+                    String msg = "You must specify a property filename when "
+                        + "using the -propertyfile argument";
+                    throw new BuildException(msg);
+                }
+            } else if (arg.equals("-k") || arg.equals("-keep-going")) {
+                keepGoingMode = true;
+            } else if (arg.equals("-nice")) {
+                try {
+                    threadPriority = Integer.decode(args[i + 1]);
+                } catch (ArrayIndexOutOfBoundsException aioobe) {
+                    throw new BuildException(
+                            "You must supply a niceness value (1-10)"
+                            + " after the -nice option");
+                } catch (NumberFormatException e) {
+                    throw new BuildException("Unrecognized niceness value: "
+                                             + args[i + 1]);
+                }
+                i++;
+                if (threadPriority.intValue() < Thread.MIN_PRIORITY
+                    || threadPriority.intValue() > Thread.MAX_PRIORITY) {
+                    throw new BuildException(
+                            "Niceness value is out of the range 1-10");
+                }
+            } else if (arg.startsWith("-")) {
+                // we don't have any more args to recognize!
+                String msg = "Unknown argument: " + arg;
+                System.out.println(msg);
+                printUsage();
+                throw new BuildException("");
+            } else {
+                // if it's no other arg, it may be the target
+                targets.addElement(arg);
+            }
+        }
+
+        // if buildFile was not specified on the command line,
+        if (buildFile == null) {
+            // but -find then search for it
+            if (searchForThis != null) {
+                buildFile = findBuildFile(System.getProperty("user.dir"),
+                                          searchForThis);
+            } else {
+                buildFile = new File(DEFAULT_BUILD_FILENAME);
+            }
+        }
+
+        // make sure buildfile exists
+        if (!buildFile.exists()) {
+            System.out.println("Buildfile: " + buildFile + " does not exist!");
+            throw new BuildException("Build failed");
+        }
+
+        // make sure it's not a directory (this falls into the ultra
+        // paranoid lets check everything category
+
+        if (buildFile.isDirectory()) {
+            System.out.println("What? Buildfile: " + buildFile + " is a dir!");
+            throw new BuildException("Build failed");
+        }
+
+        // Load the property files specified by -propertyfile
+        for (int propertyFileIndex = 0;
+             propertyFileIndex < propertyFiles.size();
+             propertyFileIndex++) {
+            String filename
+                = (String) propertyFiles.elementAt(propertyFileIndex);
+            Properties props = new Properties();
+            FileInputStream fis = null;
+            try {
+                fis = new FileInputStream(filename);
+                props.load(fis);
+            } catch (IOException e) {
+                System.out.println("Could not load property file "
+                   + filename + ": " + e.getMessage());
+            } finally {
+                if (fis != null) {
+                    try {
+                        fis.close();
+                    } catch (IOException e) {
+                        // ignore
+                    }
+                }
+            }
+
+            // ensure that -D properties take precedence
+            Enumeration propertyNames = props.propertyNames();
+            while (propertyNames.hasMoreElements()) {
+                String name = (String) propertyNames.nextElement();
+                if (definedProps.getProperty(name) == null) {
+                    definedProps.put(name, props.getProperty(name));
+                }
+            }
+        }
+
+        if (msgOutputLevel >= Project.MSG_INFO) {
+            System.out.println("Buildfile: " + buildFile);
+        }
+
+        if (logTo != null) {
+            out = logTo;
+            err = logTo;
+            System.setOut(out);
+            System.setErr(err);
+        }
+        readyToRun = true;
+    }
+
+    /**
+     * Helper to get the parent file for a given file.
+     * <p>
+     * Added to simulate File.getParentFile() from JDK 1.2.
+     * @deprecated
+     *
+     * @param file   File to find parent of. Must not be <code>null</code>.
+     * @return       Parent file or null if none
+     */
+    private File getParentFile(File file) {
+        File parent = file.getParentFile();
+
+        if (parent != null && msgOutputLevel >= Project.MSG_VERBOSE) {
+            System.out.println("Searching in " + parent.getAbsolutePath());
+        }
+
+        return parent;
+    }
+
+    /**
+     * Search parent directories for the build file.
+     * <p>
+     * Takes the given target as a suffix to append to each
+     * parent directory in search of a build file.  Once the
+     * root of the file-system has been reached an exception
+     * is thrown.
+     *
+     * @param start  Leaf directory of search.
+     *               Must not be <code>null</code>.
+     * @param suffix  Suffix filename to look for in parents.
+     *                Must not be <code>null</code>.
+     *
+     * @return A handle to the build file if one is found
+     *
+     * @exception BuildException if no build file is found
+     */
+    private File findBuildFile(String start, String suffix)
+         throws BuildException {
+        if (msgOutputLevel >= Project.MSG_INFO) {
+            System.out.println("Searching for " + suffix + " ...");
+        }
+
+        File parent = new File(new File(start).getAbsolutePath());
+        File file = new File(parent, suffix);
+
+        // check if the target file exists in the current directory
+        while (!file.exists()) {
+            // change to parent directory
+            parent = getParentFile(parent);
+
+            // if parent is null, then we are at the root of the fs,
+            // complain that we can't find the build file.
+            if (parent == null) {
+                throw new BuildException("Could not locate a build file!");
+            }
+
+            // refresh our file handle
+            file = new File(parent, suffix);
+        }
+
+        return file;
+    }
+
+    /**
+     * Executes the build. If the constructor for this instance failed
+     * (e.g. returned after issuing a warning), this method returns
+     * immediately.
+     *
+     * @param coreLoader The classloader to use to find core classes.
+     *                   May be <code>null</code>, in which case the
+     *                   system classloader is used.
+     *
+     * @exception BuildException if the build fails
+     */
+    private void runBuild(ClassLoader coreLoader) throws BuildException {
+
+        if (!readyToRun) {
+            return;
+        }
+
+        final Project project = new Project();
+        project.setCoreLoader(coreLoader);
+
+        Throwable error = null;
+
+        try {
+            addBuildListeners(project);
+            addInputHandler(project);
+
+            PrintStream err = System.err;
+            PrintStream out = System.out;
+            InputStream in = System.in;
+
+            // use a system manager that prevents from System.exit()
+            SecurityManager oldsm = null;
+            oldsm = System.getSecurityManager();
+
+                //SecurityManager can not be installed here for backwards
+                //compatibility reasons (PD). Needs to be loaded prior to
+                //ant class if we are going to implement it.
+                //System.setSecurityManager(new NoExitSecurityManager());
+            try {
+                if (allowInput) {
+                    project.setDefaultInputStream(System.in);
+                }
+                System.setIn(new DemuxInputStream(project));
+                System.setOut(new PrintStream(new DemuxOutputStream(project, false)));
+                System.setErr(new PrintStream(new DemuxOutputStream(project, true)));
+
+
+                if (!projectHelp) {
+                    project.fireBuildStarted();
+                }
+
+                // set the thread priorities
+                if (threadPriority != null) {
+                    try {
+                        project.log("Setting Ant's thread priority to "
+                                + threadPriority, Project.MSG_VERBOSE);
+                        Thread.currentThread().setPriority(threadPriority.intValue());
+                    } catch (SecurityException swallowed) {
+                        //we cannot set the priority here.
+                        project.log("A security manager refused to set the -nice value");
+                    }
+                }
+
+                project.init();
+                project.setUserProperty("ant.version", getAntVersion());
+
+                // set user-define properties
+                Enumeration e = definedProps.keys();
+                while (e.hasMoreElements()) {
+                    String arg = (String) e.nextElement();
+                    String value = (String) definedProps.get(arg);
+                    project.setUserProperty(arg, value);
+                }
+
+                project.setUserProperty("ant.file",
+                                        buildFile.getAbsolutePath());
+
+                project.setKeepGoingMode(keepGoingMode);
+
+                ProjectHelper.configureProject(project, buildFile);
+
+                if (projectHelp) {
+                    printDescription(project);
+                    printTargets(project, msgOutputLevel > Project.MSG_INFO);
+                    return;
+                }
+
+                // make sure that we have a target to execute
+                if (targets.size() == 0) {
+                    if (project.getDefaultTarget() != null) {
+                        targets.addElement(project.getDefaultTarget());
+                    }
+                }
+
+                project.executeTargets(targets);
+            } finally {
+                // put back the original security manager
+                //The following will never eval to true. (PD)
+                if (oldsm != null) {
+                    System.setSecurityManager(oldsm);
+                }
+
+                System.setOut(out);
+                System.setErr(err);
+                System.setIn(in);
+            }
+        } catch (RuntimeException exc) {
+            error = exc;
+            throw exc;
+        } catch (Error err) {
+            error = err;
+            throw err;
+        } finally {
+            if (!projectHelp) {
+                project.fireBuildFinished(error);
+            } else if (error != null) {
+                project.log(error.toString(), Project.MSG_ERR);
+            }
+        }
+    }
+
+    /**
+     * Adds the listeners specified in the command line arguments,
+     * along with the default listener, to the specified project.
+     *
+     * @param project The project to add listeners to.
+     *                Must not be <code>null</code>.
+     */
+    protected void addBuildListeners(Project project) {
+
+        // Add the default listener
+        project.addBuildListener(createLogger());
+
+        for (int i = 0; i < listeners.size(); i++) {
+            String className = (String) listeners.elementAt(i);
+            try {
+                BuildListener listener =
+                    (BuildListener) Class.forName(className).newInstance();
+                if (project != null) {
+                    project.setProjectReference(listener);
+                }
+                project.addBuildListener(listener);
+            } catch (Throwable exc) {
+                throw new BuildException("Unable to instantiate listener "
+                    + className, exc);
+            }
+        }
+    }
+
+    /**
+     * Creates the InputHandler and adds it to the project.
+     *
+     * @param project the project instance.
+     *
+     * @exception BuildException if a specified InputHandler
+     *                           implementation could not be loaded.
+     */
+    private void addInputHandler(Project project) throws BuildException {
+        InputHandler handler = null;
+        if (inputHandlerClassname == null) {
+            handler = new DefaultInputHandler();
+        } else {
+            try {
+                handler = (InputHandler)
+                    (Class.forName(inputHandlerClassname).newInstance());
+                if (project != null) {
+                    project.setProjectReference(handler);
+                }
+            } catch (ClassCastException e) {
+                String msg = "The specified input handler class "
+                    + inputHandlerClassname
+                    + " does not implement the InputHandler interface";
+                throw new BuildException(msg);
+            } catch (Exception e) {
+                String msg = "Unable to instantiate specified input handler "
+                    + "class " + inputHandlerClassname + " : "
+                    + e.getClass().getName();
+                throw new BuildException(msg);
+            }
+        }
+        project.setInputHandler(handler);
+    }
+
+    // XXX: (Jon Skeet) Any reason for writing a message and then using a bare
+    // RuntimeException rather than just using a BuildException here? Is it
+    // in case the message could end up being written to no loggers (as the
+    // loggers could have failed to be created due to this failure)?
+    /**
+     * Creates the default build logger for sending build events to the ant
+     * log.
+     *
+     * @return the logger instance for this build.
+     */
+    private BuildLogger createLogger() {
+        BuildLogger logger = null;
+        if (loggerClassname != null) {
+            try {
+                Class loggerClass = Class.forName(loggerClassname);
+                logger = (BuildLogger) (loggerClass.newInstance());
+            } catch (ClassCastException e) {
+                System.err.println("The specified logger class "
+                    + loggerClassname
+                    + " does not implement the BuildLogger interface");
+                throw new RuntimeException();
+            } catch (Exception e) {
+                System.err.println("Unable to instantiate specified logger "
+                    + "class " + loggerClassname + " : "
+                    + e.getClass().getName());
+                throw new RuntimeException();
+            }
+        } else {
+            logger = new DefaultLogger();
+        }
+
+        logger.setMessageOutputLevel(msgOutputLevel);
+        logger.setOutputPrintStream(out);
+        logger.setErrorPrintStream(err);
+        logger.setEmacsMode(emacsMode);
+
+        return logger;
+    }
+
+    /**
+     * Prints the usage information for this class to <code>System.out</code>.
+     */
+    private static void printUsage() {
+        String lSep = System.getProperty("line.separator");
+        StringBuffer msg = new StringBuffer();
+        msg.append("ant [options] [target [target2 [target3] ...]]" + lSep);
+        msg.append("Options: " + lSep);
+        msg.append("  -help, -h              print this message" + lSep);
+        msg.append("  -projecthelp, -p       print project help information" + lSep);
+        msg.append("  -version               print the version information and exit" + lSep);
+        msg.append("  -diagnostics           print information that might be helpful to" + lSep);
+        msg.append("                         diagnose or report problems." + lSep);
+        msg.append("  -quiet, -q             be extra quiet" + lSep);
+        msg.append("  -verbose, -v           be extra verbose" + lSep);
+        msg.append("  -debug, -d             print debugging information" + lSep);
+        msg.append("  -emacs, -e             produce logging information without adornments"
+                   + lSep);
+        msg.append("  -lib <path>            specifies a path to search for jars and classes"
+                   + lSep);
+        msg.append("  -logfile <file>        use given file for log" + lSep);
+        msg.append("    -l     <file>                ''" + lSep);
+        msg.append("  -logger <classname>    the class which is to perform logging" + lSep);
+        msg.append("  -listener <classname>  add an instance of class as a project listener"
+                   + lSep);
+        msg.append("  -noinput               do not allow interactive input" + lSep);
+        msg.append("  -buildfile <file>      use given buildfile" + lSep);
+        msg.append("    -file    <file>              ''" + lSep);
+        msg.append("    -f       <file>              ''" + lSep);
+        msg.append("  -D<property>=<value>   use value for given property" + lSep);
+        msg.append("  -keep-going, -k        execute all targets that do not depend" + lSep);
+        msg.append("                         on failed target(s)" + lSep);
+        msg.append("  -propertyfile <name>   load all properties from file with -D" + lSep);
+        msg.append("                         properties taking precedence" + lSep);
+        msg.append("  -inputhandler <class>  the class which will handle input requests" + lSep);
+        msg.append("  -find <file>           (s)earch for buildfile towards the root of" + lSep);
+        msg.append("    -s  <file>           the filesystem and use it" + lSep);
+        msg.append("  -nice  number          A niceness value for the main thread:" + lSep
+                   + "                         1 (lowest) to 10 (highest); 5 is the default"
+                   + lSep);
+        msg.append("  -nouserlib             Run ant without using the jar files from" + lSep
+                   + "                         ${user.home}/.ant/lib" + lSep);
+        msg.append("  -noclasspath           Run ant without using CLASSPATH");
+        System.out.println(msg.toString());
+    }
+
+    /**
+     * Prints the Ant version information to <code>System.out</code>.
+     *
+     * @exception BuildException if the version information is unavailable
+     */
+    private static void printVersion() throws BuildException {
+        System.out.println(getAntVersion());
+    }
+
+    /**
+     * Cache of the Ant version information when it has been loaded.
+     */
+    private static String antVersion = null;
+
+    /**
+     * Returns the Ant version information, if available. Once the information
+     * has been loaded once, it's cached and returned from the cache on future
+     * calls.
+     *
+     * @return the Ant version information as a String
+     *         (always non-<code>null</code>)
+     *
+     * @exception BuildException if the version information is unavailable
+     */
+    public static synchronized String getAntVersion() throws BuildException {
+        if (antVersion == null) {
+            try {
+                Properties props = new Properties();
+                InputStream in =
+                    Main.class.getResourceAsStream("/org/apache/tools/ant/version.txt");
+                props.load(in);
+                in.close();
+
+                StringBuffer msg = new StringBuffer();
+                msg.append("Apache Ant version ");
+                msg.append(props.getProperty("VERSION"));
+                msg.append(" compiled on ");
+                msg.append(props.getProperty("DATE"));
+                antVersion = msg.toString();
+            } catch (IOException ioe) {
+                throw new BuildException("Could not load the version information:"
+                                         + ioe.getMessage());
+            } catch (NullPointerException npe) {
+                throw new BuildException("Could not load the version information.");
+            }
+        }
+        return antVersion;
+    }
+
+     /**
+      * Prints the description of a project (if there is one) to
+      * <code>System.out</code>.
+      *
+      * @param project The project to display a description of.
+      *                Must not be <code>null</code>.
+      */
+    private static void printDescription(Project project) {
+       if (project.getDescription() != null) {
+          project.log(project.getDescription());
+       }
+    }
+
+    /**
+     * Prints a list of all targets in the specified project to
+     * <code>System.out</code>, optionally including subtargets.
+     *
+     * @param project The project to display a description of.
+     *                Must not be <code>null</code>.
+     * @param printSubTargets Whether or not subtarget names should also be
+     *                        printed.
+     */
+    private static void printTargets(Project project, boolean printSubTargets) {
+        // find the target with the longest name
+        int maxLength = 0;
+        Enumeration ptargets = project.getTargets().elements();
+        String targetName;
+        String targetDescription;
+        Target currentTarget;
+        // split the targets in top-level and sub-targets depending
+        // on the presence of a description
+        Vector topNames = new Vector();
+        Vector topDescriptions = new Vector();
+        Vector subNames = new Vector();
+
+        while (ptargets.hasMoreElements()) {
+            currentTarget = (Target) ptargets.nextElement();
+            targetName = currentTarget.getName();
+            if (targetName.equals("")) {
+                continue;
+            }
+            targetDescription = currentTarget.getDescription();
+            // maintain a sorted list of targets
+            if (targetDescription == null) {
+                int pos = findTargetPosition(subNames, targetName);
+                subNames.insertElementAt(targetName, pos);
+            } else {
+                int pos = findTargetPosition(topNames, targetName);
+                topNames.insertElementAt(targetName, pos);
+                topDescriptions.insertElementAt(targetDescription, pos);
+                if (targetName.length() > maxLength) {
+                    maxLength = targetName.length();
+                }
+            }
+        }
+
+        printTargets(project, topNames, topDescriptions, "Main targets:",
+                     maxLength);
+        //if there were no main targets, we list all subtargets
+        //as it means nothing has a description
+        if (topNames.size() == 0) {
+            printSubTargets = true;
+        }
+        if (printSubTargets) {
+            printTargets(project, subNames, null, "Other targets:", 0);
+        }
+
+        String defaultTarget = project.getDefaultTarget();
+        if (defaultTarget != null && !"".equals(defaultTarget)) {
+            // shouldn't need to check but...
+            project.log("Default target: " + defaultTarget);
+        }
+    }
+
+    /**
+     * Searches for the correct place to insert a name into a list so as
+     * to keep the list sorted alphabetically.
+     *
+     * @param names The current list of names. Must not be <code>null</code>.
+     * @param name  The name to find a place for.
+     *              Must not be <code>null</code>.
+     *
+     * @return the correct place in the list for the given name
+     */
+    private static int findTargetPosition(Vector names, String name) {
+        int res = names.size();
+        for (int i = 0; i < names.size() && res == names.size(); i++) {
+            if (name.compareTo((String) names.elementAt(i)) < 0) {
+                res = i;
+            }
+        }
+        return res;
+    }
+
+    /**
+     * Writes a formatted list of target names to <code>System.out</code>
+     * with an optional description.
+     *
+     *
+     * @param project the project instance.
+     * @param names The names to be printed.
+     *              Must not be <code>null</code>.
+     * @param descriptions The associated target descriptions.
+     *                     May be <code>null</code>, in which case
+     *                     no descriptions are displayed.
+     *                     If non-<code>null</code>, this should have
+     *                     as many elements as <code>names</code>.
+     * @param heading The heading to display.
+     *                Should not be <code>null</code>.
+     * @param maxlen The maximum length of the names of the targets.
+     *               If descriptions are given, they are padded to this
+     *               position so they line up (so long as the names really
+     *               <i>are</i> shorter than this).
+     */
+    private static void printTargets(Project project, Vector names,
+                                     Vector descriptions, String heading,
+                                     int maxlen) {
+        // now, start printing the targets and their descriptions
+        String lSep = System.getProperty("line.separator");
+        // got a bit annoyed that I couldn't find a pad function
+        String spaces = "    ";
+        while (spaces.length() <= maxlen) {
+            spaces += spaces;
+        }
+        StringBuffer msg = new StringBuffer();
+        msg.append(heading + lSep + lSep);
+        for (int i = 0; i < names.size(); i++) {
+            msg.append(" ");
+            msg.append(names.elementAt(i));
+            if (descriptions != null) {
+                msg.append(
+                    spaces.substring(0, maxlen - ((String) names.elementAt(i)).length() + 2));
+                msg.append(descriptions.elementAt(i));
+            }
+            msg.append(lSep);
+        }
+        project.log(msg.toString());
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/NoBannerLogger.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/NoBannerLogger.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/NoBannerLogger.java	(revision 16139)
@@ -0,0 +1,89 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Extends DefaultLogger to strip out empty targets.
+ *
+ */
+public class NoBannerLogger extends DefaultLogger {
+
+    /**
+     * Name of the current target, if it should
+     * be displayed on the next message. This is
+     * set when a target starts building, and reset
+     * to <code>null</code> after the first message for
+     * the target is logged.
+     */
+    protected String targetName;
+    protected String targetAddress;
+
+    /** Sole constructor. */
+    public NoBannerLogger() {
+    }
+
+    /**
+     * Notes the name of the target so it can be logged
+     * if it generates any messages.
+     *
+     * @param event A BuildEvent containing target information.
+     *              Must not be <code>null</code>.
+     */
+    public void targetStarted(BuildEvent event) {
+        targetName = event.getTarget().getName();
+        targetName = event.getTarget().getAddress();
+    }
+
+    /**
+     * Resets the current target name to <code>null</code>.
+     *
+     * @param event Ignored in this implementation.
+     */
+    public void targetFinished(BuildEvent event) {
+        targetName = null;
+        targetAddress = null;
+    }
+
+    /**
+     * Logs a message for a target if it is of an appropriate
+     * priority, also logging the name of the target if this
+     * is the first message which needs to be logged for the
+     * target.
+     *
+     * @param event A BuildEvent containing message information.
+     *              Must not be <code>null</code>.
+     */
+    public void messageLogged(BuildEvent event) {
+
+        if (event.getPriority() > msgOutputLevel
+            || null == event.getMessage()
+            || "".equals(event.getMessage().trim())) {
+                return;
+        }
+
+        if (null != targetName) {
+            out.println(StringUtils.LINE_SEP + targetAddress + " " + targetName + ":");
+            targetName = null;
+            targetAddress = null;
+        }
+
+        super.messageLogged(event);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/PathTokenizer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/PathTokenizer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/PathTokenizer.java	(revision 16139)
@@ -0,0 +1,164 @@
+/*
+ * Copyright  2000,2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant;
+
+import java.io.File;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+import org.apache.tools.ant.taskdefs.condition.Os;
+
+/**
+ * A Path tokenizer takes a path and returns the components that make up
+ * that path.
+ *
+ * The path can use path separators of either ':' or ';' and file separators
+ * of either '/' or '\'.
+ *
+ */
+public class PathTokenizer {
+    /**
+     * A tokenizer to break the string up based on the ':' or ';' separators.
+     */
+    private StringTokenizer tokenizer;
+
+    /**
+     * A String which stores any path components which have been read ahead
+     * due to DOS filesystem compensation.
+     */
+    private String lookahead = null;
+
+    /**
+     * A boolean that determines if we are running on Novell NetWare, which
+     * exhibits slightly different path name characteristics (multi-character
+     * volume / drive names)
+     */
+    private boolean onNetWare = Os.isFamily("netware");
+
+    /**
+     * Flag to indicate whether or not we are running on a platform with a
+     * DOS style filesystem
+     */
+    private boolean dosStyleFilesystem;
+
+    /**
+     * Constructs a path tokenizer for the specified path.
+     *
+     * @param path The path to tokenize. Must not be <code>null</code>.
+     */
+    public PathTokenizer(String path) {
+        if (onNetWare) {
+            // For NetWare, use the boolean=true mode, so we can use delimiter
+            // information to make a better decision later.
+            tokenizer = new StringTokenizer(path, ":;", true);
+        } else {
+            // on Windows and Unix, we can ignore delimiters and still have
+            // enough information to tokenize correctly.
+            tokenizer = new StringTokenizer(path, ":;", false);
+        }
+        dosStyleFilesystem = File.pathSeparatorChar == ';';
+    }
+
+    /**
+     * Tests if there are more path elements available from this tokenizer's
+     * path. If this method returns <code>true</code>, then a subsequent call
+     * to nextToken will successfully return a token.
+     *
+     * @return <code>true</code> if and only if there is at least one token
+     * in the string after the current position; <code>false</code> otherwise.
+     */
+    public boolean hasMoreTokens() {
+        if (lookahead != null) {
+            return true;
+        }
+
+        return tokenizer.hasMoreTokens();
+    }
+
+    /**
+     * Returns the next path element from this tokenizer.
+     *
+     * @return the next path element from this tokenizer.
+     *
+     * @exception NoSuchElementException if there are no more elements in this
+     *            tokenizer's path.
+     */
+    public String nextToken() throws NoSuchElementException {
+        String token = null;
+        if (lookahead != null) {
+            token = lookahead;
+            lookahead = null;
+        } else {
+            token = tokenizer.nextToken().trim();
+        }
+
+        if (!onNetWare) {
+            if (token.length() == 1 && Character.isLetter(token.charAt(0))
+                                    && dosStyleFilesystem
+                                    && tokenizer.hasMoreTokens()) {
+                // we are on a dos style system so this path could be a drive
+                // spec. We look at the next token
+                String nextToken = tokenizer.nextToken().trim();
+                if (nextToken.startsWith("\\") || nextToken.startsWith("/")) {
+                    // we know we are on a DOS style platform and the next path
+                    // starts with a slash or backslash, so we know this is a
+                    // drive spec
+                    token += ":" + nextToken;
+                } else {
+                    // store the token just read for next time
+                    lookahead = nextToken;
+                }
+            }
+        } else {
+            // we are on NetWare, tokenizing is handled a little differently,
+            // due to the fact that NetWare has multiple-character volume names.
+            if (token.equals(File.pathSeparator) || token.equals(":")) {
+                // ignore ";" and get the next token
+                token = tokenizer.nextToken().trim();
+            }
+
+            if (tokenizer.hasMoreTokens()) {
+                // this path could be a drive spec, so look at the next token
+                String nextToken = tokenizer.nextToken().trim();
+
+                // make sure we aren't going to get the path separator next
+                if (!nextToken.equals(File.pathSeparator)) {
+                    if (nextToken.equals(":")) {
+                        if (!token.startsWith("/") && !token.startsWith("\\")
+                            && !token.startsWith(".")
+                            && !token.startsWith("..")) {
+                            // it indeed is a drive spec, get the next bit
+                            String oneMore = tokenizer.nextToken().trim();
+                            if (!oneMore.equals(File.pathSeparator)) {
+                                token += ":" + oneMore;
+                            } else {
+                                token += ":";
+                                lookahead = oneMore;
+                            }
+                        }
+                        // implicit else: ignore the ':' since we have either a
+                        // UNIX or a relative path
+                    } else {
+                        // store the token just read for next time
+                        lookahead = nextToken;
+                    }
+                }
+            }
+        }
+        return token;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/Project.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/Project.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/Project.java	(revision 16139)
@@ -0,0 +1,2168 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.EOFException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Properties;
+import java.util.Stack;
+import java.util.Vector;
+import java.util.Set;
+import java.util.HashSet;
+import org.apache.tools.ant.input.DefaultInputHandler;
+import org.apache.tools.ant.input.InputHandler;
+import org.apache.tools.ant.loader.AntClassLoader2;
+import org.apache.tools.ant.helper.DefaultExecutor;
+import org.apache.tools.ant.types.FilterSet;
+import org.apache.tools.ant.types.FilterSetCollection;
+import org.apache.tools.ant.types.Description;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+
+/**
+ * Central representation of an Ant project. This class defines an
+ * Ant project with all of its targets, tasks and various other
+ * properties. It also provides the mechanism to kick off a build using
+ * a particular target name.
+ * <p>
+ * This class also encapsulates methods which allow files to be referred
+ * to using abstract path names which are translated to native system
+ * file paths at runtime.
+ *
+ */
+
+public class Project {
+    /** Message priority of &quot;error&quot;. */
+    public static final int MSG_ERR = 0;
+    /** Message priority of &quot;warning&quot;. */
+    public static final int MSG_WARN = 1;
+    /** Message priority of &quot;information&quot;. */
+    public static final int MSG_INFO = 2;
+    /** Message priority of &quot;verbose&quot;. */
+    public static final int MSG_VERBOSE = 3;
+    /** Message priority of &quot;debug&quot;. */
+    public static final int MSG_DEBUG = 4;
+
+    /**
+     * Constant for the &quot;visiting&quot; state, used when
+     * traversing a DFS of target dependencies.
+     */
+    private static final String VISITING = "VISITING";
+    /**
+     * Constant for the &quot;visited&quot; state, used when
+     * traversing a DFS of target dependencies.
+     */
+    private static final String VISITED = "VISITED";
+
+    /**
+     * Version constant for Java 1.0 .
+     *
+     * @deprecated Use {@link JavaEnvUtils#JAVA_1_0} instead.
+     */
+    public static final String JAVA_1_0 = JavaEnvUtils.JAVA_1_0;
+    /**
+     * Version constant for Java 1.1 .
+     *
+     * @deprecated Use {@link JavaEnvUtils#JAVA_1_1} instead.
+     */
+    public static final String JAVA_1_1 = JavaEnvUtils.JAVA_1_1;
+    /**
+     * Version constant for Java 1.2 .
+     *
+     * @deprecated Use {@link JavaEnvUtils#JAVA_1_2} instead.
+     */
+    public static final String JAVA_1_2 = JavaEnvUtils.JAVA_1_2;
+    /**
+     * Version constant for Java 1.3 .
+     *
+     * @deprecated Use {@link JavaEnvUtils#JAVA_1_3} instead.
+     */
+    public static final String JAVA_1_3 = JavaEnvUtils.JAVA_1_3;
+    /**
+     * Version constant for Java 1.4 .
+     *
+     * @deprecated Use {@link JavaEnvUtils#JAVA_1_4} instead.
+     */
+    public static final String JAVA_1_4 = JavaEnvUtils.JAVA_1_4;
+
+    /** Default filter start token. */
+    public static final String TOKEN_START = FilterSet.DEFAULT_TOKEN_START;
+    /** Default filter end token. */
+    public static final String TOKEN_END = FilterSet.DEFAULT_TOKEN_END;
+
+    /** Instance of a utility class to use for file operations. */
+    private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+    /** Name of this project. */
+    private String name;
+    /** Description for this project (if any). */
+    private String description;
+
+
+    /** Map of references within the project (paths etc) (String to Object). */
+    private Hashtable references = new AntRefTable(this);
+
+    /** Name of the project's default target. */
+    private String defaultTarget;
+
+    /** Map from target names to targets (String to Target). */
+    private Hashtable targets = new Hashtable();
+    /** Set of global filters. */
+    private FilterSet globalFilterSet = new FilterSet();
+    {
+        // Initialize the globalFileSet's project
+        globalFilterSet.setProject(this);
+    }
+
+    /**
+     * Wrapper around globalFilterSet. This collection only ever
+     * contains one FilterSet, but the wrapper is needed in order to
+     * make it easier to use the FileUtils interface.
+     */
+    private FilterSetCollection globalFilters
+        = new FilterSetCollection(globalFilterSet);
+
+    /** Project base directory. */
+    private File baseDir;
+
+    /** List of listeners to notify of build events. */
+    private Vector listeners = new Vector();
+
+    /**
+     * The Ant core classloader--may be <code>null</code> if using
+     * parent classloader.
+     */
+    private ClassLoader coreLoader = null;
+
+    /** Records the latest task to be executed on a thread (Thread to Task). */
+    private Hashtable threadTasks = new Hashtable();
+
+    /** Records the latest task to be executed on a thread Group. */
+    private Hashtable threadGroupTasks = new Hashtable();
+
+    /**
+     * Called to handle any input requests.
+     */
+    private InputHandler inputHandler = null;
+
+    /**
+     * The default input stream used to read any input.
+     */
+    private InputStream defaultInputStream = null;
+
+    /**
+     * Keep going flag.
+     */
+    private boolean keepGoingMode = false;
+
+    /**
+     * Flag which catches Listeners which try to use System.out or System.err .
+     */
+    private boolean loggingMessage = false;
+
+    /**
+     * Set the input handler.
+     *
+     * @param handler the InputHandler instance to use for gathering input.
+     */
+    public void setInputHandler(InputHandler handler) {
+        inputHandler = handler;
+    }
+
+    /**
+     * Set the default System input stream. Normally this stream is set to
+     * System.in. This inputStream is used when no task input redirection is
+     * being performed.
+     *
+     * @param defaultInputStream the default input stream to use when input
+     *        is requested.
+     * @since Ant 1.6
+     */
+    public void setDefaultInputStream(InputStream defaultInputStream) {
+        this.defaultInputStream = defaultInputStream;
+    }
+
+    /**
+     * Get this project's input stream.
+     *
+     * @return the InputStream instance in use by this Project instance to
+     * read input.
+     */
+    public InputStream getDefaultInputStream() {
+        return defaultInputStream;
+    }
+
+    /**
+     * Retrieve the current input handler.
+     *
+     * @return the InputHandler instance currently in place for the project
+     *         instance.
+     */
+    public InputHandler getInputHandler() {
+        return inputHandler;
+    }
+
+    /**
+     * Create a new Ant project.
+     */
+    public Project() {
+        inputHandler = new DefaultInputHandler();
+    }
+
+    /**
+     * Init a sub project--used by taskdefs.Ant .
+     * @param subProject the subproject to initialize.
+     */
+    public void initSubProject(Project subProject) {
+        ComponentHelper.getComponentHelper(subProject)
+            .initSubProject(ComponentHelper.getComponentHelper(this));
+        subProject.setKeepGoingMode(this.isKeepGoingMode());
+        subProject.setExecutor(getExecutor().getSubProjectExecutor());
+    }
+
+    /**
+     * Initialise the project.
+     *
+     * This involves setting the default task definitions and loading the
+     * system properties.
+     *
+     * @exception BuildException if the default task list cannot be loaded.
+     */
+    public void init() throws BuildException {
+        setJavaVersionProperty();
+
+        ComponentHelper.getComponentHelper(this).initDefaultDefinitions();
+
+        setSystemProperties();
+    }
+
+    /**
+     * Factory method to create a class loader for loading classes from
+     * a given path.
+     *
+     * @param path the path from which classes are to be loaded.
+     *
+     * @return an appropriate classloader.
+     */
+    public AntClassLoader createClassLoader(Path path) {
+        AntClassLoader loader = new AntClassLoader2();
+        loader.setProject(this);
+        loader.setClassPath(path);
+        return loader;
+    }
+
+    /**
+     * Set the core classloader for the project. If a <code>null</code>
+     * classloader is specified, the parent classloader should be used.
+     *
+     * @param coreLoader The classloader to use for the project.
+     *                   May be <code>null</code>.
+     */
+    public void setCoreLoader(ClassLoader coreLoader) {
+        this.coreLoader = coreLoader;
+    }
+
+    /**
+     * Return the core classloader to use for this project.
+     * This may be <code>null</code>, indicating that
+     * the parent classloader should be used.
+     *
+     * @return the core classloader to use for this project.
+     *
+     */
+    public ClassLoader getCoreLoader() {
+        return coreLoader;
+    }
+
+    /**
+     * Add a build listener to the list. This listener will
+     * be notified of build events for this project.
+     *
+     * @param listener The listener to add to the list.
+     *                 Must not be <code>null</code>.
+     */
+    public synchronized void addBuildListener(BuildListener listener) {
+        // create a new Vector to avoid ConcurrentModificationExc when
+        // the listeners get added/removed while we are in fire
+        Vector newListeners = getBuildListeners();
+        newListeners.addElement(listener);
+        listeners = newListeners;
+    }
+
+    /**
+     * Remove a build listener from the list. This listener
+     * will no longer be notified of build events for this project.
+     *
+     * @param listener The listener to remove from the list.
+     *                 Should not be <code>null</code>.
+     */
+    public synchronized void removeBuildListener(BuildListener listener) {
+        // create a new Vector to avoid ConcurrentModificationExc when
+        // the listeners get added/removed while we are in fire
+        Vector newListeners = getBuildListeners();
+        newListeners.removeElement(listener);
+        listeners = newListeners;
+    }
+
+    /**
+     * Return a copy of the list of build listeners for the project.
+     *
+     * @return a list of build listeners for the project
+     */
+    public Vector getBuildListeners() {
+        return (Vector) listeners.clone();
+    }
+
+    /**
+     * Write a message to the log with the default log level
+     * of MSG_INFO .
+     * @param message The text to log. Should not be <code>null</code>.
+     */
+
+    public void log(String message) {
+        log(message, MSG_INFO);
+    }
+
+    /**
+     * Write a project level message to the log with the given log level.
+     * @param message The text to log. Should not be <code>null</code>.
+     * @param msgLevel The log priority level to use.
+     */
+    public void log(String message, int msgLevel) {
+        fireMessageLogged(this, message, msgLevel);
+    }
+
+    /**
+     * Write a task level message to the log with the given log level.
+     * @param task The task to use in the log. Must not be <code>null</code>.
+     * @param message The text to log. Should not be <code>null</code>.
+     * @param msgLevel The log priority level to use.
+     */
+    public void log(Task task, String message, int msgLevel) {
+        fireMessageLogged(task, message, msgLevel);
+    }
+
+    /**
+     * Write a target level message to the log with the given log level.
+     * @param target The target to use in the log.
+     *               Must not be <code>null</code>.
+     * @param message The text to log. Should not be <code>null</code>.
+     * @param msgLevel The log priority level to use.
+     */
+    public void log(Target target, String message, int msgLevel) {
+        fireMessageLogged(target, message, msgLevel);
+    }
+
+    /**
+     * Return the set of global filters.
+     *
+     * @return the set of global filters.
+     */
+    public FilterSet getGlobalFilterSet() {
+        return globalFilterSet;
+    }
+
+    /**
+     * Set a property. Any existing property of the same name
+     * is overwritten, unless it is a user property.
+     * @param name The name of property to set.
+     *             Must not be <code>null</code>.
+     * @param value The new value of the property.
+     *              Must not be <code>null</code>.
+     */
+    public void setProperty(String name, String value) {
+        PropertyHelper.getPropertyHelper(this).
+                setProperty(null, name, value, true);
+    }
+
+    /**
+     * Set a property if no value currently exists. If the property
+     * exists already, a message is logged and the method returns with
+     * no other effect.
+     *
+     * @param name The name of property to set.
+     *             Must not be <code>null</code>.
+     * @param value The new value of the property.
+     *              Must not be <code>null</code>.
+     * @since 1.5
+     */
+    public void setNewProperty(String name, String value) {
+        PropertyHelper.getPropertyHelper(this).setNewProperty(null, name,
+                                                              value);
+    }
+
+    /**
+     * Set a user property, which cannot be overwritten by
+     * set/unset property calls. Any previous value is overwritten.
+     * @param name The name of property to set.
+     *             Must not be <code>null</code>.
+     * @param value The new value of the property.
+     *              Must not be <code>null</code>.
+     * @see #setProperty(String,String)
+     */
+    public void setUserProperty(String name, String value) {
+        PropertyHelper.getPropertyHelper(this).setUserProperty(null, name,
+                                                               value);
+    }
+
+    /**
+     * Set a user property, which cannot be overwritten by set/unset
+     * property calls. Any previous value is overwritten. Also marks
+     * these properties as properties that have not come from the
+     * command line.
+     *
+     * @param name The name of property to set.
+     *             Must not be <code>null</code>.
+     * @param value The new value of the property.
+     *              Must not be <code>null</code>.
+     * @see #setProperty(String,String)
+     */
+    public void setInheritedProperty(String name, String value) {
+        PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
+        ph.setInheritedProperty(null, name, value);
+    }
+
+    /**
+     * Set a property unless it is already defined as a user property
+     * (in which case the method returns silently).
+     *
+     * @param name The name of the property.
+     *             Must not be <code>null</code>.
+     * @param value The property value. Must not be <code>null</code>.
+     */
+    private void setPropertyInternal(String name, String value) {
+        PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
+        ph.setProperty(null, name, value, false);
+    }
+
+    /**
+     * Return the value of a property, if it is set.
+     *
+     * @param name The name of the property.
+     *             May be <code>null</code>, in which case
+     *             the return value is also <code>null</code>.
+     * @return the property value, or <code>null</code> for no match
+     *         or if a <code>null</code> name is provided.
+     */
+    public String getProperty(String name) {
+        PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
+        return (String) ph.getProperty(null, name);
+    }
+
+    /**
+     * Replace ${} style constructions in the given value with the
+     * string value of the corresponding data types.
+     *
+     * @param value The string to be scanned for property references.
+     *              May be <code>null</code>.
+     *
+     * @return the given string with embedded property names replaced
+     *         by values, or <code>null</code> if the given string is
+     *         <code>null</code>.
+     *
+     * @exception BuildException if the given value has an unclosed
+     *                           property name, e.g. <code>${xxx</code>.
+     */
+    public String replaceProperties(String value)
+        throws BuildException {
+        PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
+        return ph.replaceProperties(null, value, null);
+    }
+
+    /**
+     * Return the value of a user property, if it is set.
+     *
+     * @param name The name of the property.
+     *             May be <code>null</code>, in which case
+     *             the return value is also <code>null</code>.
+     * @return the property value, or <code>null</code> for no match
+     *         or if a <code>null</code> name is provided.
+     */
+     public String getUserProperty(String name) {
+        PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
+        return (String) ph.getUserProperty(null, name);
+    }
+
+    /**
+     * Return a copy of the properties table.
+     * @return a hashtable containing all properties
+     *         (including user properties).
+     */
+    public Hashtable getProperties() {
+        PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
+        return ph.getProperties();
+    }
+
+    /**
+     * Return a copy of the user property hashtable.
+     * @return a hashtable containing just the user properties.
+     */
+    public Hashtable getUserProperties() {
+        PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
+        return ph.getUserProperties();
+    }
+
+    /**
+     * Copy all user properties that have been set on the command
+     * line or a GUI tool from this instance to the Project instance
+     * given as the argument.
+     *
+     * <p>To copy all &quot;user&quot; properties, you will also have to call
+     * {@link #copyInheritedProperties copyInheritedProperties}.</p>
+     *
+     * @param other the project to copy the properties to.  Must not be null.
+     *
+     * @since Ant 1.5
+     */
+    public void copyUserProperties(Project other) {
+        PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
+        ph.copyUserProperties(other);
+    }
+
+    /**
+     * Copy all user properties that have not been set on the
+     * command line or a GUI tool from this instance to the Project
+     * instance given as the argument.
+     *
+     * <p>To copy all &quot;user&quot; properties, you will also have to call
+     * {@link #copyUserProperties copyUserProperties}.</p>
+     *
+     * @param other the project to copy the properties to.  Must not be null.
+     *
+     * @since Ant 1.5
+     */
+    public void copyInheritedProperties(Project other) {
+        PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
+        ph.copyInheritedProperties(other);
+    }
+
+    /**
+     * Set the default target of the project.
+     *
+     * @param defaultTarget The name of the default target for this project.
+     *                      May be <code>null</code>, indicating that there is
+     *                      no default target.
+     *
+     * @deprecated use setDefault
+     * @see #setDefault(String)
+     */
+    public void setDefaultTarget(String defaultTarget) {
+        this.defaultTarget = defaultTarget;
+    }
+
+    /**
+     * Return the name of the default target of the project.
+     * @return name of the default target or
+     *         <code>null</code> if no default has been set.
+     */
+    public String getDefaultTarget() {
+        return defaultTarget;
+    }
+
+    /**
+     * Set the default target of the project.
+     *
+     * @param defaultTarget The name of the default target for this project.
+     *                      May be <code>null</code>, indicating that there is
+     *                      no default target.
+     */
+    public void setDefault(String defaultTarget) {
+        this.defaultTarget = defaultTarget;
+    }
+
+    /**
+     * Set the name of the project, also setting the user
+     * property <code>ant.project.name</code>.
+     *
+     * @param name The name of the project.
+     *             Must not be <code>null</code>.
+     */
+    public void setName(String name) {
+        setUserProperty("ant.project.name",  name);
+        this.name = name;
+    }
+
+    /**
+     * Return the project name, if one has been set.
+     *
+     * @return the project name, or <code>null</code> if it hasn't been set.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Set the project description.
+     *
+     * @param description The description of the project.
+     *                    May be <code>null</code>.
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * Return the project description, if one has been set.
+     *
+     * @return the project description, or <code>null</code> if it hasn't
+     *         been set.
+     */
+    public String getDescription() {
+        if (description == null) {
+            description = Description.getDescription(this);
+        }
+        return description;
+    }
+
+    /**
+     * Add a filter to the set of global filters.
+     *
+     * @param token The token to filter.
+     *              Must not be <code>null</code>.
+     * @param value The replacement value.
+     *              Must not be <code>null</code>.
+     * @deprecated Use getGlobalFilterSet().addFilter(token,value)
+     *
+     * @see #getGlobalFilterSet()
+     * @see FilterSet#addFilter(String,String)
+     */
+    public void addFilter(String token, String value) {
+        if (token == null) {
+            return;
+        }
+        globalFilterSet.addFilter(new FilterSet.Filter(token, value));
+    }
+
+    /**
+     * Return a hashtable of global filters, mapping tokens to values.
+     *
+     * @return a hashtable of global filters, mapping tokens to values
+     *         (String to String).
+     *
+     * @deprecated Use getGlobalFilterSet().getFilterHash()
+     *
+     * @see #getGlobalFilterSet()
+     * @see FilterSet#getFilterHash()
+     */
+    public Hashtable getFilters() {
+        // we need to build the hashtable dynamically
+        return globalFilterSet.getFilterHash();
+    }
+
+    /**
+     * Set the base directory for the project, checking that
+     * the given filename exists and is a directory.
+     *
+     * @param baseD The project base directory.
+     *              Must not be <code>null</code>.
+     *
+     * @exception BuildException if the directory if invalid.
+     */
+    public void setBasedir(String baseD) throws BuildException {
+        setBaseDir(new File(baseD));
+    }
+
+    /**
+     * Set the base directory for the project, checking that
+     * the given file exists and is a directory.
+     *
+     * @param baseDir The project base directory.
+     *                Must not be <code>null</code>.
+     * @exception BuildException if the specified file doesn't exist or
+     *                           isn't a directory.
+     */
+    public void setBaseDir(File baseDir) throws BuildException {
+        baseDir = FILE_UTILS.normalize(baseDir.getAbsolutePath());
+        if (!baseDir.exists()) {
+            throw new BuildException("Basedir " + baseDir.getAbsolutePath()
+                + " does not exist");
+        }
+        if (!baseDir.isDirectory()) {
+            throw new BuildException("Basedir " + baseDir.getAbsolutePath()
+                + " is not a directory");
+        }
+        this.baseDir = baseDir;
+        setPropertyInternal("basedir", this.baseDir.getPath());
+        String msg = "Project base dir set to: " + this.baseDir;
+         log(msg, MSG_VERBOSE);
+    }
+
+    /**
+     * Return the base directory of the project as a file object.
+     *
+     * @return the project base directory, or <code>null</code> if the
+     *         base directory has not been successfully set to a valid value.
+     */
+    public File getBaseDir() {
+        if (baseDir == null) {
+            try {
+                setBasedir(".");
+            } catch (BuildException ex) {
+                ex.printStackTrace();
+            }
+        }
+        return baseDir;
+    }
+
+    /**
+     * Set &quot;keep-going&quot; mode. In this mode Ant will try to execute
+     * as many targets as possible. All targets that do not depend
+     * on failed target(s) will be executed.  If the keepGoing settor/getter
+     * methods are used in conjunction with the <code>ant.executor.class</code>
+     * property, they will have no effect.
+     * @param keepGoingMode &quot;keep-going&quot; mode
+     * @since Ant 1.6
+     */
+    public void setKeepGoingMode(boolean keepGoingMode) {
+        this.keepGoingMode = keepGoingMode;
+    }
+
+    /**
+     * Return the keep-going mode.  If the keepGoing settor/getter
+     * methods are used in conjunction with the <code>ant.executor.class</code>
+     * property, they will have no effect.
+     * @return &quot;keep-going&quot; mode
+     * @since Ant 1.6
+     */
+    public boolean isKeepGoingMode() {
+        return this.keepGoingMode;
+    }
+
+    /**
+     * Return the version of Java this class is running under.
+     * @return the version of Java as a String, e.g. "1.1" .
+     * @see org.apache.tools.ant.util.JavaEnvUtils#getJavaVersion
+     * @deprecated use org.apache.tools.ant.util.JavaEnvUtils instead
+     */
+    public static String getJavaVersion() {
+        return JavaEnvUtils.getJavaVersion();
+    }
+
+    /**
+     * Set the <code>ant.java.version</code> property and tests for
+     * unsupported JVM versions. If the version is supported,
+     * verbose log messages are generated to record the Java version
+     * and operating system name.
+     *
+     * @exception BuildException if this Java version is not supported.
+     *
+     * @see org.apache.tools.ant.util.JavaEnvUtils#getJavaVersion
+     */
+    public void setJavaVersionProperty() throws BuildException {
+        String javaVersion = JavaEnvUtils.getJavaVersion();
+        setPropertyInternal("ant.java.version", javaVersion);
+
+        // sanity check
+        if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_0)) {
+            throw new BuildException("Ant cannot work on Java 1.0");
+        }
+        log("Detected Java version: " + javaVersion + " in: "
+            + System.getProperty("java.home"), MSG_VERBOSE);
+
+        log("Detected OS: " + System.getProperty("os.name"), MSG_VERBOSE);
+    }
+
+    /**
+     * Add all system properties which aren't already defined as
+     * user properties to the project properties.
+     */
+    public void setSystemProperties() {
+        Properties systemP = System.getProperties();
+        Enumeration e = systemP.propertyNames();
+        while (e.hasMoreElements()) {
+            String propertyName = (String) e.nextElement();
+            String value = systemP.getProperty(propertyName);
+            this.setPropertyInternal(propertyName, value);
+        }
+    }
+
+    /**
+     * Add a new task definition to the project.
+     * Attempting to override an existing definition with an
+     * equivalent one (i.e. with the same classname) results in
+     * a verbose log message. Attempting to override an existing definition
+     * with a different one results in a warning log message and
+     * invalidates any tasks which have already been created with the
+     * old definition.
+     *
+     * @param taskName The name of the task to add.
+     *                 Must not be <code>null</code>.
+     * @param taskClass The full name of the class implementing the task.
+     *                  Must not be <code>null</code>.
+     *
+     * @exception BuildException if the class is unsuitable for being an Ant
+     *                           task. An error level message is logged before
+     *                           this exception is thrown.
+     *
+     * @see #checkTaskClass(Class)
+     */
+    public void addTaskDefinition(String taskName, Class taskClass)
+         throws BuildException {
+        ComponentHelper.getComponentHelper(this).addTaskDefinition(taskName,
+                taskClass);
+    }
+
+    /**
+     * Check whether or not a class is suitable for serving as Ant task.
+     * Ant task implementation classes must be public, concrete, and have
+     * a no-arg constructor.
+     *
+     * @param taskClass The class to be checked.
+     *                  Must not be <code>null</code>.
+     *
+     * @exception BuildException if the class is unsuitable for being an Ant
+     *                           task. An error level message is logged before
+     *                           this exception is thrown.
+     */
+    public void checkTaskClass(final Class taskClass) throws BuildException {
+        ComponentHelper.getComponentHelper(this).checkTaskClass(taskClass);
+
+        if (!Modifier.isPublic(taskClass.getModifiers())) {
+            final String message = taskClass + " is not public";
+            log(message, Project.MSG_ERR);
+            throw new BuildException(message);
+        }
+        if (Modifier.isAbstract(taskClass.getModifiers())) {
+            final String message = taskClass + " is abstract";
+            log(message, Project.MSG_ERR);
+            throw new BuildException(message);
+        }
+        try {
+            taskClass.getConstructor((Class[]) null);
+            // don't have to check for public, since
+            // getConstructor finds public constructors only.
+        } catch (NoSuchMethodException e) {
+            final String message = "No public no-arg constructor in "
+                + taskClass;
+            log(message, Project.MSG_ERR);
+            throw new BuildException(message);
+        } catch (LinkageError e) {
+            String message = "Could not load " + taskClass + ": " + e;
+            log(message, Project.MSG_ERR);
+            throw new BuildException(message, e);
+        }
+        if (!Task.class.isAssignableFrom(taskClass)) {
+            TaskAdapter.checkTaskClass(taskClass, this);
+        }
+    }
+
+    /**
+     * Return the current task definition hashtable. The returned hashtable is
+     * &quot;live&quot; and so should not be modified.
+     *
+     * @return a map of from task name to implementing class
+     *         (String to Class).
+     */
+    public Hashtable getTaskDefinitions() {
+        return ComponentHelper.getComponentHelper(this).getTaskDefinitions();
+    }
+
+    /**
+     * Add a new datatype definition.
+     * Attempting to override an existing definition with an
+     * equivalent one (i.e. with the same classname) results in
+     * a verbose log message. Attempting to override an existing definition
+     * with a different one results in a warning log message, but the
+     * definition is changed.
+     *
+     * @param typeName The name of the datatype.
+     *                 Must not be <code>null</code>.
+     * @param typeClass The full name of the class implementing the datatype.
+     *                  Must not be <code>null</code>.
+     */
+    public void addDataTypeDefinition(String typeName, Class typeClass) {
+        ComponentHelper.getComponentHelper(this).addDataTypeDefinition(typeName,
+                typeClass);
+    }
+
+    /**
+     * Return the current datatype definition hashtable. The returned
+     * hashtable is &quot;live&quot; and so should not be modified.
+     *
+     * @return a map of from datatype name to implementing class
+     *         (String to Class).
+     */
+    public Hashtable getDataTypeDefinitions() {
+        return ComponentHelper.getComponentHelper(this).getDataTypeDefinitions();
+    }
+
+    /**
+     * Add a <em>new</em> target to the project.
+     *
+     * @param target The target to be added to the project.
+     *               Must not be <code>null</code>.
+     *
+     * @exception BuildException if the target already exists in the project
+     *
+     * @see Project#addOrReplaceTarget
+     */
+    public void addTarget(Target target) throws BuildException {
+        addTarget(target.getName(), target);
+    }
+
+    /**
+     * Add a <em>new</em> target to the project.
+     *
+     * @param targetName The name to use for the target.
+     *             Must not be <code>null</code>.
+     * @param target The target to be added to the project.
+     *               Must not be <code>null</code>.
+     *
+     * @exception BuildException if the target already exists in the project.
+     *
+     * @see Project#addOrReplaceTarget
+     */
+     public void addTarget(String targetName, Target target)
+         throws BuildException {
+         if (targets.get(targetName) != null) {
+             throw new BuildException("Duplicate target: `" + targetName + "'");
+         }
+         addOrReplaceTarget(targetName, target);
+     }
+
+    /**
+     * Add a target to the project, or replaces one with the same
+     * name.
+     *
+     * @param target The target to be added or replaced in the project.
+     *               Must not be <code>null</code>.
+     */
+    public void addOrReplaceTarget(Target target) {
+        addOrReplaceTarget(target.getName(), target);
+    }
+
+    /**
+     * Add a target to the project, or replaces one with the same
+     * name.
+     *
+     * @param targetName The name to use for the target.
+     *                   Must not be <code>null</code>.
+     * @param target The target to be added or replaced in the project.
+     *               Must not be <code>null</code>.
+     */
+    public void addOrReplaceTarget(String targetName, Target target) {
+        String msg = " +Target: " + targetName;
+        log(msg, MSG_DEBUG);
+        target.setProject(this);
+        targets.put(targetName, target);
+    }
+
+    /**
+     * Return the hashtable of targets. The returned hashtable
+     * is &quot;live&quot; and so should not be modified.
+     * @return a map from name to target (String to Target).
+     */
+    public Hashtable getTargets() {
+        return targets;
+    }
+
+    /**
+     * Create a new instance of a task, adding it to a list of
+     * created tasks for later invalidation. This causes all tasks
+     * to be remembered until the containing project is removed
+     * @param taskType The name of the task to create an instance of.
+     *                 Must not be <code>null</code>.
+     *
+     * @return an instance of the specified task, or <code>null</code> if
+     *         the task name is not recognised.
+     *
+     * @exception BuildException if the task name is recognised but task
+     *                           creation fails.
+     */
+    public Task createTask(String taskType) throws BuildException {
+        return ComponentHelper.getComponentHelper(this).createTask(taskType);
+    }
+
+    /**
+     * Create a new instance of a data type.
+     *
+     * @param typeName The name of the data type to create an instance of.
+     *                 Must not be <code>null</code>.
+     *
+     * @return an instance of the specified data type, or <code>null</code> if
+     *         the data type name is not recognised.
+     *
+     * @exception BuildException if the data type name is recognised but
+     *                           instance creation fails.
+     */
+    public Object createDataType(String typeName) throws BuildException {
+        return ComponentHelper.getComponentHelper(this).createDataType(typeName);
+    }
+
+    /**
+     * Set the Executor instance for this Project.
+     * @param e the Executor to use.
+     */
+    public void setExecutor(Executor e) {
+        addReference("ant.executor", e);
+    }
+
+    /**
+     * Get this Project's Executor (setting it if necessary).
+     * @return an Executor instance.
+     */
+    public Executor getExecutor() {
+        Object o = getReference("ant.executor");
+        if (o == null) {
+            String classname = getProperty("ant.executor.class");
+            if (classname == null) {
+                classname = DefaultExecutor.class.getName();
+            }
+            log("Attempting to create object of type " + classname, MSG_DEBUG);
+            try {
+                o = Class.forName(classname, true, coreLoader).newInstance();
+            } catch (ClassNotFoundException seaEnEfEx) {
+                //try the current classloader
+                try {
+                    o = Class.forName(classname).newInstance();
+                } catch (Exception ex) {
+                    log(ex.toString(), MSG_ERR);
+                }
+            } catch (Exception ex) {
+                log(ex.toString(), MSG_ERR);
+            }
+            if (o == null) {
+                throw new BuildException(
+                    "Unable to obtain a Target Executor instance.");
+            }
+            setExecutor((Executor) o);
+        }
+        return (Executor) o;
+    }
+
+    /**
+     * Execute the specified sequence of targets, and the targets
+     * they depend on.
+     *
+     * @param names A vector of target name strings to execute.
+     *              Must not be <code>null</code>.
+     *
+     * @exception BuildException if the build failed.
+     */
+    public void executeTargets(Vector names) throws BuildException {
+        getExecutor().executeTargets(this,
+            (String[]) (names.toArray(new String[names.size()])));
+    }
+
+    /**
+     * Demultiplex output so that each task receives the appropriate
+     * messages. If the current thread is not currently executing a task,
+     * the message is logged directly.
+     *
+     * @param output Message to handle. Should not be <code>null</code>.
+     * @param isWarning Whether the text represents an warning (<code>true</code>)
+     *        or information (<code>false</code>).
+     */
+    public void demuxOutput(String output, boolean isWarning) {
+        Task task = getThreadTask(Thread.currentThread());
+        if (task == null) {
+            log(output, isWarning ? MSG_WARN : MSG_INFO);
+        } else {
+            if (isWarning) {
+                task.handleErrorOutput(output);
+            } else {
+                task.handleOutput(output);
+            }
+        }
+    }
+
+    /**
+     * Read data from the default input stream. If no default has been
+     * specified, System.in is used.
+     *
+     * @param buffer the buffer into which data is to be read.
+     * @param offset the offset into the buffer at which data is stored.
+     * @param length the amount of data to read.
+     *
+     * @return the number of bytes read.
+     *
+     * @exception IOException if the data cannot be read.
+     * @since Ant 1.6
+     */
+    public int defaultInput(byte[] buffer, int offset, int length)
+        throws IOException {
+        if (defaultInputStream != null) {
+            System.out.flush();
+            return defaultInputStream.read(buffer, offset, length);
+        } else {
+            throw new EOFException("No input provided for project");
+        }
+    }
+
+    /**
+     * Demux an input request to the correct task.
+     *
+     * @param buffer the buffer into which data is to be read.
+     * @param offset the offset into the buffer at which data is stored.
+     * @param length the amount of data to read.
+     *
+     * @return the number of bytes read.
+     *
+     * @exception IOException if the data cannot be read.
+     * @since Ant 1.6
+     */
+    public int demuxInput(byte[] buffer, int offset, int length)
+        throws IOException {
+        Task task = getThreadTask(Thread.currentThread());
+        if (task == null) {
+            return defaultInput(buffer, offset, length);
+        } else {
+            return task.handleInput(buffer, offset, length);
+        }
+    }
+
+    /**
+     * Demultiplex flush operations so that each task receives the appropriate
+     * messages. If the current thread is not currently executing a task,
+     * the message is logged directly.
+     *
+     * @since Ant 1.5.2
+     *
+     * @param output Message to handle. Should not be <code>null</code>.
+     * @param isError Whether the text represents an error (<code>true</code>)
+     *        or information (<code>false</code>).
+     */
+    public void demuxFlush(String output, boolean isError) {
+        Task task = getThreadTask(Thread.currentThread());
+        if (task == null) {
+            fireMessageLogged(this, output, isError ? MSG_ERR : MSG_INFO);
+        } else {
+            if (isError) {
+                task.handleErrorFlush(output);
+            } else {
+                task.handleFlush(output);
+            }
+        }
+    }
+
+    /**
+     * Execute the specified target and any targets it depends on.
+     *
+     * @param targetName The name of the target to execute.
+     *                   Must not be <code>null</code>.
+     *
+     * @exception BuildException if the build failed.
+     */
+    public void executeTarget(String targetName) throws BuildException {
+
+        // sanity check ourselves, if we've been asked to build nothing
+        // then we should complain
+
+        if (targetName == null) {
+            String msg = "No target specified";
+            throw new BuildException(msg);
+        }
+
+        // Sort and run the dependency tree.
+        // Sorting checks if all the targets (and dependencies)
+        // exist, and if there is any cycle in the dependency
+        // graph.
+        executeSortedTargets(topoSort(targetName, targets, false));
+    }
+
+    /**
+     * Execute a <code>Vector</code> of sorted targets.
+     * @param sortedTargets   the aforementioned <code>Vector</code>.
+     * @throws BuildException on error.
+     */
+    public void executeSortedTargets(Vector sortedTargets)
+        throws BuildException {
+        Set succeededTargets = new HashSet();
+        BuildException buildException = null; // first build exception
+        for (Enumeration iter = sortedTargets.elements();
+             iter.hasMoreElements();) {
+            Target curtarget = (Target) iter.nextElement();
+            boolean canExecute = true;
+            for (Enumeration depIter = curtarget.getDependencies();
+                 depIter.hasMoreElements();) {
+                String dependencyName = ((String) depIter.nextElement());
+                if (!succeededTargets.contains(dependencyName)) {
+                    canExecute = false;
+                    log(curtarget,
+                        "Cannot execute '" + curtarget.getName() + "' - '"
+                        + dependencyName + "' failed or was not executed.",
+                        MSG_ERR);
+                    break;
+                }
+            }
+            if (canExecute) {
+                Throwable thrownException = null;
+                try {
+                    curtarget.performTasks();
+                    succeededTargets.add(curtarget.getName());
+                } catch (RuntimeException ex) {
+                    if (!(keepGoingMode)) {
+                        throw ex; // throw further
+                    }
+                    thrownException = ex;
+                } catch (Throwable ex) {
+                    if (!(keepGoingMode)) {
+                        throw new BuildException(ex);
+                    }
+                    thrownException = ex;
+                }
+                if (thrownException != null) {
+                    if (thrownException instanceof BuildException) {
+                        log(curtarget,
+                            "Target '" + curtarget.getName()
+                            + "' failed with message '"
+                            + thrownException.getMessage() + "'.", MSG_ERR);
+                        // only the first build exception is reported
+                        if (buildException == null) {
+                            buildException = (BuildException) thrownException;
+                        }
+                    } else {
+                        log(curtarget,
+                            "Target '" + curtarget.getName()
+                            + "' failed with message '"
+                            + thrownException.getMessage() + "'.", MSG_ERR);
+                        thrownException.printStackTrace(System.err);
+                        if (buildException == null) {
+                            buildException =
+                                new BuildException(thrownException);
+                        }
+                    }
+                }
+            }
+        }
+        if (buildException != null) {
+            throw buildException;
+        }
+    }
+
+    /**
+     * Return the canonical form of a filename.
+     * <p>
+     * If the specified file name is relative it is resolved
+     * with respect to the given root directory.
+     *
+     * @param fileName The name of the file to resolve.
+     *                 Must not be <code>null</code>.
+     *
+     * @param rootDir  The directory respective to which relative file names
+     *                 are resolved. May be <code>null</code>, in which case
+     *                 the current directory is used.
+     *
+     * @return the resolved File.
+     *
+     * @deprecated
+     */
+    public File resolveFile(String fileName, File rootDir) {
+        return FILE_UTILS.resolveFile(rootDir, fileName);
+    }
+
+    /**
+     * Return the canonical form of a filename.
+     * <p>
+     * If the specified file name is relative it is resolved
+     * with respect to the project's base directory.
+     *
+     * @param fileName The name of the file to resolve.
+     *                 Must not be <code>null</code>.
+     *
+     * @return the resolved File.
+     *
+     */
+    public File resolveFile(String fileName) {
+        return FILE_UTILS.resolveFile(baseDir, fileName);
+    }
+
+    /**
+     * Translate a path into its native (platform specific) format.
+     * <p>
+     * This method uses PathTokenizer to separate the input path
+     * into its components. This handles DOS style paths in a relatively
+     * sensible way. The file separators are then converted to their platform
+     * specific versions.
+     *
+     * @param toProcess The path to be translated.
+     *                  May be <code>null</code>.
+     *
+     * @return the native version of the specified path or
+     *         an empty string if the path is <code>null</code> or empty.
+     *
+     * @see PathTokenizer
+     */
+    public static String translatePath(String toProcess) {
+        if (toProcess == null || toProcess.length() == 0) {
+            return "";
+        }
+        StringBuffer path = new StringBuffer(toProcess.length() + 50);
+        PathTokenizer tokenizer = new PathTokenizer(toProcess);
+        while (tokenizer.hasMoreTokens()) {
+            String pathComponent = tokenizer.nextToken();
+            pathComponent = pathComponent.replace('/', File.separatorChar);
+            pathComponent = pathComponent.replace('\\', File.separatorChar);
+            if (path.length() != 0) {
+                path.append(File.pathSeparatorChar);
+            }
+            path.append(pathComponent);
+        }
+        return path.toString();
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a destination.
+     * No filtering is performed.
+     *
+     * @param sourceFile Name of file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile Name of file to copy to.
+     *                 Must not be <code>null</code>.
+     *
+     * @exception IOException if the copying fails.
+     *
+     * @deprecated
+     */
+    public void copyFile(String sourceFile, String destFile)
+          throws IOException {
+        FILE_UTILS.copyFile(sourceFile, destFile);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a destination
+     * specifying if token filtering should be used.
+     *
+     * @param sourceFile Name of file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile Name of file to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filtering Whether or not token filtering should be used during
+     *                  the copy.
+     *
+     * @exception IOException if the copying fails.
+     *
+     * @deprecated
+     */
+    public void copyFile(String sourceFile, String destFile, boolean filtering)
+        throws IOException {
+        FILE_UTILS.copyFile(sourceFile, destFile,
+            filtering ? globalFilters : null);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a
+     * destination specifying if token filtering should be used and if
+     * source files may overwrite newer destination files.
+     *
+     * @param sourceFile Name of file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile Name of file to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filtering Whether or not token filtering should be used during
+     *                  the copy.
+     * @param overwrite Whether or not the destination file should be
+     *                  overwritten if it already exists.
+     *
+     * @exception IOException if the copying fails.
+     *
+     * @deprecated
+     */
+    public void copyFile(String sourceFile, String destFile, boolean filtering,
+                         boolean overwrite) throws IOException {
+        FILE_UTILS.copyFile(sourceFile, destFile,
+            filtering ? globalFilters : null, overwrite);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a
+     * destination specifying if token filtering should be used, if
+     * source files may overwrite newer destination files, and if the
+     * last modified time of the resulting file should be set to
+     * that of the source file.
+     *
+     * @param sourceFile Name of file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile Name of file to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filtering Whether or not token filtering should be used during
+     *                  the copy.
+     * @param overwrite Whether or not the destination file should be
+     *                  overwritten if it already exists.
+     * @param preserveLastModified Whether or not the last modified time of
+     *                             the resulting file should be set to that
+     *                             of the source file.
+     *
+     * @exception IOException if the copying fails.
+     *
+     * @deprecated
+     */
+    public void copyFile(String sourceFile, String destFile, boolean filtering,
+                         boolean overwrite, boolean preserveLastModified)
+        throws IOException {
+        FILE_UTILS.copyFile(sourceFile, destFile,
+            filtering ? globalFilters : null, overwrite, preserveLastModified);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a destination.
+     * No filtering is performed.
+     *
+     * @param sourceFile File to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile File to copy to.
+     *                 Must not be <code>null</code>.
+     *
+     * @exception IOException if the copying fails.
+     *
+     * @deprecated
+     */
+    public void copyFile(File sourceFile, File destFile) throws IOException {
+        FILE_UTILS.copyFile(sourceFile, destFile);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a destination
+     * specifying if token filtering should be used.
+     *
+     * @param sourceFile File to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile File to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filtering Whether or not token filtering should be used during
+     *                  the copy.
+     *
+     * @exception IOException if the copying fails.
+     *
+     * @deprecated
+     */
+    public void copyFile(File sourceFile, File destFile, boolean filtering)
+        throws IOException {
+        FILE_UTILS.copyFile(sourceFile, destFile,
+            filtering ? globalFilters : null);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a
+     * destination specifying if token filtering should be used and if
+     * source files may overwrite newer destination files.
+     *
+     * @param sourceFile File to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile File to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filtering Whether or not token filtering should be used during
+     *                  the copy.
+     * @param overwrite Whether or not the destination file should be
+     *                  overwritten if it already exists.
+     *
+     * @exception IOException if the file cannot be copied.
+     *
+     * @deprecated
+     */
+    public void copyFile(File sourceFile, File destFile, boolean filtering,
+                         boolean overwrite) throws IOException {
+        FILE_UTILS.copyFile(sourceFile, destFile,
+            filtering ? globalFilters : null, overwrite);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a
+     * destination specifying if token filtering should be used, if
+     * source files may overwrite newer destination files, and if the
+     * last modified time of the resulting file should be set to
+     * that of the source file.
+     *
+     * @param sourceFile File to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile File to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filtering Whether or not token filtering should be used during
+     *                  the copy.
+     * @param overwrite Whether or not the destination file should be
+     *                  overwritten if it already exists.
+     * @param preserveLastModified Whether or not the last modified time of
+     *                             the resulting file should be set to that
+     *                             of the source file.
+     *
+     * @exception IOException if the file cannot be copied.
+     *
+     * @deprecated
+     */
+    public void copyFile(File sourceFile, File destFile, boolean filtering,
+                         boolean overwrite, boolean preserveLastModified)
+        throws IOException {
+        FILE_UTILS.copyFile(sourceFile, destFile,
+            filtering ? globalFilters : null, overwrite, preserveLastModified);
+    }
+
+    /**
+     * Call File.setLastModified(long time) on Java above 1.1, and logs
+     * a warning on Java 1.1.
+     *
+     * @param file The file to set the last modified time on.
+     *             Must not be <code>null</code>.
+     *
+     * @param time the required modification time.
+     *
+     * @deprecated
+     *
+     * @exception BuildException if the last modified time cannot be set
+     *                           despite running on a platform with a version
+     *                           above 1.1.
+     */
+    public void setFileLastModified(File file, long time)
+         throws BuildException {
+        FILE_UTILS.setFileLastModified(file, time);
+        log("Setting modification time for " + file, MSG_VERBOSE);
+    }
+
+    /**
+     * Return the boolean equivalent of a string, which is considered
+     * <code>true</code> if either <code>"on"</code>, <code>"true"</code>,
+     * or <code>"yes"</code> is found, ignoring case.
+     *
+     * @param s The string to convert to a boolean value.
+     *
+     * @return <code>true</code> if the given string is <code>"on"</code>,
+     *         <code>"true"</code> or <code>"yes"</code>, or
+     *         <code>false</code> otherwise.
+     */
+    public static boolean toBoolean(String s) {
+        return ("on".equalsIgnoreCase(s)
+                || "true".equalsIgnoreCase(s)
+                || "yes".equalsIgnoreCase(s));
+    }
+
+    /**
+     * Topologically sort a set of targets.  Equivalent to calling
+     * <code>topoSort(new String[] {root}, targets, true)</code>.
+     *
+     * @param root The name of the root target. The sort is created in such
+     *             a way that the sequence of Targets up to the root
+     *             target is the minimum possible such sequence.
+     *             Must not be <code>null</code>.
+     * @param targets A Hashtable mapping names to Targets.
+     *                Must not be <code>null</code>.
+     * @return a Vector of ALL Target objects in sorted order.
+     * @exception BuildException if there is a cyclic dependency among the
+     *                           targets, or if a named target does not exist.
+     */
+    public final Vector topoSort(String root, Hashtable targets)
+        throws BuildException {
+        return topoSort(new String[] {root}, targets, true);
+    }
+
+    /**
+     * Topologically sort a set of targets.  Equivalent to calling
+     * <code>topoSort(new String[] {root}, targets, returnAll)</code>.
+     *
+     * @param root The name of the root target. The sort is created in such
+     *             a way that the sequence of Targets up to the root
+     *             target is the minimum possible such sequence.
+     *             Must not be <code>null</code>.
+     * @param targets A Hashtable mapping names to Targets.
+     *                Must not be <code>null</code>.
+     * @param returnAll <code>boolean</code> indicating whether to return all
+     *                  targets, or the execution sequence only.
+     * @return a Vector of Target objects in sorted order.
+     * @exception BuildException if there is a cyclic dependency among the
+     *                           targets, or if a named target does not exist.
+     * @since Ant 1.6.3
+     */
+    public final Vector topoSort(String root, Hashtable targets,
+                                 boolean returnAll) throws BuildException {
+        return topoSort(new String[] {root}, targets, returnAll);
+    }
+
+    /**
+     * Topologically sort a set of targets.
+     *
+     * @param root <code>String[]</code> containing the names of the root targets.
+     *             The sort is created in such a way that the ordered sequence of
+     *             Targets is the minimum possible such sequence to the specified
+     *             root targets.
+     *             Must not be <code>null</code>.
+     * @param targets A map of names to targets (String to Target).
+     *                Must not be <code>null</code>.
+     * @param returnAll <code>boolean</code> indicating whether to return all
+     *                  targets, or the execution sequence only.
+     * @return a Vector of Target objects in sorted order.
+     * @exception BuildException if there is a cyclic dependency among the
+     *                           targets, or if a named target does not exist.
+     * @since Ant 1.6.3
+     */
+    public final Vector topoSort(String[] root, Hashtable targets,
+                                 boolean returnAll) throws BuildException {
+        Vector ret = new Vector();
+        Hashtable state = new Hashtable();
+        Stack visiting = new Stack();
+
+        // We first run a DFS based sort using each root as a starting node.
+        // This creates the minimum sequence of Targets to the root node(s).
+        // We then do a sort on any remaining unVISITED targets.
+        // This is unnecessary for doing our build, but it catches
+        // circular dependencies or missing Targets on the entire
+        // dependency tree, not just on the Targets that depend on the
+        // build Target.
+
+        for (int i = 0; i < root.length; i++) {
+            String st = (String) (state.get(root[i]));
+            if (st == null) {
+                tsort(root[i], targets, state, visiting, ret);
+            } else if (st == VISITING) {
+                throw new RuntimeException("Unexpected node in visiting state: "
+                    + root[i]);
+            }
+        }
+        StringBuffer buf = new StringBuffer("Build sequence for target(s)");
+
+        for (int j = 0; j < root.length; j++) {
+            buf.append((j == 0) ? " `" : ", `").append(root[j]).append('\'');
+        }
+        buf.append(" is " + ret);
+        log(buf.toString(), MSG_VERBOSE);
+
+        Vector complete = (returnAll) ? ret : new Vector(ret);
+        for (Enumeration en = targets.keys(); en.hasMoreElements();) {
+            String curTarget = (String) en.nextElement();
+            String st = (String) state.get(curTarget);
+            if (st == null) {
+                tsort(curTarget, targets, state, visiting, complete);
+            } else if (st == VISITING) {
+                throw new RuntimeException("Unexpected node in visiting state: "
+                    + curTarget);
+            }
+        }
+        log("Complete build sequence is " + complete, MSG_VERBOSE);
+        return ret;
+    }
+
+    /**
+     * Perform a single step in a recursive depth-first-search traversal of
+     * the target dependency tree.
+     * <p>
+     * The current target is first set to the &quot;visiting&quot; state, and
+     * pushed onto the &quot;visiting&quot; stack.
+     * <p>
+     * An exception is then thrown if any child of the current node is in the
+     * visiting state, as that implies a circular dependency. The exception
+     * contains details of the cycle, using elements of the &quot;visiting&quot;
+     * stack.
+     * <p>
+     * If any child has not already been &quot;visited&quot;, this method is
+     * called recursively on it.
+     * <p>
+     * The current target is then added to the ordered list of targets. Note
+     * that this is performed after the children have been visited in order
+     * to get the correct order. The current target is set to the
+     * &quot;visited&quot; state.
+     * <p>
+     * By the time this method returns, the ordered list contains the sequence
+     * of targets up to and including the current target.
+     *
+     * @param root The current target to inspect.
+     *             Must not be <code>null</code>.
+     * @param targets A mapping from names to targets (String to Target).
+     *                Must not be <code>null</code>.
+     * @param state   A mapping from target names to states (String to String).
+     *                The states in question are &quot;VISITING&quot; and
+     *                &quot;VISITED&quot;. Must not be <code>null</code>.
+     * @param visiting A stack of targets which are currently being visited.
+     *                 Must not be <code>null</code>.
+     * @param ret     The list to add target names to. This will end up
+     *                containing the complete list of dependencies in
+     *                dependency order.
+     *                Must not be <code>null</code>.
+     *
+     * @exception BuildException if a non-existent target is specified or if
+     *                           a circular dependency is detected.
+     */
+    private void tsort(String root, Hashtable targets,
+                             Hashtable state, Stack visiting,
+                             Vector ret)
+        throws BuildException {
+        state.put(root, VISITING);
+        visiting.push(root);
+
+        Target target = (Target) targets.get(root);
+
+        // Make sure we exist
+        if (target == null) {
+            StringBuffer sb = new StringBuffer("Target `");
+            sb.append(root);
+            sb.append("' does not exist in this project. ");
+            visiting.pop();
+            if (!visiting.empty()) {
+                String parent = (String) visiting.peek();
+                sb.append("It is used from target `");
+                sb.append(parent);
+                sb.append("'.");
+            }
+            throw new BuildException(new String(sb));
+        }
+        for (Enumeration en = target.getDependencies(); en.hasMoreElements();) {
+            String cur = (String) en.nextElement();
+            String m = (String) state.get(cur);
+            if (m == null) {
+                // Not been visited
+                tsort(cur, targets, state, visiting, ret);
+            } else if (m == VISITING) {
+                // Currently visiting this node, so have a cycle
+                throw makeCircularException(cur, visiting);
+            }
+        }
+        String p = (String) visiting.pop();
+        if (root != p) {
+            throw new RuntimeException("Unexpected internal error: expected to "
+                + "pop " + root + " but got " + p);
+        }
+        state.put(root, VISITED);
+        ret.addElement(target);
+    }
+
+    /**
+     * Build an appropriate exception detailing a specified circular
+     * dependency.
+     *
+     * @param end The dependency to stop at. Must not be <code>null</code>.
+     * @param stk A stack of dependencies. Must not be <code>null</code>.
+     *
+     * @return a BuildException detailing the specified circular dependency.
+     */
+    private static BuildException makeCircularException(String end, Stack stk) {
+        StringBuffer sb = new StringBuffer("Circular dependency: ");
+        sb.append(end);
+        String c;
+        do {
+            c = (String) stk.pop();
+            sb.append(" <- ");
+            sb.append(c);
+        } while (!c.equals(end));
+        return new BuildException(new String(sb));
+    }
+
+    /**
+     * Add a reference to the project.
+     *
+     * @param name The name of the reference. Must not be <code>null</code>.
+     * @param value The value of the reference. Must not be <code>null</code>.
+     */
+    public void addReference(String name, Object value) {
+        synchronized (references) {
+            Object old = ((AntRefTable) references).getReal(name);
+            if (old == value) {
+                // no warning, this is not changing anything
+                return;
+            }
+            if (old != null && !(old instanceof UnknownElement)) {
+                log("Overriding previous definition of reference to " + name,
+                    MSG_WARN);
+            }
+            log("Adding reference: " + name, MSG_DEBUG);
+            references.put(name, value);
+        }
+    }
+
+    /**
+     * Return a map of the references in the project (String to Object).
+     * The returned hashtable is &quot;live&quot; and so must not be modified.
+     *
+     * @return a map of the references in the project (String to Object).
+     */
+    public Hashtable getReferences() {
+        return references;
+    }
+
+    /**
+     * Look up a reference by its key (ID).
+     *
+     * @param key The key for the desired reference.
+     *            Must not be <code>null</code>.
+     *
+     * @return the reference with the specified ID, or <code>null</code> if
+     *         there is no such reference in the project.
+     */
+    public Object getReference(String key) {
+        return references.get(key);
+    }
+
+    /**
+     * Return a description of the type of the given element, with
+     * special handling for instances of tasks and data types.
+     * <p>
+     * This is useful for logging purposes.
+     *
+     * @param element The element to describe.
+     *                Must not be <code>null</code>.
+     *
+     * @return a description of the element type.
+     *
+     * @since 1.95, Ant 1.5
+     */
+    public String getElementName(Object element) {
+        return ComponentHelper.getComponentHelper(this).getElementName(element);
+    }
+
+    /**
+     * Send a &quot;build started&quot; event
+     * to the build listeners for this project.
+     */
+    public void fireBuildStarted() {
+        BuildEvent event = new BuildEvent(this);
+        Iterator iter = listeners.iterator();
+        while (iter.hasNext()) {
+            BuildListener listener = (BuildListener) iter.next();
+            listener.buildStarted(event);
+        }
+    }
+
+    /**
+     * Send a &quot;build finished&quot; event to the build listeners
+     * for this project.
+     * @param exception an exception indicating a reason for a build
+     *                  failure. May be <code>null</code>, indicating
+     *                  a successful build.
+     */
+    public void fireBuildFinished(Throwable exception) {
+        BuildEvent event = new BuildEvent(this);
+        event.setException(exception);
+        Iterator iter = listeners.iterator();
+        while (iter.hasNext()) {
+            BuildListener listener = (BuildListener) iter.next();
+            listener.buildFinished(event);
+        }
+    }
+
+    /**
+     * Send a &quot;subbuild started&quot; event to the build listeners for
+     * this project.
+     *
+     * @since Ant 1.6.2
+     */
+    public void fireSubBuildStarted() {
+        BuildEvent event = new BuildEvent(this);
+        Iterator iter = listeners.iterator();
+        while (iter.hasNext()) {
+            Object listener = iter.next();
+            if (listener instanceof SubBuildListener) {
+                ((SubBuildListener) listener).subBuildStarted(event);
+            }
+        }
+    }
+
+    /**
+     * Send a &quot;subbuild finished&quot; event to the build listeners for
+     * this project.
+     * @param exception an exception indicating a reason for a build
+     *                  failure. May be <code>null</code>, indicating
+     *                  a successful build.
+     *
+     * @since Ant 1.6.2
+     */
+    public void fireSubBuildFinished(Throwable exception) {
+        BuildEvent event = new BuildEvent(this);
+        event.setException(exception);
+        Iterator iter = listeners.iterator();
+        while (iter.hasNext()) {
+            Object listener = iter.next();
+            if (listener instanceof SubBuildListener) {
+                ((SubBuildListener) listener).subBuildFinished(event);
+            }
+        }
+    }
+
+    /**
+     * Send a &quot;target started&quot; event to the build listeners
+     * for this project.
+     *
+     * @param target The target which is starting to build.
+     *               Must not be <code>null</code>.
+     */
+    protected void fireTargetStarted(Target target) {
+        BuildEvent event = new BuildEvent(target);
+        Iterator iter = listeners.iterator();
+        while (iter.hasNext()) {
+            BuildListener listener = (BuildListener) iter.next();
+            listener.targetStarted(event);
+        }
+    }
+
+    /**
+     * Send a &quot;target finished&quot; event to the build listeners
+     * for this project.
+     *
+     * @param target    The target which has finished building.
+     *                  Must not be <code>null</code>.
+     * @param exception an exception indicating a reason for a build
+     *                  failure. May be <code>null</code>, indicating
+     *                  a successful build.
+     */
+    protected void fireTargetFinished(Target target, Throwable exception) {
+        BuildEvent event = new BuildEvent(target);
+        event.setException(exception);
+        Iterator iter = listeners.iterator();
+        while (iter.hasNext()) {
+            BuildListener listener = (BuildListener) iter.next();
+            listener.targetFinished(event);
+        }
+    }
+
+    /**
+     * Send a &quot;task started&quot; event to the build listeners
+     * for this project.
+     *
+     * @param task The target which is starting to execute.
+     *               Must not be <code>null</code>.
+     */
+    protected void fireTaskStarted(Task task) {
+        // register this as the current task on the current thread.
+        registerThreadTask(Thread.currentThread(), task);
+        BuildEvent event = new BuildEvent(task);
+        Iterator iter = listeners.iterator();
+        while (iter.hasNext()) {
+            BuildListener listener = (BuildListener) iter.next();
+            listener.taskStarted(event);
+        }
+    }
+
+    /**
+     * Send a &quot;task finished&quot; event to the build listeners for this
+     * project.
+     *
+     * @param task      The task which has finished executing.
+     *                  Must not be <code>null</code>.
+     * @param exception an exception indicating a reason for a build
+     *                  failure. May be <code>null</code>, indicating
+     *                  a successful build.
+     */
+    protected void fireTaskFinished(Task task, Throwable exception) {
+        registerThreadTask(Thread.currentThread(), null);
+        System.out.flush();
+        System.err.flush();
+        BuildEvent event = new BuildEvent(task);
+        event.setException(exception);
+        Iterator iter = listeners.iterator();
+        while (iter.hasNext()) {
+            BuildListener listener = (BuildListener) iter.next();
+            listener.taskFinished(event);
+        }
+    }
+
+    /**
+     * Send a &quot;message logged&quot; event to the build listeners
+     * for this project.
+     *
+     * @param event    The event to send. This should be built up with the
+     *                 appropriate task/target/project by the caller, so that
+     *                 this method can set the message and priority, then send
+     *                 the event. Must not be <code>null</code>.
+     * @param message  The message to send. Should not be <code>null</code>.
+     * @param priority The priority of the message.
+     */
+    private void fireMessageLoggedEvent(BuildEvent event, String message,
+                                        int priority) {
+
+        if (message.endsWith(StringUtils.LINE_SEP)) {
+            int endIndex = message.length() - StringUtils.LINE_SEP.length();
+            event.setMessage(message.substring(0, endIndex), priority);
+        } else {
+            event.setMessage(message, priority);
+        }
+        synchronized (this) {
+            if (loggingMessage) {
+                /*
+                 * One of the Listeners has attempted to access
+                 * System.err or System.out.
+                 *
+                 * We used to throw an exception in this case, but
+                 * sometimes Listeners can't prevent it(like our own
+                 * Log4jListener which invokes getLogger() which in
+                 * turn wants to write to the console).
+                 *
+                 * @see http://marc.theaimsgroup.com/?t=110538624200006&r=1&w=2
+                 *
+                 * We now (Ant 1.7 and 1.6.3) simply swallow the message.
+                 */
+                return;
+            }
+            try {
+                loggingMessage = true;
+                Iterator iter = listeners.iterator();
+                while (iter.hasNext()) {
+                    BuildListener listener = (BuildListener) iter.next();
+                    listener.messageLogged(event);
+                }
+            } finally {
+                loggingMessage = false;
+            }
+        }
+    }
+
+    /**
+     * Send a &quot;message logged&quot; project level event
+     * to the build listeners for this project.
+     *
+     * @param project  The project generating the event.
+     *                 Should not be <code>null</code>.
+     * @param message  The message to send. Should not be <code>null</code>.
+     * @param priority The priority of the message.
+     */
+    protected void fireMessageLogged(Project project, String message,
+                                     int priority) {
+        BuildEvent event = new BuildEvent(project);
+        fireMessageLoggedEvent(event, message, priority);
+    }
+
+    /**
+     * Send a &quot;message logged&quot; target level event
+     * to the build listeners for this project.
+     *
+     * @param target   The target generating the event.
+     *                 Must not be <code>null</code>.
+     * @param message  The message to send. Should not be <code>null</code>.
+     * @param priority The priority of the message.
+     */
+    protected void fireMessageLogged(Target target, String message,
+                                     int priority) {
+        BuildEvent event = new BuildEvent(target);
+        fireMessageLoggedEvent(event, message, priority);
+    }
+
+    /**
+     * Send a &quot;message logged&quot; task level event
+     * to the build listeners for this project.
+     *
+     * @param task     The task generating the event.
+     *                 Must not be <code>null</code>.
+     * @param message  The message to send. Should not be <code>null</code>.
+     * @param priority The priority of the message.
+     */
+    protected void fireMessageLogged(Task task, String message, int priority) {
+        BuildEvent event = new BuildEvent(task);
+        fireMessageLoggedEvent(event, message, priority);
+    }
+
+    /**
+     * Register a task as the current task for a thread.
+     * If the task is null, the thread's entry is removed.
+     *
+     * @param thread the thread on which the task is registered.
+     * @param task the task to be registered.
+     * @since Ant 1.5
+     */
+    public synchronized void registerThreadTask(Thread thread, Task task) {
+        if (task != null) {
+            threadTasks.put(thread, task);
+            threadGroupTasks.put(thread.getThreadGroup(), task);
+        } else {
+            threadTasks.remove(thread);
+            threadGroupTasks.remove(thread.getThreadGroup());
+        }
+    }
+
+    /**
+     * Get the current task associated with a thread, if any.
+     *
+     * @param thread the thread for which the task is required.
+     * @return the task which is currently registered for the given thread or
+     *         null if no task is registered.
+     */
+    public Task getThreadTask(Thread thread) {
+        Task task = (Task) threadTasks.get(thread);
+        if (task == null) {
+            ThreadGroup group = thread.getThreadGroup();
+            while (task == null && group != null) {
+                task = (Task) threadGroupTasks.get(group);
+                group = group.getParent();
+            }
+        }
+        return task;
+    }
+
+
+    // Should move to a separate public class - and have API to add
+    // listeners, etc.
+    private static class AntRefTable extends Hashtable {
+        private Project project;
+
+        public AntRefTable(Project project) {
+            super();
+            this.project = project;
+        }
+
+        /** Returns the unmodified original object.
+         * This method should be called internally to
+         * get the &quot;real&quot; object.
+         * The normal get method will do the replacement
+         * of UnknownElement (this is similar with the JDNI
+         * refs behavior).
+         */
+        public Object getReal(Object key) {
+            return super.get(key);
+        }
+
+        /** Get method for the reference table.
+         *  It can be used to hook dynamic references and to modify
+         * some references on the fly--for example for delayed
+         * evaluation.
+         *
+         * It is important to make sure that the processing that is
+         * done inside is not calling get indirectly.
+         *
+         * @param key lookup key.
+         * @return mapped value.
+         */
+        public Object get(Object key) {
+            //System.out.println("AntRefTable.get " + key);
+            Object o = getReal(key);
+            if (o instanceof UnknownElement) {
+                // Make sure that
+                UnknownElement ue = (UnknownElement) o;
+                ue.maybeConfigure();
+                o = ue.getRealThing();
+            }
+            return o;
+        }
+    }
+
+    /**
+     * Set a reference to this Project on the parameterized object.
+     * Need to set the project before other set/add elements
+     * are called.
+     * @param obj the object to invoke setProject(this) on.
+     */
+    public final void setProjectReference(final Object obj) {
+        if (obj instanceof ProjectComponent) {
+            ((ProjectComponent) obj).setProject(this);
+            return;
+        }
+        try {
+            Method method =
+                obj.getClass().getMethod(
+                    "setProject", new Class[] {Project.class});
+            if (method != null) {
+                method.invoke(obj, new Object[] {this});
+            }
+        } catch (Throwable e) {
+            // ignore this if the object does not have
+            // a set project method or the method
+            // is private/protected.
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/ProjectComponent.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/ProjectComponent.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/ProjectComponent.java	(revision 16139)
@@ -0,0 +1,89 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+/**
+ * Base class for components of a project, including tasks and data types.
+ * Provides common facilities.
+ *
+ */
+public abstract class ProjectComponent {
+
+    /**
+     * Project object of this component.
+     * @deprecated You should not be directly accessing this variable
+     *   directly. You should access project object via the getProject()
+     *   or setProject() accessor/mutators.
+     */
+    protected Project project;
+
+    /** Sole constructor. */
+    public ProjectComponent() {
+    }
+
+    /**
+     * Sets the project object of this component. This method is used by
+     * Project when a component is added to it so that the component has
+     * access to the functions of the project. It should not be used
+     * for any other purpose.
+     *
+     * @param project Project in whose scope this component belongs.
+     *                Must not be <code>null</code>.
+     */
+    public void setProject(Project project) {
+        this.project = project;
+    }
+
+    /**
+     * Returns the project to which this component belongs.
+     *
+     * @return the components's project.
+     */
+    public Project getProject() {
+        return project;
+    }
+
+    /**
+     * Logs a message with the default (INFO) priority.
+     *
+     * @param msg The message to be logged. Should not be <code>null</code>.
+     */
+    public void log(String msg) {
+        log(msg, Project.MSG_INFO);
+    }
+
+    /**
+     * Logs a message with the given priority.
+     *
+     * @param msg The message to be logged. Should not be <code>null</code>.
+     * @param msgLevel the message priority at which this message is
+     *                 to be logged.
+     */
+    public void log(String msg, int msgLevel) {
+        if (project != null) {
+            project.log(msg, msgLevel);
+        } else {
+            // 'reasonable' default, if the component is used without
+            // a Project ( for example as a standalone Bean ).
+            // Most ant components can be used this way.
+            if (msgLevel <= Project.MSG_INFO) {
+                System.err.println(msg);
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/ProjectHelper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/ProjectHelper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/ProjectHelper.java	(revision 16139)
@@ -0,0 +1,543 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.Vector;
+import org.apache.tools.ant.helper.ProjectHelper2;
+import org.apache.tools.ant.util.LoaderUtils;
+import org.xml.sax.AttributeList;
+
+/**
+ * Configures a Project (complete with Targets and Tasks) based on
+ * a XML build file. It'll rely on a plugin to do the actual processing
+ * of the xml file.
+ *
+ * This class also provide static wrappers for common introspection.
+ *
+ * All helper plugins must provide backward compatibility with the
+ * original ant patterns, unless a different behavior is explicitly
+ * specified. For example, if namespace is used on the &lt;project&gt; tag
+ * the helper can expect the entire build file to be namespace-enabled.
+ * Namespaces or helper-specific tags can provide meta-information to
+ * the helper, allowing it to use new ( or different policies ).
+ *
+ * However, if no namespace is used the behavior should be exactly
+ * identical with the default helper.
+ *
+ */
+public class ProjectHelper {
+    /** The URI for ant name space */
+    public static final String ANT_CORE_URI    = "antlib:org.apache.tools.ant";
+
+    /** The URI for antlib current definitions */
+    public static final String ANT_CURRENT_URI      = "ant:current";
+
+    /** The URI for defined types/tasks - the format is antlib:<package> */
+    public static final String ANTLIB_URI     = "antlib:";
+
+    /** Polymorphic attribute  */
+    public static final String ANT_TYPE = "ant-type";
+
+    /**
+     * Name of JVM system property which provides the name of the
+     * ProjectHelper class to use.
+     */
+    public static final String HELPER_PROPERTY =
+        "org.apache.tools.ant.ProjectHelper";
+
+    /**
+     * The service identifier in jars which provide Project Helper
+     * implementations.
+     */
+    public static final String SERVICE_ID =
+        "META-INF/services/org.apache.tools.ant.ProjectHelper";
+
+    /**
+     * Configures the project with the contents of the specified XML file.
+     *
+     * @param project The project to configure. Must not be <code>null</code>.
+     * @param buildFile An XML file giving the project's configuration.
+     *                  Must not be <code>null</code>.
+     *
+     * @deprecated Use the non-static parse method
+     * @exception BuildException if the configuration is invalid or cannot
+     *                           be read
+     */
+    public static void configureProject(Project project, File buildFile)
+        throws BuildException {
+        ProjectHelper helper = ProjectHelper.getProjectHelper();
+        project.addReference("ant.projectHelper", helper);
+        helper.parse(project, buildFile);
+    }
+
+    /** Default constructor */
+    public ProjectHelper() {
+    }
+
+    // -------------------- Common properties  --------------------
+    // The following properties are required by import ( and other tasks
+    // that read build files using ProjectHelper ).
+
+    // A project helper may process multiple files. We'll keep track
+    // of them - to avoid loops and to allow caching. The caching will
+    // probably accelerate things like <antCall>.
+    // The key is the absolute file, the value is a processed tree.
+    // Since the tree is composed of UE and RC - it can be reused !
+    // protected Hashtable processedFiles=new Hashtable();
+
+    private Vector importStack = new Vector();
+
+    // Temporary - until we figure a better API
+    /** EXPERIMENTAL WILL_CHANGE
+     *
+     */
+//    public Hashtable getProcessedFiles() {
+//        return processedFiles;
+//    }
+
+    /** EXPERIMENTAL WILL_CHANGE
+     *  Import stack.
+     *  Used to keep track of imported files. Error reporting should
+     *  display the import path.
+     *
+     * @return the stack of import source objects.
+     */
+    public Vector getImportStack() {
+        return importStack;
+    }
+
+
+    // --------------------  Parse method  --------------------
+    /**
+     * Parses the project file, configuring the project as it goes.
+     *
+     * @param project The project for the resulting ProjectHelper to configure.
+     *                Must not be <code>null</code>.
+     * @param source The source for XML configuration. A helper must support
+     *               at least File, for backward compatibility. Helpers may
+     *               support URL, InputStream, etc or specialized types.
+     *
+     * @since Ant1.5
+     * @exception BuildException if the configuration is invalid or cannot
+     *                           be read
+     */
+    public void parse(Project project, Object source) throws BuildException {
+        throw new BuildException("ProjectHelper.parse() must be implemented "
+            + "in a helper plugin " + this.getClass().getName());
+    }
+
+
+    /**
+     * Discovers a project helper instance. Uses the same patterns
+     * as JAXP, commons-logging, etc: a system property, a JDK1.3
+     * service discovery, default.
+     *
+     * @return a ProjectHelper, either a custom implementation
+     * if one is available and configured, or the default implementation
+     * otherwise.
+     *
+     * @exception BuildException if a specified helper class cannot
+     * be loaded/instantiated.
+     */
+    public static ProjectHelper getProjectHelper()
+        throws BuildException {
+        // Identify the class loader we will be using. Ant may be
+        // in a webapp or embedded in a different app
+        ProjectHelper helper = null;
+
+        // First, try the system property
+        String helperClass = System.getProperty(HELPER_PROPERTY);
+        try {
+            if (helperClass != null) {
+                helper = newHelper(helperClass);
+            }
+        } catch (SecurityException e) {
+            System.out.println("Unable to load ProjectHelper class \""
+                + helperClass + " specified in system property "
+                + HELPER_PROPERTY);
+        }
+
+        // A JDK1.3 'service' ( like in JAXP ). That will plug a helper
+        // automatically if in CLASSPATH, with the right META-INF/services.
+        if (helper == null) {
+            try {
+                ClassLoader classLoader = LoaderUtils.getContextClassLoader();
+                InputStream is = null;
+                if (classLoader != null) {
+                    is = classLoader.getResourceAsStream(SERVICE_ID);
+                }
+                if (is == null) {
+                    is = ClassLoader.getSystemResourceAsStream(SERVICE_ID);
+                }
+
+                if (is != null) {
+                    // This code is needed by EBCDIC and other strange systems.
+                    // It's a fix for bugs reported in xerces
+                    InputStreamReader isr;
+                    try {
+                        isr = new InputStreamReader(is, "UTF-8");
+                    } catch (java.io.UnsupportedEncodingException e) {
+                        isr = new InputStreamReader(is);
+                    }
+                    BufferedReader rd = new BufferedReader(isr);
+
+                    String helperClassName = rd.readLine();
+                    rd.close();
+
+                    if (helperClassName != null
+                        && !"".equals(helperClassName)) {
+
+                        helper = newHelper(helperClassName);
+                    }
+                }
+            } catch (Exception ex) {
+                System.out.println("Unable to load ProjectHelper "
+                    + "from service \"" + SERVICE_ID);
+            }
+        }
+
+        if (helper != null) {
+            return helper;
+        } else {
+            try {
+                // Default
+                // return new ProjectHelperImpl();
+                return new ProjectHelper2();
+            } catch (Throwable e) {
+                String message = "Unable to load default ProjectHelper due to "
+                    + e.getClass().getName() + ": " + e.getMessage();
+                throw new BuildException(message, e);
+            }
+        }
+    }
+
+    /**
+     * Creates a new helper instance from the name of the class.
+     * It'll first try the thread class loader, then Class.forName()
+     * will load from the same loader that loaded this class.
+     *
+     * @param helperClass The name of the class to create an instance
+     *                    of. Must not be <code>null</code>.
+     *
+     * @return a new instance of the specified class.
+     *
+     * @exception BuildException if the class cannot be found or
+     * cannot be appropriate instantiated.
+     */
+    private static ProjectHelper newHelper(String helperClass)
+        throws BuildException {
+        ClassLoader classLoader = LoaderUtils.getContextClassLoader();
+        try {
+            Class clazz = null;
+            if (classLoader != null) {
+                try {
+                    clazz = classLoader.loadClass(helperClass);
+                } catch (ClassNotFoundException ex) {
+                    // try next method
+                }
+            }
+            if (clazz == null) {
+                clazz = Class.forName(helperClass);
+            }
+            return ((ProjectHelper) clazz.newInstance());
+        } catch (Exception e) {
+            throw new BuildException(e);
+        }
+    }
+
+    /**
+     * JDK1.1 compatible access to the context class loader.
+     * Cut&paste from JAXP.
+     *
+     * @deprecated Use LoaderUtils.getContextClassLoader()
+     * @return the current context class loader, or <code>null</code>
+     * if the context class loader is unavailable.
+     */
+    public static ClassLoader getContextClassLoader() {
+        if (!LoaderUtils.isContextLoaderAvailable()) {
+            return null;
+        }
+
+        return LoaderUtils.getContextClassLoader();
+    }
+
+    // -------------------- Static utils, used by most helpers ----------------
+
+    /**
+     * Configures an object using an introspection handler.
+     *
+     * @param target The target object to be configured.
+     *               Must not be <code>null</code>.
+     * @param attrs  A list of attributes to configure within the target.
+     *               Must not be <code>null</code>.
+     * @param project The project containing the target.
+     *                Must not be <code>null</code>.
+     *
+     * @deprecated Use IntrospectionHelper for each property
+     * @exception BuildException if any of the attributes can't be handled by
+     *                           the target
+     */
+    public static void configure(Object target, AttributeList attrs,
+                                 Project project) throws BuildException {
+        if (target instanceof TypeAdapter) {
+            target = ((TypeAdapter) target).getProxy();
+        }
+
+        IntrospectionHelper ih =
+            IntrospectionHelper.getHelper(target.getClass());
+
+        project.addBuildListener(ih);
+
+        for (int i = 0; i < attrs.getLength(); i++) {
+            // reflect these into the target
+            String value = replaceProperties(project, attrs.getValue(i),
+                                             project.getProperties());
+            try {
+                ih.setAttribute(project, target,
+                                attrs.getName(i).toLowerCase(Locale.US), value);
+
+            } catch (BuildException be) {
+                // id attribute must be set externally
+                if (!attrs.getName(i).equals("id")) {
+                    throw be;
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds the content of #PCDATA sections to an element.
+     *
+     * @param project The project containing the target.
+     *                Must not be <code>null</code>.
+     * @param target  The target object to be configured.
+     *                Must not be <code>null</code>.
+     * @param buf A character array of the text within the element.
+     *            Will not be <code>null</code>.
+     * @param start The start element in the array.
+     * @param count The number of characters to read from the array.
+     *
+     * @exception BuildException if the target object doesn't accept text
+     */
+    public static void addText(Project project, Object target, char[] buf,
+        int start, int count) throws BuildException {
+        addText(project, target, new String(buf, start, count));
+    }
+
+    /**
+     * Adds the content of #PCDATA sections to an element.
+     *
+     * @param project The project containing the target.
+     *                Must not be <code>null</code>.
+     * @param target  The target object to be configured.
+     *                Must not be <code>null</code>.
+     * @param text    Text to add to the target.
+     *                May be <code>null</code>, in which case this
+     *                method call is a no-op.
+     *
+     * @exception BuildException if the target object doesn't accept text
+     */
+    public static void addText(Project project, Object target, String text)
+        throws BuildException {
+
+        if (text == null) {
+            return;
+        }
+
+        if (target instanceof TypeAdapter) {
+            target = ((TypeAdapter) target).getProxy();
+        }
+
+        IntrospectionHelper.getHelper(target.getClass()).addText(project,
+            target, text);
+    }
+
+    /**
+     * Stores a configured child element within its parent object.
+     *
+     * @param project Project containing the objects.
+     *                May be <code>null</code>.
+     * @param parent  Parent object to add child to.
+     *                Must not be <code>null</code>.
+     * @param child   Child object to store in parent.
+     *                Should not be <code>null</code>.
+     * @param tag     Name of element which generated the child.
+     *                May be <code>null</code>, in which case
+     *                the child is not stored.
+     */
+    public static void storeChild(Project project, Object parent,
+         Object child, String tag) {
+        IntrospectionHelper ih
+            = IntrospectionHelper.getHelper(parent.getClass());
+        ih.storeElement(project, parent, child, tag);
+    }
+
+    /**
+     * Replaces <code>${xxx}</code> style constructions in the given value with
+     * the string value of the corresponding properties.
+     *
+     * @param project The project containing the properties to replace.
+     *                Must not be <code>null</code>.
+     *
+     * @param value The string to be scanned for property references.
+     *              May be <code>null</code>.
+     *
+     * @exception BuildException if the string contains an opening
+     *                           <code>${</code> without a closing
+     *                           <code>}</code>
+     * @return the original string with the properties replaced, or
+     *         <code>null</code> if the original string is <code>null</code>.
+     *
+     * @deprecated Use project.replaceProperties()
+     * @since 1.5
+     */
+     public static String replaceProperties(Project project, String value)
+            throws BuildException {
+        // needed since project properties are not accessible
+         return project.replaceProperties(value);
+     }
+
+    /**
+     * Replaces <code>${xxx}</code> style constructions in the given value
+     * with the string value of the corresponding data types.
+     *
+     * @param project The container project. This is used solely for
+     *                logging purposes. Must not be <code>null</code>.
+     * @param value The string to be scanned for property references.
+     *              May be <code>null</code>, in which case this
+     *              method returns immediately with no effect.
+     * @param keys  Mapping (String to String) of property names to their
+     *              values. Must not be <code>null</code>.
+     *
+     * @exception BuildException if the string contains an opening
+     *                           <code>${</code> without a closing
+     *                           <code>}</code>
+     * @return the original string with the properties replaced, or
+     *         <code>null</code> if the original string is <code>null</code>.
+     * @deprecated Use PropertyHelper
+     */
+     public static String replaceProperties(Project project, String value,
+         Hashtable keys) throws BuildException {
+        PropertyHelper ph = PropertyHelper.getPropertyHelper(project);
+        return ph.replaceProperties(null, value, keys);
+    }
+
+    /**
+     * Parses a string containing <code>${xxx}</code> style property
+     * references into two lists. The first list is a collection
+     * of text fragments, while the other is a set of string property names.
+     * <code>null</code> entries in the first list indicate a property
+     * reference from the second list.
+     *
+     * @param value     Text to parse. Must not be <code>null</code>.
+     * @param fragments List to add text fragments to.
+     *                  Must not be <code>null</code>.
+     * @param propertyRefs List to add property names to.
+     *                     Must not be <code>null</code>.
+     *
+     * @deprecated Use PropertyHelper
+     * @exception BuildException if the string contains an opening
+     *                           <code>${</code> without a closing
+     *                           <code>}</code>
+     */
+    public static void parsePropertyString(String value, Vector fragments,
+                                           Vector propertyRefs)
+        throws BuildException {
+        PropertyHelper.parsePropertyStringDefault(value, fragments,
+                propertyRefs);
+    }
+    /**
+     * Map a namespaced {uri,name} to an internal string format.
+     * For BC purposes the names from the ant core uri will be
+     * mapped to "name", other names will be mapped to
+     * uri + ":" + name.
+     * @param uri   The namepace URI
+     * @param name  The localname
+     * @return      The stringified form of the ns name
+     */
+    public static String genComponentName(String uri, String name) {
+        if (uri == null || uri.equals("") || uri.equals(ANT_CORE_URI)) {
+            return name;
+        }
+        return uri + ":" + name;
+    }
+
+    /**
+     * extract a uri from a component name
+     *
+     * @param componentName  The stringified form for {uri, name}
+     * @return               The uri or "" if not present
+     */
+    public static String extractUriFromComponentName(String componentName) {
+        if (componentName == null) {
+            return "";
+        }
+        int index = componentName.lastIndexOf(':');
+        if (index == -1) {
+            return "";
+        }
+        return componentName.substring(0, index);
+    }
+
+    /**
+     * extract the element name from a component name
+     *
+     * @param componentName  The stringified form for {uri, name}
+     * @return               The element name of the component
+     */
+    public static String extractNameFromComponentName(String componentName) {
+        int index = componentName.lastIndexOf(':');
+        if (index == -1) {
+            return componentName;
+        }
+        return componentName.substring(index + 1);
+    }
+
+    /**
+     * Add location to build exception.
+     * @param ex the build exception, if the build exception
+     *           does not include
+     * @param newLocation the location of the calling task (may be null)
+     * @return a new build exception based in the build exception with
+     *         location set to newLocation. If the original exception
+     *         did not have a location, just return the build exception
+     */
+    public static BuildException addLocationToBuildException(
+        BuildException ex, Location newLocation) {
+        if (ex.getLocation() == null || ex.getMessage() == null) {
+            return ex;
+        }
+        String errorMessage
+            = "The following error occurred while executing this line:"
+            + System.getProperty("line.separator")
+            + ex.getLocation().toString()
+            + ex.getMessage();
+        if (newLocation == null) {
+            return new BuildException(errorMessage, ex);
+        } else {
+            return new BuildException(
+                errorMessage, ex, newLocation);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/PropertyHelper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/PropertyHelper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/PropertyHelper.java	(revision 16139)
@@ -0,0 +1,606 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.Enumeration;
+
+
+/* ISSUES:
+ - ns param. It could be used to provide "namespaces" for properties, which
+ may be more flexible.
+ - Object value. In ant1.5 String is used for Properties - but it would be nice
+ to support generic Objects (the property remains immutable - you can't change
+ the associated object). This will also allow JSP-EL style setting using the
+ Object if an attribute contains only the property (name="${property}" could
+ avoid Object->String->Object conversion)
+ - Currently we "chain" only for get and set property (probably most users
+ will only need that - if they need more they can replace the top helper).
+ Need to discuss this and find if we need more.
+ */
+
+/** NOT FINAL. API MAY CHANGE
+ *
+ * Deals with properties - substitution, dynamic properties, etc.
+ *
+ * This is the same code as in Ant1.5. The main addition is the ability
+ * to chain multiple PropertyHelpers and to replace the default.
+ *
+ * @since Ant 1.6
+ */
+public class PropertyHelper {
+
+    private Project project;
+    private PropertyHelper next;
+
+    /** Project properties map (usually String to String). */
+    private Hashtable properties = new Hashtable();
+
+    /**
+     * Map of "user" properties (as created in the Ant task, for example).
+     * Note that these key/value pairs are also always put into the
+     * project properties, so only the project properties need to be queried.
+     * Mapping is String to String.
+     */
+    private Hashtable userProperties = new Hashtable();
+
+    /**
+     * Map of inherited "user" properties - that are those "user"
+     * properties that have been created by tasks and not been set
+     * from the command line or a GUI tool.
+     * Mapping is String to String.
+     */
+    private Hashtable inheritedProperties = new Hashtable();
+
+    /**
+     * Default constructor.
+     */
+    protected PropertyHelper() {
+    }
+
+    // --------------------  Hook management  --------------------
+
+    /**
+     * Set the project for which this helper is performing property resolution
+     *
+     * @param p the project instance.
+     */
+    public void setProject(Project p) {
+        this.project = p;
+    }
+
+    /** There are 2 ways to hook into property handling:
+     *  - you can replace the main PropertyHelper. The replacement is required
+     * to support the same semantics (of course :-)
+     *
+     *  - you can chain a property helper capable of storing some properties.
+     *  Again, you are required to respect the immutability semantics (at
+     *  least for non-dynamic properties)
+     *
+     * @param next the next property helper in the chain.
+     */
+    public void setNext(PropertyHelper next) {
+        this.next = next;
+    }
+
+    /**
+     * Get the next property helper in the chain.
+     *
+     * @return the next property helper.
+     */
+    public PropertyHelper getNext() {
+        return next;
+    }
+
+    /**
+     * Factory method to create a property processor.
+     * Users can provide their own or replace it using "ant.PropertyHelper"
+     * reference. User tasks can also add themselves to the chain, and provide
+     * dynamic properties.
+     *
+     * @param project the project fro which the property helper is required.
+     *
+     * @return the project's property helper.
+     */
+    public static synchronized
+        PropertyHelper getPropertyHelper(Project project) {
+        PropertyHelper helper
+            = (PropertyHelper) project.getReference("ant.PropertyHelper");
+        if (helper != null) {
+            return helper;
+        }
+        helper = new PropertyHelper();
+        helper.setProject(project);
+
+        project.addReference("ant.PropertyHelper", helper);
+        return helper;
+    }
+
+    // --------------------  Methods to override  --------------------
+
+    /**
+     * Sets a property. Any existing property of the same name
+     * is overwritten, unless it is a user property. Will be called
+     * from setProperty().
+     *
+     * If all helpers return false, the property will be saved in
+     * the default properties table by setProperty.
+     *
+     * @param ns   The namespace that the property is in (currently
+     *             not used.
+     * @param name The name of property to set.
+     *             Must not be <code>null</code>.
+     * @param value The new value of the property.
+     *              Must not be <code>null</code>.
+     * @param inherited True if this property is inherited (an [sub]ant[call] property).
+     * @param user      True if this property is a user property.
+     * @param isNew     True is this is a new property.
+     * @return true if this helper has stored the property, false if it
+     *    couldn't. Each helper should delegate to the next one (unless it
+     *    has a good reason not to).
+     */
+    public boolean setPropertyHook(String ns, String name,
+                                   Object value,
+                                   boolean inherited, boolean user,
+                                   boolean isNew) {
+        if (getNext() != null) {
+            boolean subst = getNext().setPropertyHook(ns, name, value,
+                    inherited, user, isNew);
+            // If next has handled the property
+            if (subst) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /** Get a property. If all hooks return null, the default
+     * tables will be used.
+     *
+     * @param ns namespace of the sought property.
+     * @param name name of the sought property.
+     * @param user True if this is a user property.
+     * @return The property, if returned by a hook, or null if none.
+     */
+    public Object getPropertyHook(String ns, String name, boolean user) {
+        if (getNext() != null) {
+            Object o = getNext().getPropertyHook(ns, name, user);
+            if (o != null) {
+                return o;
+            }
+        }
+        // Experimental/Testing, will be removed
+        if (name.startsWith("toString:")) {
+            name = name.substring("toString:".length());
+            Object v = project.getReference(name);
+            if (v == null) {
+                return null;
+            }
+            return v.toString();
+        }
+
+
+        return null;
+    }
+
+    // -------------------- Optional methods   --------------------
+    // You can override those methods if you want to optimize or
+    // do advanced things (like support a special syntax).
+    // The methods do not chain - you should use them when embedding ant
+    // (by replacing the main helper)
+
+    /**
+     * Parses a string containing <code>${xxx}</code> style property
+     * references into two lists. The first list is a collection
+     * of text fragments, while the other is a set of string property names.
+     * <code>null</code> entries in the first list indicate a property
+     * reference from the second list.
+     *
+     * It can be overridden with a more efficient or customized version.
+     *
+     * @param value     Text to parse. Must not be <code>null</code>.
+     * @param fragments List to add text fragments to.
+     *                  Must not be <code>null</code>.
+     * @param propertyRefs List to add property names to.
+     *                     Must not be <code>null</code>.
+     *
+     * @exception BuildException if the string contains an opening
+     *                           <code>${</code> without a closing
+     *                           <code>}</code>
+     */
+    public void parsePropertyString(String value, Vector fragments,
+                                    Vector propertyRefs)
+        throws BuildException {
+        parsePropertyStringDefault(value, fragments, propertyRefs);
+    }
+
+    /**
+     * Replaces <code>${xxx}</code> style constructions in the given value
+     * with the string value of the corresponding data types.
+     *
+     * @param ns    The namespace for the property.
+     * @param value The string to be scanned for property references.
+     *              May be <code>null</code>, in which case this
+     *              method returns immediately with no effect.
+     * @param keys  Mapping (String to String) of property names to their
+     *              values. If <code>null</code>, only project properties will
+     *              be used.
+     *
+     * @exception BuildException if the string contains an opening
+     *                           <code>${</code> without a closing
+     *                           <code>}</code>
+     * @return the original string with the properties replaced, or
+     *         <code>null</code> if the original string is <code>null</code>.
+     */
+    public String replaceProperties(String ns, String value,
+                                    Hashtable keys)
+            throws BuildException {
+        if (value == null) {
+            return null;
+        }
+
+        Vector fragments = new Vector();
+        Vector propertyRefs = new Vector();
+        parsePropertyString(value, fragments, propertyRefs);
+
+        StringBuffer sb = new StringBuffer();
+        Enumeration i = fragments.elements();
+        Enumeration j = propertyRefs.elements();
+
+        while (i.hasMoreElements()) {
+            String fragment = (String) i.nextElement();
+            if (fragment == null) {
+                String propertyName = (String) j.nextElement();
+                Object replacement = null;
+
+                // try to get it from the project or keys
+                // Backward compatibility
+                if (keys != null) {
+                    replacement = keys.get(propertyName);
+                }
+                if (replacement == null) {
+                    replacement = getProperty(ns, propertyName);
+                }
+
+                if (replacement == null) {
+                    project.log("Property ${" + propertyName
+                            + "} has not been set", Project.MSG_VERBOSE);
+                }
+                fragment = (replacement != null)
+                        ? replacement.toString()
+                        : "${" + propertyName + "}";
+            }
+            sb.append(fragment);
+        }
+
+        return sb.toString();
+    }
+
+    // -------------------- Default implementation  --------------------
+    // Methods used to support the default behavior and provide backward
+    // compatibility. Some will be deprecated, you should avoid calling them.
+
+
+    /** Default implementation of setProperty. Will be called from Project.
+     *  This is the original 1.5 implementation, with calls to the hook
+     *  added.
+     *  @param ns      The namespace for the property (currently not used).
+     *  @param name    The name of the property.
+     *  @param value   The value to set the property to.
+     *  @param verbose If this is true output extra log messages.
+     *  @return true if the property is set.
+     */
+    public synchronized boolean setProperty(String ns, String name,
+                                            Object value, boolean verbose) {
+        // user (CLI) properties take precedence
+        if (null != userProperties.get(name)) {
+            if (verbose) {
+                project.log("Override ignored for user property " + name,
+                        Project.MSG_VERBOSE);
+            }
+            return false;
+        }
+
+        boolean done = setPropertyHook(ns, name, value, false, false, false);
+        if (done) {
+            return true;
+        }
+
+        if (null != properties.get(name) && verbose) {
+            project.log("Overriding previous definition of property " + name,
+                    Project.MSG_VERBOSE);
+        }
+
+        if (verbose) {
+            project.log("Setting project property: " + name + " -> "
+                + value, Project.MSG_DEBUG);
+        }
+        properties.put(name, value);
+        return true;
+    }
+
+    /**
+     * Sets a property if no value currently exists. If the property
+     * exists already, a message is logged and the method returns with
+     * no other effect.
+     *
+     * @param ns   The namespace for the property (currently not used).
+     * @param name The name of property to set.
+     *             Must not be <code>null</code>.
+     * @param value The new value of the property.
+     *              Must not be <code>null</code>.
+     * @since Ant 1.6
+     */
+    public synchronized void setNewProperty(String ns, String name,
+                                            Object value) {
+        if (null != properties.get(name)) {
+            project.log("Override ignored for property " + name,
+                    Project.MSG_VERBOSE);
+            return;
+        }
+
+        boolean done = setPropertyHook(ns, name, value, false, false, true);
+        if (done) {
+            return;
+        }
+
+        project.log("Setting project property: " + name + " -> "
+            + value, Project.MSG_DEBUG);
+        if (name != null && value != null) {
+            properties.put(name, value);
+        }
+    }
+
+    /**
+     * Sets a user property, which cannot be overwritten by
+     * set/unset property calls. Any previous value is overwritten.
+     * @param ns   The namespace for the property (currently not used).
+     * @param name The name of property to set.
+     *             Must not be <code>null</code>.
+     * @param value The new value of the property.
+     *              Must not be <code>null</code>.
+     */
+    public synchronized void setUserProperty(String ns, String name,
+                                             Object value) {
+        project.log("Setting ro project property: " + name + " -> "
+            + value, Project.MSG_DEBUG);
+        userProperties.put(name, value);
+
+        boolean done = setPropertyHook(ns, name, value, false, true, false);
+        if (done) {
+            return;
+        }
+        properties.put(name, value);
+    }
+
+    /**
+     * Sets an inherited user property, which cannot be overwritten by set/unset
+     * property calls. Any previous value is overwritten. Also marks
+     * these properties as properties that have not come from the
+     * command line.
+     *
+     * @param ns   The namespace for the property (currently not used).
+     * @param name The name of property to set.
+     *             Must not be <code>null</code>.
+     * @param value The new value of the property.
+     *              Must not be <code>null</code>.
+     */
+    public synchronized void setInheritedProperty(String ns, String name,
+                                                  Object value) {
+        inheritedProperties.put(name, value);
+
+        project.log("Setting ro project property: " + name + " -> "
+            + value, Project.MSG_DEBUG);
+        userProperties.put(name, value);
+
+        boolean done = setPropertyHook(ns, name, value, true, false, false);
+        if (done) {
+            return;
+        }
+        properties.put(name, value);
+    }
+
+    // -------------------- Getting properties  --------------------
+
+    /**
+     * Returns the value of a property, if it is set.  You can override
+     * this method in order to plug your own storage.
+     *
+     * @param ns   The namespace for the property (currently not used).
+     * @param name The name of the property.
+     *             May be <code>null</code>, in which case
+     *             the return value is also <code>null</code>.
+     * @return the property value, or <code>null</code> for no match
+     *         or if a <code>null</code> name is provided.
+     */
+    public synchronized Object getProperty(String ns, String name) {
+        if (name == null) {
+            return null;
+        }
+
+        Object o = getPropertyHook(ns, name, false);
+        if (o != null) {
+            return o;
+        }
+
+        return properties.get(name);
+    }
+    /**
+     * Returns the value of a user property, if it is set.
+     *
+     * @param ns   The namespace for the property (currently not used).
+     * @param name The name of the property.
+     *             May be <code>null</code>, in which case
+     *             the return value is also <code>null</code>.
+     * @return the property value, or <code>null</code> for no match
+     *         or if a <code>null</code> name is provided.
+     */
+    public synchronized Object getUserProperty(String ns, String name) {
+        if (name == null) {
+            return null;
+        }
+        Object o = getPropertyHook(ns, name, true);
+        if (o != null) {
+            return o;
+        }
+        return  userProperties.get(name);
+    }
+
+
+    // -------------------- Access to property tables  --------------------
+    // This is used to support ant call and similar tasks. It should be
+    // deprecated, it is possible to use a better (more efficient)
+    // mechanism to preserve the context.
+
+    // TODO: do we need to delegate ?
+
+    /**
+     * Returns a copy of the properties table.
+     * @return a hashtable containing all properties
+     *         (including user properties).
+     */
+    public Hashtable getProperties() {
+        return new Hashtable(properties);
+        // There is a better way to save the context. This shouldn't
+        // delegate to next, it's for backward compatibility only.
+    }
+
+    /**
+     * Returns a copy of the user property hashtable
+     * @return a hashtable containing just the user properties
+     */
+    public Hashtable getUserProperties() {
+        return new Hashtable(userProperties);
+    }
+
+    /**
+     * Copies all user properties that have not been set on the
+     * command line or a GUI tool from this instance to the Project
+     * instance given as the argument.
+     *
+     * <p>To copy all "user" properties, you will also have to call
+     * {@link #copyUserProperties copyUserProperties}.</p>
+     *
+     * @param other the project to copy the properties to.  Must not be null.
+     *
+     * @since Ant 1.6
+     */
+    public void copyInheritedProperties(Project other) {
+        Enumeration e = inheritedProperties.keys();
+        while (e.hasMoreElements()) {
+            String arg = e.nextElement().toString();
+            if (other.getUserProperty(arg) != null) {
+                continue;
+            }
+            Object value = inheritedProperties.get(arg);
+            other.setInheritedProperty(arg, value.toString());
+        }
+    }
+
+    /**
+     * Copies all user properties that have been set on the command
+     * line or a GUI tool from this instance to the Project instance
+     * given as the argument.
+     *
+     * <p>To copy all "user" properties, you will also have to call
+     * {@link #copyInheritedProperties copyInheritedProperties}.</p>
+     *
+     * @param other the project to copy the properties to.  Must not be null.
+     *
+     * @since Ant 1.6
+     */
+    public void copyUserProperties(Project other) {
+        Enumeration e = userProperties.keys();
+        while (e.hasMoreElements()) {
+            Object arg = e.nextElement();
+            if (inheritedProperties.containsKey(arg)) {
+                continue;
+            }
+            Object value = userProperties.get(arg);
+            other.setUserProperty(arg.toString(), value.toString());
+        }
+    }
+
+    // -------------------- Property parsing  --------------------
+    // Moved from ProjectHelper. You can override the static method -
+    // this is used for backward compatibility (for code that calls
+    // the parse method in ProjectHelper).
+
+    /** Default parsing method. It is here only to support backward compatibility
+     * for the static ProjectHelper.parsePropertyString().
+     */
+    static void parsePropertyStringDefault(String value, Vector fragments,
+                                    Vector propertyRefs)
+        throws BuildException {
+        int prev = 0;
+        int pos;
+        //search for the next instance of $ from the 'prev' position
+        while ((pos = value.indexOf("$", prev)) >= 0) {
+
+            //if there was any text before this, add it as a fragment
+            //TODO, this check could be modified to go if pos>prev;
+            //seems like this current version could stick empty strings
+            //into the list
+            if (pos > 0) {
+                fragments.addElement(value.substring(prev, pos));
+            }
+            //if we are at the end of the string, we tack on a $
+            //then move past it
+            if (pos == (value.length() - 1)) {
+                fragments.addElement("$");
+                prev = pos + 1;
+            } else if (value.charAt(pos + 1) != '{') {
+                //peek ahead to see if the next char is a property or not
+                //not a property: insert the char as a literal
+                /*
+                fragments.addElement(value.substring(pos + 1, pos + 2));
+                prev = pos + 2;
+                */
+                if (value.charAt(pos + 1) == '$') {
+                    //backwards compatibility two $ map to one mode
+                    fragments.addElement("$");
+                    prev = pos + 2;
+                } else {
+                    //new behaviour: $X maps to $X for all values of X!='$'
+                    fragments.addElement(value.substring(pos, pos + 2));
+                    prev = pos + 2;
+                }
+
+            } else {
+                //property found, extract its name or bail on a typo
+                int endName = value.indexOf('}', pos);
+                if (endName < 0) {
+                    throw new BuildException("Syntax error in property: "
+                                                 + value);
+                }
+                String propertyName = value.substring(pos + 2, endName);
+                fragments.addElement(null);
+                propertyRefs.addElement(propertyName);
+                prev = endName + 1;
+            }
+        }
+        //no more $ signs found
+        //if there is any tail to the file, append it
+        if (prev < value.length()) {
+            fragments.addElement(value.substring(prev));
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/RuntimeConfigurable.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/RuntimeConfigurable.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/RuntimeConfigurable.java	(revision 16139)
@@ -0,0 +1,473 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Iterator;
+
+import org.apache.tools.ant.util.CollectionUtils;
+import org.xml.sax.AttributeList;
+import org.xml.sax.helpers.AttributeListImpl;
+
+/**
+ * Wrapper class that holds the attributes of an element, its children, and
+ * any text within it. It then takes care of configuring that element at
+ * runtime.
+ *
+ */
+public class RuntimeConfigurable implements Serializable {
+
+    /** Polymorphic attribute (May be XML NS attribute later) */
+    private static final String ANT_TYPE = "ant-type";
+
+    /** Name of the element to configure. */
+    private String elementTag = null;
+
+    /** List of child element wrappers. */
+    private List/*<RuntimeConfigurable>*/ children = null;
+
+    /** The element to configure. It is only used during
+     * maybeConfigure.
+     */
+    private transient Object wrappedObject = null;
+
+    /** the creator used to make the wrapped object */
+    private transient IntrospectionHelper.Creator creator;
+
+    /**
+     * @deprecated
+     * XML attributes for the element.
+     */
+    private transient AttributeList attributes;
+
+    /** Attribute names and values. While the XML spec doesn't require
+     *  preserving the order ( AFAIK ), some ant tests do rely on the
+     *  exact order. The following code is copied from AttributeImpl.
+     *  We could also just use SAX2 Attributes and convert to SAX1 ( DOM
+     *  attribute Nodes can also be stored in SAX2 Attributes )
+     *  XXX under JDK 1.4 you can just use a LinkedHashMap for this purpose -jglick
+     */
+    private List/*<String>*/ attributeNames = null;
+
+    /** Map of attribute names to values */
+    private Map/*<String,String>*/ attributeMap = null;
+
+    /** Text appearing within the element. */
+    private StringBuffer characters = null;
+
+    /** Indicates if the wrapped object has been configured */
+    private boolean proxyConfigured = false;
+
+    /** the polymorphic type */
+    private String polyType = null;
+
+    /**
+     * Sole constructor creating a wrapper for the specified object.
+     *
+     * @param proxy The element to configure. Must not be <code>null</code>.
+     * @param elementTag The tag name generating this element.
+     *                   Should not be <code>null</code>.
+     */
+    public RuntimeConfigurable(Object proxy, String elementTag) {
+        wrappedObject = proxy;
+        this.elementTag = elementTag;
+        proxyConfigured = false;
+        // Most likely an UnknownElement
+        if (proxy instanceof Task) {
+            ((Task) proxy).setRuntimeConfigurableWrapper(this);
+        }
+    }
+
+    /**
+     * Sets the element to configure.
+     *
+     * @param proxy The element to configure. Must not be <code>null</code>.
+     */
+    public void setProxy(Object proxy) {
+        wrappedObject = proxy;
+        proxyConfigured = false;
+    }
+
+    /**
+     * Sets the creator of the element to be configured
+     * used to store the element in the parent;
+     *
+     * @param creator the creator object
+     */
+    void setCreator(IntrospectionHelper.Creator creator) {
+        this.creator = creator;
+    }
+
+    /**
+     * Get the object for which this RuntimeConfigurable holds the configuration
+     * information
+     *
+     * @return the object whose configure is held by this instance.
+     */
+    public Object getProxy() {
+        return wrappedObject;
+    }
+
+    /**
+     * get the polymorphic type for this element
+     * @return the ant component type name, null if not set
+     */
+    public String getPolyType() {
+        return polyType;
+    }
+
+    /**
+     * set the polymorphic type for this element
+     * @param polyType the ant component type name, null if not set
+     */
+    public void setPolyType(String polyType) {
+        this.polyType = polyType;
+    }
+
+    /**
+     * Sets the attributes for the wrapped element.
+     *
+     * @deprecated
+     * @param attributes List of attributes defined in the XML for this
+     *                   element. May be <code>null</code>.
+     */
+    public void setAttributes(AttributeList attributes) {
+        this.attributes = new AttributeListImpl(attributes);
+        for (int i = 0; i < attributes.getLength(); i++) {
+            setAttribute(attributes.getName(i), attributes.getValue(i));
+        }
+    }
+
+    /**
+     * Set an attribute to a given value
+     *
+     * @param name the name of the attribute.
+     * @param value the attribute's value.
+     */
+    public void setAttribute(String name, String value) {
+        if (name.equalsIgnoreCase(ANT_TYPE)) {
+            this.polyType = value;
+        } else {
+            if (attributeNames == null) {
+                attributeNames = new ArrayList();
+                attributeMap = new HashMap();
+            }
+            attributeNames.add(name);
+            attributeMap.put(name, value);
+        }
+    }
+
+    /** Return the attribute map.
+     *
+     * @return Attribute name to attribute value map
+     * @since Ant 1.6
+     */
+    public Hashtable getAttributeMap() {
+        if (attributeMap != null) {
+            return new Hashtable(attributeMap);
+        } else {
+            return new Hashtable(1);
+        }
+    }
+
+    /**
+     * Returns the list of attributes for the wrapped element.
+     *
+     * @deprecated Deprecated since Ant 1.6 in favor of {@link #getAttributeMap}.
+     * @return An AttributeList representing the attributes defined in the
+     *         XML for this element. May be <code>null</code>.
+     */
+    public AttributeList getAttributes() {
+        return attributes;
+    }
+
+    /**
+     * Adds a child element to the wrapped element.
+     *
+     * @param child The child element wrapper to add to this one.
+     *              Must not be <code>null</code>.
+     */
+    public void addChild(RuntimeConfigurable child) {
+        if (children == null) {
+            children = new ArrayList();
+        }
+        children.add(child);
+    }
+
+    /**
+     * Returns the child wrapper at the specified position within the list.
+     *
+     * @param index The index of the child to return.
+     *
+     * @return The child wrapper at position <code>index</code> within the
+     *         list.
+     */
+    RuntimeConfigurable getChild(int index) {
+        return (RuntimeConfigurable) children.get(index);
+    }
+
+    /**
+     * Returns an enumeration of all child wrappers.
+     * @return an enumeration of the child wrappers.
+     * @since Ant 1.6
+     */
+    public Enumeration getChildren() {
+        if (children != null) {
+            return Collections.enumeration(children);
+        } else {
+            return new CollectionUtils.EmptyEnumeration();
+        }
+    }
+
+    /**
+     * Adds characters from #PCDATA areas to the wrapped element.
+     *
+     * @param data Text to add to the wrapped element.
+     *        Should not be <code>null</code>.
+     */
+    public void addText(String data) {
+        if (data.length() == 0) {
+            return;
+        }
+        if (characters != null) {
+            characters.append(data);
+        } else {
+            characters = new StringBuffer(data);
+        }
+    }
+
+    /**
+     * Adds characters from #PCDATA areas to the wrapped element.
+     *
+     * @param buf A character array of the text within the element.
+     *            Must not be <code>null</code>.
+     * @param start The start element in the array.
+     * @param count The number of characters to read from the array.
+     *
+     */
+    public void addText(char[] buf, int start, int count) {
+        if (count == 0) {
+            return;
+        }
+        if (characters == null) {
+            characters = new StringBuffer(count);
+        }
+        characters.append(buf, start, count);
+    }
+
+    /** Get the text content of this element. Various text chunks are
+     * concatenated, there is no way ( currently ) of keeping track of
+     * multiple fragments.
+     *
+     * @return the text content of this element.
+     * @since Ant 1.6
+     */
+    public StringBuffer getText() {
+        if (characters != null) {
+            return characters;
+        } else {
+            return new StringBuffer(0);
+        }
+    }
+
+    /**
+     * Returns the tag name of the wrapped element.
+     *
+     * @return The tag name of the wrapped element. This is unlikely
+     *         to be <code>null</code>, but may be.
+     */
+    public String getElementTag() {
+        return elementTag;
+    }
+
+    /**
+     * Configures the wrapped element and all its children.
+     * The attributes and text for the wrapped element are configured,
+     * and then each child is configured and added. Each time the
+     * wrapper is configured, the attributes and text for it are
+     * reset.
+     *
+     * If the element has an <code>id</code> attribute, a reference
+     * is added to the project as well.
+     *
+     * @param p The project containing the wrapped element.
+     *          Must not be <code>null</code>.
+     *
+     * @exception BuildException if the configuration fails, for instance due
+     *            to invalid attributes or children, or text being added to
+     *            an element which doesn't accept it.
+     */
+    public void maybeConfigure(Project p) throws BuildException {
+        maybeConfigure(p, true);
+    }
+
+    /**
+     * Configures the wrapped element.  The attributes and text for
+     * the wrapped element are configured.  Each time the wrapper is
+     * configured, the attributes and text for it are reset.
+     *
+     * If the element has an <code>id</code> attribute, a reference
+     * is added to the project as well.
+     *
+     * @param p The project containing the wrapped element.
+     *          Must not be <code>null</code>.
+     *
+     * @param configureChildren Whether to configure child elements as
+     * well.  if true, child elements will be configured after the
+     * wrapped element.
+     *
+     * @exception BuildException if the configuration fails, for instance due
+     *            to invalid attributes or children, or text being added to
+     *            an element which doesn't accept it.
+     */
+    public void maybeConfigure(Project p, boolean configureChildren)
+        throws BuildException {
+        String id = null;
+
+        if (proxyConfigured) {
+            return;
+        }
+
+        // Configure the object
+        Object target = (wrappedObject instanceof TypeAdapter)
+            ? ((TypeAdapter) wrappedObject).getProxy() : wrappedObject;
+
+        //PropertyHelper ph=PropertyHelper.getPropertyHelper(p);
+        IntrospectionHelper ih =
+            IntrospectionHelper.getHelper(p, target.getClass());
+
+        if (attributeNames != null) {
+            for (int i = 0; i < attributeNames.size(); i++) {
+                String name = (String) attributeNames.get(i);
+                String value = (String) attributeMap.get(name);
+
+                // reflect these into the target
+                value = p.replaceProperties(value);
+                try {
+                    ih.setAttribute(p, target,
+                                    name.toLowerCase(Locale.US), value);
+                } catch (BuildException be) {
+                    // id attribute must be set externally
+                    if (!name.equals("id")) {
+                        throw be;
+                    }
+                }
+            }
+            id = (String) attributeMap.get("id");
+        }
+
+        if (characters != null) {
+            ProjectHelper.addText(p, wrappedObject, characters.substring(0));
+        }
+
+        Enumeration e = getChildren();
+        while (e.hasMoreElements()) {
+            RuntimeConfigurable child
+                    = (RuntimeConfigurable) e.nextElement();
+            if (child.wrappedObject instanceof Task) {
+                Task childTask = (Task) child.wrappedObject;
+                childTask.setRuntimeConfigurableWrapper(child);
+            }
+
+            if ((child.creator != null) && configureChildren) {
+                child.maybeConfigure(p);
+                child.creator.store();
+                continue;
+            }
+            /*
+             * backwards compatibility - element names of nested
+             * elements have been all lower-case in Ant, except for
+             * tasks in TaskContainers.
+             *
+             * For TaskContainers, we simply skip configuration here.
+             */
+            String tag = child.getElementTag().toLowerCase(Locale.US);
+            if (configureChildren
+                && ih.supportsNestedElement(tag)) {
+                child.maybeConfigure(p);
+                ProjectHelper.storeChild(p, target, child.wrappedObject,
+                                         tag);
+            }
+        }
+
+        if (id != null) {
+            p.addReference(id, wrappedObject);
+        }
+        proxyConfigured = true;
+    }
+
+    /**
+     * Reconfigure the element, even if it has already been configured.
+     *
+     * @param p the project instance for this configuration.
+     */
+    public void reconfigure(Project p) {
+        proxyConfigured = false;
+        maybeConfigure(p);
+    }
+
+
+    /**
+     * Apply presets, attributes and text are set if not currently set.
+     * nested elements are prepended.
+     *
+     * @param r a <code>RuntimeConfigurable</code> value
+     */
+    public void applyPreSet(RuntimeConfigurable r) {
+        // Attributes
+        if (r.attributeMap != null) {
+            for (Iterator i = r.attributeMap.keySet().iterator(); i.hasNext();) {
+                String name = (String) i.next();
+                if (attributeMap == null || attributeMap.get(name) == null) {
+                    setAttribute(name, (String) r.attributeMap.get(name));
+                }
+            }
+        }
+        // poly type
+        if (r.polyType != null && polyType == null) {
+            polyType = r.polyType;
+        }
+
+        // Children (this is a shadow of unknownElement#children)
+        if (r.children != null) {
+            List newChildren = new ArrayList();
+            newChildren.addAll(r.children);
+            if (children != null) {
+                newChildren.addAll(children);
+            }
+            children = newChildren;
+        }
+
+        // Text
+        if (r.characters != null) {
+            if (characters == null
+                || characters.toString().trim().length() == 0) {
+                characters =
+                    new StringBuffer(r.characters.toString());
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/RuntimeConfigurable.java.orig
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/RuntimeConfigurable.java.orig	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/RuntimeConfigurable.java.orig	(revision 16139)
@@ -0,0 +1,471 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Iterator;
+
+import org.apache.tools.ant.util.CollectionUtils;
+import org.xml.sax.AttributeList;
+import org.xml.sax.helpers.AttributeListImpl;
+
+/**
+ * Wrapper class that holds the attributes of an element, its children, and
+ * any text within it. It then takes care of configuring that element at
+ * runtime.
+ *
+ */
+public class RuntimeConfigurable implements Serializable {
+
+    /** Polymorphic attribute (May be XML NS attribute later) */
+    private static final String ANT_TYPE = "ant-type";
+
+    /** Name of the element to configure. */
+    private String elementTag = null;
+
+    /** List of child element wrappers. */
+    private List/*<RuntimeConfigurable>*/ children = null;
+
+    /** The element to configure. It is only used during
+     * maybeConfigure.
+     */
+    private transient Object wrappedObject = null;
+
+    /** the creator used to make the wrapped object */
+    private transient IntrospectionHelper.Creator creator;
+
+    /**
+     * @deprecated
+     * XML attributes for the element.
+     */
+    private transient AttributeList attributes;
+
+    /** Attribute names and values. While the XML spec doesn't require
+     *  preserving the order ( AFAIK ), some ant tests do rely on the
+     *  exact order. The following code is copied from AttributeImpl.
+     *  We could also just use SAX2 Attributes and convert to SAX1 ( DOM
+     *  attribute Nodes can also be stored in SAX2 Attributes )
+     *  XXX under JDK 1.4 you can just use a LinkedHashMap for this purpose -jglick
+     */
+    private List/*<String>*/ attributeNames = null;
+
+    /** Map of attribute names to values */
+    private Map/*<String,String>*/ attributeMap = null;
+
+    /** Text appearing within the element. */
+    private StringBuffer characters = null;
+
+    /** Indicates if the wrapped object has been configured */
+    private boolean proxyConfigured = false;
+
+    /** the polymorphic type */
+    private String polyType = null;
+
+    /**
+     * Sole constructor creating a wrapper for the specified object.
+     *
+     * @param proxy The element to configure. Must not be <code>null</code>.
+     * @param elementTag The tag name generating this element.
+     *                   Should not be <code>null</code>.
+     */
+    public RuntimeConfigurable(Object proxy, String elementTag) {
+        wrappedObject = proxy;
+        this.elementTag = elementTag;
+        proxyConfigured = false;
+        // Most likely an UnknownElement
+        if (proxy instanceof Task) {
+            ((Task) proxy).setRuntimeConfigurableWrapper(this);
+        }
+    }
+
+    /**
+     * Sets the element to configure.
+     *
+     * @param proxy The element to configure. Must not be <code>null</code>.
+     */
+    public void setProxy(Object proxy) {
+        wrappedObject = proxy;
+        proxyConfigured = false;
+    }
+
+    /**
+     * Sets the creator of the element to be configured
+     * used to store the element in the parent;
+     *
+     * @param creator the creator object
+     */
+    void setCreator(IntrospectionHelper.Creator creator) {
+        this.creator = creator;
+    }
+
+    /**
+     * Get the object for which this RuntimeConfigurable holds the configuration
+     * information
+     *
+     * @return the object whose configure is held by this instance.
+     */
+    public Object getProxy() {
+        return wrappedObject;
+    }
+
+    /**
+     * get the polymorphic type for this element
+     * @return the ant component type name, null if not set
+     */
+    public String getPolyType() {
+        return polyType;
+    }
+
+    /**
+     * set the polymorphic type for this element
+     * @param polyType the ant component type name, null if not set
+     */
+    public void setPolyType(String polyType) {
+        this.polyType = polyType;
+    }
+
+    /**
+     * Sets the attributes for the wrapped element.
+     *
+     * @deprecated
+     * @param attributes List of attributes defined in the XML for this
+     *                   element. May be <code>null</code>.
+     */
+    public void setAttributes(AttributeList attributes) {
+        this.attributes = new AttributeListImpl(attributes);
+        for (int i = 0; i < attributes.getLength(); i++) {
+            setAttribute(attributes.getName(i), attributes.getValue(i));
+        }
+    }
+
+    /**
+     * Set an attribute to a given value
+     *
+     * @param name the name of the attribute.
+     * @param value the attribute's value.
+     */
+    public void setAttribute(String name, String value) {
+        if (name.equalsIgnoreCase(ANT_TYPE)) {
+            this.polyType = value;
+        } else {
+            if (attributeNames == null) {
+                attributeNames = new ArrayList();
+                attributeMap = new HashMap();
+            }
+            attributeNames.add(name);
+            attributeMap.put(name, value);
+        }
+    }
+
+    /** Return the attribute map.
+     *
+     * @return Attribute name to attribute value map
+     */
+    public Hashtable getAttributeMap() {
+        if (attributeMap != null) {
+            return new Hashtable(attributeMap);
+        } else {
+            return new Hashtable(1);
+        }
+    }
+
+    /**
+     * Returns the list of attributes for the wrapped element.
+     *
+     * @deprecated
+     * @return An AttributeList representing the attributes defined in the
+     *         XML for this element. May be <code>null</code>.
+     */
+    public AttributeList getAttributes() {
+        return attributes;
+    }
+
+    /**
+     * Adds a child element to the wrapped element.
+     *
+     * @param child The child element wrapper to add to this one.
+     *              Must not be <code>null</code>.
+     */
+    public void addChild(RuntimeConfigurable child) {
+        if (children == null) {
+            children = new ArrayList();
+        }
+        children.add(child);
+    }
+
+    /**
+     * Returns the child wrapper at the specified position within the list.
+     *
+     * @param index The index of the child to return.
+     *
+     * @return The child wrapper at position <code>index</code> within the
+     *         list.
+     */
+    RuntimeConfigurable getChild(int index) {
+        return (RuntimeConfigurable) children.get(index);
+    }
+
+    /**
+     * Returns an enumeration of all child wrappers.
+     * @return an enumeration of the child wrappers.
+     * @since Ant 1.5.1
+     */
+    public Enumeration getChildren() {
+        if (children != null) {
+            return Collections.enumeration(children);
+        } else {
+            return new CollectionUtils.EmptyEnumeration();
+        }
+    }
+
+    /**
+     * Adds characters from #PCDATA areas to the wrapped element.
+     *
+     * @param data Text to add to the wrapped element.
+     *        Should not be <code>null</code>.
+     */
+    public void addText(String data) {
+        if (data.length() == 0) {
+            return;
+        }
+        if (characters != null) {
+            characters.append(data);
+        } else {
+            characters = new StringBuffer(data);
+        }
+    }
+
+    /**
+     * Adds characters from #PCDATA areas to the wrapped element.
+     *
+     * @param buf A character array of the text within the element.
+     *            Must not be <code>null</code>.
+     * @param start The start element in the array.
+     * @param count The number of characters to read from the array.
+     *
+     */
+    public void addText(char[] buf, int start, int count) {
+        if (count == 0) {
+            return;
+        }
+        if (characters == null) {
+            characters = new StringBuffer(count);
+        }
+        characters.append(buf, start, count);
+    }
+
+    /** Get the text content of this element. Various text chunks are
+     * concatenated, there is no way ( currently ) of keeping track of
+     * multiple fragments.
+     *
+     * @return the text content of this element.
+     */
+    public StringBuffer getText() {
+        if (characters != null) {
+            return characters;
+        } else {
+            return new StringBuffer(0);
+        }
+    }
+
+    /**
+     * Returns the tag name of the wrapped element.
+     *
+     * @return The tag name of the wrapped element. This is unlikely
+     *         to be <code>null</code>, but may be.
+     */
+    public String getElementTag() {
+        return elementTag;
+    }
+
+    /**
+     * Configures the wrapped element and all its children.
+     * The attributes and text for the wrapped element are configured,
+     * and then each child is configured and added. Each time the
+     * wrapper is configured, the attributes and text for it are
+     * reset.
+     *
+     * If the element has an <code>id</code> attribute, a reference
+     * is added to the project as well.
+     *
+     * @param p The project containing the wrapped element.
+     *          Must not be <code>null</code>.
+     *
+     * @exception BuildException if the configuration fails, for instance due
+     *            to invalid attributes or children, or text being added to
+     *            an element which doesn't accept it.
+     */
+    public void maybeConfigure(Project p) throws BuildException {
+        maybeConfigure(p, true);
+    }
+
+    /**
+     * Configures the wrapped element.  The attributes and text for
+     * the wrapped element are configured.  Each time the wrapper is
+     * configured, the attributes and text for it are reset.
+     *
+     * If the element has an <code>id</code> attribute, a reference
+     * is added to the project as well.
+     *
+     * @param p The project containing the wrapped element.
+     *          Must not be <code>null</code>.
+     *
+     * @param configureChildren Whether to configure child elements as
+     * well.  if true, child elements will be configured after the
+     * wrapped element.
+     *
+     * @exception BuildException if the configuration fails, for instance due
+     *            to invalid attributes or children, or text being added to
+     *            an element which doesn't accept it.
+     */
+    public void maybeConfigure(Project p, boolean configureChildren)
+        throws BuildException {
+        String id = null;
+
+        if (proxyConfigured) {
+            return;
+        }
+
+        // Configure the object
+        Object target = (wrappedObject instanceof TypeAdapter)
+            ? ((TypeAdapter) wrappedObject).getProxy() : wrappedObject;
+
+        //PropertyHelper ph=PropertyHelper.getPropertyHelper(p);
+        IntrospectionHelper ih =
+            IntrospectionHelper.getHelper(p, target.getClass());
+
+        if (attributeNames != null) {
+            for (int i = 0; i < attributeNames.size(); i++) {
+                String name = (String) attributeNames.get(i);
+                String value = (String) attributeMap.get(name);
+
+                // reflect these into the target
+                value = p.replaceProperties(value);
+                try {
+                    ih.setAttribute(p, target,
+                                    name.toLowerCase(Locale.US), value);
+                } catch (BuildException be) {
+                    // id attribute must be set externally
+                    if (!name.equals("id")) {
+                        throw be;
+                    }
+                }
+            }
+            id = (String) attributeMap.get("id");
+        }
+
+        if (characters != null) {
+            ProjectHelper.addText(p, wrappedObject, characters.substring(0));
+        }
+
+        Enumeration e = getChildren();
+        while (e.hasMoreElements()) {
+            RuntimeConfigurable child
+                    = (RuntimeConfigurable) e.nextElement();
+            if (child.wrappedObject instanceof Task) {
+                Task childTask = (Task) child.wrappedObject;
+                childTask.setRuntimeConfigurableWrapper(child);
+            }
+
+            if ((child.creator != null) && configureChildren) {
+                child.maybeConfigure(p);
+                child.creator.store();
+                continue;
+            }
+            /*
+             * backwards compatibility - element names of nested
+             * elements have been all lower-case in Ant, except for
+             * tasks in TaskContainers.
+             *
+             * For TaskContainers, we simply skip configuration here.
+             */
+            String tag = child.getElementTag().toLowerCase(Locale.US);
+            if (configureChildren
+                && ih.supportsNestedElement(tag)) {
+                child.maybeConfigure(p);
+                ProjectHelper.storeChild(p, target, child.wrappedObject,
+                                         tag);
+            }
+        }
+
+        if (id != null) {
+            p.addReference(id, wrappedObject);
+        }
+        proxyConfigured = true;
+    }
+
+    /**
+     * Reconfigure the element, even if it has already been configured.
+     *
+     * @param p the project instance for this configuration.
+     */
+    public void reconfigure(Project p) {
+        proxyConfigured = false;
+        maybeConfigure(p);
+    }
+
+
+    /**
+     * Apply presets, attributes and text are set if not currently set.
+     * nested elements are prepended.
+     *
+     * @param r a <code>RuntimeConfigurable</code> value
+     */
+    public void applyPreSet(RuntimeConfigurable r) {
+        // Attributes
+        if (r.attributeMap != null) {
+            for (Iterator i = r.attributeMap.keySet().iterator(); i.hasNext();) {
+                String name = (String) i.next();
+                if (attributeMap == null || attributeMap.get(name) == null) {
+                    setAttribute(name, (String) r.attributeMap.get(name));
+                }
+            }
+        }
+        // poly type
+        if (r.polyType != null && polyType == null) {
+            polyType = r.polyType;
+        }
+
+        // Children (this is a shadow of unknownElement#children)
+        if (r.children != null) {
+            List newChildren = new ArrayList();
+            newChildren.addAll(r.children);
+            if (children != null) {
+                newChildren.addAll(children);
+            }
+            children = newChildren;
+        }
+
+        // Text
+        if (r.characters != null) {
+            if (characters == null
+                || characters.toString().trim().length() == 0) {
+                characters =
+                    new StringBuffer(r.characters.toString());
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/SubBuildListener.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/SubBuildListener.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/SubBuildListener.java	(revision 16139)
@@ -0,0 +1,57 @@
+/*
+ * Copyright  2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+/**
+ * Instances of classes that implement this interface can register
+ * to be also notified when things happened during a subbuild.
+ *
+ * <p>A subbuild is a separate project instance created by the
+ * <code>&lt;ant&gt;</code> task family.  These project instances will
+ * never fire the buildStarted and buildFinished events, but they will
+ * fire subBuildStarted/ and subBuildFinished.  The main project
+ * instance - the one created by running Ant in the first place - will
+ * never invoke one of the methods of this interface.</p>
+ *
+ * @see BuildEvent
+ * @see Project#addBuildListener(BuildListener)
+ *
+ * @since Ant 1.6.2
+ */
+public interface SubBuildListener extends BuildListener {
+
+    /**
+     * Signals that a subbuild has started. This event
+     * is fired before any targets have started.
+     *
+     * @param event An event with any relevant extra information.
+     *              Must not be <code>null</code>.
+     */
+    void subBuildStarted(BuildEvent event);
+
+    /**
+     * Signals that the last target has finished. This event
+     * will still be fired if an error occurred during the build.
+     *
+     * @param event An event with any relevant extra information.
+     *              Must not be <code>null</code>.
+     *
+     * @see BuildEvent#getException()
+     */
+    void subBuildFinished(BuildEvent event);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/Target.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/Target.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/Target.java	(revision 16139)
@@ -0,0 +1,480 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.util.CollectionUtils;
+
+/**
+ * Class to implement a target object with required parameters.
+ *
+ */
+public class Target implements TaskContainer {
+
+    /** Name of this target. */
+    private String name;
+    /** The "if" condition to test on execution. */
+    private String ifCondition = "";
+    /** The "unless" condition to test on execution. */
+    private String unlessCondition = "";
+    /** List of targets this target is dependent on. */
+    private List dependencies = null;
+    /** Children of this target (tasks and data types). */
+    private List children = new ArrayList();
+    /** Since Ant 1.6.2 */
+    private Location location = Location.UNKNOWN_LOCATION;
+
+    /** Oran's: The node address stuff for this target. */
+    private String address = null;
+	 private int nextSubAddress = 1;
+
+
+    /** Project this target belongs to. */
+    private Project project;
+
+    /** Description of this target, if any. */
+    private String description = null;
+
+    /** Sole constructor. */
+    public Target() {
+    }
+
+    /**
+     * Sets the project this target belongs to.
+     *
+     * @param project The project this target belongs to.
+     *                Must not be <code>null</code>.
+     */
+    public void setProject(Project project) {
+        this.project = project;
+    }
+
+    /**
+     * Returns the project this target belongs to.
+     *
+     * @return The project this target belongs to, or <code>null</code> if
+     *         the project has not been set yet.
+     */
+    public Project getProject() {
+        return project;
+    }
+
+    /**
+     * Sets the location of this target's definition.
+     *
+     * @param location   <CODE>Location</CODE>
+     * @since 1.6.2
+     */
+    public void setLocation(Location location) {
+        this.location = location;
+    }
+
+    /**
+     * Get the location of this target's definition.
+     *
+     * @return <CODE>Location</CODE>
+     * @since 1.6.2
+     */
+    public Location getLocation() {
+        return location;
+    }
+
+    /**
+     * Sets the list of targets this target is dependent on.
+     * The targets themselves are not resolved at this time.
+     *
+     * @param depS A comma-separated list of targets this target
+     *             depends on. Must not be <code>null</code>.
+     */
+    public void setDepends(String depS) {
+        if (depS.length() > 0) {
+            StringTokenizer tok =
+                new StringTokenizer(depS, ",", true);
+            while (tok.hasMoreTokens()) {
+                String token = tok.nextToken().trim();
+
+                // Make sure the dependency is not empty string
+                if (token.equals("") || token.equals(",")) {
+                    throw new BuildException("Syntax Error: Depend "
+                        + "attribute for target \"" + getName()
+                        + "\" has an empty string for dependency.");
+                }
+
+                addDependency(token);
+
+                // Make sure that depends attribute does not
+                // end in a ,
+                if (tok.hasMoreTokens()) {
+                    token = tok.nextToken();
+                    if (!tok.hasMoreTokens() || !token.equals(",")) {
+                        throw new BuildException("Syntax Error: Depend "
+                            + "attribute for target \"" + getName()
+                            + "\" ends with a , character");
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets the name of this target.
+     *
+     * @param name The name of this target. Should not be <code>null</code>.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Returns the name of this target.
+     *
+     * @return the name of this target, or <code>null</code> if the
+     *         name has not been set yet.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Adds a task to this target.
+     *
+     * @param task The task to be added. Must not be <code>null</code>.
+     */
+    public void addTask(Task task) {
+        children.add(task);
+    }
+
+    /**
+     * Adds the wrapper for a data type element to this target.
+     *
+     * @param r The wrapper for the data type element to be added.
+     *          Must not be <code>null</code>.
+     */
+    public void addDataType(RuntimeConfigurable r) {
+        children.add(r);
+    }
+
+    /**
+     * Returns the current set of tasks to be executed by this target.
+     *
+     * @return an array of the tasks currently within this target
+     */
+    public Task[] getTasks() {
+        List tasks = new ArrayList(children.size());
+        Iterator it = children.iterator();
+        while (it.hasNext()) {
+            Object o = it.next();
+            if (o instanceof Task) {
+                tasks.add(o);
+            }
+        }
+
+        return (Task[]) tasks.toArray(new Task[tasks.size()]);
+    }
+
+    /**
+     * Adds a dependency to this target.
+     *
+     * @param dependency The name of a target this target is dependent on.
+     *                   Must not be <code>null</code>.
+     */
+    public void addDependency(String dependency) {
+        if (dependencies == null) {
+            dependencies = new ArrayList(2);
+        }
+        dependencies.add(dependency);
+    }
+
+    /**
+     * Returns an enumeration of the dependencies of this target.
+     *
+     * @return an enumeration of the dependencies of this target
+     */
+    public Enumeration getDependencies() {
+        if (dependencies != null) {
+            return Collections.enumeration(dependencies);
+        } else {
+            return new CollectionUtils.EmptyEnumeration();
+        }
+    }
+
+    /**
+     * Does this target depend on the named target?
+     * @param other the other named target.
+     * @return true if the target does depend on the named target
+     * @since Ant 1.6
+     */
+    public boolean dependsOn(String other) {
+        Project p = getProject();
+        Hashtable t = (p == null) ? null : p.getTargets();
+        return (p != null && p.topoSort(getName(), t, false).contains(t.get(other)));
+    }
+
+    /**
+     * Sets the "if" condition to test on execution. This is the
+     * name of a property to test for existence - if the property
+     * is not set, the task will not execute. The property goes
+     * through property substitution once before testing, so if
+     * property <code>foo</code> has value <code>bar</code>, setting
+     * the "if" condition to <code>${foo}_x</code> will mean that the
+     * task will only execute if property <code>bar_x</code> is set.
+     *
+     * @param property The property condition to test on execution.
+     *                 May be <code>null</code>, in which case
+     *                 no "if" test is performed.
+     */
+    public void setIf(String property) {
+        this.ifCondition = (property == null) ? "" : property;
+    }
+
+    /**
+     * Returns the "if" property condition of this target.
+     *
+     * @return the "if" property condition or <code>null</code> if no
+     *         "if" condition had been defined.
+     * @since 1.6.2
+     */
+    public String getIf() {
+        return ("".equals(ifCondition) ? null : ifCondition);
+    }
+
+    /**
+     * Sets the "unless" condition to test on execution. This is the
+     * name of a property to test for existence - if the property
+     * is set, the task will not execute. The property goes
+     * through property substitution once before testing, so if
+     * property <code>foo</code> has value <code>bar</code>, setting
+     * the "unless" condition to <code>${foo}_x</code> will mean that the
+     * task will only execute if property <code>bar_x</code> isn't set.
+     *
+     * @param property The property condition to test on execution.
+     *                 May be <code>null</code>, in which case
+     *                 no "unless" test is performed.
+     */
+    public void setUnless(String property) {
+        this.unlessCondition = (property == null) ? "" : property;
+    }
+
+    /**
+     * Returns the "unless" property condition of this target.
+     *
+     * @return the "unless" property condition or <code>null</code>
+     *         if no "unless" condition had been defined.
+     * @since 1.6.2
+     */
+    public String getUnless() {
+        return ("".equals(unlessCondition) ? null : unlessCondition);
+    }
+
+    /**
+     * Sets the description of this target.
+     *
+     * @param description The description for this target.
+     *                    May be <code>null</code>, indicating that no
+     *                    description is available.
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * Returns the description of this target.
+     *
+     * @return the description of this target, or <code>null</code> if no
+     *         description is available.
+     */
+    public String getDescription() {
+        return description;
+    }
+
+	/**
+	* Sets the address of this target.
+	*
+	* @param address The address of this target. Should not be <code>null</code>.
+	*/
+	public void setAddress(String address) {
+		//check in the form int.int.int
+		
+		//set
+		this.address = address;
+	}
+
+	/**
+	* Returns the address of this target.
+	*
+	* @return the address of this target, or <code>null</code> if the
+	*         address has not been set yet.
+	*/
+	public String getAddress() {
+	   return address;
+	}
+
+	public void incrementNextSubAddress() {
+		nextSubAddress++;
+	}
+
+	public int getNextSubAddress() {
+		return nextSubAddress;
+	}
+
+
+
+
+    /**
+     * Returns the name of this target.
+     *
+     * @return the name of this target, or <code>null</code> if the
+     *         name has not been set yet.
+     */
+    public String toString() {
+        return name;
+    }
+
+    /**
+     * Executes the target if the "if" and "unless" conditions are
+     * satisfied. Dependency checking should be done before calling this
+     * method, as it does no checking of its own. If either the "if"
+     * or "unless" test prevents this target from being executed, a verbose
+     * message is logged giving the reason. It is recommended that clients
+     * of this class call performTasks rather than this method so that
+     * appropriate build events are fired.
+     *
+     * @exception BuildException if any of the tasks fail or if a data type
+     *                           configuration fails.
+     *
+     * @see #performTasks()
+     * @see #setIf(String)
+     * @see #setUnless(String)
+     */
+    public void execute() throws BuildException {
+        if (testIfCondition() && testUnlessCondition()) {
+            for (int taskPosition = 0;
+                 taskPosition < children.size();
+                 ++taskPosition) {
+                Object o = children.get(taskPosition);
+                if (o instanceof Task) {
+                    Task task = (Task) o;
+                    task.perform();
+                } else {
+                    RuntimeConfigurable r = (RuntimeConfigurable) o;
+                    r.maybeConfigure(project);
+                }
+            }
+        } else if (!testIfCondition()) {
+            project.log(this, "Skipped because property '"
+                        + project.replaceProperties(this.ifCondition)
+                        + "' not set.", Project.MSG_VERBOSE);
+        } else {
+            project.log(this, "Skipped because property '"
+                        + project.replaceProperties(this.unlessCondition)
+                        + "' set.", Project.MSG_VERBOSE);
+        }
+    }
+
+    /**
+     * Performs the tasks within this target (if the conditions are met),
+     * firing target started/target finished messages around a call to
+     * execute.
+     *
+     * @see #execute()
+     */
+    public final void performTasks() {
+        RuntimeException thrown = null;
+        project.fireTargetStarted(this);
+        try {
+            execute();
+        } catch (RuntimeException exc) {
+            thrown = exc;
+            throw exc;
+        } finally {
+            project.fireTargetFinished(this, thrown);
+        }
+    }
+
+    /**
+     * Replaces all occurrences of the given task in the list
+     * of children with the replacement data type wrapper.
+     *
+     * @param el The task to replace.
+     *           Must not be <code>null</code>.
+     * @param o  The data type wrapper to replace <code>el</code> with.
+     */
+    void replaceChild(Task el, RuntimeConfigurable o) {
+        int index;
+        while ((index = children.indexOf(el)) >= 0) {
+            children.set(index, o);
+        }
+    }
+
+    /**
+     * Replaces all occurrences of the given task in the list
+     * of children with the replacement task.
+     *
+     * @param el The task to replace.
+     *           Must not be <code>null</code>.
+     * @param o  The task to replace <code>el</code> with.
+     */
+    void replaceChild(Task el, Task o) {
+        int index;
+        while ((index = children.indexOf(el)) >= 0) {
+            children.set(index, o);
+        }
+    }
+
+    /**
+     * Tests whether or not the "if" condition is satisfied.
+     *
+     * @return whether or not the "if" condition is satisfied. If no
+     *         condition (or an empty condition) has been set,
+     *         <code>true</code> is returned.
+     *
+     * @see #setIf(String)
+     */
+    private boolean testIfCondition() {
+        if ("".equals(ifCondition)) {
+            return true;
+        }
+
+        String test = project.replaceProperties(ifCondition);
+        return project.getProperty(test) != null;
+    }
+
+    /**
+     * Tests whether or not the "unless" condition is satisfied.
+     *
+     * @return whether or not the "unless" condition is satisfied. If no
+     *         condition (or an empty condition) has been set,
+     *         <code>true</code> is returned.
+     *
+     * @see #setUnless(String)
+     */
+    private boolean testUnlessCondition() {
+        if ("".equals(unlessCondition)) {
+            return true;
+        }
+        String test = project.replaceProperties(unlessCondition);
+        return project.getProperty(test) == null;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/Task.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/Task.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/Task.java	(revision 16139)
@@ -0,0 +1,477 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.util.Enumeration;
+import java.io.IOException;
+
+/**
+ * Base class for all tasks.
+ *
+ * Use Project.createTask to create a new task instance rather than
+ * using this class directly for construction.
+ *
+ * @see Project#createTask
+ */
+public abstract class Task extends ProjectComponent {
+    /**
+     * Target this task belongs to, if any.
+     * @deprecated You should not be accessing this variable directly.
+     *   Please use the {@link #getOwningTarget()} method.
+     */
+    protected Target target;
+
+    /**
+     * Description of this task, if any.
+     * @deprecated You should not be accessing this variable directly.
+     */
+    protected String description;
+
+    /**
+     * Location within the build file of this task definition.
+     * @deprecated You should not be accessing this variable directly.
+     *   Please use the {@link #getLocation()} method.
+     */
+    protected Location location = Location.UNKNOWN_LOCATION;
+
+    /**
+     * Name of this task to be used for logging purposes.
+     * This defaults to the same as the type, but may be
+     * overridden by the user. For instance, the name "java"
+     * isn't terribly descriptive for a task used within
+     * another task - the outer task code can probably
+     * provide a better one.
+     * @deprecated You should not be accessing this variable directly.
+     *   Please use the {@link #getTaskName()} method.
+     */
+    protected String taskName;
+
+    /**
+     * Type of this task.
+     *
+     * @deprecated You should not be accessing this variable directly.
+     *   Please use the {@link #getTaskType()} method.
+     */
+    protected String taskType;
+
+    /**
+     * Wrapper for this object, used to configure it at runtime.
+     *
+     * @deprecated You should not be accessing this variable directly.
+     *   Please use the {@link #getWrapper()} method.
+     */
+    protected RuntimeConfigurable wrapper;
+
+    /**
+     * Whether or not this task is invalid. A task becomes invalid
+     * if a conflicting class is specified as the implementation for
+     * its type.
+     */
+    private boolean invalid;
+
+    /** Sole constructor. */
+    public Task() {
+    }
+
+    /**
+     * Sets the target container of this task.
+     *
+     * @param target Target in whose scope this task belongs.
+     *               May be <code>null</code>, indicating a top-level task.
+     */
+    public void setOwningTarget(Target target) {
+        this.target = target;
+    }
+
+    /**
+     * Returns the container target of this task.
+     *
+     * @return The target containing this task, or <code>null</code> if
+     *         this task is a top-level task.
+     */
+    public Target getOwningTarget() {
+        return target;
+    }
+
+    /**
+     * Sets the name to use in logging messages.
+     *
+     * @param name The name to use in logging messages.
+     *             Should not be <code>null</code>.
+     */
+    public void setTaskName(String name) {
+        this.taskName = name;
+    }
+
+    /**
+     * Returns the name to use in logging messages.
+     *
+     * @return the name to use in logging messages.
+     */
+    public String getTaskName() {
+        return taskName;
+    }
+
+    /**
+     * Sets the name with which the task has been invoked.
+     *
+     * @param type The name the task has been invoked as.
+     *             Should not be <code>null</code>.
+     */
+    public void setTaskType(String type) {
+        this.taskType = type;
+    }
+
+    /**
+     * Sets a description of the current action. This may be used for logging
+     * purposes.
+     *
+     * @param desc Description of the current action.
+     *             May be <code>null</code>, indicating that no description is
+     *             available.
+     *
+     */
+    public void setDescription(String desc) {
+        description = desc;
+    }
+
+    /**
+     * Returns the description of the current action.
+     *
+     * @return the description of the current action, or <code>null</code> if
+     *         no description is available.
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Called by the project to let the task initialize properly.
+     * The default implementation is a no-op.
+     *
+     * @exception BuildException if something goes wrong with the build
+     */
+    public void init() throws BuildException {
+    }
+
+    /**
+     * Called by the project to let the task do its work. This method may be
+     * called more than once, if the task is invoked more than once.
+     * For example,
+     * if target1 and target2 both depend on target3, then running
+     * "ant target1 target2" will run all tasks in target3 twice.
+     *
+     * @exception BuildException if something goes wrong with the build
+     */
+    public void execute() throws BuildException {
+    }
+
+    /**
+     * Returns the file/location where this task was defined.
+     *
+     * @return the file/location where this task was defined.
+     *         Should not return <code>null</code>. Location.UNKNOWN_LOCATION
+     *         is used for unknown locations.
+     *
+     * @see Location#UNKNOWN_LOCATION
+     */
+    public Location getLocation() {
+        return location;
+    }
+
+    /**
+     * Sets the file/location where this task was defined.
+     *
+     * @param location The file/location where this task was defined.
+     *                 Should not be <code>null</code> - use
+     *                 Location.UNKNOWN_LOCATION if the location isn't known.
+     *
+     * @see Location#UNKNOWN_LOCATION
+     */
+    public void setLocation(Location location) {
+        this.location = location;
+    }
+
+    /**
+     * Returns the wrapper used for runtime configuration.
+     *
+     * @return the wrapper used for runtime configuration. This
+     *         method will generate a new wrapper (and cache it)
+     *         if one isn't set already.
+     */
+    public RuntimeConfigurable getRuntimeConfigurableWrapper() {
+        if (wrapper == null) {
+            wrapper = new RuntimeConfigurable(this, getTaskName());
+        }
+        return wrapper;
+    }
+
+    /**
+     * Sets the wrapper to be used for runtime configuration.
+     *
+     * This method should be used only by the ProjectHelper and ant internals.
+     * It is public to allow helper plugins to operate on tasks, normal tasks
+     * should never use it.
+     *
+     * @param wrapper The wrapper to be used for runtime configuration.
+     *                May be <code>null</code>, in which case the next call
+     *                to getRuntimeConfigurableWrapper will generate a new
+     *                wrapper.
+     */
+    public void setRuntimeConfigurableWrapper(RuntimeConfigurable wrapper) {
+        this.wrapper = wrapper;
+    }
+
+    // XXX: (Jon Skeet) The comment "if it hasn't been done already" may
+    // not be strictly true. wrapper.maybeConfigure() won't configure the same
+    // attributes/text more than once, but it may well add the children again,
+    // unless I've missed something.
+    /**
+     * Configures this task - if it hasn't been done already.
+     * If the task has been invalidated, it is replaced with an
+     * UnknownElement task which uses the new definition in the project.
+     *
+     * @exception BuildException if the task cannot be configured.
+     */
+    public void maybeConfigure() throws BuildException {
+        if (!invalid) {
+            if (wrapper != null) {
+                wrapper.maybeConfigure(getProject());
+            }
+        } else {
+            getReplacement();
+        }
+    }
+
+    /**
+     * Force the task to be reconfigured from it's RuntimeConfigurable
+     *
+     */
+    public void reconfigure() {
+        if (wrapper != null) {
+            wrapper.reconfigure(getProject());
+        }
+    }
+
+    /**
+     * Handles output by logging it with the INFO priority.
+     *
+     * @param output The output to log. Should not be <code>null</code>.
+     */
+    protected void handleOutput(String output) {
+        log(output, Project.MSG_INFO);
+    }
+
+    /**
+     * Handles output by logging it with the INFO priority.
+     *
+     * @param output The output to log. Should not be <code>null</code>.
+     *
+     * @since Ant 1.5.2
+     */
+    protected void handleFlush(String output) {
+        handleOutput(output);
+    }
+
+    /**
+     * Handle an input request by this task
+     *
+     * @param buffer the buffer into which data is to be read.
+     * @param offset the offset into the buffer at which data is stored.
+     * @param length the amount of data to read
+     *
+     * @return the number of bytes read
+     *
+     * @exception IOException if the data cannot be read
+     * @since Ant 1.6
+     */
+    protected int handleInput(byte[] buffer, int offset, int length)
+        throws IOException {
+        return getProject().defaultInput(buffer, offset, length);
+    }
+
+    /**
+     * Handles an error output by logging it with the WARN priority.
+     *
+     * @param output The error output to log. Should not be <code>null</code>.
+     */
+    protected void handleErrorOutput(String output) {
+        log(output, Project.MSG_WARN);
+    }
+
+    /**
+     * Handles an error line by logging it with the WARN priority.
+     *
+     * @param output The error output to log. Should not be <code>null</code>.
+     *
+     * @since Ant 1.5.2
+     */
+    protected void handleErrorFlush(String output) {
+        handleErrorOutput(output);
+    }
+
+    /**
+     * Logs a message with the default (INFO) priority.
+     *
+     * @param msg The message to be logged. Should not be <code>null</code>.
+     */
+    public void log(String msg) {
+        log(msg, Project.MSG_INFO);
+    }
+
+    /**
+     * Logs a message with the given priority. This delegates
+     * the actual logging to the project.
+     *
+     * @param msg The message to be logged. Should not be <code>null</code>.
+     * @param msgLevel The message priority at which this message is to
+     *                 be logged.
+     */
+    public void log(String msg, int msgLevel) {
+        getProject().log(this, msg, msgLevel);
+    }
+
+    /**
+     * Performs this task if it's still valid, or gets a replacement
+     * version and performs that otherwise.
+     *
+     * Performing a task consists of firing a task started event,
+     * configuring the task, executing it, and then firing task finished
+     * event. If a runtime exception is thrown, the task finished event
+     * is still fired, but with the exception as the cause.
+     */
+    public final void perform() {
+        if (!invalid) {
+            getProject().fireTaskStarted(this);
+            Throwable reason = null;
+            try {
+                maybeConfigure();
+                execute();
+            } catch (BuildException ex) {
+                if (ex.getLocation() == Location.UNKNOWN_LOCATION) {
+                    ex.setLocation(getLocation());
+                }
+                reason = ex;
+                throw ex;
+            } catch (Exception ex) {
+                reason = ex;
+                BuildException be = new BuildException(ex);
+                be.setLocation(getLocation());
+                throw be;
+            } catch (Error ex) {
+                reason = ex;
+                throw ex;
+            } finally {
+                getProject().fireTaskFinished(this, reason);
+            }
+        } else {
+            UnknownElement ue = getReplacement();
+            Task task = ue.getTask();
+            task.perform();
+        }
+    }
+
+    /**
+     * Marks this task as invalid. Any further use of this task
+     * will go through a replacement with the updated definition.
+     */
+    final void markInvalid() {
+        invalid = true;
+    }
+
+    /**
+     * Has this task been marked invalid?
+     *
+     * @return true if this task is no longer valid. A new task should be
+     * configured in this case.
+     *
+     * @since Ant 1.5
+     */
+    protected final boolean isInvalid() {
+        return invalid;
+    }
+
+    /**
+     * Replacement element used if this task is invalidated.
+     */
+    private UnknownElement replacement;
+
+    /**
+     * Creates an UnknownElement that can be used to replace this task.
+     * Once this has been created once, it is cached and returned by
+     * future calls.
+     *
+     * @return the UnknownElement instance for the new definition of this task.
+     */
+    private UnknownElement getReplacement() {
+        if (replacement == null) {
+            replacement = new UnknownElement(taskType);
+            replacement.setProject(getProject());
+            replacement.setTaskType(taskType);
+            replacement.setTaskName(taskName);
+            replacement.setLocation(location);
+            replacement.setOwningTarget(target);
+            replacement.setRuntimeConfigurableWrapper(wrapper);
+            wrapper.setProxy(replacement);
+            replaceChildren(wrapper, replacement);
+            target.replaceChild(this, replacement);
+            replacement.maybeConfigure();
+        }
+        return replacement;
+    }
+
+    /**
+     * Recursively adds an UnknownElement instance for each child
+     * element of replacement.
+     *
+     * @since Ant 1.5.1
+     */
+    private void replaceChildren(RuntimeConfigurable wrapper,
+                                 UnknownElement parentElement) {
+        Enumeration e = wrapper.getChildren();
+        while (e.hasMoreElements()) {
+            RuntimeConfigurable childWrapper =
+                (RuntimeConfigurable) e.nextElement();
+            UnknownElement childElement =
+                new UnknownElement(childWrapper.getElementTag());
+            parentElement.addChild(childElement);
+            childElement.setProject(getProject());
+            childElement.setRuntimeConfigurableWrapper(childWrapper);
+            childWrapper.setProxy(childElement);
+            replaceChildren(childWrapper, childElement);
+        }
+    }
+
+    /**
+     * Return the type of task
+     *
+     * @return the type of task
+     */
+    public String getTaskType() {
+        return taskType;
+    }
+
+    /**
+     * Return the runtime configurable structure for this task
+     *
+     * @return the runtime structure for this task
+     */
+    protected RuntimeConfigurable getWrapper() {
+        return wrapper;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/TaskAdapter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/TaskAdapter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/TaskAdapter.java	(revision 16139)
@@ -0,0 +1,158 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.lang.reflect.Method;
+
+/**
+ * Uses introspection to "adapt" an arbitrary Bean which doesn't
+ * itself extend Task, but still contains an execute method and optionally
+ * a setProject method.
+ *
+ */
+public class TaskAdapter extends Task implements TypeAdapter {
+
+    /** Object to act as a proxy for. */
+    private Object proxy;
+
+    /**
+     * Checks whether or not a class is suitable to be adapted by TaskAdapter.
+     *
+     * This only checks conditions which are additionally required for
+     * tasks adapted by TaskAdapter. Thus, this method should be called by
+     * Project.checkTaskClass.
+     *
+     * Throws a BuildException and logs as Project.MSG_ERR for
+     * conditions that will cause the task execution to fail.
+     * Logs other suspicious conditions with Project.MSG_WARN.
+     *
+     * @param taskClass Class to test for suitability.
+     *                  Must not be <code>null</code>.
+     * @param project   Project to log warnings/errors to.
+     *                  Must not be <code>null</code>.
+     *
+     * @see Project#checkTaskClass(Class)
+     */
+    public static void checkTaskClass(final Class taskClass,
+                                      final Project project) {
+        // don't have to check for interface, since then
+        // taskClass would be abstract too.
+        try {
+            final Method executeM = taskClass.getMethod("execute", null);
+            // don't have to check for public, since
+            // getMethod finds public method only.
+            // don't have to check for abstract, since then
+            // taskClass would be abstract too.
+            if (!Void.TYPE.equals(executeM.getReturnType())) {
+                final String message = "return type of execute() should be "
+                    + "void but was \"" + executeM.getReturnType() + "\" in "
+                    + taskClass;
+                project.log(message, Project.MSG_WARN);
+            }
+        } catch (NoSuchMethodException e) {
+            final String message = "No public execute() in " + taskClass;
+            project.log(message, Project.MSG_ERR);
+            throw new BuildException(message);
+        } catch (LinkageError e) {
+            String message = "Could not load " + taskClass + ": " + e;
+            project.log(message, Project.MSG_ERR);
+            throw new BuildException(message, e);
+        }
+    }
+
+    /**
+     * check if the proxy class is a valid class to use
+     * with this adapter.
+     * the class must have a public no-arg "execute()" method.
+     * @param proxyClass the class to check
+     */
+    public void checkProxyClass(Class proxyClass) {
+        checkTaskClass(proxyClass, getProject());
+    }
+
+    /**
+     * Executes the proxied task.
+     *
+     * @exception BuildException if the project could not be set
+     * or the method could not be executed.
+     */
+    public void execute() throws BuildException {
+        Method setProjectM = null;
+        try {
+            Class c = proxy.getClass();
+            setProjectM =
+                c.getMethod("setProject", new Class[] {Project.class});
+            if (setProjectM != null) {
+                setProjectM.invoke(proxy, new Object[] {getProject()});
+            }
+        } catch (NoSuchMethodException e) {
+            // ignore this if the class being used as a task does not have
+            // a set project method.
+        } catch (Exception ex) {
+            log("Error setting project in " + proxy.getClass(),
+                Project.MSG_ERR);
+            throw new BuildException(ex);
+        }
+
+
+        Method executeM = null;
+        try {
+            Class c = proxy.getClass();
+            executeM = c.getMethod("execute", new Class[0]);
+            if (executeM == null) {
+                log("No public execute() in " + proxy.getClass(),
+                    Project.MSG_ERR);
+                throw new BuildException("No public execute() in "
+                    + proxy.getClass());
+            }
+            executeM.invoke(proxy, null);
+            return;
+        } catch (java.lang.reflect.InvocationTargetException ie) {
+            log("Error in " + proxy.getClass(), Project.MSG_VERBOSE);
+            Throwable t = ie.getTargetException();
+            if (t instanceof BuildException) {
+                throw ((BuildException) t);
+            } else {
+                throw new BuildException(t);
+            }
+        } catch (Exception ex) {
+            log("Error in " + proxy.getClass(), Project.MSG_VERBOSE);
+            throw new BuildException(ex);
+        }
+
+    }
+
+    /**
+     * Sets the target object to proxy for.
+     *
+     * @param o The target object. Must not be <code>null</code>.
+     */
+    public void setProxy(Object o) {
+        this.proxy = o;
+    }
+
+    /**
+     * Returns the target object being proxied.
+     *
+     * @return the target proxy object
+     */
+    public Object getProxy() {
+        return proxy;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/TaskContainer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/TaskContainer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/TaskContainer.java	(revision 16139)
@@ -0,0 +1,41 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+/**
+ * Interface for objects which can contain tasks.
+ * <p>
+ * It is recommended that implementations call perform rather than
+ * execute for the tasks they contain, as this method ensures that the
+ * appropriate BuildEvents will be generated.
+ *
+ * @see Task#perform
+ * @see Task#execute
+ * @see BuildEvent
+ *
+ */
+public interface TaskContainer {
+    /**
+     * Adds a task to this task container
+     *
+     * @param task The task to be added to this container.
+     *             Must not be <code>null</code>.
+     */
+    void addTask(Task task);
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/TypeAdapter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/TypeAdapter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/TypeAdapter.java	(revision 16139)
@@ -0,0 +1,65 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+/**
+ * Used to wrap types.
+ *
+ */
+public interface TypeAdapter {
+
+    /**
+     * Sets the project
+     *
+     * @param p the project instance.
+     */
+    void setProject(Project p);
+
+    /**
+     * Gets the project
+     *
+     * @return the project instance.
+     */
+    Project getProject();
+
+    /**
+     * Sets the proxy object, whose methods are going to be
+     * invoked by ant.
+     * A proxy object is normally the object defined by
+     * a &lt;typedef/&gt; task that is adapted by the "adapter"
+     * attribute.
+     *
+     * @param o The target object. Must not be <code>null</code>.
+     */
+    void setProxy(Object o);
+
+    /**
+     * Returns the proxy object.
+     *
+     * @return the target proxy object
+     */
+    Object getProxy();
+
+    /**
+     * Check if the proxy class is compatible with this adapter - i.e.
+     * the adapter will be able to adapt instances of the give class.
+     *
+     * @param proxyClass the class to be checked.
+     */
+    void checkProxyClass(Class proxyClass);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/UnknownElement.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/UnknownElement.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/UnknownElement.java	(revision 16139)
@@ -0,0 +1,632 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.io.IOException;
+import org.apache.tools.ant.taskdefs.PreSetDef;
+
+/**
+ * Wrapper class that holds all the information necessary to create a task
+ * or data type that did not exist when Ant started, or one which
+ * has had its definition updated to use a different implementation class.
+ *
+ */
+public class UnknownElement extends Task {
+
+    /**
+     * Holds the name of the task/type or nested child element of a
+     * task/type that hasn't been defined at parser time or has
+     * been redefined since original creation.
+     */
+    private String elementName;
+
+    /**
+     * Holds the namespace of the element.
+     */
+    private String namespace;
+
+    /**
+     * Holds the namespace qname of the element.
+     */
+    private String qname;
+
+    /**
+     * The real object after it has been loaded.
+     */
+    private Object realThing;
+
+    /**
+     * List of child elements (UnknownElements).
+     */
+    private List/*<UnknownElement>*/ children = null;
+
+    /** Specifies if a predefined definition has been done */
+    private boolean presetDefed = false;
+
+    /**
+     * Creates an UnknownElement for the given element name.
+     *
+     * @param elementName The name of the unknown element.
+     *                    Must not be <code>null</code>.
+     */
+    public UnknownElement (String elementName) {
+        this.elementName = elementName;
+    }
+
+    /**
+     * @return the list of nested UnknownElements for this UnknownElement.
+     */
+    public List getChildren() {
+        return children;
+    }
+
+    /**
+     * Returns the name of the XML element which generated this unknown
+     * element.
+     *
+     * @return the name of the XML element which generated this unknown
+     *         element.
+     */
+    public String getTag() {
+        return elementName;
+    }
+
+    /** Return the namespace of the XML element associated with this component.
+     *
+     * @return Namespace URI used in the xmlns declaration.
+     */
+    public String getNamespace() {
+        return namespace;
+    }
+
+    /**
+     * Set the namespace of the XML element associated with this component.
+     * This method is typically called by the XML processor.
+     * If the namespace is "ant:current", the component helper
+     * is used to get the current antlib uri.
+     *
+     * @param namespace URI used in the xmlns declaration.
+     */
+    public void setNamespace(String namespace) {
+        if (namespace.equals(ProjectHelper.ANT_CURRENT_URI)) {
+            ComponentHelper helper = ComponentHelper.getComponentHelper(
+                getProject());
+            namespace = helper.getCurrentAntlibUri();
+        }
+        this.namespace = namespace;
+    }
+
+    /** Return the qname of the XML element associated with this component.
+     *
+     * @return namespace Qname used in the element declaration.
+     */
+    public String getQName() {
+        return qname;
+    }
+
+    /** Set the namespace qname of the XML element.
+     * This method is typically called by the XML processor.
+     *
+     * @param qname the qualified name of the element
+     */
+    public void setQName(String qname) {
+        this.qname = qname;
+    }
+
+
+    /**
+     * Get the RuntimeConfigurable instance for this UnknownElement, containing
+     * the configuration information.
+     *
+     * @return the configuration info.
+     */
+    public RuntimeConfigurable getWrapper() {
+        return super.getWrapper();
+    }
+
+    /**
+     * Creates the real object instance and child elements, then configures
+     * the attributes and text of the real object. This unknown element
+     * is then replaced with the real object in the containing target's list
+     * of children.
+     *
+     * @exception BuildException if the configuration fails
+     */
+    public void maybeConfigure() throws BuildException {
+        //ProjectComponentHelper helper=ProjectComponentHelper.getProjectComponentHelper();
+        //realThing = helper.createProjectComponent( this, getProject(), null,
+        //                                           this.getTag());
+
+        configure(makeObject(this, getWrapper()));
+    }
+
+    /**
+     * Configure the given object from this UnknownElement
+     *
+     * @param realObject the real object this UnknownElement is representing.
+     *
+     */
+    public void configure(Object realObject) {
+        realThing = realObject;
+
+        getWrapper().setProxy(realThing);
+        Task task = null;
+        if (realThing instanceof Task) {
+            task = (Task) realThing;
+
+            task.setRuntimeConfigurableWrapper(getWrapper());
+
+            // For Script to work. Ugly
+            // The reference is replaced by RuntimeConfigurable
+            this.getOwningTarget().replaceChild(this, (Task) realThing);
+        }
+
+        handleChildren(realThing, getWrapper());
+
+        // configure attributes of the object and it's children. If it is
+        // a task container, defer the configuration till the task container
+        // attempts to use the task
+
+        if (task != null) {
+            task.maybeConfigure();
+        } else {
+            getWrapper().maybeConfigure(getProject());
+        }
+    }
+
+    /**
+     * Handles output sent to System.out by this task or its real task.
+     *
+     * @param output The output to log. Should not be <code>null</code>.
+     */
+    protected void handleOutput(String output) {
+        if (realThing instanceof Task) {
+            ((Task) realThing).handleOutput(output);
+        } else {
+            super.handleOutput(output);
+        }
+    }
+
+    /**
+     * @see Task#handleInput(byte[], int, int)
+     *
+     * @since Ant 1.6
+     */
+    protected int handleInput(byte[] buffer, int offset, int length)
+        throws IOException {
+        if (realThing instanceof Task) {
+            return ((Task) realThing).handleInput(buffer, offset, length);
+        } else {
+            return super.handleInput(buffer, offset, length);
+        }
+
+    }
+    /**
+     * Handles output sent to System.out by this task or its real task.
+     *
+     * @param output The output to log. Should not be <code>null</code>.
+     */
+    protected void handleFlush(String output) {
+        if (realThing instanceof Task) {
+            ((Task) realThing).handleFlush(output);
+        } else {
+            super.handleFlush(output);
+        }
+    }
+
+    /**
+     * Handles error output sent to System.err by this task or its real task.
+     *
+     * @param output The error output to log. Should not be <code>null</code>.
+     */
+    protected void handleErrorOutput(String output) {
+        if (realThing instanceof Task) {
+            ((Task) realThing).handleErrorOutput(output);
+        } else {
+            super.handleErrorOutput(output);
+        }
+    }
+
+
+    /**
+     * Handles error output sent to System.err by this task or its real task.
+     *
+     * @param output The error output to log. Should not be <code>null</code>.
+     */
+    protected void handleErrorFlush(String output) {
+        if (realThing instanceof Task) {
+            ((Task) realThing).handleErrorOutput(output);
+        } else {
+            super.handleErrorOutput(output);
+        }
+    }
+
+    /**
+     * Executes the real object if it's a task. If it's not a task
+     * (e.g. a data type) then this method does nothing.
+     */
+    public void execute() {
+        if (realThing == null) {
+            // plain impossible to get here, maybeConfigure should
+            // have thrown an exception.
+            throw new BuildException("Could not create task of type: "
+                                     + elementName, getLocation());
+        }
+
+        if (realThing instanceof Task) {
+            ((Task) realThing).execute();
+        }
+
+        // the task will not be reused ( a new init() will be called )
+        // Let GC do its job
+        realThing = null;
+    }
+
+    /**
+     * Adds a child element to this element.
+     *
+     * @param child The child element to add. Must not be <code>null</code>.
+     */
+    public void addChild(UnknownElement child) {
+        if (children == null) {
+            children = new ArrayList();
+        }
+        children.add(child);
+    }
+
+    /**
+     * Creates child elements, creates children of the children
+     * (recursively), and sets attributes of the child elements.
+     *
+     * @param parent The configured object for the parent.
+     *               Must not be <code>null</code>.
+     *
+     * @param parentWrapper The wrapper containing child wrappers
+     *                      to be configured. Must not be <code>null</code>
+     *                      if there are any children.
+     *
+     * @exception BuildException if the children cannot be configured.
+     */
+    protected void handleChildren(
+        Object parent,
+        RuntimeConfigurable parentWrapper)
+        throws BuildException {
+        if (parent instanceof TypeAdapter) {
+            parent = ((TypeAdapter) parent).getProxy();
+        }
+
+        String parentUri = getNamespace();
+        Class parentClass = parent.getClass();
+        IntrospectionHelper ih = IntrospectionHelper.getHelper(parentClass);
+
+
+        if (children != null) {
+            Iterator it = children.iterator();
+            for (int i = 0; it.hasNext(); i++) {
+                RuntimeConfigurable childWrapper = parentWrapper.getChild(i);
+                UnknownElement child = (UnknownElement) it.next();
+                if (!handleChild(
+                        parentUri, ih, parent, child, childWrapper)) {
+                    if (!(parent instanceof TaskContainer)) {
+                        ih.throwNotSupported(getProject(), parent,
+                                             child.getTag());
+                    } else {
+                        // a task container - anything could happen - just add the
+                        // child to the container
+                        TaskContainer container = (TaskContainer) parent;
+                        container.addTask(child);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * @return the component name - uses ProjectHelper#genComponentName()
+     */
+    protected String getComponentName() {
+        return ProjectHelper.genComponentName(getNamespace(), getTag());
+    }
+
+    /**
+     * This is used then the realobject of the UE is a PreSetDefinition.
+     * This is also used when a presetdef is used on a presetdef
+     * The attributes, elements and text are applied to this
+     * UE.
+     *
+     * @param u an UnknownElement containing the attributes, elements and text
+     */
+    public void applyPreSet(UnknownElement u) {
+        if (presetDefed) {
+            return;
+        }
+        // Do the runtime
+        getWrapper().applyPreSet(u.getWrapper());
+        if (u.children != null) {
+            List newChildren = new ArrayList();
+            newChildren.addAll(u.children);
+            if (children != null) {
+                newChildren.addAll(children);
+            }
+            children = newChildren;
+        }
+        presetDefed = true;
+    }
+
+    /**
+     * Creates a named task or data type. If the real object is a task,
+     * it is configured up to the init() stage.
+     *
+     * @param ue The unknown element to create the real object for.
+     *           Must not be <code>null</code>.
+     * @param w  Ignored in this implementation.
+     *
+     * @return the task or data type represented by the given unknown element.
+     */
+    protected Object makeObject(UnknownElement ue, RuntimeConfigurable w) {
+        ComponentHelper helper = ComponentHelper.getComponentHelper(
+            getProject());
+        String name = ue.getComponentName();
+        Object o = helper.createComponent(ue, ue.getNamespace(), name);
+
+        if (o == null) {
+            throw getNotFoundException("task or type", name);
+        }
+
+        if (o instanceof PreSetDef.PreSetDefinition) {
+            PreSetDef.PreSetDefinition def = (PreSetDef.PreSetDefinition) o;
+            o = def.createObject(ue.getProject());
+            if (o == null) {
+                throw getNotFoundException(
+                    "preset " + name,
+                    def.getPreSets().getComponentName());
+            }
+            ue.applyPreSet(def.getPreSets());
+            if (o instanceof Task) {
+                Task task = (Task) o;
+                task.setTaskType(ue.getTaskType());
+                task.setTaskName(ue.getTaskName());
+            }
+        }
+
+        if (o instanceof Task) {
+            Task task = (Task) o;
+            task.setOwningTarget(getOwningTarget());
+            task.init();
+        }
+        return o;
+    }
+
+    /**
+     * Creates a named task and configures it up to the init() stage.
+     *
+     * @param ue The UnknownElement to create the real task for.
+     *           Must not be <code>null</code>.
+     * @param w  Ignored.
+     *
+     * @return the task specified by the given unknown element, or
+     *         <code>null</code> if the task name is not recognised.
+     */
+    protected Task makeTask(UnknownElement ue, RuntimeConfigurable w) {
+        Task task = getProject().createTask(ue.getTag());
+
+        if (task != null) {
+            task.setLocation(getLocation());
+            // UnknownElement always has an associated target
+            task.setOwningTarget(getOwningTarget());
+            task.init();
+        }
+        return task;
+    }
+
+    /**
+     * Returns a very verbose exception for when a task/data type cannot
+     * be found.
+     *
+     * @param what The kind of thing being created. For example, when
+     *             a task name could not be found, this would be
+     *             <code>"task"</code>. Should not be <code>null</code>.
+     * @param elementName The name of the element which could not be found.
+     *                    Should not be <code>null</code>.
+     *
+     * @return a detailed description of what might have caused the problem.
+     */
+    protected BuildException getNotFoundException(String what,
+                                                  String elementName) {
+        String lSep = System.getProperty("line.separator");
+        String msg = "Could not create " + what + " of type: " + elementName
+            + "." + lSep + lSep
+            + "Ant could not find the task or a class this "
+            + "task relies upon." + lSep + lSep
+            + "This is common and has a number of causes; the usual " + lSep
+            + "solutions are to read the manual pages then download and" + lSep
+            + "install needed JAR files, or fix the build file: " + lSep
+            + " - You have misspelt '" + elementName + "'." + lSep
+            + "   Fix: check your spelling." + lSep
+            + " - The task needs an external JAR file to execute" + lSep
+            + "     and this is not found at the right place in the classpath." + lSep
+            + "   Fix: check the documentation for dependencies." + lSep
+            + "   Fix: declare the task." + lSep
+            + " - The task is an Ant optional task and the JAR file and/or libraries" + lSep
+            + "     implementing the functionality were not found at the time you" + lSep
+            + "     yourself built your installation of Ant from the Ant sources." + lSep
+            + "   Fix: Look in the ANT_HOME/lib for the 'ant-' JAR corresponding to the" + lSep
+            + "     task and make sure it contains more than merely a META-INF/MANIFEST.MF." + lSep
+            + "     If all it contains is the manifest, then rebuild Ant with the needed" + lSep
+            + "     libraries present in ${ant.home}/lib/optional/ , or alternatively," + lSep
+            + "     download a pre-built release version from apache.org" + lSep
+            + " - The build file was written for a later version of Ant" + lSep
+            + "   Fix: upgrade to at least the latest release version of Ant" + lSep
+            + " - The task is not an Ant core or optional task " + lSep
+            + "     and needs to be declared using <taskdef>." + lSep
+            + " - You are attempting to use a task defined using " + lSep
+            + "    <presetdef> or <macrodef> but have spelt wrong or not " + lSep
+            + "   defined it at the point of use" + lSep
+            + lSep
+            + "Remember that for JAR files to be visible to Ant tasks implemented" + lSep
+            + "in ANT_HOME/lib, the files must be in the same directory or on the" + lSep
+            + "classpath" + lSep
+            + lSep
+            + "Please neither file bug reports on this problem, nor email the" + lSep
+            + "Ant mailing lists, until all of these causes have been explored," + lSep
+            + "as this is not an Ant bug.";
+
+
+        return new BuildException(msg, getLocation());
+    }
+
+    /**
+     * Returns the name to use in logging messages.
+     *
+     * @return the name to use in logging messages.
+     */
+    public String getTaskName() {
+        //return elementName;
+        return realThing == null
+            || !(realThing instanceof Task) ? super.getTaskName()
+                                            : ((Task) realThing).getTaskName();
+    }
+
+    /**
+     * Returns the task instance after it has been created and if it is a task.
+     *
+     * @return a task instance or <code>null</code> if the real object is not
+     *         a task.
+     */
+    public Task getTask() {
+        if (realThing instanceof Task) {
+            return (Task) realThing;
+        }
+        return null;
+    }
+
+    /**
+     * Return the configured object
+     *
+     * @return the real thing whatever it is
+     *
+     * @since ant 1.6
+     */
+    public Object getRealThing() {
+        return realThing;
+    }
+    /**
+     * Try to create a nested element of <code>parent</code> for the
+     * given tag.
+     *
+     * @return whether the creation has been successful
+     */
+    private boolean handleChild(
+        String parentUri,
+        IntrospectionHelper ih,
+        Object parent, UnknownElement child,
+        RuntimeConfigurable childWrapper) {
+        String childName = ProjectHelper.genComponentName(
+            child.getNamespace(), child.getTag());
+        if (ih.supportsNestedElement(parentUri, childName)) {
+            IntrospectionHelper.Creator creator =
+                ih.getElementCreator(
+                    getProject(), parentUri, parent, childName, child);
+            creator.setPolyType(childWrapper.getPolyType());
+            Object realChild = creator.create();
+            if (realChild instanceof PreSetDef.PreSetDefinition) {
+                PreSetDef.PreSetDefinition def =
+                    (PreSetDef.PreSetDefinition) realChild;
+                realChild = creator.getRealObject();
+                child.applyPreSet(def.getPreSets());
+            }
+            childWrapper.setCreator(creator);
+            childWrapper.setProxy(realChild);
+            if (realChild instanceof Task) {
+                Task childTask = (Task) realChild;
+                childTask.setRuntimeConfigurableWrapper(childWrapper);
+                childTask.setTaskName(childName);
+                childTask.setTaskType(childName);
+                childTask.setLocation(child.getLocation());
+            }
+            child.handleChildren(realChild, childWrapper);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * like contents equals, but ignores project
+     * @param obj the object to check against
+     * @return true if this unknownelement has the same contents the other
+     */
+    public boolean similar(Object obj) {
+        if (obj == null) {
+            return false;
+        }
+        if (!getClass().getName().equals(obj.getClass().getName())) {
+            return false;
+        }
+        UnknownElement other = (UnknownElement) obj;
+        // Are the names the same ?
+        if (!equalsString(elementName, other.elementName)) {
+            return false;
+        }
+        if (!namespace.equals(other.namespace)) {
+            return false;
+        }
+        if (!qname.equals(other.qname)) {
+            return false;
+        }
+        // Are attributes the same ?
+        if (!getWrapper().getAttributeMap().equals(
+                other.getWrapper().getAttributeMap())) {
+            return false;
+        }
+        // Is the text the same?
+        //   Need to use equals on the string and not
+        //   on the stringbuffer as equals on the string buffer
+        //   does not compare the contents.
+        if (!getWrapper().getText().toString().equals(
+                other.getWrapper().getText().toString())) {
+            return false;
+        }
+        // Are the sub elements the same ?
+        if (children == null || children.size() == 0) {
+            return other.children == null || other.children.size() == 0;
+        }
+        if (other.children == null) {
+            return false;
+        }
+        if (children.size() != other.children.size()) {
+            return false;
+        }
+        for (int i = 0; i < children.size(); ++i) {
+            UnknownElement child = (UnknownElement) children.get(i);
+            if (!child.similar(other.children.get(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    private boolean equalsString(String a, String b) {
+        if (a == null) {
+            return b == null;
+        }
+        return a.equals(b);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/UnsupportedAttributeException.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/UnsupportedAttributeException.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/UnsupportedAttributeException.java	(revision 16139)
@@ -0,0 +1,47 @@
+/*
+ * Copyright  2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant;
+
+/**
+ * Used to report attempts to set an unsupported attribute
+ *
+ * @since Ant 1.6.3
+ */
+public class UnsupportedAttributeException extends BuildException {
+
+    private String attribute;
+
+    /**
+     * Constructs an unsupported attribute exception.
+     * @param msg       The string containing the message.
+     * @param attribute The unsupported attribute.
+     */
+    public UnsupportedAttributeException(String msg, String attribute) {
+        super(msg);
+        this.attribute = attribute;
+    }
+
+    /**
+     * Get the attribute that is wrong.
+     *
+     * @return the attribute name.
+     */
+    public String getAttribute() {
+        return attribute;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/UnsupportedElementException.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/UnsupportedElementException.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/UnsupportedElementException.java	(revision 16139)
@@ -0,0 +1,55 @@
+/*
+ * Copyright  2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant;
+
+/**
+ * Used to report attempts to set an unsupported element
+ * When the attempt to set the element is made,
+ * the code does not not know the name of the task/type
+ * based on a mapping from the classname to the task/type.
+ * However one class may be used by a lot of task/types.
+ * This exception may be caught by code that does know
+ * the task/type and it will reset the message to the
+ * correct message.
+ * This will be done once (in the case of a recursive
+ * call to handlechildren).
+ *
+ * @since Ant 1.6.3
+ */
+public class UnsupportedElementException extends BuildException {
+
+    private String element;
+
+    /**
+     * Constructs an unsupported element exception.
+     * @param msg The string containing the message.
+     * @param element The name of the incorrect element.
+     */
+    public UnsupportedElementException(String msg, String element) {
+        super(msg);
+        this.element = element;
+    }
+
+    /**
+     * Get the element that is wrong.
+     *
+     * @return the element name.
+     */
+    public String getElement() {
+        return element;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/XmlLogger.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/XmlLogger.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/XmlLogger.java	(revision 16139)
@@ -0,0 +1,476 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.Writer;
+import java.util.Hashtable;
+import java.util.Stack;
+import java.util.Enumeration;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.apache.tools.ant.util.StringUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Text;
+
+/**
+ * Generates a file in the current directory with
+ * an XML description of what happened during a build.
+ * The default filename is "log.xml", but this can be overridden
+ * with the property <code>XmlLogger.file</code>.
+ *
+ * This implementation assumes in its sanity checking that only one
+ * thread runs a particular target/task at a time. This is enforced
+ * by the way that parallel builds and antcalls are done - and
+ * indeed all but the simplest of tasks could run into problems
+ * if executed in parallel.
+ *
+ * @see Project#addBuildListener(BuildListener)
+ */
+public class XmlLogger implements BuildLogger {
+
+    private int msgOutputLevel = Project.MSG_DEBUG;
+    private PrintStream outStream;
+
+    /** DocumentBuilder to use when creating the document to start with. */
+    private static DocumentBuilder builder = getDocumentBuilder();
+
+    /**
+     * Returns a default DocumentBuilder instance or throws an
+     * ExceptionInInitializerError if it can't be created.
+     *
+     * @return a default DocumentBuilder instance.
+     */
+    private static DocumentBuilder getDocumentBuilder() {
+        try {
+            return DocumentBuilderFactory.newInstance().newDocumentBuilder();
+        } catch (Exception exc) {
+            throw new ExceptionInInitializerError(exc);
+        }
+    }
+
+    /** XML element name for a build. */
+    private static final String BUILD_TAG = "build";
+    /** XML element name for a target. */
+    private static final String TARGET_TAG = "target";
+    /** XML element name for a task. */
+    private static final String TASK_TAG = "task";
+    /** XML element name for a message. */
+    private static final String MESSAGE_TAG = "message";
+    /** XML attribute name for a name. */
+    private static final String NAME_ATTR = "name";
+    /** XML attribute name for a time. */
+    private static final String TIME_ATTR = "time";
+    /** XML attribute name for a message priority. */
+    private static final String PRIORITY_ATTR = "priority";
+    /** XML attribute name for a file location. */
+    private static final String LOCATION_ATTR = "location";
+    /** XML attribute name for an error description. */
+    private static final String ERROR_ATTR = "error";
+    /** XML element name for a stack trace. */
+    private static final String STACKTRACE_TAG = "stacktrace";
+
+    /** The complete log document for this build. */
+    private Document doc = builder.newDocument();
+    /** Mapping for when tasks started (Task to TimedElement). */
+    private Hashtable tasks = new Hashtable();
+    /** Mapping for when targets started (Task to TimedElement). */
+    private Hashtable targets = new Hashtable();
+    /**
+     * Mapping of threads to stacks of elements
+     * (Thread to Stack of TimedElement).
+     */
+    private Hashtable threadStacks = new Hashtable();
+    /**
+     * When the build started.
+     */
+    private TimedElement buildElement = null;
+
+    /** Utility class representing the time an element started. */
+    private static class TimedElement {
+        /**
+         * Start time in milliseconds
+         * (as returned by <code>System.currentTimeMillis()</code>).
+         */
+        private long startTime;
+        /** Element created at the start time. */
+        private Element element;
+        public String toString() {
+            return element.getTagName() + ":" + element.getAttribute("name");
+        }
+    }
+
+    /**
+     *  Constructs a new BuildListener that logs build events to an XML file.
+     */
+    public XmlLogger() {
+    }
+
+    /**
+     * Fired when the build starts, this builds the top-level element for the
+     * document and remembers the time of the start of the build.
+     *
+     * @param event Ignored.
+     */
+    public void buildStarted(BuildEvent event) {
+        buildElement = new TimedElement();
+        buildElement.startTime = System.currentTimeMillis();
+        buildElement.element = doc.createElement(BUILD_TAG);
+    }
+
+    /**
+     * Fired when the build finishes, this adds the time taken and any
+     * error stacktrace to the build element and writes the document to disk.
+     *
+     * @param event An event with any relevant extra information.
+     *              Will not be <code>null</code>.
+     */
+    public void buildFinished(BuildEvent event) {
+        long totalTime = System.currentTimeMillis() - buildElement.startTime;
+        buildElement.element.setAttribute(TIME_ATTR,
+                DefaultLogger.formatTime(totalTime));
+
+        if (event.getException() != null) {
+            buildElement.element.setAttribute(ERROR_ATTR,
+                    event.getException().toString());
+            // print the stacktrace in the build file it is always useful...
+            // better have too much info than not enough.
+            Throwable t = event.getException();
+            Text errText = doc.createCDATASection(StringUtils.getStackTrace(t));
+            Element stacktrace = doc.createElement(STACKTRACE_TAG);
+            stacktrace.appendChild(errText);
+            buildElement.element.appendChild(stacktrace);
+        }
+
+        String outFilename = event.getProject().getProperty("XmlLogger.file");
+        if (outFilename == null) {
+            outFilename = "log.xml";
+        }
+        String xslUri
+                = event.getProject().getProperty("ant.XmlLogger.stylesheet.uri");
+        if (xslUri == null) {
+            xslUri = "log.xsl";
+        }
+        Writer out = null;
+        try {
+            // specify output in UTF8 otherwise accented characters will blow
+            // up everything
+            OutputStream stream = outStream;
+            if (stream == null) {
+                stream = new FileOutputStream(outFilename);
+            }
+            out = new OutputStreamWriter(stream, "UTF8");
+            out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+            if (xslUri.length() > 0) {
+                out.write("<?xml-stylesheet type=\"text/xsl\" href=\""
+                        + xslUri + "\"?>\n\n");
+            }
+            (new DOMElementWriter()).write(buildElement.element, out, 0, "\t");
+            out.flush();
+        } catch (IOException exc) {
+            throw new BuildException("Unable to write log file", exc);
+        } finally {
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+        buildElement = null;
+    }
+
+    /**
+     * Returns the stack of timed elements for the current thread.
+     * @return the stack of timed elements for the current thread
+     */
+    private Stack getStack() {
+        Stack threadStack = (Stack) threadStacks.get(Thread.currentThread());
+        if (threadStack == null) {
+            threadStack = new Stack();
+            threadStacks.put(Thread.currentThread(), threadStack);
+        }
+        /* For debugging purposes uncomment:
+        org.w3c.dom.Comment s = doc.createComment("stack=" + threadStack);
+        buildElement.element.appendChild(s);
+         */
+        return threadStack;
+    }
+
+    /**
+     * Fired when a target starts building, this pushes a timed element
+     * for the target onto the stack of elements for the current thread,
+     * remembering the current time and the name of the target.
+     *
+     * @param event An event with any relevant extra information.
+     *              Will not be <code>null</code>.
+     */
+    public void targetStarted(BuildEvent event) {
+        Target target = event.getTarget();
+        TimedElement targetElement = new TimedElement();
+        targetElement.startTime = System.currentTimeMillis();
+        targetElement.element = doc.createElement(TARGET_TAG);
+        targetElement.element.setAttribute(NAME_ATTR, target.getName());
+        targets.put(target, targetElement);
+        getStack().push(targetElement);
+    }
+
+    /**
+     * Fired when a target finishes building, this adds the time taken
+     * and any error stacktrace to the appropriate target element in the log.
+     *
+     * @param event An event with any relevant extra information.
+     *              Will not be <code>null</code>.
+     */
+    public void targetFinished(BuildEvent event) {
+        Target target = event.getTarget();
+        TimedElement targetElement = (TimedElement) targets.get(target);
+        if (targetElement != null) {
+            long totalTime
+                    = System.currentTimeMillis() - targetElement.startTime;
+            targetElement.element.setAttribute(TIME_ATTR,
+                    DefaultLogger.formatTime(totalTime));
+
+            TimedElement parentElement = null;
+            Stack threadStack = getStack();
+            if (!threadStack.empty()) {
+                TimedElement poppedStack = (TimedElement) threadStack.pop();
+                if (poppedStack != targetElement) {
+                    throw new RuntimeException("Mismatch - popped element = "
+                            + poppedStack
+                            + " finished target element = "
+                            + targetElement);
+                }
+                if (!threadStack.empty()) {
+                    parentElement = (TimedElement) threadStack.peek();
+                }
+            }
+            if (parentElement == null) {
+                buildElement.element.appendChild(targetElement.element);
+            } else {
+                parentElement.element.appendChild(targetElement.element);
+            }
+        }
+        targets.remove(target);
+    }
+
+    /**
+     * Fired when a task starts building, this pushes a timed element
+     * for the task onto the stack of elements for the current thread,
+     * remembering the current time and the name of the task.
+     *
+     * @param event An event with any relevant extra information.
+     *              Will not be <code>null</code>.
+     */
+    public void taskStarted(BuildEvent event) {
+        TimedElement taskElement = new TimedElement();
+        taskElement.startTime = System.currentTimeMillis();
+        taskElement.element = doc.createElement(TASK_TAG);
+
+        Task task = event.getTask();
+        String name = event.getTask().getTaskName();
+        if (name == null) {
+            name = "";
+        }
+        taskElement.element.setAttribute(NAME_ATTR, name);
+        taskElement.element.setAttribute(LOCATION_ATTR,
+                event.getTask().getLocation().toString());
+        tasks.put(task, taskElement);
+        getStack().push(taskElement);
+    }
+
+    /**
+     * Fired when a task finishes building, this adds the time taken
+     * and any error stacktrace to the appropriate task element in the log.
+     *
+     * @param event An event with any relevant extra information.
+     *              Will not be <code>null</code>.
+     */
+    public void taskFinished(BuildEvent event) {
+        Task task = event.getTask();
+        TimedElement taskElement = (TimedElement) tasks.get(task);
+        if (taskElement != null) {
+            long totalTime = System.currentTimeMillis() - taskElement.startTime;
+            taskElement.element.setAttribute(TIME_ATTR,
+                    DefaultLogger.formatTime(totalTime));
+            Target target = task.getOwningTarget();
+            TimedElement targetElement = null;
+            if (target != null) {
+                targetElement = (TimedElement) targets.get(target);
+            }
+            if (targetElement == null) {
+                buildElement.element.appendChild(taskElement.element);
+            } else {
+                targetElement.element.appendChild(taskElement.element);
+            }
+            Stack threadStack = getStack();
+            if (!threadStack.empty()) {
+                TimedElement poppedStack = (TimedElement) threadStack.pop();
+                if (poppedStack != taskElement) {
+                    throw new RuntimeException("Mismatch - popped element = "
+                            + poppedStack + " finished task element = "
+                            + taskElement);
+                }
+            }
+            tasks.remove(task);
+        } else {
+            throw new RuntimeException("Unknown task " + task + " not in " + tasks);
+        }
+    }
+
+
+    /**
+     * Get the TimedElement associated with a task.
+     *
+     * Where the task is not found directly, search for unknown elements which
+     * may be hiding the real task
+     */
+    private TimedElement getTaskElement(Task task) {
+        TimedElement element = (TimedElement) tasks.get(task);
+        if (element != null) {
+            return element;
+        }
+
+        for (Enumeration e = tasks.keys(); e.hasMoreElements();) {
+            Task key = (Task) e.nextElement();
+            if (key instanceof UnknownElement) {
+                if (((UnknownElement) key).getTask() == task) {
+                    return (TimedElement) tasks.get(key);
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Fired when a message is logged, this adds a message element to the
+     * most appropriate parent element (task, target or build) and records
+     * the priority and text of the message.
+     *
+     * @param event An event with any relevant extra information.
+     *              Will not be <code>null</code>.
+     */
+    public void messageLogged(BuildEvent event) {
+        int priority = event.getPriority();
+        if (priority > msgOutputLevel) {
+            return;
+        }
+        Element messageElement = doc.createElement(MESSAGE_TAG);
+
+        String name = "debug";
+        switch (event.getPriority()) {
+            case Project.MSG_ERR:
+                name = "error";
+                break;
+            case Project.MSG_WARN:
+                name = "warn";
+                break;
+            case Project.MSG_INFO:
+                name = "info";
+                break;
+            default:
+                name = "debug";
+                break;
+        }
+        messageElement.setAttribute(PRIORITY_ATTR, name);
+
+        Text messageText = doc.createCDATASection(event.getMessage());
+        messageElement.appendChild(messageText);
+
+        TimedElement parentElement = null;
+
+        Task task = event.getTask();
+
+        Target target = event.getTarget();
+        if (task != null) {
+            parentElement = getTaskElement(task);
+        }
+        if (parentElement == null && target != null) {
+            parentElement = (TimedElement) targets.get(target);
+        }
+
+        /*
+        if (parentElement == null) {
+            Stack threadStack
+                    = (Stack) threadStacks.get(Thread.currentThread());
+            if (threadStack != null) {
+                if (!threadStack.empty()) {
+                    parentElement = (TimedElement) threadStack.peek();
+                }
+            }
+        }
+        */
+
+        if (parentElement != null) {
+            parentElement.element.appendChild(messageElement);
+        } else {
+            buildElement.element.appendChild(messageElement);
+        }
+    }
+
+    // -------------------------------------------------- BuildLogger interface
+
+    /**
+     * Set the logging level when using this as a Logger
+     *
+     * @param level the logging level -
+     *        see {@link org.apache.tools.ant.Project#MSG_ERR Project}
+     *        class for level definitions
+     */
+    public void setMessageOutputLevel(int level) {
+        msgOutputLevel = level;
+    }
+
+    /**
+     * Set the output stream to which logging output is sent when operating
+     * as a logger.
+     *
+     * @param output the output PrintStream.
+     */
+    public void setOutputPrintStream(PrintStream output) {
+        this.outStream = new PrintStream(output, true);
+    }
+
+    /**
+     * Ignore emacs mode, as it has no meaning in XML format
+     *
+     * @param emacsMode true if logger should produce emacs compatible
+     *        output
+     */
+    public void setEmacsMode(boolean emacsMode) {
+    }
+
+    /**
+     * Ignore error print stream. All output will be written to
+     * either the XML log file or the PrintStream provided to
+     * setOutputPrintStream
+     *
+     * @param err the stream we are going to ignore.
+     */
+    public void setErrorPrintStream(PrintStream err) {
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/defaultManifest.mf
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/defaultManifest.mf	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/defaultManifest.mf	(revision 16139)
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Ant-Version: Apache Ant @VERSION@
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/BaseFilterReader.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/BaseFilterReader.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/BaseFilterReader.java	(revision 16139)
@@ -0,0 +1,201 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.FilterReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Base class for core filter readers.
+ *
+ */
+public abstract class BaseFilterReader extends FilterReader {
+    /** Buffer size used when reading */
+    private static final int BUFFER_SIZE = 8192;
+
+    /** Have the parameters passed been interpreted? */
+    private boolean initialized = false;
+
+    /** The Ant project this filter is part of. */
+    private Project project = null;
+
+    /**
+     * Constructor used by Ant's introspection mechanism.
+     * The original filter reader is only used for chaining
+     * purposes, never for filtering purposes (and indeed
+     * it would be useless for filtering purposes, as it has
+     * no real data to filter). ChainedReaderHelper uses
+     * this placeholder instance to create a chain of real filters.
+     */
+    public BaseFilterReader() {
+        super(new StringReader(new String()));
+        try {
+            close();
+        } catch (IOException  ioe) {
+            // Ignore
+        }
+    }
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     *
+     */
+    public BaseFilterReader(final Reader in) {
+        super(in);
+    }
+
+    /**
+     * Reads characters into a portion of an array.  This method will block
+     * until some input is available, an I/O error occurs, or the end of the
+     * stream is reached.
+     *
+     * @param      cbuf  Destination buffer to write characters to.
+     *                   Must not be <code>null</code>.
+     * @param      off   Offset at which to start storing characters.
+     * @param      len   Maximum number of characters to read.
+     *
+     * @return     the number of characters read, or -1 if the end of the
+     *             stream has been reached
+     *
+     * @exception  IOException  If an I/O error occurs
+     */
+    public final int read(final char[] cbuf, final int off,
+                          final int len) throws IOException {
+        for (int i = 0; i < len; i++) {
+            final int ch = read();
+            if (ch == -1) {
+                if (i == 0) {
+                    return -1;
+                } else {
+                    return i;
+                }
+            }
+            cbuf[off + i] = (char) ch;
+        }
+        return len;
+    }
+
+    /**
+     * Skips characters.  This method will block until some characters are
+     * available, an I/O error occurs, or the end of the stream is reached.
+     *
+     * @param  n  The number of characters to skip
+     *
+     * @return    the number of characters actually skipped
+     *
+     * @exception  IllegalArgumentException  If <code>n</code> is negative.
+     * @exception  IOException  If an I/O error occurs
+     */
+    public final long skip(final long n)
+        throws IOException, IllegalArgumentException {
+        if (n < 0L) {
+            throw new IllegalArgumentException("skip value is negative");
+        }
+
+        for (long i = 0; i < n; i++) {
+            if (read() == -1) {
+                return i;
+            }
+        }
+        return n;
+    }
+
+    /**
+     * Sets the initialized status.
+     *
+     * @param initialized Whether or not the filter is initialized.
+     */
+    protected final void setInitialized(final boolean initialized) {
+        this.initialized = initialized;
+    }
+
+    /**
+     * Returns the initialized status.
+     *
+     * @return whether or not the filter is initialized
+     */
+    protected final boolean getInitialized() {
+        return initialized;
+    }
+
+    /**
+     * Sets the project to work with.
+     *
+     * @param project The project this filter is part of.
+     *                Should not be <code>null</code>.
+     */
+    public final void setProject(final Project project) {
+        this.project = project;
+    }
+
+    /**
+     * Returns the project this filter is part of.
+     *
+     * @return the project this filter is part of
+     */
+    protected final Project getProject() {
+        return project;
+    }
+
+    /**
+     * Reads a line of text ending with '\n' (or until the end of the stream).
+     * The returned String retains the '\n'.
+     *
+     * @return the line read, or <code>null</code> if the end of the stream
+     * has already been reached
+     *
+     * @exception IOException if the underlying reader throws one during
+     *                        reading
+     */
+    protected final String readLine() throws IOException {
+        int ch = in.read();
+
+        if (ch == -1) {
+            return null;
+        }
+
+        StringBuffer line = new StringBuffer();
+
+        while (ch != -1) {
+            line.append ((char) ch);
+            if (ch == '\n') {
+                break;
+            }
+            ch = in.read();
+        }
+        return line.toString();
+    }
+
+    /**
+     * Reads to the end of the stream, returning the contents as a String.
+     *
+     * @return the remaining contents of the reader, as a String
+     *
+     * @exception IOException if the underlying reader throws one during
+     *            reading
+     */
+    protected final String readFully() throws IOException {
+        return FileUtils.readFully(in, BUFFER_SIZE);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/BaseParamFilterReader.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/BaseParamFilterReader.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/BaseParamFilterReader.java	(revision 16139)
@@ -0,0 +1,72 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.Reader;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.Parameterizable;
+
+/**
+ * Parameterized base class for core filter readers.
+ *
+ */
+public abstract class BaseParamFilterReader
+    extends BaseFilterReader
+    implements Parameterizable {
+    /** The passed in parameter array. */
+    private Parameter[] parameters;
+
+    /**
+     * Constructor for "dummy" instances.
+     *
+     * @see BaseFilterReader#BaseFilterReader()
+     */
+    public BaseParamFilterReader() {
+        super();
+    }
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     */
+    public BaseParamFilterReader(final Reader in) {
+        super(in);
+    }
+
+    /**
+     * Sets the parameters used by this filter, and sets
+     * the filter to an uninitialized status.
+     *
+     * @param parameters The parameters to be used by this filter.
+     *                   Should not be <code>null</code>.
+     */
+    public final void setParameters(final Parameter[] parameters) {
+        this.parameters = parameters;
+        setInitialized(false);
+    }
+
+    /**
+     * Returns the parameters to be used by this filter.
+     *
+     * @return the parameters to be used by this filter
+     */
+    protected final Parameter[] getParameters() {
+        return parameters;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/ChainableReader.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/ChainableReader.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/ChainableReader.java	(revision 16139)
@@ -0,0 +1,36 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.Reader;
+
+/**
+ * Interface indicating that a reader may be chained to another one.
+ *
+ */
+public interface ChainableReader {
+    /**
+     * Returns a reader with the same configuration as this one,
+     * but filtering input from the specified reader.
+     *
+     * @param rdr the reader which the returned reader should be filtering
+     *
+     * @return a reader with the same configuration as this one, but
+     *         filtering input from the specified reader
+     */
+    Reader chain(Reader rdr);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/ClassConstants.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/ClassConstants.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/ClassConstants.java	(revision 16139)
@@ -0,0 +1,157 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+
+/**
+ * Assembles the constants declared in a Java class in
+ * <code>key1=value1(line separator)key2=value2</code>
+ * format.
+ *<p>
+ * Notes:
+ * <ol>
+ * <li>This filter uses the BCEL external toolkit.
+ * <li>This assembles only those constants that are not created
+ * using the syntax <code>new whatever()</code>
+ * <li>This assembles constants declared using the basic datatypes
+ * and String only.</li>
+ * <li>The access modifiers of the declared constants do not matter.</li>
+ *</ol>
+ * Example:<br>
+ * <pre>&lt;classconstants/&gt;</pre>
+ * Or:
+ * <pre>&lt;filterreader
+ *    classname=&quot;org.apache.tools.ant.filters.ClassConstants&quot;/&gt;</pre>
+ */
+public final class ClassConstants
+    extends BaseFilterReader
+    implements ChainableReader {
+    /** Data that must be read from, if not null. */
+    private String queuedData = null;
+
+    /** Helper Class to be invoked via reflection. */
+    private static final String JAVA_CLASS_HELPER =
+        "org.apache.tools.ant.filters.util.JavaClassHelper";
+
+    /**
+     * Constructor for "dummy" instances.
+     *
+     * @see BaseFilterReader#BaseFilterReader()
+     */
+    public ClassConstants() {
+        super();
+    }
+
+    /**
+     * Creates a new filtered reader. The contents of the passed-in reader
+     * are expected to be the name of the class from which to produce a
+     * list of constants.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     */
+    public ClassConstants(final Reader in) {
+        super(in);
+    }
+
+    /**
+     * Reads and assembles the constants declared in a class file.
+     *
+     * @return the next character in the list of constants, or -1
+     * if the end of the resulting stream has been reached
+     *
+     * @exception IOException if the underlying stream throws an IOException
+     * during reading, or if the constants for the specified class cannot
+     * be read (for example due to the class not being found).
+     */
+    public final int read() throws IOException {
+
+        int ch = -1;
+
+        if (queuedData != null && queuedData.length() == 0) {
+            queuedData = null;
+        }
+
+        if (queuedData != null) {
+            ch = queuedData.charAt(0);
+            queuedData = queuedData.substring(1);
+            if (queuedData.length() == 0) {
+                queuedData = null;
+            }
+        } else {
+            final String clazz = readFully();
+            if (clazz == null) {
+                ch = -1;
+            } else {
+                final byte[] bytes = clazz.getBytes();
+                try {
+                    final Class javaClassHelper =
+                        Class.forName(JAVA_CLASS_HELPER);
+                    if (javaClassHelper != null) {
+                        final Class[] params = {
+                            byte[].class
+                        };
+                        final Method getConstants =
+                            javaClassHelper.getMethod("getConstants", params);
+                        final Object[] args = {
+                            bytes
+                        };
+                        // getConstants is a static method, no need to
+                        // pass in the object
+                        final StringBuffer sb = (StringBuffer)
+                                getConstants.invoke(null, args);
+                        if (sb.length() > 0) {
+                            queuedData = sb.toString();
+                            return read();
+                        }
+                    }
+                } catch (ClassNotFoundException cnfe) {
+                    throw new IOException(cnfe.getMessage());
+                } catch (NoSuchMethodException nsme) {
+                    throw new IOException(nsme.getMessage());
+                } catch (IllegalAccessException iae) {
+                    throw new IOException(iae.getMessage());
+                } catch (IllegalArgumentException iarge) {
+                    throw new IOException(iarge.getMessage());
+                } catch (InvocationTargetException ite) {
+                    throw new IOException(ite.getMessage());
+                }
+            }
+        }
+        return ch;
+    }
+
+    /**
+     * Creates a new ClassConstants using the passed in
+     * Reader for instantiation.
+     *
+     * @param rdr A Reader object providing the underlying stream.
+     *            Must not be <code>null</code>.
+     *
+     * @return a new filter based on this configuration, but filtering
+     *         the specified reader
+     */
+    public final Reader chain(final Reader rdr) {
+        ClassConstants newFilter = new ClassConstants(rdr);
+        return newFilter;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/ConcatFilter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/ConcatFilter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/ConcatFilter.java	(revision 16139)
@@ -0,0 +1,214 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.File;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Concats a file before and/or after the file.
+ *
+ * <p>Example:<pre>
+ * <copy todir="build">
+ *     <fileset dir="src" includes="*.java"/>
+ *     <filterchain>
+ *         <concatfilter prepend="apache-license-java.txt"/>
+ *     </filterchain>
+ * </copy>
+ * </pre>
+ * Copies all java sources from <i>src</i> to <i>build</i> and adds the
+ * content of <i>apache-license-java.txt</i> add the beginning of each
+ * file.</p>
+ *
+ * @since 1.6
+ * @version 2003-09-23
+ */
+public final class ConcatFilter extends BaseParamFilterReader
+    implements ChainableReader {
+
+    /** File to add before the content. */
+    private File prepend;
+
+    /** File to add after the content. */
+    private File append;
+
+    /** Reader for prepend-file. */
+    private Reader prependReader = null;
+
+    /** Reader for append-file. */
+    private Reader appendReader = null;
+
+    /**
+     * Constructor for "dummy" instances.
+     *
+     * @see BaseFilterReader#BaseFilterReader()
+     */
+    public ConcatFilter() {
+        super();
+    }
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     */
+    public ConcatFilter(final Reader in) {
+        super(in);
+    }
+
+    /**
+     * Returns the next character in the filtered stream. If the desired
+     * number of lines have already been read, the resulting stream is
+     * effectively at an end. Otherwise, the next character from the
+     * underlying stream is read and returned.
+     *
+     * @return the next character in the resulting stream, or -1
+     * if the end of the resulting stream has been reached
+     *
+     * @exception IOException if the underlying stream throws an IOException
+     * during reading
+     */
+    public int read() throws IOException {
+        // do the "singleton" initialization
+        if (!getInitialized()) {
+            initialize();
+            setInitialized(true);
+        }
+
+        int ch = -1;
+
+        // The readers return -1 if they end. So simply read the "prepend"
+        // after that the "content" and at the end the "append" file.
+        if (prependReader != null) {
+            ch = prependReader.read();
+            if (ch == -1) {
+                // I am the only one so I have to close the reader
+                prependReader.close();
+                prependReader = null;
+            }
+        }
+        if (ch == -1) {
+            ch = super.read();
+        }
+        if (ch == -1) {
+            // don't call super.close() because that reader is used
+            // on other places ...
+            if (appendReader != null) {
+                ch = appendReader.read();
+                if (ch == -1) {
+                    // I am the only one so I have to close the reader
+                    appendReader.close();
+                    appendReader = null;
+                }
+            }
+        }
+
+        return ch;
+    }
+
+    /**
+     * Sets <i>prepend</i> attribute.
+     * @param prepend new value
+     */
+    public void setPrepend(final File prepend) {
+        this.prepend = prepend;
+    }
+
+    /**
+     * Returns <i>prepend</i> attribute.
+     * @return prepend attribute
+     */
+    public File getPrepend() {
+        return prepend;
+    }
+
+    /**
+     * Sets <i>append</i> attribute.
+     * @param append new value
+     */
+    public void setAppend(final File append) {
+        this.append = append;
+    }
+
+    /**
+     * Returns <i>append</i> attribute.
+     * @return append attribute
+     */
+    public File getAppend() {
+        return append;
+    }
+
+    /**
+     * Creates a new ConcatReader using the passed in
+     * Reader for instantiation.
+     *
+     * @param rdr A Reader object providing the underlying stream.
+     *            Must not be <code>null</code>.
+     *
+     * @return a new filter based on this configuration, but filtering
+     *         the specified reader
+     */
+    public Reader chain(final Reader rdr) {
+        ConcatFilter newFilter = new ConcatFilter(rdr);
+        newFilter.setPrepend(getPrepend());
+        newFilter.setAppend(getAppend());
+        // Usually the initialized is set to true. But here it must not.
+        // Because the prepend and append readers have to be instantiated
+        // on runtime
+        //newFilter.setInitialized(true);
+        return newFilter;
+    }
+
+    /**
+     * Scans the parameters list for the "lines" parameter and uses
+     * it to set the number of lines to be returned in the filtered stream.
+     * also scan for skip parameter.
+     */
+    private void initialize() throws IOException {
+        // get parameters
+        Parameter[] params = getParameters();
+        if (params != null) {
+            for (int i = 0; i < params.length; i++) {
+                if ("prepend".equals(params[i].getName())) {
+                    setPrepend(new File(params[i].getValue()));
+                    continue;
+                }
+                if ("append".equals(params[i].getName())) {
+                    setAppend(new File(params[i].getValue()));
+                    continue;
+                }
+            }
+        }
+        if (prepend != null) {
+            if (!prepend.isAbsolute()) {
+                prepend = new File(getProject().getBaseDir(), prepend.getPath());
+            }
+            prependReader = new BufferedReader(new FileReader(prepend));
+        }
+        if (append != null) {
+            if (!append.isAbsolute()) {
+                append = new File(getProject().getBaseDir(), append.getPath());
+            }
+            appendReader = new BufferedReader(new FileReader(append));
+        }
+   }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/EscapeUnicode.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/EscapeUnicode.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/EscapeUnicode.java	(revision 16139)
@@ -0,0 +1,127 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * This method converts non-latin characters to unicode escapes.
+ * Useful to load properties containing non latin
+ * Example:
+ *
+ * <pre>&lt;escapeunicode&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader
+        classname=&quot;org.apache.tools.ant.filters.EscapeUnicode&quot;/&gt;
+ *  </pre>
+ *
+ * @since Ant 1.6
+ */
+public class EscapeUnicode
+    extends BaseParamFilterReader
+    implements ChainableReader {
+    //this field will hold unnnn right after reading a non latin character
+    //afterwards it will be truncated of one char every call to read
+    private StringBuffer unicodeBuf;
+
+    /**
+     * Constructor for "dummy" instances.
+     *
+     * @see BaseFilterReader#BaseFilterReader()
+     */
+    public EscapeUnicode() {
+        super();
+        unicodeBuf = new StringBuffer();
+    }
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     */
+    public EscapeUnicode(final Reader in) {
+        super(in);
+        unicodeBuf = new StringBuffer();
+    }
+
+    /**
+     * Returns the next character in the filtered stream, converting non latin
+     * characters to unicode escapes.
+     *
+     * @return the next character in the resulting stream, or -1
+     * if the end of the resulting stream has been reached
+     *
+     * @exception IOException if the underlying stream throws
+     * an IOException during reading
+     */
+    public final int read() throws IOException {
+        if (!getInitialized()) {
+            initialize();
+            setInitialized(true);
+        }
+
+        int ch = -1;
+        if (unicodeBuf.length() == 0) {
+            ch = in.read();
+            if (ch != -1) {
+                char achar = (char) ch;
+                if (achar >= '\u0080') {
+                    unicodeBuf = new StringBuffer("u0000");
+                    String s = Integer.toHexString(ch);
+                    //replace the last 0s by the chars contained in s
+                    for (int i = 0; i < s.length(); i++) {
+                        unicodeBuf.setCharAt(unicodeBuf.length()
+                                             - s.length() + i,
+                                             s.charAt(i));
+                    }
+                    ch = '\\';
+                }
+            }
+        } else {
+            ch = (int) unicodeBuf.charAt(0);
+            unicodeBuf.deleteCharAt(0);
+        }
+        return ch;
+    }
+
+    /**
+     * Creates a new EscapeUnicode using the passed in
+     * Reader for instantiation.
+     *
+     * @param rdr A Reader object providing the underlying stream.
+     *            Must not be <code>null</code>.
+     *
+     * @return a new filter based on this configuration, but filtering
+     *         the specified reader
+     */
+    public final Reader chain(final Reader rdr) {
+        EscapeUnicode newFilter = new EscapeUnicode(rdr);
+        newFilter.setInitialized(true);
+        return newFilter;
+    }
+
+    /**
+     * Parses the parameters (currently unused)
+     */
+    private final void initialize() {
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/ExpandProperties.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/ExpandProperties.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/ExpandProperties.java	(revision 16139)
@@ -0,0 +1,112 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import org.apache.tools.ant.Project;
+
+/**
+ * Expands Ant properties, if any, in the data.
+ * <p>
+ * Example:<br>
+ * <pre>&lt;expandproperties/&gt;</pre>
+ * Or:
+ * <pre>&lt;filterreader
+ *    classname=&quot;org.apache.tools.ant.filters.ExpandProperties&quot;/&gt;</pre>
+ *
+ */
+public final class ExpandProperties
+    extends BaseFilterReader
+    implements ChainableReader {
+    /** Data that must be read from, if not null. */
+    private String queuedData = null;
+
+    /**
+     * Constructor for "dummy" instances.
+     *
+     * @see BaseFilterReader#BaseFilterReader()
+     */
+    public ExpandProperties() {
+        super();
+    }
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     */
+    public ExpandProperties(final Reader in) {
+        super(in);
+    }
+
+    /**
+     * Returns the next character in the filtered stream. The original
+     * stream is first read in fully, and the Ant properties are expanded.
+     * The results of this expansion are then queued so they can be read
+     * character-by-character.
+     *
+     * @return the next character in the resulting stream, or -1
+     * if the end of the resulting stream has been reached
+     *
+     * @exception IOException if the underlying stream throws an IOException
+     * during reading
+     */
+    public final int read() throws IOException {
+
+        int ch = -1;
+
+        if (queuedData != null && queuedData.length() == 0) {
+            queuedData = null;
+        }
+
+        if (queuedData != null) {
+            ch = queuedData.charAt(0);
+            queuedData = queuedData.substring(1);
+            if (queuedData.length() == 0) {
+                queuedData = null;
+            }
+        } else {
+            queuedData = readFully();
+            if (queuedData == null) {
+                ch = -1;
+            } else {
+                Project project = getProject();
+                queuedData = project.replaceProperties(queuedData);
+                return read();
+            }
+        }
+        return ch;
+    }
+
+    /**
+     * Creates a new ExpandProperties filter using the passed in
+     * Reader for instantiation.
+     *
+     * @param rdr A Reader object providing the underlying stream.
+     *            Must not be <code>null</code>.
+     *
+     * @return a new filter based on this configuration, but filtering
+     *         the specified reader
+     */
+    public final Reader chain(final Reader rdr) {
+        ExpandProperties newFilter = new ExpandProperties(rdr);
+        newFilter.setProject(getProject());
+        return newFilter;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/HeadFilter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/HeadFilter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/HeadFilter.java	(revision 16139)
@@ -0,0 +1,213 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import org.apache.tools.ant.util.LineTokenizer;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Reads the first <code>n</code> lines of a stream.
+ * (Default is first 10 lines.)
+ * <p>
+ * Example:
+ * <pre>&lt;headfilter lines=&quot;3&quot;/&gt;</pre>
+ * Or:
+ * <pre>&lt;filterreader classname=&quot;org.apache.tools.ant.filters.HeadFilter&quot;&gt;
+ *    &lt;param name=&quot;lines&quot; value=&quot;3&quot;/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ */
+public final class HeadFilter extends BaseParamFilterReader
+    implements ChainableReader {
+    /** Parameter name for the number of lines to be returned. */
+    private static final String LINES_KEY = "lines";
+
+    /** Parameter name for the number of lines to be skipped. */
+    private static final String SKIP_KEY = "skip";
+
+    /** Number of lines currently read in. */
+    private long linesRead = 0;
+
+    /** Default number of lines to show */
+    private static final int DEFAULT_NUM_LINES = 10;
+
+    /** Number of lines to be returned in the filtered stream. */
+    private long lines = DEFAULT_NUM_LINES;
+
+    /** Number of lines to be skipped. */
+    private long skip = 0;
+
+    /** A line tokenizer */
+    private LineTokenizer lineTokenizer = null;
+
+    /** the current line from the input stream */
+    private String    line      = null;
+    /** the position in the current line */
+    private int       linePos   = 0;
+
+    /**
+     * Constructor for "dummy" instances.
+     *
+     * @see BaseFilterReader#BaseFilterReader()
+     */
+    public HeadFilter() {
+        super();
+    }
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     */
+    public HeadFilter(final Reader in) {
+        super(in);
+        lineTokenizer = new LineTokenizer();
+        lineTokenizer.setIncludeDelims(true);
+    }
+
+    /**
+     * Returns the next character in the filtered stream. If the desired
+     * number of lines have already been read, the resulting stream is
+     * effectively at an end. Otherwise, the next character from the
+     * underlying stream is read and returned.
+     *
+     * @return the next character in the resulting stream, or -1
+     * if the end of the resulting stream has been reached
+     *
+     * @exception IOException if the underlying stream throws an IOException
+     * during reading
+     */
+    public final int read() throws IOException {
+        if (!getInitialized()) {
+            initialize();
+            setInitialized(true);
+        }
+
+        while (line == null || line.length() == 0) {
+            line = lineTokenizer.getToken(in);
+            if (line == null) {
+                return -1;
+            }
+            line = headFilter(line);
+            linePos = 0;
+        }
+
+        int ch = line.charAt(linePos);
+        linePos++;
+        if (linePos == line.length()) {
+            line = null;
+        }
+        return ch;
+    }
+
+    /**
+     * Sets the number of lines to be returned in the filtered stream.
+     *
+     * @param lines the number of lines to be returned in the filtered stream
+     */
+    public final void setLines(final long lines) {
+        this.lines = lines;
+    }
+
+    /**
+     * Returns the number of lines to be returned in the filtered stream.
+     *
+     * @return the number of lines to be returned in the filtered stream
+     */
+    private final long getLines() {
+        return lines;
+    }
+
+    /**
+     * Sets the number of lines to be skipped in the filtered stream.
+     *
+     * @param skip the number of lines to be skipped in the filtered stream
+     */
+    public final void setSkip(final long skip) {
+        this.skip = skip;
+    }
+
+    /**
+     * Returns the number of lines to be skipped in the filtered stream.
+     *
+     * @return the number of lines to be skipped in the filtered stream
+     */
+    private final long getSkip() {
+        return skip;
+    }
+
+    /**
+     * Creates a new HeadFilter using the passed in
+     * Reader for instantiation.
+     *
+     * @param rdr A Reader object providing the underlying stream.
+     *            Must not be <code>null</code>.
+     *
+     * @return a new filter based on this configuration, but filtering
+     *         the specified reader
+     */
+    public final Reader chain(final Reader rdr) {
+        HeadFilter newFilter = new HeadFilter(rdr);
+        newFilter.setLines(getLines());
+        newFilter.setSkip(getSkip());
+        newFilter.setInitialized(true);
+        return newFilter;
+    }
+
+    /**
+     * Scans the parameters list for the "lines" parameter and uses
+     * it to set the number of lines to be returned in the filtered stream.
+     * also scan for skip parameter.
+     */
+    private final void initialize() {
+        Parameter[] params = getParameters();
+        if (params != null) {
+            for (int i = 0; i < params.length; i++) {
+                if (LINES_KEY.equals(params[i].getName())) {
+                    lines = new Long(params[i].getValue()).longValue();
+                    continue;
+                }
+                if (SKIP_KEY.equals(params[i].getName())) {
+                    skip = new Long(params[i].getValue()).longValue();
+                    continue;
+                }
+            }
+        }
+    }
+
+    /**
+     * implements a head filter on the input stream
+     */
+    private String headFilter(String line) {
+        linesRead++;
+        if (skip > 0) {
+            if ((linesRead - 1) < skip) {
+                return null;
+            }
+        }
+
+        if (lines > 0) {
+            if (linesRead > (lines + skip)) {
+                return null;
+            }
+        }
+        return line;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/LineContains.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/LineContains.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/LineContains.java	(revision 16139)
@@ -0,0 +1,227 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Vector;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Filter which includes only those lines that contain all the user-specified
+ * strings.
+ *
+ * Example:
+ *
+ * <pre>&lt;linecontains&gt;
+ *   &lt;contains value=&quot;foo&quot;&gt;
+ *   &lt;contains value=&quot;bar&quot;&gt;
+ * &lt;/linecontains&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader classname=&quot;org.apache.tools.ant.filters.LineContains&quot;&gt;
+ *    &lt;param type=&quot;contains&quot; value=&quot;foo&quot;/&gt;
+ *    &lt;param type=&quot;contains&quot; value=&quot;bar&quot;/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ * This will include only those lines that contain <code>foo</code> and
+ * <code>bar</code>.
+ *
+ */
+public final class LineContains
+    extends BaseParamFilterReader
+    implements ChainableReader {
+    /** Parameter name for the words to filter on. */
+    private static final String CONTAINS_KEY = "contains";
+
+    /** Vector that holds the strings that input lines must contain. */
+    private Vector contains = new Vector();
+
+    /**
+     * Remaining line to be read from this filter, or <code>null</code> if
+     * the next call to <code>read()</code> should read the original stream
+     * to find the next matching line.
+     */
+    private String line = null;
+
+    /**
+     * Constructor for "dummy" instances.
+     *
+     * @see BaseFilterReader#BaseFilterReader()
+     */
+    public LineContains() {
+        super();
+    }
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     */
+    public LineContains(final Reader in) {
+        super(in);
+    }
+
+    /**
+     * Returns the next character in the filtered stream, only including
+     * lines from the original stream which contain all of the specified words.
+     *
+     * @return the next character in the resulting stream, or -1
+     * if the end of the resulting stream has been reached
+     *
+     * @exception IOException if the underlying stream throws an IOException
+     * during reading
+     */
+    public final int read() throws IOException {
+        if (!getInitialized()) {
+            initialize();
+            setInitialized(true);
+        }
+
+        int ch = -1;
+
+        if (line != null) {
+            ch = line.charAt(0);
+            if (line.length() == 1) {
+                line = null;
+            } else {
+                line = line.substring(1);
+            }
+        } else {
+            line = readLine();
+            final int containsSize = contains.size();
+
+            while (line != null) {
+                for (int i = 0; i < containsSize; i++) {
+                    String containsStr = (String) contains.elementAt(i);
+                    if (line.indexOf(containsStr) == -1) {
+                        line = null;
+                        break;
+                    }
+                }
+
+                if (line == null) {
+                    // line didn't match
+                    line = readLine();
+                } else {
+                    break;
+                }
+            }
+
+            if (line != null) {
+                return read();
+            }
+        }
+
+        return ch;
+    }
+
+    /**
+     * Adds a <code>contains</code> element.
+     *
+     * @param contains The <code>contains</code> element to add.
+     *                 Must not be <code>null</code>.
+     */
+    public final void addConfiguredContains(final Contains contains) {
+        this.contains.addElement(contains.getValue());
+    }
+
+    /**
+     * Sets the vector of words which must be contained within a line read
+     * from the original stream in order for it to match this filter.
+     *
+     * @param contains A vector of words which must be contained within a line
+     * in order for it to match in this filter. Must not be <code>null</code>.
+     */
+    private void setContains(final Vector contains) {
+        this.contains = contains;
+    }
+
+    /**
+     * Returns the vector of words which must be contained within a line read
+     * from the original stream in order for it to match this filter.
+     *
+     * @return the vector of words which must be contained within a line read
+     * from the original stream in order for it to match this filter. The
+     * returned object is "live" - in other words, changes made to the
+     * returned object are mirrored in the filter.
+     */
+    private final Vector getContains() {
+        return contains;
+    }
+
+    /**
+     * Creates a new LineContains using the passed in
+     * Reader for instantiation.
+     *
+     * @param rdr A Reader object providing the underlying stream.
+     *            Must not be <code>null</code>.
+     *
+     * @return a new filter based on this configuration, but filtering
+     *         the specified reader
+     */
+    public final Reader chain(final Reader rdr) {
+        LineContains newFilter = new LineContains(rdr);
+        newFilter.setContains(getContains());
+        newFilter.setInitialized(true);
+        return newFilter;
+    }
+
+    /**
+     * Parses the parameters to add user-defined contains strings.
+     */
+    private final void initialize() {
+        Parameter[] params = getParameters();
+        if (params != null) {
+            for (int i = 0; i < params.length; i++) {
+                if (CONTAINS_KEY.equals(params[i].getType())) {
+                    contains.addElement(params[i].getValue());
+                }
+            }
+        }
+    }
+
+    /**
+     * Holds a contains element
+     */
+    public static class Contains {
+
+        /** User defined contains string */
+        private String value;
+
+        /**
+         * Sets the contains string
+         *
+         * @param contains The contains string to set.
+         *                 Must not be <code>null</code>.
+         */
+        public final void setValue(String contains) {
+            value = contains;
+        }
+
+        /**
+         * Returns the contains string.
+         *
+         * @return the contains string for this element
+         */
+        public final String getValue() {
+            return value;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/LineContainsRegExp.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/LineContainsRegExp.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/LineContainsRegExp.java	(revision 16139)
@@ -0,0 +1,207 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Vector;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.RegularExpression;
+import org.apache.tools.ant.util.regexp.Regexp;
+
+/**
+ * Filter which includes only those lines that contain the user-specified
+ * regular expression matching strings.
+ *
+ * Example:
+ * <pre>&lt;linecontainsregexp&gt;
+ *   &lt;regexp pattern=&quot;foo*&quot;&gt;
+ * &lt;/linecontainsregexp&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader classname=&quot;org.apache.tools.ant.filters.LineContainsRegExp&quot;&gt;
+ *    &lt;param type=&quot;regexp&quot; value=&quot;foo*&quot;/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ * This will fetch all those lines that contain the pattern <code>foo</code>
+ *
+ */
+public final class LineContainsRegExp
+    extends BaseParamFilterReader
+    implements ChainableReader {
+    /** Parameter name for the regular expression to filter on. */
+    private static final String REGEXP_KEY = "regexp";
+
+    /** Vector that holds the expressions that input lines must contain. */
+    private Vector regexps = new Vector();
+
+    /**
+     * Remaining line to be read from this filter, or <code>null</code> if
+     * the next call to <code>read()</code> should read the original stream
+     * to find the next matching line.
+     */
+    private String line = null;
+
+    /**
+     * Constructor for "dummy" instances.
+     *
+     * @see BaseFilterReader#BaseFilterReader()
+     */
+    public LineContainsRegExp() {
+        super();
+    }
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     */
+    public LineContainsRegExp(final Reader in) {
+        super(in);
+    }
+
+    /**
+     * Returns the next character in the filtered stream, only including
+     * lines from the original stream which match all of the specified
+     * regular expressions.
+     *
+     * @return the next character in the resulting stream, or -1
+     * if the end of the resulting stream has been reached
+     *
+     * @exception IOException if the underlying stream throws an IOException
+     * during reading
+     */
+    public final int read() throws IOException {
+        if (!getInitialized()) {
+            initialize();
+            setInitialized(true);
+        }
+
+        int ch = -1;
+
+        if (line != null) {
+            ch = line.charAt(0);
+            if (line.length() == 1) {
+                line = null;
+            } else {
+                line = line.substring(1);
+            }
+        } else {
+            line = readLine();
+            final int regexpsSize = regexps.size();
+
+            while (line != null) {
+                for (int i = 0; i < regexpsSize; i++) {
+                    RegularExpression regexp = (RegularExpression)
+                                                        regexps.elementAt(i);
+                    Regexp re = regexp.getRegexp(getProject());
+                    boolean matches = re.matches(line);
+                    if (!matches) {
+                        line = null;
+                        break;
+                    }
+                }
+
+                if (line == null) {
+                    // line didn't match
+                    line = readLine();
+                } else {
+                    break;
+                }
+            }
+
+            if (line != null) {
+                return read();
+            }
+        }
+
+        return ch;
+    }
+
+    /**
+     * Adds a <code>regexp</code> element.
+     *
+     * @param regExp The <code>regexp</code> element to add.
+     *               Must not be <code>null</code>.
+     */
+    public final void addConfiguredRegexp(final RegularExpression regExp) {
+        this.regexps.addElement(regExp);
+    }
+
+    /**
+     * Sets the vector of regular expressions which must be contained within
+     * a line read from the original stream in order for it to match this
+     * filter.
+     *
+     * @param regexps A vector of regular expressions which must be contained
+     * within a line in order for it to match in this filter. Must not be
+     * <code>null</code>.
+     */
+    private void setRegexps(final Vector regexps) {
+        this.regexps = regexps;
+    }
+
+    /**
+     * Returns the vector of regular expressions which must be contained within
+     * a line read from the original stream in order for it to match this
+     * filter.
+     *
+     * @return the vector of regular expressions which must be contained within
+     * a line read from the original stream in order for it to match this
+     * filter. The returned object is "live" - in other words, changes made to
+     * the returned object are mirrored in the filter.
+     */
+    private final Vector getRegexps() {
+        return regexps;
+    }
+
+    /**
+     * Creates a new LineContainsRegExp using the passed in
+     * Reader for instantiation.
+     *
+     * @param rdr A Reader object providing the underlying stream.
+     *            Must not be <code>null</code>.
+     *
+     * @return a new filter based on this configuration, but filtering
+     *         the specified reader
+     */
+    public final Reader chain(final Reader rdr) {
+        LineContainsRegExp newFilter = new LineContainsRegExp(rdr);
+        newFilter.setRegexps(getRegexps());
+        newFilter.setInitialized(true);
+        return newFilter;
+    }
+
+    /**
+     * Parses parameters to add user defined regular expressions.
+     */
+    private final void initialize() {
+        Parameter[] params = getParameters();
+        if (params != null) {
+            for (int i = 0; i < params.length; i++) {
+                if (REGEXP_KEY.equals(params[i].getType())) {
+                    String pattern = params[i].getValue();
+                    RegularExpression regexp = new RegularExpression();
+                    regexp.setPattern(pattern);
+                    regexps.addElement(regexp);
+                }
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/PrefixLines.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/PrefixLines.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/PrefixLines.java	(revision 16139)
@@ -0,0 +1,162 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Attaches a prefix to every line.
+ *
+ * Example:
+ * <pre>&lt;prefixlines prefix=&quot;Foo&quot;/&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader classname=&quot;org.apache.tools.ant.filters.PrefixLines&quot;&gt;
+ *  &lt;param name=&quot;prefix&quot; value=&quot;Foo&quot;/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ */
+public final class PrefixLines
+    extends BaseParamFilterReader
+    implements ChainableReader {
+    /** Parameter name for the prefix. */
+    private static final String PREFIX_KEY = "prefix";
+
+    /** The prefix to be used. */
+    private String prefix = null;
+
+    /** Data that must be read from, if not null. */
+    private String queuedData = null;
+
+    /**
+     * Constructor for "dummy" instances.
+     *
+     * @see BaseFilterReader#BaseFilterReader()
+     */
+    public PrefixLines() {
+        super();
+    }
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     */
+    public PrefixLines(final Reader in) {
+        super(in);
+    }
+
+    /**
+     * Returns the next character in the filtered stream. One line is read
+     * from the original input, and the prefix added. The resulting
+     * line is then used until it ends, at which point the next original line
+     * is read, etc.
+     *
+     * @return the next character in the resulting stream, or -1
+     * if the end of the resulting stream has been reached
+     *
+     * @exception IOException if the underlying stream throws an IOException
+     * during reading
+     */
+    public final int read() throws IOException {
+        if (!getInitialized()) {
+            initialize();
+            setInitialized(true);
+        }
+
+        int ch = -1;
+
+        if (queuedData != null && queuedData.length() == 0) {
+            queuedData = null;
+        }
+
+        if (queuedData != null) {
+            ch = queuedData.charAt(0);
+            queuedData = queuedData.substring(1);
+            if (queuedData.length() == 0) {
+                queuedData = null;
+            }
+        } else {
+            queuedData = readLine();
+            if (queuedData == null) {
+                ch = -1;
+            } else {
+                if (prefix != null) {
+                    queuedData = prefix + queuedData;
+                }
+                return read();
+            }
+        }
+        return ch;
+    }
+
+    /**
+     * Sets the prefix to add at the start of each input line.
+     *
+     * @param prefix The prefix to add at the start of each input line.
+     *               May be <code>null</code>, in which case no prefix
+     *               is added.
+     */
+    public final void setPrefix(final String prefix) {
+        this.prefix = prefix;
+    }
+
+    /**
+     * Returns the prefix which will be added at the start of each input line.
+     *
+     * @return the prefix which will be added at the start of each input line
+     */
+    private final String getPrefix() {
+        return prefix;
+    }
+
+    /**
+     * Creates a new PrefixLines filter using the passed in
+     * Reader for instantiation.
+     *
+     * @param rdr A Reader object providing the underlying stream.
+     *            Must not be <code>null</code>.
+     *
+     * @return a new filter based on this configuration, but filtering
+     *         the specified reader
+     */
+    public final Reader chain(final Reader rdr) {
+        PrefixLines newFilter = new PrefixLines(rdr);
+        newFilter.setPrefix(getPrefix());
+        newFilter.setInitialized(true);
+        return newFilter;
+    }
+
+    /**
+     * Initializes the prefix if it is available from the parameters.
+     */
+    private final void initialize() {
+        Parameter[] params = getParameters();
+        if (params != null) {
+            for (int i = 0; i < params.length; i++) {
+                if (PREFIX_KEY.equals(params[i].getName())) {
+                    prefix = params[i].getValue();
+                    break;
+                }
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/ReplaceTokens.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/ReplaceTokens.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/ReplaceTokens.java	(revision 16139)
@@ -0,0 +1,342 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Hashtable;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Replaces tokens in the original input with user-supplied values.
+ *
+ * Example:
+ *
+ * <pre>&lt;replacetokens begintoken=&quot;#&quot; endtoken=&quot;#&quot;&gt;
+ *   &lt;token key=&quot;DATE&quot; value=&quot;${TODAY}&quot;/&gt;
+ * &lt;/replacetokens&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader classname="org.apache.tools.ant.filters.ReplaceTokens"&gt;
+ *   &lt;param type="tokenchar" name="begintoken" value="#"/&gt;
+ *   &lt;param type="tokenchar" name="endtoken" value="#"/&gt;
+ *   &lt;param type="token" name="DATE" value="${TODAY}"/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ */
+public final class ReplaceTokens
+    extends BaseParamFilterReader
+    implements ChainableReader {
+    /** Default "begin token" character. */
+    private static final char DEFAULT_BEGIN_TOKEN = '@';
+
+    /** Default "end token" character. */
+    private static final char DEFAULT_END_TOKEN = '@';
+
+    /** Data to be used before reading from stream again */
+    private String queuedData = null;
+
+    /** replacement test from a token */
+    private String replaceData = null;
+
+    /** Index into replacement data */
+    private int replaceIndex = -1;
+
+    /** Index into queue data */
+    private int queueIndex = -1;
+
+    /** Hashtable to hold the replacee-replacer pairs (String to String). */
+    private Hashtable hash = new Hashtable();
+
+    /** Character marking the beginning of a token. */
+    private char beginToken = DEFAULT_BEGIN_TOKEN;
+
+    /** Character marking the end of a token. */
+    private char endToken = DEFAULT_END_TOKEN;
+
+    /**
+     * Constructor for "dummy" instances.
+     *
+     * @see BaseFilterReader#BaseFilterReader()
+     */
+    public ReplaceTokens() {
+        super();
+    }
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     */
+    public ReplaceTokens(final Reader in) {
+        super(in);
+    }
+
+    private int getNextChar() throws IOException {
+        if (queueIndex != -1) {
+            final int ch = queuedData.charAt(queueIndex++);
+            if (queueIndex >= queuedData.length()) {
+                queueIndex = -1;
+            }
+            return ch;
+        }
+
+        return in.read();
+    }
+
+    /**
+     * Returns the next character in the filtered stream, replacing tokens
+     * from the original stream.
+     *
+     * @return the next character in the resulting stream, or -1
+     * if the end of the resulting stream has been reached
+     *
+     * @exception IOException if the underlying stream throws an IOException
+     * during reading
+     */
+    public final int read() throws IOException {
+        if (!getInitialized()) {
+            initialize();
+            setInitialized(true);
+        }
+
+        if (replaceIndex != -1) {
+            final int ch = replaceData.charAt(replaceIndex++);
+            if (replaceIndex >= replaceData.length()) {
+                replaceIndex = -1;
+            }
+            return ch;
+        }
+
+        int ch = getNextChar();
+
+        if (ch == beginToken) {
+            final StringBuffer key = new StringBuffer("");
+            do  {
+                ch = getNextChar();
+                if (ch != -1) {
+                    key.append((char) ch);
+                } else {
+                    break;
+                }
+            } while (ch != endToken);
+
+            if (ch == -1) {
+                if (queuedData == null || queueIndex == -1) {
+                    queuedData = key.toString();
+                } else {
+                    queuedData
+                        = key.toString() + queuedData.substring(queueIndex);
+                }
+                queueIndex = 0;
+                return beginToken;
+            } else {
+                key.setLength(key.length() - 1);
+
+                final String replaceWith = (String) hash.get(key.toString());
+                if (replaceWith != null) {
+                    if (replaceWith.length() > 0) {
+                        replaceData = replaceWith;
+                        replaceIndex = 0;
+                    }
+                    return read();
+                } else {
+                    String newData = key.toString() + endToken;
+                    if (queuedData == null || queueIndex == -1) {
+                        queuedData = newData;
+                    } else {
+                        queuedData = newData + queuedData.substring(queueIndex);
+                    }
+                    queueIndex = 0;
+                    return beginToken;
+                }
+            }
+        }
+        return ch;
+    }
+
+    /**
+     * Sets the "begin token" character.
+     *
+     * @param beginToken the character used to denote the beginning of a token
+     */
+    public final void setBeginToken(final char beginToken) {
+        this.beginToken = beginToken;
+    }
+
+    /**
+     * Returns the "begin token" character.
+     *
+     * @return the character used to denote the beginning of a token
+     */
+    private final char getBeginToken() {
+        return beginToken;
+    }
+
+    /**
+     * Sets the "end token" character.
+     *
+     * @param endToken the character used to denote the end of a token
+     */
+    public final void setEndToken(final char endToken) {
+        this.endToken = endToken;
+    }
+
+    /**
+     * Returns the "end token" character.
+     *
+     * @return the character used to denote the end of a token
+     */
+    private final char getEndToken() {
+        return endToken;
+    }
+
+    /**
+     * Adds a token element to the map of tokens to replace.
+     *
+     * @param token The token to add to the map of replacements.
+     *              Must not be <code>null</code>.
+     */
+    public final void addConfiguredToken(final Token token) {
+        hash.put(token.getKey(), token.getValue());
+    }
+
+    /**
+     * Sets the map of tokens to replace.
+     *
+     * @param hash A map (String->String) of token keys to replacement
+     * values. Must not be <code>null</code>.
+     */
+    private void setTokens(final Hashtable hash) {
+        this.hash = hash;
+    }
+
+    /**
+     * Returns the map of tokens which will be replaced.
+     *
+     * @return a map (String->String) of token keys to replacement
+     * values
+     */
+    private final Hashtable getTokens() {
+        return hash;
+    }
+
+    /**
+     * Creates a new ReplaceTokens using the passed in
+     * Reader for instantiation.
+     *
+     * @param rdr A Reader object providing the underlying stream.
+     *            Must not be <code>null</code>.
+     *
+     * @return a new filter based on this configuration, but filtering
+     *         the specified reader
+     */
+    public final Reader chain(final Reader rdr) {
+        ReplaceTokens newFilter = new ReplaceTokens(rdr);
+        newFilter.setBeginToken(getBeginToken());
+        newFilter.setEndToken(getEndToken());
+        newFilter.setTokens(getTokens());
+        newFilter.setInitialized(true);
+        return newFilter;
+    }
+
+    /**
+     * Initializes tokens and loads the replacee-replacer hashtable.
+     */
+    private final void initialize() {
+        Parameter[] params = getParameters();
+        if (params != null) {
+            for (int i = 0; i < params.length; i++) {
+                if (params[i] != null) {
+                    final String type = params[i].getType();
+                    if ("tokenchar".equals(type)) {
+                        final String name = params[i].getName();
+                        String value = params[i].getValue();
+                        if ("begintoken".equals(name)) {
+                            if (value.length() == 0) {
+                                throw new BuildException("Begin token cannot "
+                                    + "be empty");
+                            }
+                            beginToken = params[i].getValue().charAt(0);
+                        } else if ("endtoken".equals(name)) {
+                            if (value.length() == 0) {
+                                throw new BuildException("End token cannot "
+                                    + "be empty");
+                            }
+                            endToken = params[i].getValue().charAt(0);
+                        }
+                    } else if ("token".equals(type)) {
+                        final String name = params[i].getName();
+                        final String value = params[i].getValue();
+                        hash.put(name, value);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Holds a token
+     */
+    public static class Token {
+
+        /** Token key */
+        private String key;
+
+        /** Token value */
+        private String value;
+
+        /**
+         * Sets the token key
+         *
+         * @param key The key for this token. Must not be <code>null</code>.
+         */
+        public final void setKey(String key) {
+            this.key = key;
+        }
+
+        /**
+         * Sets the token value
+         *
+         * @param value The value for this token. Must not be <code>null</code>.
+         */
+        public final void setValue(String value) {
+            this.value = value;
+        }
+
+        /**
+         * Returns the key for this token.
+         *
+         * @return the key for this token
+         */
+        public final String getKey() {
+            return key;
+        }
+
+        /**
+         * Returns the value for this token.
+         *
+         * @return the value for this token
+         */
+        public final String getValue() {
+            return value;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/StringInputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/StringInputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/StringInputStream.java	(revision 16139)
@@ -0,0 +1,50 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.StringReader;
+
+import org.apache.tools.ant.util.ReaderInputStream;
+
+/**
+ * Wraps a String as an InputStream.
+ *
+ */
+public class StringInputStream extends ReaderInputStream {
+
+    /**
+     * Composes a stream from a String
+     *
+     * @param source The string to read from. Must not be <code>null</code>.
+     */
+    public StringInputStream(String source) {
+        super(new StringReader(source));
+    }
+
+    /**
+     * Composes a stream from a String with the specified encoding
+     *
+     * @param source The string to read from. Must not be <code>null</code>.
+     * @param encoding The encoding scheme.  Also must not be <CODE>null</CODE>.
+     */
+    public StringInputStream(String source, String encoding) {
+        super(new StringReader(source), encoding);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/StripJavaComments.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/StripJavaComments.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/StripJavaComments.java	(revision 16139)
@@ -0,0 +1,144 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * This is a Java comment and string stripper reader that filters
+ * those lexical tokens out for purposes of simple Java parsing.
+ * (if you have more complex Java parsing needs, use a real lexer).
+ * Since this class heavily relies on the single char read function,
+ * you are recommended to make it work on top of a buffered reader.
+ *
+ */
+public final class StripJavaComments
+    extends BaseFilterReader
+    implements ChainableReader {
+
+    /**
+     * The read-ahead character, used for effectively pushing a single
+     * character back. A value of -1 indicates that no character is in the
+     * buffer.
+     */
+    private int readAheadCh = -1;
+
+    /**
+     * Whether or not the parser is currently in the middle of a string
+     * literal.
+     */
+    private boolean inString = false;
+
+    /**
+     * Whether or not the last char has been a backslash.
+     */
+    private boolean quoted = false;
+
+    /**
+     * Constructor for "dummy" instances.
+     *
+     * @see BaseFilterReader#BaseFilterReader()
+     */
+    public StripJavaComments() {
+        super();
+    }
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     */
+    public StripJavaComments(final Reader in) {
+        super(in);
+    }
+
+    /**
+     * Returns the next character in the filtered stream, not including
+     * Java comments.
+     *
+     * @return the next character in the resulting stream, or -1
+     * if the end of the resulting stream has been reached
+     *
+     * @exception IOException if the underlying stream throws an IOException
+     * during reading
+     */
+    public final int read() throws IOException {
+        int ch = -1;
+        if (readAheadCh != -1) {
+            ch = readAheadCh;
+            readAheadCh = -1;
+        } else {
+            ch = in.read();
+            if (ch == '"' && !quoted) {
+                inString = !inString;
+                quoted = false;
+            } else if (ch == '\\') {
+                quoted = !quoted;
+            } else {
+                quoted = false;
+                if (!inString) {
+                    if (ch == '/') {
+                        ch = in.read();
+                        if (ch == '/') {
+                            while (ch != '\n' && ch != -1 && ch != '\r') {
+                                ch = in.read();
+                            }
+                        } else if (ch == '*') {
+                            while (ch != -1) {
+                                ch = in.read();
+                                if (ch == '*') {
+                                    ch = in.read();
+                                    while (ch == '*' && ch != -1) {
+                                        ch = in.read();
+                                    }
+
+                                    if (ch == '/') {
+                                        ch = read();
+                                        break;
+                                    }
+                                }
+                            }
+                        } else {
+                            readAheadCh = ch;
+                            ch = '/';
+                        }
+                    }
+                }
+            }
+        }
+
+        return ch;
+    }
+
+    /**
+     * Creates a new StripJavaComments using the passed in
+     * Reader for instantiation.
+     *
+     * @param rdr A Reader object providing the underlying stream.
+     *            Must not be <code>null</code>.
+     *
+     * @return a new filter based on this configuration, but filtering
+     *         the specified reader
+     */
+
+    public final Reader chain(final Reader rdr) {
+        StripJavaComments newFilter = new StripJavaComments(rdr);
+        return newFilter;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/StripLineBreaks.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/StripLineBreaks.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/StripLineBreaks.java	(revision 16139)
@@ -0,0 +1,152 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Filter to flatten the stream to a single line.
+ *
+ * Example:
+ *
+ * <pre>&lt;striplinebreaks/&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader
+ *   classname=&quot;org.apache.tools.ant.filters.StripLineBreaks&quot;/&gt;</pre>
+ *
+ */
+public final class StripLineBreaks
+    extends BaseParamFilterReader
+    implements ChainableReader {
+    /**
+     * Line-breaking characters.
+     * What should we do on funny IBM mainframes with odd line endings?
+     */
+    private static final String DEFAULT_LINE_BREAKS = "\r\n";
+
+    /** Parameter name for the line-breaking characters parameter. */
+    private static final String LINE_BREAKS_KEY = "linebreaks";
+
+    /** The characters that are recognized as line breaks. */
+    private String lineBreaks = DEFAULT_LINE_BREAKS;
+
+    /**
+     * Constructor for "dummy" instances.
+     *
+     * @see BaseFilterReader#BaseFilterReader()
+     */
+    public StripLineBreaks() {
+        super();
+    }
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     */
+    public StripLineBreaks(final Reader in) {
+        super(in);
+    }
+
+    /**
+     * Returns the next character in the filtered stream, only including
+     * characters not in the set of line-breaking characters.
+     *
+     * @return the next character in the resulting stream, or -1
+     * if the end of the resulting stream has been reached
+     *
+     * @exception IOException if the underlying stream throws an IOException
+     * during reading
+     */
+    public final int read() throws IOException {
+        if (!getInitialized()) {
+            initialize();
+            setInitialized(true);
+        }
+
+        int ch = in.read();
+        while (ch != -1) {
+            if (lineBreaks.indexOf(ch) == -1) {
+                break;
+            } else {
+                ch = in.read();
+            }
+        }
+        return ch;
+    }
+
+    /**
+     * Sets the line-breaking characters.
+     *
+     * @param lineBreaks A String containing all the characters to be
+     *                   considered as line-breaking.
+     */
+    public final void setLineBreaks(final String lineBreaks) {
+        this.lineBreaks = lineBreaks;
+    }
+
+    /**
+     * Returns the line-breaking characters as a String.
+     *
+     * @return a String containing all the characters considered as
+     *         line-breaking
+     */
+    private final String getLineBreaks() {
+        return lineBreaks;
+    }
+
+    /**
+     * Creates a new StripLineBreaks using the passed in
+     * Reader for instantiation.
+     *
+     * @param rdr A Reader object providing the underlying stream.
+     *            Must not be <code>null</code>.
+     *
+     * @return a new filter based on this configuration, but filtering
+     *         the specified reader
+     */
+    public final Reader chain(final Reader rdr) {
+        StripLineBreaks newFilter = new StripLineBreaks(rdr);
+        newFilter.setLineBreaks(getLineBreaks());
+        newFilter.setInitialized(true);
+        return newFilter;
+    }
+
+    /**
+     * Parses the parameters to set the line-breaking characters.
+     */
+    private final void initialize() {
+        String userDefinedLineBreaks = null;
+        Parameter[] params = getParameters();
+        if (params != null) {
+            for (int i = 0; i < params.length; i++) {
+                if (LINE_BREAKS_KEY.equals(params[i].getName())) {
+                    userDefinedLineBreaks = params[i].getValue();
+                    break;
+                }
+            }
+        }
+        if (userDefinedLineBreaks != null) {
+            lineBreaks = userDefinedLineBreaks;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/StripLineComments.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/StripLineComments.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/StripLineComments.java	(revision 16139)
@@ -0,0 +1,222 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Vector;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * This filter strips line comments.
+ *
+ * Example:
+ *
+ * <pre>&lt;striplinecomments&gt;
+ *   &lt;comment value=&quot;#&quot;/&gt;
+ *   &lt;comment value=&quot;--&quot;/&gt;
+ *   &lt;comment value=&quot;REM &quot;/&gt;
+ *   &lt;comment value=&quot;rem &quot;/&gt;
+ *   &lt;comment value=&quot;//&quot;/&gt;
+ * &lt;/striplinecomments&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader
+ *      classname=&quot;org.apache.tools.ant.filters.StripLineComments&quot;&gt;
+ *   &lt;param type=&quot;comment&quot; value="#&quot;/&gt;
+ *   &lt;param type=&quot;comment&quot; value=&quot;--&quot;/&gt;
+ *   &lt;param type=&quot;comment&quot; value=&quot;REM &quot;/&gt;
+ *   &lt;param type=&quot;comment&quot; value=&quot;rem &quot;/&gt;
+ *   &lt;param type=&quot;comment&quot; value=&quot;//&quot;/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ */
+public final class StripLineComments
+    extends BaseParamFilterReader
+    implements ChainableReader {
+    /** Parameter name for the comment prefix. */
+    private static final String COMMENTS_KEY = "comment";
+
+    /** Vector that holds the comment prefixes. */
+    private Vector comments = new Vector();
+
+    /** The line that has been read ahead. */
+    private String line = null;
+
+    /**
+     * Constructor for "dummy" instances.
+     *
+     * @see BaseFilterReader#BaseFilterReader()
+     */
+    public StripLineComments() {
+        super();
+    }
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     */
+    public StripLineComments(final Reader in) {
+        super(in);
+    }
+
+    /**
+     * Returns the next character in the filtered stream, only including
+     * lines from the original stream which don't start with any of the
+     * specified comment prefixes.
+     *
+     * @return the next character in the resulting stream, or -1
+     * if the end of the resulting stream has been reached
+     *
+     * @exception IOException if the underlying stream throws an IOException
+     * during reading
+     */
+    public final int read() throws IOException {
+        if (!getInitialized()) {
+            initialize();
+            setInitialized(true);
+        }
+
+        int ch = -1;
+
+        if (line != null) {
+            ch = line.charAt(0);
+            if (line.length() == 1) {
+                line = null;
+            } else {
+                line = line.substring(1);
+            }
+        } else {
+            line = readLine();
+            final int commentsSize = comments.size();
+
+            while (line != null) {
+                for (int i = 0; i < commentsSize; i++) {
+                    String comment = (String) comments.elementAt(i);
+                    if (line.startsWith(comment)) {
+                        line = null;
+                        break;
+                    }
+                }
+
+                if (line == null) {
+                    // line started with comment
+                    line = readLine();
+                } else {
+                    break;
+                }
+            }
+
+            if (line != null) {
+                return read();
+            }
+        }
+
+        return ch;
+    }
+
+    /**
+     * Adds a <code>comment</code> element to the list of prefixes.
+     *
+     * @param comment The <code>comment</code> element to add to the
+     * list of comment prefixes to strip. Must not be <code>null</code>.
+     */
+    public final void addConfiguredComment(final Comment comment) {
+        comments.addElement(comment.getValue());
+    }
+
+    /**
+     * Sets the list of comment prefixes to strip.
+     *
+     * @param comments A list of strings, each of which is a prefix
+     * for a comment line. Must not be <code>null</code>.
+     */
+    private void setComments(final Vector comments) {
+        this.comments = comments;
+    }
+
+    /**
+     * Returns the list of comment prefixes to strip.
+     *
+     * @return the list of comment prefixes to strip.
+     */
+    private final Vector getComments() {
+        return comments;
+    }
+
+    /**
+     * Creates a new StripLineComments using the passed in
+     * Reader for instantiation.
+     *
+     * @param rdr A Reader object providing the underlying stream.
+     *            Must not be <code>null</code>.
+     *
+     * @return a new filter based on this configuration, but filtering
+     *         the specified reader
+     */
+    public final Reader chain(final Reader rdr) {
+        StripLineComments newFilter = new StripLineComments(rdr);
+        newFilter.setComments(getComments());
+        newFilter.setInitialized(true);
+        return newFilter;
+    }
+
+    /**
+     * Parses the parameters to set the comment prefixes.
+     */
+    private final void initialize() {
+        Parameter[] params = getParameters();
+        if (params != null) {
+            for (int i = 0; i < params.length; i++) {
+                if (COMMENTS_KEY.equals(params[i].getType())) {
+                    comments.addElement(params[i].getValue());
+                }
+            }
+        }
+    }
+
+    /**
+     * The class that holds a comment representation.
+     */
+    public static class Comment {
+
+        /** The prefix for a line comment. */
+        private String value;
+
+        /**
+         * Sets the prefix for this type of line comment.
+         *
+         * @param comment The prefix for a line comment of this type.
+         * Must not be <code>null</code>.
+         */
+        public final void setValue(String comment) {
+            value = comment;
+        }
+
+        /**
+         * Returns the prefix for this type of line comment.
+         *
+         * @return the prefix for this type of line comment.
+         */
+        public final String getValue() {
+            return value;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/TabsToSpaces.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/TabsToSpaces.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/TabsToSpaces.java	(revision 16139)
@@ -0,0 +1,154 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Converts tabs to spaces.
+ *
+ * Example:
+ *
+ * <pre>&lt;tabtospaces tablength=&quot;8&quot;/&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader classname=&quot;org.apache.tools.ant.filters.TabsToSpaces&quot;&gt;
+ *   &lt;param name=&quot;tablength&quot; value=&quot;8&quot;/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ */
+public final class TabsToSpaces
+    extends BaseParamFilterReader
+    implements ChainableReader {
+    /** The default tab length. */
+    private static final int DEFAULT_TAB_LENGTH = 8;
+
+    /** Parameter name for the length of a tab. */
+    private static final String TAB_LENGTH_KEY = "tablength";
+
+    /** Tab length in this filter. */
+    private int tabLength = DEFAULT_TAB_LENGTH;
+
+    /** The number of spaces still to be read to represent the last-read tab. */
+    private int spacesRemaining = 0;
+
+    /**
+     * Constructor for "dummy" instances.
+     *
+     * @see BaseFilterReader#BaseFilterReader()
+     */
+    public TabsToSpaces() {
+        super();
+    }
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     */
+    public TabsToSpaces(final Reader in) {
+        super(in);
+    }
+
+    /**
+     * Returns the next character in the filtered stream, converting tabs
+     * to the specified number of spaces.
+     *
+     * @return the next character in the resulting stream, or -1
+     * if the end of the resulting stream has been reached
+     *
+     * @exception IOException if the underlying stream throws an IOException
+     * during reading
+     */
+    public final int read() throws IOException {
+        if (!getInitialized()) {
+            initialize();
+            setInitialized(true);
+        }
+
+        int ch = -1;
+
+        if (spacesRemaining > 0) {
+            spacesRemaining--;
+            ch = ' ';
+        } else {
+            ch = in.read();
+            if (ch == '\t') {
+                spacesRemaining = tabLength - 1;
+                ch = ' ';
+            }
+        }
+        return ch;
+    }
+
+    /**
+     * Sets the tab length.
+     *
+     * @param tabLength the number of spaces to be used when converting a tab.
+     */
+    public final void setTablength(final int tabLength) {
+        this.tabLength = tabLength;
+    }
+
+    /**
+     * Returns the tab length.
+     *
+     * @return the number of spaces used when converting a tab
+     */
+    private final int getTablength() {
+        return tabLength;
+    }
+
+    /**
+     * Creates a new TabsToSpaces using the passed in
+     * Reader for instantiation.
+     *
+     * @param rdr A Reader object providing the underlying stream.
+     *            Must not be <code>null</code>.
+     *
+     * @return a new filter based on this configuration, but filtering
+     *         the specified reader
+     */
+    public final Reader chain(final Reader rdr) {
+        TabsToSpaces newFilter = new TabsToSpaces(rdr);
+        newFilter.setTablength(getTablength());
+        newFilter.setInitialized(true);
+        return newFilter;
+    }
+
+    /**
+     * Parses the parameters to set the tab length.
+     */
+    private final void initialize() {
+        Parameter[] params = getParameters();
+        if (params != null) {
+            for (int i = 0; i < params.length; i++) {
+                if (params[i] != null) {
+                    if (TAB_LENGTH_KEY.equals(params[i].getName())) {
+                        tabLength =
+                            new Integer(params[i].getValue()).intValue();
+                        break;
+                    }
+                }
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/TailFilter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/TailFilter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/TailFilter.java	(revision 16139)
@@ -0,0 +1,241 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.LinkedList;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.util.LineTokenizer;
+
+/**
+ * Reads the last <code>n</code> lines of a stream. (Default is last10 lines.)
+ *
+ * Example:
+ *
+ * <pre>&lt;tailfilter lines=&quot;3&quot;/&gt;</pre>
+ *
+ * Or:
+ *
+ * <pre>&lt;filterreader classname=&quot;org.apache.tools.ant.filters.TailFilter&quot;&gt;
+ *   &lt;param name=&quot;lines&quot; value=&quot;3&quot;/&gt;
+ * &lt;/filterreader&gt;</pre>
+ *
+ */
+public final class TailFilter extends BaseParamFilterReader
+    implements ChainableReader {
+    /** Parameter name for the number of lines to be returned. */
+    private static final String LINES_KEY = "lines";
+
+    /** Parameter name for the number of lines to be skipped. */
+    private static final String SKIP_KEY = "skip";
+
+    /** Default number of lines to show */
+    private static final int DEFAULT_NUM_LINES = 10;
+
+    /** Number of lines to be returned in the filtered stream. */
+    private long lines = DEFAULT_NUM_LINES;
+
+    /** Number of lines to be skipped. */
+    private long skip = 0;
+
+    /** Whether or not read-ahead been completed. */
+    private boolean completedReadAhead = false;
+
+    /** A line tokenizer */
+    private LineTokenizer lineTokenizer = null;
+
+    /** the current line from the input stream */
+    private String    line      = null;
+    /** the position in the current line */
+    private int       linePos   = 0;
+
+    private LinkedList lineList = new LinkedList();
+
+    /**
+     * Constructor for "dummy" instances.
+     *
+     * @see BaseFilterReader#BaseFilterReader()
+     */
+    public TailFilter() {
+        super();
+    }
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     */
+    public TailFilter(final Reader in) {
+        super(in);
+        lineTokenizer = new LineTokenizer();
+        lineTokenizer.setIncludeDelims(true);
+    }
+
+    /**
+     * Returns the next character in the filtered stream. If the read-ahead
+     * has been completed, the next character in the buffer is returned.
+     * Otherwise, the stream is read to the end and buffered (with the buffer
+     * growing as necessary), then the appropriate position in the buffer is
+     * set to read from.
+     *
+     * @return the next character in the resulting stream, or -1
+     * if the end of the resulting stream has been reached
+     *
+     * @exception IOException if the underlying stream throws an IOException
+     * during reading
+     */
+    public final int read() throws IOException {
+        if (!getInitialized()) {
+            initialize();
+            setInitialized(true);
+        }
+
+        while (line == null || line.length() == 0) {
+            line = lineTokenizer.getToken(in);
+            line = tailFilter(line);
+            if (line == null) {
+                return -1;
+            }
+            linePos = 0;
+        }
+
+        int ch = line.charAt(linePos);
+        linePos++;
+        if (linePos == line.length()) {
+            line = null;
+        }
+        return ch;
+    }
+
+    /**
+     * Sets the number of lines to be returned in the filtered stream.
+     *
+     * @param lines the number of lines to be returned in the filtered stream
+     */
+    public final void setLines(final long lines) {
+        this.lines = lines;
+    }
+
+    /**
+     * Returns the number of lines to be returned in the filtered stream.
+     *
+     * @return the number of lines to be returned in the filtered stream
+     */
+    private final long getLines() {
+        return lines;
+    }
+
+    /**
+     * Sets the number of lines to be skipped in the filtered stream.
+     *
+     * @param skip the number of lines to be skipped in the filtered stream
+     */
+    public final void setSkip(final long skip) {
+        this.skip = skip;
+    }
+
+    /**
+     * Returns the number of lines to be skipped in the filtered stream.
+     *
+     * @return the number of lines to be skipped in the filtered stream
+     */
+    private final long getSkip() {
+        return skip;
+    }
+
+    /**
+     * Creates a new TailFilter using the passed in
+     * Reader for instantiation.
+     *
+     * @param rdr A Reader object providing the underlying stream.
+     *            Must not be <code>null</code>.
+     *
+     * @return a new filter based on this configuration, but filtering
+     *         the specified reader
+     */
+    public final Reader chain(final Reader rdr) {
+        TailFilter newFilter = new TailFilter(rdr);
+        newFilter.setLines(getLines());
+        newFilter.setSkip(getSkip());
+        newFilter.setInitialized(true);
+        return newFilter;
+    }
+
+    /**
+     * Scans the parameters list for the "lines" parameter and uses
+     * it to set the number of lines to be returned in the filtered stream.
+     * also scan for "skip" parameter.
+     */
+    private final void initialize() {
+        Parameter[] params = getParameters();
+        if (params != null) {
+            for (int i = 0; i < params.length; i++) {
+                if (LINES_KEY.equals(params[i].getName())) {
+                    setLines(new Long(params[i].getValue()).longValue());
+                    continue;
+                }
+                if (SKIP_KEY.equals(params[i].getName())) {
+                    skip = new Long(params[i].getValue()).longValue();
+                    continue;
+                }
+            }
+        }
+    }
+
+    /**
+     * implement a tail filter on a stream of lines.
+     * line = null is the end of the stream.
+     * @return "" while reading in the lines,
+     *         line while outputting the lines
+     *         null at the end of outputting the lines
+     */
+    private String tailFilter(String line) {
+        if (!completedReadAhead) {
+            if (line != null) {
+                lineList.add(line);
+                if (lines == -1) {
+                    if (lineList.size() > skip) {
+                        return (String) lineList.removeFirst();
+                    }
+                } else {
+                    long linesToKeep = lines + (skip > 0 ? skip : 0);
+                    if (linesToKeep < lineList.size()) {
+                        lineList.removeFirst();
+                    }
+                }
+                return "";
+            }
+            completedReadAhead = true;
+            if (skip > 0) {
+                for (int i = 0; i < skip; ++i) {
+                    lineList.removeLast();
+                }
+            }
+            if (lines > -1) {
+                while (lineList.size() > lines) {
+                    lineList.removeFirst();
+                }
+            }
+        }
+        if (lineList.size() > 0) {
+            return (String) lineList.removeFirst();
+        }
+        return null;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/TokenFilter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/TokenFilter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/TokenFilter.java	(revision 16139)
@@ -0,0 +1,896 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Vector;
+import java.util.Enumeration;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.RegularExpression;
+import org.apache.tools.ant.types.Substitution;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.Tokenizer;
+import org.apache.tools.ant.util.LineTokenizer;
+import org.apache.tools.ant.util.regexp.Regexp;
+
+/**
+ * This splits up input into tokens and passes
+ * the tokens to a sequence of filters.
+ *
+ * @since Ant 1.6
+ * @see BaseFilterReader
+ * @see ChainableReader
+ * @see org.apache.tools.ant.DynamicConfigurator
+ */
+public class TokenFilter extends BaseFilterReader
+    implements ChainableReader {
+    /**
+     * string filters implement this interface
+     */
+    public interface Filter {
+        /**
+         * filter and/of modify a string
+         *
+         * @param string the string to filter
+         * @return the modified string or null if the
+         *         string did not pass the filter
+         */
+        String filter(String string);
+    }
+
+
+    /** string filters */
+    private Vector    filters   = new Vector();
+    /** the tokenizer to use on the input stream */
+    private Tokenizer tokenizer = null;
+    /** the output token termination */
+    private String    delimOutput = null;
+    /** the current string token from the input stream */
+    private String    line      = null;
+    /** the position in the current string token */
+    private int       linePos   = 0;
+
+    /**
+     * Constructor for "dummy" instances.
+     *
+     * @see BaseFilterReader#BaseFilterReader()
+     */
+    public TokenFilter() {
+        super();
+    }
+
+    /**
+     * Creates a new filtered reader.
+     *
+     * @param in A Reader object providing the underlying stream.
+     *           Must not be <code>null</code>.
+     */
+    public TokenFilter(final Reader in) {
+        super(in);
+    }
+
+
+    /**
+     * Returns the next character in the filtered stream, only including
+     * lines from the original stream which match all of the specified
+     * regular expressions.
+     *
+     * @return the next character in the resulting stream, or -1
+     * if the end of the resulting stream has been reached
+     *
+     * @exception IOException if the underlying stream throws an IOException
+     * during reading
+     */
+
+    public int read() throws IOException {
+        if (tokenizer == null) {
+            tokenizer = new LineTokenizer();
+        }
+        while (line == null || line.length() == 0) {
+            line = tokenizer.getToken(in);
+            if (line == null) {
+                return -1;
+            }
+            for (Enumeration e = filters.elements(); e.hasMoreElements();) {
+                Filter filter = (Filter) e.nextElement();
+                line = filter.filter(line);
+                if (line == null) {
+                    break;
+                }
+            }
+            linePos = 0;
+            if (line != null) {
+                if (tokenizer.getPostToken().length() != 0) {
+                    if (delimOutput != null) {
+                        line = line + delimOutput;
+                    } else {
+                        line = line + tokenizer.getPostToken();
+                    }
+                }
+            }
+        }
+        int ch = line.charAt(linePos);
+        linePos++;
+        if (linePos == line.length()) {
+            line = null;
+        }
+        return ch;
+    }
+
+    /**
+     * Creates a new TokenFilter using the passed in
+     * Reader for instantiation.
+     *
+     * @param reader A Reader object providing the underlying stream.
+     *
+     * @return a new filter based on this configuration
+     */
+
+    public final Reader chain(final Reader reader) {
+        TokenFilter newFilter = new TokenFilter(reader);
+        newFilter.filters = filters;
+        newFilter.tokenizer = tokenizer;
+        newFilter.delimOutput = delimOutput;
+        newFilter.setProject(getProject());
+        return newFilter;
+    }
+
+    /**
+     * set the output delimiter.
+     * @param delimOutput replaces the delim string returned by the
+     *                    tokenizer, if present.
+     */
+
+    public void setDelimOutput(String delimOutput) {
+        this.delimOutput = resolveBackSlash(delimOutput);
+    }
+
+    // -----------------------------------------
+    //  Predefined tokenizers
+    // -----------------------------------------
+
+    /**
+     * add a line tokenizer - this is the default.
+     * @param tokenizer the line tokenizer
+     */
+
+    public void addLineTokenizer(LineTokenizer tokenizer) {
+        add(tokenizer);
+    }
+
+    /**
+     * add a string tokenizer
+     * @param tokenizer the string tokenizer
+     */
+
+    public void addStringTokenizer(StringTokenizer tokenizer) {
+        add(tokenizer);
+    }
+
+    /**
+     * add a file tokenizer
+     * @param tokenizer the file tokenizer
+     */
+    public void addFileTokenizer(FileTokenizer tokenizer) {
+        add(tokenizer);
+    }
+
+    /**
+     * add an arbitrary tokenizer
+     * @param tokenizer the tokenizer to all, only one allowed
+     */
+
+    public void add(Tokenizer tokenizer) {
+        if (this.tokenizer != null) {
+            throw new BuildException("Only one tokenizer allowed");
+        }
+        this.tokenizer = tokenizer;
+    }
+
+    // -----------------------------------------
+    //  Predefined filters
+    // -----------------------------------------
+
+    /**
+     * replace string filter
+     * @param filter the replace string filter
+     */
+    public void addReplaceString(ReplaceString filter) {
+        filters.addElement(filter);
+    }
+
+    /**
+     * contains string filter
+     * @param filter the contains string filter
+     */
+    public void addContainsString(ContainsString filter) {
+        filters.addElement(filter);
+    }
+
+    /**
+     * replace regex filter
+     * @param filter the replace regex filter
+     */
+    public void addReplaceRegex(ReplaceRegex filter) {
+        filters.addElement(filter);
+    }
+
+    /**
+     * contains regex filter
+     * @param filter the contains regex filter
+     */
+    public void addContainsRegex(ContainsRegex filter) {
+        filters.addElement(filter);
+    }
+
+    /**
+     * trim filter
+     * @param filter the trim filter
+     */
+    public void addTrim(Trim filter) {
+        filters.addElement(filter);
+    }
+
+    /**
+     * ignore blank filter
+     * @param filter the ignore blank filter
+     */
+    public void addIgnoreBlank(IgnoreBlank filter) {
+        filters.addElement(filter);
+    }
+
+    /**
+     * delete chars
+     * @param filter the delete characters filter
+     */
+    public void addDeleteCharacters(DeleteCharacters filter) {
+        filters.addElement(filter);
+    }
+
+    /**
+     * Add an arbitrary filter
+     * @param filter the filter to add
+     */
+    public void add(Filter filter) {
+        filters.addElement(filter);
+    }
+
+
+    // --------------------------------------------
+    //
+    //      Tokenizer Classes
+    //
+    // --------------------------------------------
+
+    /**
+     * class to read the complete input into a string
+     */
+    public static class FileTokenizer extends ProjectComponent
+        implements Tokenizer {
+        /**
+         * Get the complete input as a string
+         * @param in the reader object
+         * @return the complete input
+         * @throws IOException if error reading
+         */
+        public String getToken(Reader in) throws IOException {
+            return FileUtils.readFully(in);
+        }
+
+        /**
+         * Return the intra-token string
+         * @return an empty string always
+         */
+        public String getPostToken() {
+            return "";
+        }
+    }
+
+    /**
+     * class to tokenize the input as areas separated
+     * by white space, or by a specified list of
+     * delim characters. Behaves like java.util.StringTokenizer.
+     * if the stream starts with delim characters, the first
+     * token will be an empty string (unless the treat tokens
+     * as delims flag is set).
+     */
+    public static class StringTokenizer extends ProjectComponent
+        implements Tokenizer {
+        private String intraString = "";
+        private int    pushed = -2;
+        private char[] delims = null;
+        private boolean delimsAreTokens = false;
+        private boolean suppressDelims = false;
+        private boolean includeDelims = false;
+
+        /**
+         * attribute delims - the delimiter characters
+         * @param delims a string containing the delimiter characters
+         */
+        public void setDelims(String delims) {
+            this.delims = resolveBackSlash(delims).toCharArray();
+        }
+
+        /**
+         * attribute delimsaretokens - treat delimiters as
+         * separate tokens.
+         * @param delimsAreTokens true if delimiters are to be separate
+         */
+
+        public void setDelimsAreTokens(boolean delimsAreTokens) {
+            this.delimsAreTokens = delimsAreTokens;
+        }
+        /**
+         * attribute suppressdelims - suppress delimiters.
+         * default - false
+         * @param suppressDelims if true do not report delimiters
+         */
+        public void setSuppressDelims(boolean suppressDelims) {
+            this.suppressDelims = suppressDelims;
+        }
+
+        /**
+         * attribute includedelims - treat delimiters as part
+         * of the token.
+         * default - false
+         * @param includeDelims if true add delimiters to the token
+         */
+        public void setIncludeDelims(boolean includeDelims) {
+            this.includeDelims = includeDelims;
+        }
+
+        /**
+         * find and return the next token
+         *
+         * @param in the input stream
+         * @return the token
+         * @exception IOException if an error occurs reading
+         */
+        public String getToken(Reader in) throws IOException {
+            int ch = -1;
+            if (pushed != -2) {
+                ch = pushed;
+                pushed = -2;
+            } else {
+                ch = in.read();
+            }
+            if (ch == -1) {
+                return null;
+            }
+            boolean inToken = true;
+            intraString = "";
+            StringBuffer word = new StringBuffer();
+            StringBuffer padding = new StringBuffer();
+            while (ch != -1) {
+                char c = (char) ch;
+                boolean isDelim = isDelim(c);
+                if (inToken) {
+                    if (isDelim) {
+                        if (delimsAreTokens) {
+                            if (word.length() == 0) {
+                                word.append(c);
+                            } else {
+                                pushed = ch;
+                            }
+                            break;
+                        }
+                        padding.append(c);
+                        inToken = false;
+                    } else {
+                        word.append(c);
+                    }
+                } else {
+                    if (isDelim) {
+                        padding.append(c);
+                    } else {
+                        pushed = ch;
+                        break;
+                    }
+                }
+                ch = in.read();
+            }
+            intraString = padding.toString();
+            if (includeDelims) {
+                word.append(intraString);
+            }
+            return word.toString();
+        }
+
+        /**
+         * @return the intratoken string
+         */
+        public String getPostToken() {
+            if (suppressDelims || includeDelims) {
+                return "";
+            }
+            return intraString;
+        }
+
+        private boolean isDelim(char ch) {
+            if (delims == null) {
+                return Character.isWhitespace(ch);
+            }
+            for (int i = 0; i < delims.length; ++i) {
+                if (delims[i] == ch) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    // --------------------------------------------
+    //
+    //      Filter classes
+    //
+    // --------------------------------------------
+
+    /**
+     * Abstract class that converts derived filter classes into
+     * ChainableReaderFilter's
+     */
+    public abstract static class ChainableReaderFilter extends ProjectComponent
+        implements ChainableReader, Filter {
+        private boolean byLine = true;
+
+        /**
+         * set whether to use filetokenizer or line tokenizer
+         * @param byLine if true use a linetokenizer (default) otherwise
+         *               use a filetokenizer
+         */
+        public void setByLine(boolean byLine) {
+            this.byLine = byLine;
+        }
+
+        /**
+         * Chain a tokenfilter reader to a reader,
+         *
+         * @param reader the input reader object
+         * @return the chained reader object
+         */
+        public Reader chain(Reader reader) {
+            TokenFilter tokenFilter = new TokenFilter(reader);
+            if (!byLine) {
+                tokenFilter.add(new FileTokenizer());
+            }
+            tokenFilter.add(this);
+            return tokenFilter;
+        }
+    }
+
+    /**
+     * Simple replace string filter.
+     */
+    public static class ReplaceString extends ChainableReaderFilter {
+        private String from;
+        private String to;
+
+        /**
+         * the from attribute
+         *
+         * @param from the string to replace
+         */
+        public void setFrom(String from) {
+            this.from = from;
+        }
+
+        /**
+         * the to attribute
+         *
+         * @param to the string to replace 'from' with
+         */
+        public void setTo(String to) {
+            this.to = to;
+        }
+
+        /**
+         * Filter a string 'line' replacing from with to
+         * (C&P from the Replace task)
+         * @param line the string to be filtered
+         * @return the filtered line
+         */
+        public String filter(String line) {
+            if (from == null) {
+                throw new BuildException("Missing from in stringreplace");
+            }
+            StringBuffer ret = new StringBuffer();
+            int start = 0;
+            int found = line.indexOf(from);
+            while (found >= 0) {
+                // write everything up to the from
+                if (found > start) {
+                    ret.append(line.substring(start, found));
+                }
+
+                // write the replacement to
+                if (to != null) {
+                    ret.append(to);
+                }
+
+                // search again
+                start = found + from.length();
+                found = line.indexOf(from, start);
+            }
+
+            // write the remaining characters
+            if (line.length() > start) {
+                ret.append(line.substring(start, line.length()));
+            }
+
+            return ret.toString();
+        }
+    }
+
+    /**
+     * Simple filter to filter lines contains strings
+     */
+    public static class ContainsString extends ProjectComponent
+        implements Filter {
+        private String contains;
+
+        /**
+         * the contains attribute
+         * @param contains the string that the token should contain
+         */
+        public void setContains(String contains) {
+            this.contains = contains;
+        }
+
+        /**
+         * Filter strings that contain the contains attribute
+         *
+         * @param string the string to be filtered
+         * @return null if the string does not contain "contains",
+         *              string otherwise
+         */
+        public String filter(String string) {
+            if (contains == null) {
+                throw new BuildException("Missing contains in containsstring");
+            }
+            if (string.indexOf(contains) > -1) {
+                return string;
+            }
+            return null;
+        }
+    }
+
+    /**
+     * filter to replace regex.
+     */
+    public static class ReplaceRegex extends ChainableReaderFilter {
+        private String             from;
+        private String             to;
+        private RegularExpression  regularExpression;
+        private Substitution       substitution;
+        private boolean            initialized = false;
+        private String             flags = "";
+        private int                options;
+        private Regexp             regexp;
+
+        /**
+         * the from attribute
+         * @param from the regex string
+         */
+        public void setPattern(String from) {
+            this.from = from;
+        }
+        /**
+         * the to attribute
+         * @param to the replacement string
+         */
+        public void setReplace(String to) {
+            this.to = to;
+        }
+
+        /**
+         * @param flags the regex flags
+         */
+        public void setFlags(String flags) {
+            this.flags = flags;
+        }
+
+        private void initialize() {
+            if (initialized) {
+                return;
+            }
+            options = convertRegexOptions(flags);
+            if (from == null) {
+                throw new BuildException("Missing pattern in replaceregex");
+            }
+            regularExpression = new RegularExpression();
+            regularExpression.setPattern(from);
+            regexp = regularExpression.getRegexp(getProject());
+            if (to == null) {
+                to = "";
+            }
+            substitution = new Substitution();
+            substitution.setExpression(to);
+        }
+
+        /**
+         * @param line the string to modify
+         * @return the modified string
+         */
+        public String filter(String line) {
+            initialize();
+
+            if (!regexp.matches(line, options)) {
+                return line;
+            }
+            return regexp.substitute(
+                line, substitution.getExpression(getProject()), options);
+        }
+    }
+
+    /**
+     * filter to filter tokens matching regular expressions.
+     */
+    public static class ContainsRegex extends ChainableReaderFilter {
+        private String             from;
+        private String             to;
+        private Project            project;
+        private RegularExpression  regularExpression;
+        private Substitution       substitution;
+        private boolean            initialized = false;
+        private String             flags = "";
+        private int                options;
+        private Regexp             regexp;
+
+
+        /**
+         * @param from the regex pattern
+         */
+        public void setPattern(String from) {
+            this.from = from;
+        }
+
+        /**
+         * @param to the replacement string
+         */
+        public void setReplace(String to) {
+            this.to = to;
+        }
+
+        /**
+         * @param flags the regex flags
+         */
+        public void setFlags(String flags) {
+            this.flags = flags;
+        }
+
+        private void initialize() {
+            if (initialized) {
+                return;
+            }
+            options = convertRegexOptions(flags);
+            if (from == null) {
+                throw new BuildException("Missing from in containsregex");
+            }
+            regularExpression = new RegularExpression();
+            regularExpression.setPattern(from);
+            regexp = regularExpression.getRegexp(project);
+            if (to == null) {
+                return;
+            }
+            substitution = new Substitution();
+            substitution.setExpression(to);
+        }
+
+        /**
+         * apply regex and substitution on a string
+         * @param string the string to apply filter on
+         * @return the filtered string
+         */
+        public String filter(String string) {
+            initialize();
+            if (!regexp.matches(string, options)) {
+                return null;
+            }
+            if (substitution == null) {
+                return string;
+            }
+            return regexp.substitute(
+                string, substitution.getExpression(getProject()), options);
+        }
+    }
+
+    /** Filter to trim white space */
+    public static class Trim extends ChainableReaderFilter {
+        /**
+         * @param line the string to be trimmed
+         * @return the trimmed string
+         */
+        public String filter(String line) {
+            return line.trim();
+        }
+    }
+
+
+
+    /** Filter remove empty tokens */
+    public static class IgnoreBlank extends ChainableReaderFilter {
+        /**
+         * @param line the line to modify
+         * @return the trimmed line
+         */
+        public String filter(String line) {
+            if (line.trim().length() == 0) {
+                return null;
+            }
+            return line;
+        }
+    }
+
+    /**
+     * Filter to delete characters
+     */
+    public static class DeleteCharacters extends ProjectComponent
+        implements Filter, ChainableReader {
+        // Attributes
+        /** the list of characters to remove from the input */
+        private String deleteChars = "";
+
+        /**
+         * Set the list of characters to delete
+         * @param deleteChars the list of characters
+         */
+        public void setChars(String deleteChars) {
+            this.deleteChars = resolveBackSlash(deleteChars);
+        }
+
+        /**
+         * remove characters from a string
+         * @param string the string to remove the characters from
+         * @return the converted string
+         */
+        public String filter(String string) {
+            StringBuffer output = new StringBuffer(string.length());
+            for (int i = 0; i < string.length(); ++i) {
+                char ch = string.charAt(i);
+                if (!(isDeleteCharacter(ch))) {
+                    output.append(ch);
+                }
+            }
+            return output.toString();
+        }
+
+        /**
+         * factory method to provide a reader that removes
+         * the characters from a reader as part of a filter
+         * chain
+         * @param reader the reader object
+         * @return the chained reader object
+         */
+        public Reader chain(Reader reader) {
+            return new BaseFilterReader(reader) {
+                /**
+                 * @return the next non delete character
+                 */
+                public int read()
+                    throws IOException {
+                    while (true) {
+                        int c = in.read();
+                        if (c == -1) {
+                            return c;
+                        }
+                        if (!(isDeleteCharacter((char) c))) {
+                            return c;
+                        }
+                    }
+                }
+            };
+        }
+
+        /** check if the character c is to be deleted */
+        private boolean isDeleteCharacter(char c) {
+            for (int d = 0; d < deleteChars.length(); ++d) {
+                if (deleteChars.charAt(d) ==  c) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+    // --------------------------------------------------------
+    //  static utility methods - could be placed somewhere else
+    // --------------------------------------------------------
+
+    /**
+     * xml does not do "c" like interpretation of strings.
+     * i.e. \n\r\t etc.
+     * this method processes \n, \r, \t, \f, \\
+     * also subs \s -> " \n\r\t\f"
+     * a trailing '\' will be ignored
+     *
+     * @param input raw string with possible embedded '\'s
+     * @return converted string
+     */
+    public static String resolveBackSlash(String input) {
+        StringBuffer b = new StringBuffer();
+        boolean backSlashSeen = false;
+        for (int i = 0; i < input.length(); ++i) {
+            char c = input.charAt(i);
+            if (!backSlashSeen) {
+                if (c == '\\') {
+                    backSlashSeen = true;
+                } else {
+                    b.append(c);
+                }
+            } else {
+                switch (c) {
+                    case '\\':
+                        b.append((char) '\\');
+                        break;
+                    case 'n':
+                        b.append((char) '\n');
+                        break;
+                    case 'r':
+                        b.append((char) '\r');
+                        break;
+                    case 't':
+                        b.append((char) '\t');
+                        break;
+                    case 'f':
+                        b.append((char) '\f');
+                        break;
+                    case 's':
+                        b.append(" \t\n\r\f");
+                        break;
+                    default:
+                        b.append(c);
+                }
+                backSlashSeen = false;
+            }
+        }
+        return b.toString();
+    }
+
+    /**
+     * convert regex option flag characters to regex options
+     * <dl>
+     *   <li>g -  Regexp.REPLACE_ALL</li>
+     *   <li>i -  Regexp.MATCH_CASE_INSENSITIVE</li>
+     *   <li>m -  Regexp.MATCH_MULTILINE</li>
+     *   <li>s -  Regexp.MATCH_SINGLELINE</li>
+     * </dl>
+     * @param flags the string containing the flags
+     * @return the Regexp option bits
+     */
+    public static int convertRegexOptions(String flags) {
+        if (flags == null) {
+            return 0;
+        }
+        int options = 0;
+        if (flags.indexOf('g') != -1) {
+            options |= Regexp.REPLACE_ALL;
+        }
+        if (flags.indexOf('i') != -1) {
+            options |= Regexp.MATCH_CASE_INSENSITIVE;
+        }
+        if (flags.indexOf('m') != -1) {
+            options |= Regexp.MATCH_MULTILINE;
+        }
+        if (flags.indexOf('s') != -1) {
+            options |= Regexp.MATCH_SINGLELINE;
+        }
+        return options;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/util/ChainReaderHelper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/util/ChainReaderHelper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/util/ChainReaderHelper.java	(revision 16139)
@@ -0,0 +1,233 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters.util;
+
+import java.io.FilterReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Vector;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.filters.BaseFilterReader;
+import org.apache.tools.ant.filters.ChainableReader;
+import org.apache.tools.ant.types.AntFilterReader;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.Parameterizable;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Process a FilterReader chain.
+ *
+ */
+public final class ChainReaderHelper {
+
+    // default buffer size
+    private static final int DEFAULT_BUFFER_SIZE = 8192;
+    /**
+     * The primary reader to which the reader chain is to be attached.
+     */
+    public Reader primaryReader;
+
+    /**
+     * The size of the buffer to be used.
+     */
+    public int bufferSize = DEFAULT_BUFFER_SIZE;
+
+    /**
+     * Chain of filters
+     */
+    public Vector filterChains = new Vector();
+
+    /** The Ant project */
+    private Project project = null;
+
+    /**
+     * Sets the primary reader
+     * @param rdr the reader object
+     */
+    public final void setPrimaryReader(Reader rdr) {
+        primaryReader = rdr;
+    }
+
+    /**
+     * Set the project to work with
+     * @param project the current project
+     */
+    public final void setProject(final Project project) {
+        this.project = project;
+    }
+
+    /**
+     * Get the project
+     *
+     * @return the current project
+     */
+    public final Project getProject() {
+        return project;
+    }
+
+    /**
+     * Sets the buffer size to be used.  Defaults to 4096,
+     * if this method is not invoked.
+     * @param size the buffer size to use
+     */
+    public final void setBufferSize(int size) {
+        bufferSize = size;
+    }
+
+    /**
+     * Sets the collection of filter reader sets
+     *
+     * @param fchain the filter chains collection
+     */
+    public final void setFilterChains(Vector fchain) {
+        filterChains = fchain;
+    }
+
+    /**
+     * Assemble the reader
+     * @return the assembled reader
+     * @exception BuildException if an error occurs
+     */
+    public final Reader getAssembledReader() throws BuildException {
+        if (primaryReader == null) {
+            throw new BuildException("primaryReader must not be null.");
+        }
+
+        Reader instream = primaryReader;
+        final int filterReadersCount = filterChains.size();
+        final Vector finalFilters = new Vector();
+
+        for (int i = 0; i < filterReadersCount; i++) {
+            final FilterChain filterchain =
+                (FilterChain) filterChains.elementAt(i);
+            final Vector filterReaders = filterchain.getFilterReaders();
+            final int readerCount = filterReaders.size();
+            for (int j = 0; j < readerCount; j++) {
+                finalFilters.addElement(filterReaders.elementAt(j));
+            }
+        }
+
+        final int filtersCount = finalFilters.size();
+
+        if (filtersCount > 0) {
+            for (int i = 0; i < filtersCount; i++) {
+                Object o = finalFilters.elementAt(i);
+
+                if (o instanceof AntFilterReader) {
+                    final AntFilterReader filter
+                        = (AntFilterReader) finalFilters.elementAt(i);
+                    final String className = filter.getClassName();
+                    final Path classpath = filter.getClasspath();
+                    final Project project = filter.getProject();
+                    if (className != null) {
+                        try {
+                            Class clazz = null;
+                            if (classpath == null) {
+                                clazz = Class.forName(className);
+                            } else {
+                                AntClassLoader al
+                                    = project.createClassLoader(classpath);
+                                clazz = Class.forName(className, true, al);
+                            }
+                            if (clazz != null) {
+                                if (!FilterReader.class.isAssignableFrom(clazz)) {
+                                    throw new BuildException(className
+                                        + " does not extend java.io.FilterReader");
+                                }
+                                final Constructor[] constructors =
+                                    clazz.getConstructors();
+                                int j = 0;
+                                boolean consPresent = false;
+                                for (; j < constructors.length; j++) {
+                                    Class[] types = constructors[j]
+                                                      .getParameterTypes();
+                                    if (types.length == 1
+                                        && types[0].isAssignableFrom(Reader.class)) {
+                                        consPresent = true;
+                                        break;
+                                    }
+                                }
+                                if (!consPresent) {
+                                    throw new BuildException(className
+                                        + " does not define a public constructor"
+                                        + " that takes in a Reader as its "
+                                        + "single argument.");
+                                }
+                                final Reader[] rdr = {instream};
+                                instream =
+                                    (Reader) constructors[j].newInstance(rdr);
+                                setProjectOnObject(instream);
+                                if (Parameterizable.class.isAssignableFrom(clazz)) {
+                                    final Parameter[] params = filter.getParams();
+                                    ((Parameterizable)
+                                        instream).setParameters(params);
+                                }
+                            }
+                        } catch (final ClassNotFoundException cnfe) {
+                            throw new BuildException(cnfe);
+                        } catch (final InstantiationException ie) {
+                            throw new BuildException(ie);
+                        } catch (final IllegalAccessException iae) {
+                            throw new BuildException(iae);
+                        } catch (final InvocationTargetException ite) {
+                            throw new BuildException(ite);
+                        }
+                    }
+                } else if (o instanceof ChainableReader) {
+                    setProjectOnObject(o);
+                    instream = ((ChainableReader) o).chain(instream);
+                    setProjectOnObject(instream);
+                }
+            }
+        }
+        return instream;
+    }
+
+    /**
+     * helper method to set the project on an object.
+     * the reflection setProject does not work for anonymous/protected/private
+     * classes, even if they have public methods.
+     */
+    private void setProjectOnObject(Object obj) {
+        if (project == null) {
+            return;
+        }
+        if (obj instanceof BaseFilterReader) {
+            ((BaseFilterReader) obj).setProject(project);
+            return;
+        }
+        project.setProjectReference(obj);
+    }
+
+    /**
+     * Read data from the reader and return the
+     * contents as a string.
+     * @param rdr the reader object
+     * @return the contents of the file as a string
+     * @exception IOException if an error occurs
+     */
+    public final String readFully(Reader rdr)
+        throws IOException {
+        return FileUtils.readFully(rdr, bufferSize);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/util/JavaClassHelper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/util/JavaClassHelper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/filters/util/JavaClassHelper.java	(revision 16139)
@@ -0,0 +1,68 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.filters.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import org.apache.bcel.classfile.ClassParser;
+import org.apache.bcel.classfile.ConstantValue;
+import org.apache.bcel.classfile.Field;
+import org.apache.bcel.classfile.JavaClass;
+
+/**
+ * Helper class that filters constants from a Java Class
+ *
+ */
+public final class JavaClassHelper {
+
+    /** System specific line separator. */
+    private static final String LS = System.getProperty("line.separator");
+
+    /**
+     * Get the constants declared in a file as name=value
+     *
+     * @param bytes the class as a array of bytes
+     * @return a StringBuffer contains the name=value pairs
+     * @exception IOException if an error occurs
+     */
+    public static final StringBuffer getConstants(byte[] bytes)
+        throws IOException {
+        final StringBuffer sb = new StringBuffer();
+        final ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
+        final ClassParser parser = new ClassParser(bis, "");
+        final JavaClass javaClass = parser.parse();
+        final Field[] fields = javaClass.getFields();
+        for (int i = 0; i < fields.length; i++) {
+            final Field field = fields[i];
+            if (field != null) {
+                final ConstantValue cv = field.getConstantValue();
+                if (cv != null) {
+                    String cvs = cv.toString();
+                    //Remove start and end quotes if field is a String
+                    if (cvs.startsWith("\"") && cvs.endsWith("\"")) {
+                        cvs = cvs.substring(1, cvs.length() - 1);
+                    }
+                    sb.append(field.getName());
+                    sb.append('=');
+                    sb.append(cvs);
+                    sb.append(LS);
+                }
+            }
+        }
+        return sb;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/helper/AntXMLContext.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/helper/AntXMLContext.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/helper/AntXMLContext.java	(revision 16139)
@@ -0,0 +1,366 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.helper;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import org.xml.sax.Locator;
+import org.xml.sax.Attributes;
+
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.RuntimeConfigurable;
+
+
+/**
+ * Context information for the ant processing.
+ *
+ */
+public class AntXMLContext {
+    /** The project to configure. */
+    private Project project;
+
+    /** The configuration file to parse. */
+    private File buildFile;
+
+    /** Vector with all the targets, in the order they are
+     * defined. Project maintains a Hashtable, which is not ordered.
+     * This will allow description to know the original order.
+     */
+    private Vector targetVector = new Vector();
+
+    /**
+     * Parent directory of the build file. Used for resolving entities
+     * and setting the project's base directory.
+     */
+    private File buildFileParent;
+
+    /** Name of the current project */
+    private String currentProjectName;
+
+    /**
+     * Locator for the configuration file parser.
+     * Used for giving locations of errors etc.
+     */
+    private Locator locator;
+
+     /**
+      * Target that all other targets will depend upon implicitly.
+      *
+      * <p>This holds all tasks and data type definitions that have
+      * been placed outside of targets.</p>
+      */
+    private Target implicitTarget = new Target();
+
+    /** Current target ( no need for a stack as the processing model
+        allows only one level of target ) */
+    private Target currentTarget = null;
+
+    /** The stack of RuntimeConfigurable2 wrapping the
+        objects.
+    */
+    private Vector wStack = new Vector();
+
+    /**
+     * Indicates whether the project tag attributes are to be ignored
+     * when processing a particular build file.
+     */
+    private boolean ignoreProjectTag = false;
+
+    /** Keeps track of prefix -> uri mapping during parsing */
+    private Map prefixMapping = new HashMap();
+
+
+    /** Keeps track of targets in files */
+    private Map currentTargets = null;
+
+    /**
+     * constructor
+     * @param project the project to which this antxml context belongs to
+     */
+    public AntXMLContext(Project project) {
+        this.project = project;
+        implicitTarget.setProject(project);
+        implicitTarget.setName("");
+        targetVector.addElement(implicitTarget);
+    }
+
+    /**
+     * sets the build file to which the XML context belongs
+     * @param buildFile  ant build file
+     */
+    public void setBuildFile(File buildFile) {
+        this.buildFile = buildFile;
+        this.buildFileParent = new File(buildFile.getParent());
+        implicitTarget.setLocation(new Location(buildFile.getAbsolutePath()));
+    }
+
+    /**
+     * find out the build file
+     * @return  the build file to which the xml context belongs
+     */
+    public File getBuildFile() {
+        return buildFile;
+    }
+
+    /**
+     * find out the parent build file of this build file
+     * @return the parent build file of this build file
+     */
+    public File getBuildFileParent() {
+        return buildFileParent;
+    }
+
+    /**
+     * find out the project to which this antxml context belongs
+     * @return project
+     */
+    public Project getProject() {
+        return project;
+    }
+
+    /**
+     * find out the current project name
+     * @return current project name
+     */
+    public String getCurrentProjectName() {
+        return currentProjectName;
+    }
+
+    /**
+     * set the name of the current project
+     * @param name name of the current project
+     */
+    public void setCurrentProjectName(String name) {
+        this.currentProjectName = name;
+    }
+
+    /**
+     * get the current runtime configurable wrapper
+     * can return null
+     * @return runtime configurable wrapper
+     */
+    public RuntimeConfigurable currentWrapper() {
+        if (wStack.size() < 1) {
+            return null;
+        }
+        return (RuntimeConfigurable) wStack.elementAt(wStack.size() - 1);
+    }
+
+    /**
+     * get the runtime configurable wrapper of the parent project
+     * can return null
+     * @return runtime configurable wrapper  of the parent project
+     */
+    public RuntimeConfigurable parentWrapper() {
+        if (wStack.size() < 2) {
+            return null;
+        }
+        return (RuntimeConfigurable) wStack.elementAt(wStack.size() - 2);
+    }
+
+    /**
+     * add a runtime configurable wrapper to the internal stack
+     * @param wrapper runtime configurable wrapper
+     */
+    public void pushWrapper(RuntimeConfigurable wrapper) {
+        wStack.addElement(wrapper);
+    }
+
+    /**
+     * remove a runtime configurable wrapper from the stack
+     */
+    public void popWrapper() {
+        if (wStack.size() > 0) {
+            wStack.removeElementAt(wStack.size() - 1);
+        }
+    }
+
+    /**
+     * access the stack of wrappers
+     * @return the stack of wrappers
+     */
+    public Vector getWrapperStack() {
+        return wStack;
+    }
+
+    /**
+     * add a new target
+     * @param target target to add
+     */
+    public void addTarget(Target target) {
+        targetVector.addElement(target);
+        currentTarget = target;
+    }
+
+    /**
+     * get the current target
+     * @return current target
+     */
+    public Target getCurrentTarget() {
+        return currentTarget;
+    }
+
+    /**
+     * get the implicit target
+     * @return implicit target
+     */
+    public Target getImplicitTarget() {
+        return implicitTarget;
+    }
+
+    /**
+     * sets the current target
+     * @param target current target
+     */
+    public void setCurrentTarget(Target target) {
+        this.currentTarget = target;
+    }
+
+    /**
+     * sets the implicit target
+     * @param target the implicit target
+     */
+    public void setImplicitTarget(Target target) {
+        this.implicitTarget = target;
+    }
+
+    /**
+     * access the vector of targets
+     * @return vector of targets
+     */
+    public Vector getTargets() {
+        return targetVector;
+    }
+
+    /**
+     * Scans an attribute list for the <code>id</code> attribute and
+     * stores a reference to the target object in the project if an
+     * id is found.
+     * <p>
+     * This method was moved out of the configure method to allow
+     * it to be executed at parse time.
+     * @param element the current element
+     * @param attr attributes of the current element
+     */
+    public void configureId(Object element, Attributes attr) {
+        String id = attr.getValue("id");
+        if (id != null) {
+            project.addReference(id, element);
+        }
+    }
+
+    /**
+     * access the locator
+     * @return locator
+     */
+    public Locator getLocator() {
+        return locator;
+    }
+
+    /**
+     * sets the locator
+     * @param locator locator
+     */
+    public void setLocator(Locator locator) {
+        this.locator = locator;
+    }
+
+    /**
+     * tells whether the project tag is being ignored
+     * @return whether the project tag is being ignored
+     */
+    public boolean isIgnoringProjectTag() {
+        return ignoreProjectTag;
+    }
+
+    /**
+     *  sets the flag to ignore the project tag
+     * @param flag to ignore the project tag
+     */
+    public void setIgnoreProjectTag(boolean flag) {
+        this.ignoreProjectTag = flag;
+    }
+
+    /**
+     * Called during parsing, stores the prefix to uri mapping.
+     *
+     * @param prefix a namespace prefix
+     * @param uri    a namespace uri
+     */
+    public void startPrefixMapping(String prefix, String uri) {
+        List list = (List) prefixMapping.get(prefix);
+        if (list == null) {
+            list = new ArrayList();
+            prefixMapping.put(prefix, list);
+        }
+        list.add(uri);
+    }
+
+    /**
+     * End of prefix to uri mapping.
+     *
+     * @param prefix the namespace prefix
+     */
+    public void endPrefixMapping(String prefix) {
+        List list = (List) prefixMapping.get(prefix);
+        if (list == null || list.size() == 0) {
+            return; // Should not happen
+        }
+        list.remove(list.size() - 1);
+    }
+
+    /**
+     * prefix to namespace uri mapping
+     *
+     * @param prefix the prefix to map
+     * @return the uri for this prefix, null if not present
+     */
+    public String getPrefixMapping(String prefix) {
+        List list = (List) prefixMapping.get(prefix);
+        if (list == null || list.size() == 0) {
+            return null;
+        }
+        return (String) list.get(list.size() - 1);
+    }
+
+    /**
+     * Get the targets in the current source file.
+     * @return the current targets.
+     */
+    public Map getCurrentTargets() {
+        return currentTargets;
+    }
+
+    /**
+     * Set the map of the targets in the current source file.
+     * @param currentTargets a map of targets.
+     */
+    public void setCurrentTargets(Map currentTargets) {
+        this.currentTargets = currentTargets;
+    }
+
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/helper/DefaultExecutor.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/helper/DefaultExecutor.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/helper/DefaultExecutor.java	(revision 16139)
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.helper;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Executor;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Default Target executor implementation. Runs each target individually
+ * (including all of its dependencies). If an error occurs, behavior is
+ * determined by the Project's "keep-going" mode.
+ * @since Ant 1.6.3
+ */
+public class DefaultExecutor implements Executor {
+
+    private static final SingleCheckExecutor SUB_EXECUTOR = new SingleCheckExecutor();
+
+    //inherit doc
+    public void executeTargets(Project project, String[] targetNames)
+        throws BuildException {
+        BuildException thrownException = null;
+        for (int i = 0; i < targetNames.length; i++) {
+            try {
+                project.executeTarget(targetNames[i]);
+            } catch (BuildException ex) {
+                if (project.isKeepGoingMode()) {
+                    thrownException = ex;
+                } else {
+                    throw ex;
+                }
+            }
+        }
+        if (thrownException != null) {
+            throw thrownException;
+        }
+    }
+
+    //inherit doc
+    public Executor getSubProjectExecutor() {
+        return SUB_EXECUTOR;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/helper/ProjectHelper2.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/helper/ProjectHelper2.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/helper/ProjectHelper2.java	(revision 16139)
@@ -0,0 +1,1063 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.helper;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Stack;
+
+import org.xml.sax.Locator;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.SAXException;
+import org.xml.sax.Attributes;
+import org.xml.sax.helpers.DefaultHandler;
+
+import org.apache.tools.ant.util.JAXPUtils;
+import org.apache.tools.ant.util.FileUtils;
+
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.RuntimeConfigurable;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.UnknownElement;
+
+import org.xml.sax.XMLReader;
+
+/**
+ * Sax2 based project reader
+ *
+ */
+public class ProjectHelper2 extends ProjectHelper {
+    /* Stateless */
+
+    // singletons - since all state is in the context
+    private static AntHandler elementHandler = new ElementHandler();
+    private static AntHandler targetHandler = new TargetHandler();
+    private static AntHandler mainHandler = new MainHandler();
+    private static AntHandler projectHandler = new ProjectHandler();
+
+    /**
+     * helper for path -> URI and URI -> path conversions.
+     */
+    private static FileUtils fu = FileUtils.newFileUtils();
+
+    /**
+     * Parse an unknown element from a url
+     *
+     * @param project the current project
+     * @param source  the url containing the task
+     * @return a configured task
+     * @exception BuildException if an error occurs
+     */
+    public UnknownElement parseUnknownElement(Project project, URL source)
+        throws BuildException {
+        Target dummyTarget = new Target();
+        dummyTarget.setProject(project);
+
+        AntXMLContext context = new AntXMLContext(project);
+        context.addTarget(dummyTarget);
+        context.setImplicitTarget(dummyTarget);
+
+        parse(context.getProject(), source,
+              new RootHandler(context, elementHandler));
+        Task[] tasks = dummyTarget.getTasks();
+        if (tasks.length != 1) {
+            throw new BuildException("No tasks defined");
+        }
+        return (UnknownElement) tasks[0];
+    }
+    /**
+     * Parse a source xml input.
+     *
+     * @param project the current project
+     * @param source  the xml source
+     * @exception BuildException if an error occurs
+     */
+    public void parse(Project project, Object source)
+            throws BuildException {
+        getImportStack().addElement(source);
+        //System.out.println("Adding " + source);
+        AntXMLContext context = null;
+        context = (AntXMLContext) project.getReference("ant.parsing.context");
+//        System.out.println("Parsing " + getImportStack().size() + " " +
+//                context+ " " + getImportStack() );
+        if (context == null) {
+            context = new AntXMLContext(project);
+            project.addReference("ant.parsing.context", context);
+            project.addReference("ant.targets", context.getTargets());
+        }
+
+        if (getImportStack().size() > 1) {
+            // we are in an imported file.
+            context.setIgnoreProjectTag(true);
+            Target currentTarget = context.getCurrentTarget();
+            Target currentImplicit = context.getImplicitTarget();
+            Map    currentTargets = context.getCurrentTargets();
+            try {
+                Target newCurrent = new Target();
+                newCurrent.setProject(project);
+                newCurrent.setName("");
+                context.setCurrentTarget(newCurrent);
+                context.setCurrentTargets(new HashMap());
+                context.setImplicitTarget(newCurrent);
+                parse(project, source, new RootHandler(context, mainHandler));
+                newCurrent.execute();
+            } finally {
+                context.setCurrentTarget(currentTarget);
+                context.setImplicitTarget(currentImplicit);
+                context.setCurrentTargets(currentTargets);
+            }
+        } else {
+            // top level file
+            context.setCurrentTargets(new HashMap());
+            parse(project, source, new RootHandler(context, mainHandler));
+            // Execute the top-level target
+            context.getImplicitTarget().execute();
+        }
+    }
+
+    /**
+     * Parses the project file, configuring the project as it goes.
+     *
+     * @param project the current project
+     * @param source  the xml source
+     * @param handler the root handler to use (contains the current context)
+     * @exception BuildException if the configuration is invalid or cannot
+     *                           be read
+     */
+    public void parse(Project project, Object source, RootHandler handler)
+            throws BuildException {
+
+        AntXMLContext context = handler.context;
+
+        File buildFile = null;
+        URL  url = null;
+        String buildFileName = null;
+
+        if (source instanceof File) {
+            buildFile = (File) source;
+            buildFile = fu.normalize(buildFile.getAbsolutePath());
+            context.setBuildFile(buildFile);
+            buildFileName = buildFile.toString();
+//         } else if (source instanceof InputStream ) {
+        } else if (source instanceof URL) {
+            if (handler.getCurrentAntHandler() != elementHandler) {
+                throw new BuildException(
+                    "Source " + source.getClass().getName()
+                    + " not supported by this plugin for "
+                    + " non task xml");
+            }
+            url = (URL) source;
+            buildFileName = url.toString();
+//         } else if (source instanceof InputSource ) {
+        } else {
+            throw new BuildException("Source " + source.getClass().getName()
+                                     + " not supported by this plugin");
+        }
+
+        InputStream inputStream = null;
+        InputSource inputSource = null;
+
+
+        try {
+            /**
+             * SAX 2 style parser used to parse the given file.
+             */
+            XMLReader parser = JAXPUtils.getNamespaceXMLReader();
+
+            String uri = null;
+            if (buildFile != null) {
+                uri = fu.toURI(buildFile.getAbsolutePath());
+                inputStream = new FileInputStream(buildFile);
+            } else {
+                inputStream = url.openStream();
+                uri = url.toString(); // ?? OK ??
+            }
+
+            inputSource = new InputSource(inputStream);
+            if (uri != null) {
+                inputSource.setSystemId(uri);
+            }
+            project.log("parsing buildfile " + buildFileName
+                        + " with URI = " + uri, Project.MSG_VERBOSE);
+
+            DefaultHandler hb = handler;
+
+            parser.setContentHandler(hb);
+            parser.setEntityResolver(hb);
+            parser.setErrorHandler(hb);
+            parser.setDTDHandler(hb);
+            parser.parse(inputSource);
+        } catch (SAXParseException exc) {
+            Location location = new Location(exc.getSystemId(),
+                exc.getLineNumber(), exc.getColumnNumber());
+
+            Throwable t = exc.getException();
+            if (t instanceof BuildException) {
+                BuildException be = (BuildException) t;
+                if (be.getLocation() == Location.UNKNOWN_LOCATION) {
+                    be.setLocation(location);
+                }
+                throw be;
+            } else if (t == null) {
+                t = exc;
+            }
+
+            throw new BuildException(exc.getMessage(), t, location);
+        } catch (SAXException exc) {
+            Throwable t = exc.getException();
+            if (t instanceof BuildException) {
+                throw (BuildException) t;
+            } else if (t == null) {
+                t = exc;
+            }
+            throw new BuildException(exc.getMessage(), t);
+        } catch (FileNotFoundException exc) {
+            throw new BuildException(exc);
+        } catch (UnsupportedEncodingException exc) {
+              throw new BuildException("Encoding of project file "
+                                       + buildFileName + " is invalid.",
+                                       exc);
+        } catch (IOException exc) {
+            throw new BuildException("Error reading project file "
+                                     + buildFileName + ": " + exc.getMessage(),
+                                     exc);
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException ioe) {
+                    // ignore this
+                }
+            }
+        }
+    }
+
+    /**
+     * The common superclass for all SAX event handlers used to parse
+     * the configuration file.
+     *
+     * The context will hold all state information. At each time
+     * there is one active handler for the current element. It can
+     * use onStartChild() to set an alternate handler for the child.
+     */
+    public static class AntHandler  {
+        /**
+         * Handles the start of an element. This base implementation does
+         * nothing.
+         *
+         * @param uri the namespace URI for the tag
+         * @param tag The name of the element being started.
+         *            Will not be <code>null</code>.
+         * @param qname The qualified name of the element.
+         * @param attrs Attributes of the element being started.
+         *              Will not be <code>null</code>.
+         * @param context The context that this element is in.
+         *
+         * @exception SAXParseException if this method is not overridden, or in
+         *                              case of error in an overridden version
+         */
+        public void onStartElement(String uri, String tag, String qname,
+                                   Attributes attrs,
+                                   AntXMLContext context)
+            throws SAXParseException {
+        }
+
+        /**
+         * Handles the start of an element. This base implementation just
+         * throws an exception - you must override this method if you expect
+         * child elements.
+         *
+         * @param uri The namespace uri for this element.
+         * @param tag The name of the element being started.
+         *            Will not be <code>null</code>.
+         * @param qname The qualified name for this element.
+         * @param attrs Attributes of the element being started.
+         *              Will not be <code>null</code>.
+         * @param context The current context.
+         * @return a handler (in the derived classes)
+         *
+         * @exception SAXParseException if this method is not overridden, or in
+         *                              case of error in an overridden version
+         */
+        public AntHandler onStartChild(String uri, String tag, String qname,
+                                       Attributes attrs,
+                                       AntXMLContext context)
+            throws SAXParseException {
+            throw new SAXParseException("Unexpected element \"" + qname
+                + " \"", context.getLocator());
+        }
+
+        /**
+         * Handle the end of a element.
+         *
+         * @param uri the namespace uri of the element
+         * @param tag the tag of the element
+         * @param qname the qualified name of the element
+         * @param context the current context
+         * @exception SAXParseException if an error occurs
+         */
+        public void onEndChild(String uri, String tag, String qname,
+                                     AntXMLContext context)
+            throws SAXParseException {
+        }
+
+        /**
+         * This method is called when this element and all elements nested into it have been
+         * handled. I.e., this happens at the &lt;/end_tag_of_the_element&gt;.
+         * @param uri the namespace uri for this element
+         * @param tag the element name
+         * @param context the current context
+         */
+        public void onEndElement(String uri, String tag,
+                                 AntXMLContext context) {
+        }
+
+        /**
+         * Handles text within an element. This base implementation just
+         * throws an exception, you must override it if you expect content.
+         *
+         * @param buf A character array of the text within the element.
+         *            Will not be <code>null</code>.
+         * @param start The start element in the array.
+         * @param count The number of characters to read from the array.
+         * @param context The current context.
+         *
+         * @exception SAXParseException if this method is not overridden, or in
+         *                              case of error in an overridden version
+         */
+        public void characters(char[] buf, int start, int count, AntXMLContext context)
+            throws SAXParseException {
+            String s = new String(buf, start, count).trim();
+
+            if (s.length() > 0) {
+                throw new SAXParseException("Unexpected text \"" + s
+                    + "\"", context.getLocator());
+            }
+        }
+
+        /**
+         * Will be called every time a namespace is reached.
+         * It'll verify if the ns was processed, and if not load the task
+         * definitions.
+         * @param uri The namespace uri.
+         */
+        protected void checkNamespace(String uri) {
+
+        }
+    }
+
+    /**
+     * Handler for ant processing. Uses a stack of AntHandlers to
+     * implement each element ( the original parser used a recursive behavior,
+     * with the implicit execution stack )
+     */
+    public static class RootHandler extends DefaultHandler {
+        private Stack antHandlers = new Stack();
+        private AntHandler currentHandler = null;
+        private AntXMLContext context;
+
+        /**
+         * Creates a new RootHandler instance.
+         *
+         * @param context The context for the handler.
+         * @param rootHandler The handler for the root element.
+         */
+        public RootHandler(AntXMLContext context, AntHandler rootHandler) {
+            currentHandler = rootHandler;
+            antHandlers.push(currentHandler);
+            this.context = context;
+        }
+
+        /**
+         * Returns the current ant handler object.
+         * @return the current ant handler.
+         */
+        public AntHandler getCurrentAntHandler() {
+            return currentHandler;
+        }
+
+        /**
+         * Resolves file: URIs relative to the build file.
+         *
+         * @param publicId The public identifier, or <code>null</code>
+         *                 if none is available. Ignored in this
+         *                 implementation.
+         * @param systemId The system identifier provided in the XML
+         *                 document. Will not be <code>null</code>.
+         * @return an inputsource for this identifier
+         */
+        public InputSource resolveEntity(String publicId,
+                                         String systemId) {
+
+            context.getProject().log("resolving systemId: "
+                + systemId, Project.MSG_VERBOSE);
+
+            if (systemId.startsWith("file:")) {
+                String path = fu.fromURI(systemId);
+
+                File file = new File(path);
+                if (!file.isAbsolute()) {
+                    file = fu.resolveFile(context.getBuildFileParent(), path);
+                }
+                try {
+                    InputSource inputSource =
+                            new InputSource(new FileInputStream(file));
+                    inputSource.setSystemId(fu.toURI(file.getAbsolutePath()));
+                    return inputSource;
+                } catch (FileNotFoundException fne) {
+                    context.getProject().log(file.getAbsolutePath()
+                        + " could not be found", Project.MSG_WARN);
+                }
+
+            }
+            // use default if not file or file not found
+            return null;
+        }
+
+        /**
+         * Handles the start of a project element. A project handler is created
+         * and initialised with the element name and attributes.
+         *
+         * @param uri The namespace uri for this element.
+         * @param tag The name of the element being started.
+         *            Will not be <code>null</code>.
+         * @param qname The qualified name for this element.
+         * @param attrs Attributes of the element being started.
+         *              Will not be <code>null</code>.
+         *
+         * @exception org.xml.sax.SAXParseException if the tag given is not
+         *                              <code>"project"</code>
+         */
+        public void startElement(String uri, String tag, String qname, Attributes attrs)
+            throws SAXParseException {
+            AntHandler next
+                = currentHandler.onStartChild(uri, tag, qname, attrs, context);
+            antHandlers.push(currentHandler);
+            currentHandler = next;
+            currentHandler.onStartElement(uri, tag, qname, attrs, context);
+        }
+
+        /**
+         * Sets the locator in the project helper for future reference.
+         *
+         * @param locator The locator used by the parser.
+         *                Will not be <code>null</code>.
+         */
+        public void setDocumentLocator(Locator locator) {
+            context.setLocator(locator);
+        }
+
+        /**
+         * Handles the end of an element. Any required clean-up is performed
+         * by the onEndElement() method and then the original handler
+         * is restored to the parser.
+         *
+         * @param uri  The namespace URI for this element.
+         * @param name The name of the element which is ending.
+         *             Will not be <code>null</code>.
+         * @param qName The qualified name for this element.
+         *
+         * @exception SAXException in case of error (not thrown in
+         *                         this implementation)
+         *
+         */
+        public void endElement(String uri, String name, String qName) throws SAXException {
+            currentHandler.onEndElement(uri, name, context);
+            AntHandler prev = (AntHandler) antHandlers.pop();
+            currentHandler = prev;
+            if (currentHandler != null) {
+                currentHandler.onEndChild(uri, name, qName, context);
+            }
+        }
+
+        /**
+         * Handle text within an element, calls currentHandler.characters.
+         *
+         * @param buf  A character array of the test.
+         * @param start The start offset in the array.
+         * @param count The number of characters to read.
+         * @exception SAXParseException if an error occurs
+         */
+        public void characters(char[] buf, int start, int count)
+            throws SAXParseException {
+            currentHandler.characters(buf, start, count, context);
+        }
+
+        /**
+         * Start a namespace prefix to uri mapping
+         *
+         * @param prefix the namespace prefix
+         * @param uri the namespace uri
+         */
+        public void startPrefixMapping(String prefix, String uri) {
+            context.startPrefixMapping(prefix, uri);
+        }
+
+        /**
+         * End a namepace prefix to uri mapping
+         *
+         * @param prefix the prefix that is not mapped anymore
+         */
+        public void endPrefixMapping(String prefix) {
+            context.endPrefixMapping(prefix);
+        }
+    }
+
+    /**
+     * The main handler - it handles the &lt;project&gt; tag.
+     *
+     * @see org.apache.tools.ant.helper.ProjectHelper2.AntHandler
+     */
+    public static class MainHandler extends AntHandler {
+
+        /**
+         * Handle the project tag
+         *
+         * @param uri The namespace uri.
+         * @param name The element tag.
+         * @param qname The element qualified name.
+         * @param attrs The attributes of the element.
+         * @param context The current context.
+         * @return The project handler that handles subelements of project
+         * @exception SAXParseException if the qualified name is not "project".
+         */
+        public AntHandler onStartChild(String uri, String name, String qname,
+                                       Attributes attrs,
+                                       AntXMLContext context)
+            throws SAXParseException {
+            if (name.equals("project")
+                && (uri.equals("") || uri.equals(ANT_CORE_URI))) {
+                return ProjectHelper2.projectHandler;
+            } else {
+//                 if (context.importlevel > 0) {
+//                     // we are in an imported file. Allow top-level <target>.
+//                     if (qname.equals( "target" ) )
+//                         return ProjectHelper2.targetHandler;
+//                 }
+                if (name.equals(qname)) {
+                    throw new SAXParseException("Unexpected element \"{" + uri
+                    + "}" + name + "\" {" + ANT_CORE_URI + "}" + name,
+                    context.getLocator());
+                } else {
+                    throw new SAXParseException("Unexpected element \"" + qname
+                    + "\" " + name, context.getLocator());
+                }
+            }
+        }
+    }
+
+    /**
+     * Handler for the top level "project" element.
+     */
+    public static class ProjectHandler extends AntHandler {
+
+        /**
+         * Initialisation routine called after handler creation
+         * with the element name and attributes. The attributes which
+         * this handler can deal with are: <code>"default"</code>,
+         * <code>"name"</code>, <code>"id"</code> and <code>"basedir"</code>.
+         *
+         * @param uri The namespace URI for this element.
+         * @param tag Name of the element which caused this handler
+         *            to be created. Should not be <code>null</code>.
+         *            Ignored in this implementation.
+         * @param qname The qualified name for this element.
+         * @param attrs Attributes of the element which caused this
+         *              handler to be created. Must not be <code>null</code>.
+         * @param context The current context.
+         *
+         * @exception SAXParseException if an unexpected attribute is
+         *            encountered or if the <code>"default"</code> attribute
+         *            is missing.
+         */
+        public void onStartElement(String uri, String tag, String qname,
+                                   Attributes attrs,
+                                   AntXMLContext context)
+            throws SAXParseException {
+            String id = null;
+            String baseDir = null;
+            boolean nameAttributeSet = false;
+
+            Project project = context.getProject();
+            // Set the location of the implicit target associated with the project tag
+            context.getImplicitTarget().setLocation(
+                new Location(context.getLocator()));
+
+            /** XXX I really don't like this - the XML processor is still
+             * too 'involved' in the processing. A better solution (IMO)
+             * would be to create UE for Project and Target too, and
+             * then process the tree and have Project/Target deal with
+             * its attributes ( similar with Description ).
+             *
+             * If we eventually switch to ( or add support for ) DOM,
+             * things will work smoothly - UE can be avoided almost completely
+             * ( it could still be created on demand, for backward compatibility )
+             */
+
+            for (int i = 0; i < attrs.getLength(); i++) {
+                String attrUri = attrs.getURI(i);
+                if (attrUri != null
+                    && !attrUri.equals("")
+                    && !attrUri.equals(uri)) {
+                    continue; // Ignore attributes from unknown uris
+                }
+                String key = attrs.getLocalName(i);
+                String value = attrs.getValue(i);
+
+                if (key.equals("default")) {
+                    if (value != null && !value.equals("")) {
+                        if (!context.isIgnoringProjectTag()) {
+                            project.setDefault(value);
+                        }
+                    }
+                } else if (key.equals("name")) {
+                    if (value != null) {
+                        context.setCurrentProjectName(value);
+                        nameAttributeSet = true;
+                        if (!context.isIgnoringProjectTag()) {
+                            project.setName(value);
+                            project.addReference(value, project);
+                        }
+                    }
+                } else if (key.equals("id")) {
+                    if (value != null) {
+                        // What's the difference between id and name ?
+                        if (!context.isIgnoringProjectTag()) {
+                            project.addReference(value, project);
+                        }
+                    }
+                } else if (key.equals("basedir")) {
+                    if (!context.isIgnoringProjectTag()) {
+                        baseDir = value;
+                    }
+                } else {
+                    // XXX ignore attributes in a different NS ( maybe store them ? )
+                    throw new SAXParseException("Unexpected attribute \""
+                        + attrs.getQName(i) + "\"", context.getLocator());
+                }
+            }
+
+            // XXX Move to Project ( so it is shared by all helpers )
+            String antFileProp = "ant.file." + context.getCurrentProjectName();
+            String dup = project.getProperty(antFileProp);
+            if (dup != null && nameAttributeSet) {
+                File dupFile = new File(dup);
+                if (context.isIgnoringProjectTag()
+                    && !dupFile.equals(context.getBuildFile())) {
+                    project.log("Duplicated project name in import. Project "
+                        + context.getCurrentProjectName() + " defined first in "
+                        + dup + " and again in " + context.getBuildFile(),
+                        Project.MSG_WARN);
+                }
+            }
+
+            if (context.getBuildFile() != null) {
+                project.setUserProperty("ant.file."
+                    + context.getCurrentProjectName(),
+                    context.getBuildFile().toString());
+            }
+
+            if (context.isIgnoringProjectTag()) {
+                // no further processing
+                return;
+            }
+            // set explicitly before starting ?
+            if (project.getProperty("basedir") != null) {
+                project.setBasedir(project.getProperty("basedir"));
+            } else {
+                // Default for baseDir is the location of the build file.
+                if (baseDir == null) {
+                    project.setBasedir(context.getBuildFileParent().getAbsolutePath());
+                } else {
+                    // check whether the user has specified an absolute path
+                    if ((new File(baseDir)).isAbsolute()) {
+                        project.setBasedir(baseDir);
+                    } else {
+                        project.setBaseDir(fu.resolveFile(
+                                               context.getBuildFileParent(), baseDir));
+                    }
+                }
+            }
+
+            project.addTarget("", context.getImplicitTarget());
+            context.setCurrentTarget(context.getImplicitTarget());
+        }
+
+        /**
+         * Handles the start of a top-level element within the project. An
+         * appropriate handler is created and initialised with the details
+         * of the element.
+         *
+         * @param uri The namespace URI for this element.
+         * @param name The name of the element being started.
+         *            Will not be <code>null</code>.
+         * @param qname The qualified name for this element.
+         * @param attrs Attributes of the element being started.
+         *              Will not be <code>null</code>.
+         * @param context The context for this element.
+         * @return a target or an element handler.
+         *
+         * @exception org.xml.sax.SAXParseException if the tag given is not
+         *            <code>"taskdef"</code>, <code>"typedef"</code>,
+         *            <code>"property"</code>, <code>"target"</code>
+         *            or a data type definition
+         */
+        public AntHandler onStartChild(String uri, String name, String qname,
+                                       Attributes attrs,
+                                       AntXMLContext context)
+            throws SAXParseException {
+            if (name.equals("target")
+                && (uri.equals("") || uri.equals(ANT_CORE_URI))) {
+                return ProjectHelper2.targetHandler;
+            } else {
+                return ProjectHelper2.elementHandler;
+            }
+        }
+
+    }
+
+    /**
+     * Handler for "target" elements.
+     */
+    public static class TargetHandler extends AntHandler {
+
+        /**
+         * Initialisation routine called after handler creation
+         * with the element name and attributes. The attributes which
+         * this handler can deal with are: <code>"name"</code>,
+         * <code>"depends"</code>, <code>"if"</code>,
+         * <code>"unless"</code>, <code>"id"</code> and
+         * <code>"description"</code>.
+         *
+         * @param uri The namespace URI for this element.
+         * @param tag Name of the element which caused this handler
+         *            to be created. Should not be <code>null</code>.
+         *            Ignored in this implementation.
+         * @param qname The qualified name for this element.
+         * @param attrs Attributes of the element which caused this
+         *              handler to be created. Must not be <code>null</code>.
+         * @param context The current context.
+         *
+         * @exception SAXParseException if an unexpected attribute is encountered
+         *            or if the <code>"name"</code> attribute is missing.
+         */
+        public void onStartElement(String uri, String tag, String qname,
+                                   Attributes attrs,
+                                   AntXMLContext context)
+            throws SAXParseException {
+            String name = null;
+            String depends = "";
+
+            Project project = context.getProject();
+            Target target = new Target();
+            target.setProject(project);
+            target.setLocation(new Location(context.getLocator()));
+            context.addTarget(target);
+
+            for (int i = 0; i < attrs.getLength(); i++) {
+                String attrUri = attrs.getURI(i);
+                if (attrUri != null
+                    && !attrUri.equals("")
+                    && !attrUri.equals(uri)) {
+                    continue; // Ignore attributes from unknown uris
+                }
+                String key = attrs.getLocalName(i);
+                String value = attrs.getValue(i);
+
+                if (key.equals("name")) {
+                    name = value;
+                    if ("".equals(name)) {
+                        throw new BuildException("name attribute must "
+                            + "not be empty");
+                    }
+                } else if (key.equals("depends")) {
+                    depends = value;
+                } else if (key.equals("if")) {
+                    target.setIf(value);
+                } else if (key.equals("unless")) {
+                    target.setUnless(value);
+                } else if (key.equals("id")) {
+                    if (value != null && !value.equals("")) {
+                        context.getProject().addReference(value, target);
+                    }
+                } else if (key.equals("description")) {
+                    target.setDescription(value);
+                } else if (key.equals("address")) {
+                    target.setAddress(value);
+                } else {
+                    throw new SAXParseException("Unexpected attribute \""
+                        + key + "\"", context.getLocator());
+                }
+            }
+
+            if (name == null) {
+                throw new SAXParseException("target element appears without "
+                    + "a name attribute", context.getLocator());
+            }
+
+            Hashtable currentTargets = project.getTargets();
+
+            // If the name has already been defined ( import for example )
+            if (currentTargets.containsKey(name)) {
+                if (context.getCurrentTargets().get(name) != null) {
+                    throw new BuildException(
+                        "Duplicate target '" + name + "'", target.getLocation());
+                }
+                // Alter the name.
+                if (context.getCurrentProjectName() != null) {
+                    String newName = context.getCurrentProjectName()
+                        + "." + name;
+                    project.log("Already defined in main or a previous import, "
+                        + "define " + name + " as " + newName,
+                                Project.MSG_VERBOSE);
+                    name = newName;
+                } else {
+                    project.log("Already defined in main or a previous import, "
+                        + "ignore " + name, Project.MSG_VERBOSE);
+                    name = null;
+                }
+            }
+
+            if (name != null) {
+                target.setName(name);
+                context.getCurrentTargets().put(name, target);
+                project.addOrReplaceTarget(name, target);
+            }
+
+            // take care of dependencies
+            if (depends.length() > 0) {
+                target.setDepends(depends);
+            }
+        }
+
+        /**
+         * Handles the start of an element within a target.
+         *
+         * @param uri The namespace URI for this element.
+         * @param name The name of the element being started.
+         *            Will not be <code>null</code>.
+         * @param qname The qualified name for this element.
+         * @param attrs Attributes of the element being started.
+         *              Will not be <code>null</code>.
+         * @param context The current context.
+         * @return an element handler.
+         *
+         * @exception SAXParseException if an error occurs when initialising
+         *                              the appropriate child handler
+         */
+        public AntHandler onStartChild(String uri, String name, String qname,
+                                       Attributes attrs,
+                                       AntXMLContext context)
+            throws SAXParseException {
+            return ProjectHelper2.elementHandler;
+        }
+
+        /**
+         * Handle the end of the project, sets the current target of the
+         * context to be the implicit target.
+         *
+         * @param uri The namespace URI of the element.
+         * @param tag The name of the element.
+         * @param context The current context.
+         */
+        public void onEndElement(String uri, String tag, AntXMLContext context) {
+            context.setCurrentTarget(context.getImplicitTarget());
+        }
+    }
+
+    /**
+     * Handler for all project elements ( tasks, data types )
+     */
+    public static class ElementHandler extends AntHandler {
+
+        /**
+         * Constructor.
+         */
+        public ElementHandler() {
+        }
+
+        /**
+         * Initialisation routine called after handler creation
+         * with the element name and attributes. This configures
+         * the element with its attributes and sets it up with
+         * its parent container (if any). Nested elements are then
+         * added later as the parser encounters them.
+         *
+         * @param uri The namespace URI for this element.
+         * @param tag Name of the element which caused this handler
+         *            to be created. Must not be <code>null</code>.
+         * @param qname The qualified name for this element.
+         * @param attrs Attributes of the element which caused this
+         *              handler to be created. Must not be <code>null</code>.
+         * @param context The current context.
+         *
+         * @exception SAXParseException in case of error (not thrown in
+         *                              this implementation)
+         */
+        public void onStartElement(String uri, String tag, String qname,
+                                   Attributes attrs,
+                                   AntXMLContext context)
+            throws SAXParseException {
+            RuntimeConfigurable parentWrapper = context.currentWrapper();
+            Object parent = null;
+
+            if (parentWrapper != null) {
+                parent = parentWrapper.getProxy();
+            }
+
+            /* UnknownElement is used for tasks and data types - with
+               delayed eval */
+            UnknownElement task = new UnknownElement(tag);
+            task.setProject(context.getProject());
+            task.setNamespace(uri);
+            task.setQName(qname);
+            task.setTaskType(
+                ProjectHelper.genComponentName(task.getNamespace(), tag));
+            task.setTaskName(qname);
+
+            Location location = new Location(context.getLocator().getSystemId(),
+                    context.getLocator().getLineNumber(),
+                    context.getLocator().getColumnNumber());
+            task.setLocation(location);
+            task.setOwningTarget(context.getCurrentTarget());
+
+            context.configureId(task, attrs);
+
+            if (parent != null) {
+                // Nested element
+                ((UnknownElement) parent).addChild(task);
+            }  else {
+                // Task included in a target ( including the default one ).
+                context.getCurrentTarget().addTask(task);
+            }
+
+            // container.addTask(task);
+            // This is a nop in UE: task.init();
+
+            RuntimeConfigurable wrapper
+                = new RuntimeConfigurable(task, task.getTaskName());
+
+            for (int i = 0; i < attrs.getLength(); i++) {
+                String name = attrs.getLocalName(i);
+                String attrUri = attrs.getURI(i);
+                if (attrUri != null
+                    && !attrUri.equals("")
+                    && !attrUri.equals(uri)) {
+                    name = attrUri + ":" + attrs.getQName(i);
+                }
+                String value = attrs.getValue(i);
+                // PR: Hack for ant-type value
+                //  an ant-type is a component name which can
+                // be namespaced, need to extract the name
+                // and convert from qualified name to uri/name
+                if (ANT_TYPE.equals(name)
+                    || (ANT_CORE_URI.equals(attrUri)
+                        && ANT_TYPE.equals(attrs.getLocalName(i)))) {
+                    name = ANT_TYPE;
+                    int index = value.indexOf(":");
+                    if (index != -1) {
+                        String prefix = value.substring(0, index);
+                        String mappedUri = context.getPrefixMapping(prefix);
+                        if (mappedUri == null) {
+                            throw new BuildException(
+                                "Unable to find XML NS prefix " + prefix);
+                        }
+                        value = ProjectHelper.genComponentName(
+                            mappedUri, value.substring(index + 1));
+                    }
+                }
+                wrapper.setAttribute(name, value);
+            }
+
+            if (parentWrapper != null) {
+                parentWrapper.addChild(wrapper);
+            }
+
+            context.pushWrapper(wrapper);
+        }
+
+        /**
+         * Adds text to the task, using the wrapper
+         *
+         * @param buf A character array of the text within the element.
+         *            Will not be <code>null</code>.
+         * @param start The start element in the array.
+         * @param count The number of characters to read from the array.
+         * @param context The current context.
+         *
+         * @exception SAXParseException if the element doesn't support text
+         *
+         * @see ProjectHelper#addText(Project,java.lang.Object,char[],int,int)
+         */
+        public void characters(char[] buf, int start, int count,
+                               AntXMLContext context)
+            throws SAXParseException {
+            RuntimeConfigurable wrapper = context.currentWrapper();
+            wrapper.addText(buf, start, count);
+        }
+
+        /**
+         * Handles the start of an element within a target. Task containers
+         * will always use another task handler, and all other tasks
+         * will always use a nested element handler.
+         *
+         * @param uri The namespace URI for this element.
+         * @param tag The name of the element being started.
+         *            Will not be <code>null</code>.
+         * @param qname The qualified name for this element.
+         * @param attrs Attributes of the element being started.
+         *              Will not be <code>null</code>.
+         * @param context The current context.
+         * @return The handler for elements.
+         *
+         * @exception SAXParseException if an error occurs when initialising
+         *                              the appropriate child handler
+         */
+        public AntHandler onStartChild(String uri, String tag, String qname,
+                                       Attributes attrs,
+                                       AntXMLContext context)
+            throws SAXParseException {
+            return ProjectHelper2.elementHandler;
+        }
+
+        /**
+         * Handles the end of the element. This pops the wrapper from
+         * the context.
+         *
+         * @param uri The namespace URI for the element.
+         * @param tag The name of the element.
+         * @param context The current context.
+         */
+        public void onEndElement(String uri, String tag, AntXMLContext context) {
+            context.popWrapper();
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/helper/ProjectHelperImpl.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/helper/ProjectHelperImpl.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/helper/ProjectHelperImpl.java	(revision 16139)
@@ -0,0 +1,1033 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.helper;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Locale;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.IntrospectionHelper;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.RuntimeConfigurable;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TypeAdapter;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.UnknownElement;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JAXPUtils;
+import org.xml.sax.AttributeList;
+import org.xml.sax.DocumentHandler;
+import org.xml.sax.HandlerBase;
+import org.xml.sax.InputSource;
+import org.xml.sax.Locator;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.XMLReaderAdapter;
+
+/**
+ * Original helper.
+ *
+ */
+public class ProjectHelperImpl extends ProjectHelper {
+
+    /**
+     * SAX 1 style parser used to parse the given file. This may
+     * in fact be a SAX 2 XMLReader wrapped in an XMLReaderAdapter.
+     */
+    private org.xml.sax.Parser parser;
+
+    /** The project to configure. */
+    private Project project;
+    /** The configuration file to parse. */
+    private File buildFile;
+    /**
+     * Parent directory of the build file. Used for resolving entities
+     * and setting the project's base directory.
+     */
+    private File buildFileParent;
+    /**
+     * Locator for the configuration file parser.
+     * Used for giving locations of errors etc.
+     */
+    private Locator locator;
+    /**
+     * Target that all other targets will depend upon implicitly.
+     *
+     * <p>This holds all tasks and data type definitions that have
+     * been placed outside of targets.</p>
+     */
+    private Target implicitTarget = new Target();
+    /**
+     * helper for path -> URI and URI -> path conversions.
+     */
+    private static FileUtils fu = FileUtils.newFileUtils();
+
+    /**
+     * default constructor
+     */
+    public ProjectHelperImpl() {
+        implicitTarget.setName("");
+    }
+
+    /**
+     * Parses the project file, configuring the project as it goes.
+     *
+     * @param project project instance to be configured.
+     * @param source the source from which the project is read.
+     * @exception BuildException if the configuration is invalid or cannot
+     *                           be read.
+     */
+    public void parse(Project project, Object source) throws BuildException {
+        if (!(source instanceof File)) {
+            throw new BuildException("Only File source supported by "
+                + "default plugin");
+        }
+        File buildFile = (File) source;
+        FileInputStream inputStream = null;
+        InputSource inputSource = null;
+
+        this.project = project;
+        this.buildFile = new File(buildFile.getAbsolutePath());
+        buildFileParent = new File(this.buildFile.getParent());
+
+        try {
+            try {
+                parser = JAXPUtils.getParser();
+            } catch (BuildException e) {
+                parser = new XMLReaderAdapter(JAXPUtils.getXMLReader());
+            }
+
+
+            String uri = fu.toURI(buildFile.getAbsolutePath());
+            inputStream = new FileInputStream(buildFile);
+            inputSource = new InputSource(inputStream);
+            inputSource.setSystemId(uri);
+            project.log("parsing buildfile " + buildFile + " with URI = "
+                + uri, Project.MSG_VERBOSE);
+            HandlerBase hb = new RootHandler(this);
+            parser.setDocumentHandler(hb);
+            parser.setEntityResolver(hb);
+            parser.setErrorHandler(hb);
+            parser.setDTDHandler(hb);
+            parser.parse(inputSource);
+        } catch (SAXParseException exc) {
+            Location location =
+                new Location(exc.getSystemId(), exc.getLineNumber(),
+                    exc.getColumnNumber());
+
+            Throwable t = exc.getException();
+            if (t instanceof BuildException) {
+                BuildException be = (BuildException) t;
+                if (be.getLocation() == Location.UNKNOWN_LOCATION) {
+                    be.setLocation(location);
+                }
+                throw be;
+            }
+
+            throw new BuildException(exc.getMessage(), t, location);
+        } catch (SAXException exc) {
+            Throwable t = exc.getException();
+            if (t instanceof BuildException) {
+                throw (BuildException) t;
+            }
+            throw new BuildException(exc.getMessage(), t);
+        } catch (FileNotFoundException exc) {
+            throw new BuildException(exc);
+        } catch (UnsupportedEncodingException exc) {
+            throw new BuildException("Encoding of project file is invalid.",
+                                     exc);
+        } catch (IOException exc) {
+            throw new BuildException("Error reading project file: "
+                                     + exc.getMessage(), exc);
+        } finally {
+            if (inputStream != null) {
+                try {
+                    inputStream.close();
+                } catch (IOException ioe) {
+                    // ignore this
+                }
+            }
+        }
+    }
+
+    /**
+     * The common superclass for all SAX event handlers used to parse
+     * the configuration file. Each method just throws an exception,
+     * so subclasses should override what they can handle.
+     *
+     * Each type of XML element (task, target, etc.) in Ant has
+     * a specific subclass.
+     *
+     * In the constructor, this class takes over the handling of SAX
+     * events from the parent handler and returns
+     * control back to the parent in the endElement method.
+     */
+    static class AbstractHandler extends HandlerBase {
+
+        /**
+         * Previous handler for the document.
+         * When the next element is finished, control returns
+         * to this handler.
+         */
+        protected DocumentHandler parentHandler;
+
+        /** Helper impl. With non-static internal classes, the compiler will generate
+            this automatically - but this will fail with some compilers ( reporting
+            "Expecting to find object/array on stack" ). If we pass it
+            explicitly it'll work with more compilers.
+        */
+        ProjectHelperImpl helperImpl;
+
+        /**
+         * Creates a handler and sets the parser to use it
+         * for the current element.
+         *
+         * @param helperImpl the ProjectHelperImpl instance associated
+         *                   with this handler.
+         *
+         * @param parentHandler The handler which should be restored to the
+         *                      parser at the end of the element.
+         *                      Must not be <code>null</code>.
+         */
+        public AbstractHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler) {
+            this.parentHandler = parentHandler;
+            this.helperImpl = helperImpl;
+
+            // Start handling SAX events
+            helperImpl.parser.setDocumentHandler(this);
+        }
+
+        /**
+         * Handles the start of an element. This base implementation just
+         * throws an exception.
+         *
+         * @param tag The name of the element being started.
+         *            Will not be <code>null</code>.
+         * @param attrs Attributes of the element being started.
+         *              Will not be <code>null</code>.
+         *
+         * @exception SAXParseException if this method is not overridden, or in
+         *                              case of error in an overridden version
+         */
+        public void startElement(String tag, AttributeList attrs) throws SAXParseException {
+            throw new SAXParseException("Unexpected element \"" + tag + "\"", helperImpl.locator);
+        }
+
+        /**
+         * Handles text within an element. This base implementation just
+         * throws an exception.
+         *
+         * @param buf A character array of the text within the element.
+         *            Will not be <code>null</code>.
+         * @param start The start element in the array.
+         * @param count The number of characters to read from the array.
+         *
+         * @exception SAXParseException if this method is not overridden, or in
+         *                              case of error in an overridden version
+         */
+        public void characters(char[] buf, int start, int count) throws SAXParseException {
+            String s = new String(buf, start, count).trim();
+
+            if (s.length() > 0) {
+                throw new SAXParseException("Unexpected text \"" + s + "\"", helperImpl.locator);
+            }
+        }
+
+        /**
+         * Handles the end of an element. Any required clean-up is performed
+         * by the finished() method and then the original handler is restored to
+         * the parser.
+         *
+         * @param name The name of the element which is ending.
+         *             Will not be <code>null</code>.
+         *
+         * @exception SAXException in case of error (not thrown in
+         *                         this implementation)
+         */
+        public void endElement(String name) throws SAXException {
+            // Let parent resume handling SAX events
+            helperImpl.parser.setDocumentHandler(parentHandler);
+        }
+    }
+
+    /**
+     * Handler for the root element. Its only child must be the "project" element.
+     */
+    static class RootHandler extends HandlerBase {
+        ProjectHelperImpl helperImpl;
+
+        public RootHandler(ProjectHelperImpl helperImpl) {
+            this.helperImpl = helperImpl;
+        }
+
+        /**
+         * Resolves file: URIs relative to the build file.
+         *
+         * @param publicId The public identifier, or <code>null</code>
+         *                 if none is available. Ignored in this
+         *                 implementation.
+         * @param systemId The system identifier provided in the XML
+         *                 document. Will not be <code>null</code>.
+         */
+        public InputSource resolveEntity(String publicId,
+                                         String systemId) {
+
+            helperImpl.project.log("resolving systemId: " + systemId, Project.MSG_VERBOSE);
+
+            if (systemId.startsWith("file:")) {
+                String path = fu.fromURI(systemId);
+
+                File file = new File(path);
+                if (!file.isAbsolute()) {
+                    file = fu.resolveFile(helperImpl.buildFileParent, path);
+                }
+                try {
+                    InputSource inputSource = new InputSource(new FileInputStream(file));
+                    inputSource.setSystemId(fu.toURI(file.getAbsolutePath()));
+                    return inputSource;
+                } catch (FileNotFoundException fne) {
+                    helperImpl.project.log(file.getAbsolutePath() + " could not be found",
+                                Project.MSG_WARN);
+                }
+            }
+            // use default if not file or file not found
+            return null;
+        }
+
+        /**
+         * Handles the start of a project element. A project handler is created
+         * and initialised with the element name and attributes.
+         *
+         * @param tag The name of the element being started.
+         *            Will not be <code>null</code>.
+         * @param attrs Attributes of the element being started.
+         *              Will not be <code>null</code>.
+         *
+         * @exception SAXParseException if the tag given is not
+         *                              <code>"project"</code>
+         */
+        public void startElement(String tag, AttributeList attrs) throws SAXParseException {
+            if (tag.equals("project")) {
+                new ProjectHandler(helperImpl, this).init(tag, attrs);
+            } else {
+                throw new SAXParseException("Config file is not of expected "
+                    + "XML type", helperImpl.locator);
+            }
+        }
+
+        /**
+         * Sets the locator in the project helper for future reference.
+         *
+         * @param locator The locator used by the parser.
+         *                Will not be <code>null</code>.
+         */
+        public void setDocumentLocator(Locator locator) {
+            helperImpl.locator = locator;
+        }
+    }
+
+    /**
+     * Handler for the top level "project" element.
+     */
+    static class ProjectHandler extends AbstractHandler {
+
+        /**
+         * Constructor which just delegates to the superconstructor.
+         *
+         * @param parentHandler The handler which should be restored to the
+         *                      parser at the end of the element.
+         *                      Must not be <code>null</code>.
+         */
+        public ProjectHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler) {
+            super(helperImpl, parentHandler);
+        }
+
+        /**
+         * Initialisation routine called after handler creation
+         * with the element name and attributes. The attributes which
+         * this handler can deal with are: <code>"default"</code>,
+         * <code>"name"</code>, <code>"id"</code> and <code>"basedir"</code>.
+         *
+         * @param tag Name of the element which caused this handler
+         *            to be created. Should not be <code>null</code>.
+         *            Ignored in this implementation.
+         * @param attrs Attributes of the element which caused this
+         *              handler to be created. Must not be <code>null</code>.
+         *
+         * @exception SAXParseException if an unexpected attribute is
+         *            encountered or if the <code>"default"</code> attribute
+         *            is missing.
+         */
+        public void init(String tag, AttributeList attrs) throws SAXParseException {
+            String def = null;
+            String name = null;
+            String id = null;
+            String baseDir = null;
+
+            for (int i = 0; i < attrs.getLength(); i++) {
+                String key = attrs.getName(i);
+                String value = attrs.getValue(i);
+
+                if (key.equals("default")) {
+                    def = value;
+                } else if (key.equals("name")) {
+                    name = value;
+                } else if (key.equals("id")) {
+                    id = value;
+                } else if (key.equals("basedir")) {
+                    baseDir = value;
+                } else {
+                    throw new SAXParseException("Unexpected attribute \"" + attrs.getName(i) + "\"",
+                                                helperImpl.locator);
+                }
+            }
+
+            if (def != null && !def.equals("")) {
+                helperImpl.project.setDefaultTarget(def);
+            } else {
+                throw new BuildException("The default attribute is required");
+            }
+
+            if (name != null) {
+                helperImpl.project.setName(name);
+                helperImpl.project.addReference(name, helperImpl.project);
+            }
+
+            if (id != null) {
+                helperImpl.project.addReference(id, helperImpl.project);
+            }
+
+            if (helperImpl.project.getProperty("basedir") != null) {
+                helperImpl.project.setBasedir(helperImpl.project.getProperty("basedir"));
+            } else {
+                if (baseDir == null) {
+                    helperImpl.project.setBasedir(helperImpl.buildFileParent.getAbsolutePath());
+                } else {
+                    // check whether the user has specified an absolute path
+                    if ((new File(baseDir)).isAbsolute()) {
+                        helperImpl.project.setBasedir(baseDir);
+                    } else {
+                        File resolvedBaseDir = helperImpl.project.resolveFile(baseDir,
+                                helperImpl.buildFileParent);
+                        helperImpl.project.setBaseDir(resolvedBaseDir);
+                    }
+                }
+            }
+
+            helperImpl.project.addTarget("", helperImpl.implicitTarget);
+        }
+
+        /**
+         * Handles the start of a top-level element within the project. An
+         * appropriate handler is created and initialised with the details
+         * of the element.
+         *
+         * @param name The name of the element being started.
+         *            Will not be <code>null</code>.
+         * @param attrs Attributes of the element being started.
+         *              Will not be <code>null</code>.
+         *
+         * @exception SAXParseException if the tag given is not
+         *            <code>"taskdef"</code>, <code>"typedef"</code>,
+         *            <code>"property"</code>, <code>"target"</code>
+         *            or a data type definition
+         */
+        public void startElement(String name, AttributeList attrs) throws SAXParseException {
+            if (name.equals("target")) {
+                handleTarget(name, attrs);
+            } else {
+                handleElement(helperImpl, this, helperImpl.implicitTarget,
+                              name, attrs);
+            }
+        }
+
+        /**
+         * Handles a target definition element by creating a target handler
+         * and initialising is with the details of the element.
+         *
+         * @param tag The name of the element to be handled.
+         *            Will not be <code>null</code>.
+         * @param attrs Attributes of the element to be handled.
+         *              Will not be <code>null</code>.
+         *
+         * @exception SAXParseException if an error occurs initialising
+         *                              the handler
+         */
+        private void handleTarget(String tag, AttributeList attrs) throws SAXParseException {
+            new TargetHandler(helperImpl, this).init(tag, attrs);
+        }
+
+    }
+
+    /**
+     * Handler for "target" elements.
+     */
+    static class TargetHandler extends AbstractHandler {
+        private Target target;
+
+        /**
+         * Constructor which just delegates to the superconstructor.
+         *
+         * @param parentHandler The handler which should be restored to the
+         *                      parser at the end of the element.
+         *                      Must not be <code>null</code>.
+         */
+        public TargetHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler) {
+            super(helperImpl, parentHandler);
+        }
+
+        /**
+         * Initialisation routine called after handler creation
+         * with the element name and attributes. The attributes which
+         * this handler can deal with are: <code>"name"</code>,
+         * <code>"depends"</code>, <code>"if"</code>,
+         * <code>"unless"</code>, <code>"id"</code> and
+         * <code>"description"</code>.
+         *
+         * @param tag Name of the element which caused this handler
+         *            to be created. Should not be <code>null</code>.
+         *            Ignored in this implementation.
+         * @param attrs Attributes of the element which caused this
+         *              handler to be created. Must not be <code>null</code>.
+         *
+         * @exception SAXParseException if an unexpected attribute is encountered
+         *            or if the <code>"name"</code> attribute is missing.
+         */
+        public void init(String tag, AttributeList attrs) throws SAXParseException {
+            String name = null;
+            String depends = "";
+            String ifCond = null;
+            String unlessCond = null;
+            String id = null;
+            String description = null;
+
+            for (int i = 0; i < attrs.getLength(); i++) {
+                String key = attrs.getName(i);
+                String value = attrs.getValue(i);
+
+                if (key.equals("name")) {
+                    name = value;
+                    if (name.equals("")) {
+                        throw new BuildException("name attribute must not"
+                                                 + " be empty",
+                                                 new Location(helperImpl.locator));
+                    }
+                } else if (key.equals("depends")) {
+                    depends = value;
+                } else if (key.equals("if")) {
+                    ifCond = value;
+                } else if (key.equals("unless")) {
+                    unlessCond = value;
+                } else if (key.equals("id")) {
+                    id = value;
+                } else if (key.equals("description")) {
+                    description = value;
+                } else if (key.equals("address")) {
+                    target.setAddress(value);
+                } else {
+                    throw new SAXParseException("Unexpected attribute \""
+                        + key + "\"", helperImpl.locator);
+                }
+            }
+
+            if (name == null) {
+                throw new SAXParseException("target element appears without a name attribute",
+                                            helperImpl.locator);
+            }
+
+            target = new Target();
+
+            // implicit target must be first on dependency list
+            target.addDependency("");
+
+            target.setName(name);
+            target.setIf(ifCond);
+            target.setUnless(unlessCond);
+            target.setDescription(description);
+            helperImpl.project.addTarget(name, target);
+
+            if (id != null && !id.equals("")) {
+                helperImpl.project.addReference(id, target);
+            }
+
+            // take care of dependencies
+
+            if (depends.length() > 0) {
+                target.setDepends(depends);
+            }
+        }
+
+        /**
+         * Handles the start of an element within a target.
+         *
+         * @param name The name of the element being started.
+         *            Will not be <code>null</code>.
+         * @param attrs Attributes of the element being started.
+         *              Will not be <code>null</code>.
+         *
+         * @exception SAXParseException if an error occurs when initialising
+         *                              the appropriate child handler
+         */
+        public void startElement(String name, AttributeList attrs) throws SAXParseException {
+            handleElement(helperImpl, this, target, name, attrs);
+        }
+    }
+
+    /**
+     * Start a new DataTypeHandler if element is known to be a
+     * data-type and a TaskHandler otherwise.
+     *
+     * <p>Factored out of TargetHandler.</p>
+     *
+     * @since Ant 1.6
+     */
+    private static void handleElement(ProjectHelperImpl helperImpl,
+                                      DocumentHandler parent,
+                                      Target target, String elementName,
+                                      AttributeList attrs)
+        throws SAXParseException {
+        if (elementName.equals("description")) {
+            new DescriptionHandler(helperImpl, parent);
+        } else if (helperImpl.project.getDataTypeDefinitions()
+                   .get(elementName) != null) {
+            new DataTypeHandler(helperImpl, parent, target)
+                .init(elementName, attrs);
+        } else {
+            new TaskHandler(helperImpl, parent, target, null, target)
+                .init(elementName, attrs);
+        }
+    }
+
+    /**
+     * Handler for "description" elements.
+     */
+    static class DescriptionHandler extends AbstractHandler {
+
+        /**
+         * Constructor which just delegates to the superconstructor.
+         *
+         * @param parentHandler The handler which should be restored to the
+         *                      parser at the end of the element.
+         *                      Must not be <code>null</code>.
+         */
+        public DescriptionHandler(ProjectHelperImpl helperImpl,
+                                  DocumentHandler parentHandler) {
+            super(helperImpl, parentHandler);
+        }
+
+        /**
+         * Adds the text as description to the project.
+         *
+         * @param buf A character array of the text within the element.
+         *            Will not be <code>null</code>.
+         * @param start The start element in the array.
+         * @param count The number of characters to read from the array.
+         */
+        public void characters(char[] buf, int start, int count) {
+            String text = new String(buf, start, count);
+            String currentDescription = helperImpl.project.getDescription();
+            if (currentDescription == null) {
+                helperImpl.project.setDescription(text);
+            } else {
+                helperImpl.project.setDescription(currentDescription + text);
+            }
+        }
+
+    }
+
+    /**
+     * Handler for all task elements.
+     */
+    static class TaskHandler extends AbstractHandler {
+        /** Containing target, if any. */
+        private Target target;
+        /**
+         * Container for the task, if any. If target is
+         * non-<code>null</code>, this must be too.
+         */
+        private TaskContainer container;
+        /**
+         * Task created by this handler.
+         */
+        private Task task;
+        /**
+         * Wrapper for the parent element, if any. The wrapper for this
+         * element will be added to this wrapper as a child.
+         */
+        private RuntimeConfigurable parentWrapper;
+        /**
+         * Wrapper for this element which takes care of actually configuring
+         * the element, if this element is contained within a target.
+         * Otherwise the configuration is performed with the configure method.
+         * @see ProjectHelper#configure(Object,AttributeList,Project)
+         */
+        private RuntimeConfigurable wrapper = null;
+
+        /**
+         * Constructor.
+         *
+         * @param parentHandler The handler which should be restored to the
+         *                      parser at the end of the element.
+         *                      Must not be <code>null</code>.
+         *
+         * @param container     Container for the element.
+         *                      Must not be <code>null</code>.
+         *
+         * @param parentWrapper Wrapper for the parent element, if any.
+         *                      May be <code>null</code>.
+         *
+         * @param target        Target this element is part of.
+         *                      Must not be <code>null</code>.
+         */
+        public TaskHandler(ProjectHelperImpl helperImpl, DocumentHandler parentHandler,
+                           TaskContainer container,
+                           RuntimeConfigurable parentWrapper, Target target) {
+            super(helperImpl, parentHandler);
+            this.container = container;
+            this.parentWrapper = parentWrapper;
+            this.target = target;
+        }
+
+        /**
+         * Initialisation routine called after handler creation
+         * with the element name and attributes. This configures
+         * the element with its attributes and sets it up with
+         * its parent container (if any). Nested elements are then
+         * added later as the parser encounters them.
+         *
+         * @param tag Name of the element which caused this handler
+         *            to be created. Must not be <code>null</code>.
+         *
+         * @param attrs Attributes of the element which caused this
+         *              handler to be created. Must not be <code>null</code>.
+         *
+         * @exception SAXParseException in case of error (not thrown in
+         *                              this implementation)
+         */
+        public void init(String tag, AttributeList attrs) throws SAXParseException {
+            try {
+                task = helperImpl.project.createTask(tag);
+            } catch (BuildException e) {
+                // swallow here, will be thrown again in
+                // UnknownElement.maybeConfigure if the problem persists.
+            }
+
+            if (task == null) {
+                task = new UnknownElement(tag);
+                task.setProject(helperImpl.project);
+                //XXX task.setTaskType(tag);
+                task.setTaskName(tag);
+            }
+
+            task.setLocation(new Location(helperImpl.locator));
+            helperImpl.configureId(task, attrs);
+
+            task.setOwningTarget(target);
+            container.addTask(task);
+            task.init();
+            wrapper = task.getRuntimeConfigurableWrapper();
+            wrapper.setAttributes(attrs);
+            if (parentWrapper != null) {
+                parentWrapper.addChild(wrapper);
+            }
+        }
+
+        /**
+         * Adds text to the task, using the wrapper.
+         *
+         * @param buf A character array of the text within the element.
+         *            Will not be <code>null</code>.
+         * @param start The start element in the array.
+         * @param count The number of characters to read from the array.
+         */
+        public void characters(char[] buf, int start, int count) {
+            wrapper.addText(buf, start, count);
+        }
+
+        /**
+         * Handles the start of an element within a target. Task containers
+         * will always use another task handler, and all other tasks
+         * will always use a nested element handler.
+         *
+         * @param name The name of the element being started.
+         *            Will not be <code>null</code>.
+         * @param attrs Attributes of the element being started.
+         *              Will not be <code>null</code>.
+         *
+         * @exception SAXParseException if an error occurs when initialising
+         *                              the appropriate child handler
+         */
+        public void startElement(String name, AttributeList attrs) throws SAXParseException {
+            if (task instanceof TaskContainer) {
+                // task can contain other tasks - no other nested elements possible
+                new TaskHandler(helperImpl, this, (TaskContainer) task,
+                    wrapper, target).init(name, attrs);
+            } else {
+                new NestedElementHandler(helperImpl, this, task,
+                    wrapper, target).init(name, attrs);
+            }
+        }
+    }
+
+    /**
+     * Handler for all nested properties.
+     */
+    static class NestedElementHandler extends AbstractHandler {
+        /** Parent object (task/data type/etc). */
+        private Object parent;
+        /** The nested element itself. */
+        private Object child;
+        /**
+         * Wrapper for the parent element, if any. The wrapper for this
+         * element will be added to this wrapper as a child.
+         */
+        private RuntimeConfigurable parentWrapper;
+        /**
+         * Wrapper for this element which takes care of actually configuring
+         * the element, if a parent wrapper is provided.
+         * Otherwise the configuration is performed with the configure method.
+         * @see ProjectHelper#configure(Object,AttributeList,Project)
+         */
+        private RuntimeConfigurable childWrapper = null;
+        /** Target this element is part of, if any. */
+        private Target target;
+
+        /**
+         * Constructor.
+         *
+         * @param parentHandler The handler which should be restored to the
+         *                      parser at the end of the element.
+         *                      Must not be <code>null</code>.
+         *
+         * @param parent        Parent of this element (task/data type/etc).
+         *                      Must not be <code>null</code>.
+         *
+         * @param parentWrapper Wrapper for the parent element, if any.
+         *                      Must not be <code>null</code>.
+         *
+         * @param target        Target this element is part of.
+         *                      Must not be <code>null</code>.
+         */
+        public NestedElementHandler(ProjectHelperImpl helperImpl,
+                                    DocumentHandler parentHandler,
+                                    Object parent,
+                                    RuntimeConfigurable parentWrapper,
+                                    Target target) {
+            super(helperImpl, parentHandler);
+
+            if (parent instanceof TypeAdapter) {
+                this.parent = ((TypeAdapter) parent).getProxy();
+            } else {
+                this.parent = parent;
+            }
+            this.parentWrapper = parentWrapper;
+            this.target = target;
+        }
+
+        /**
+         * Initialisation routine called after handler creation
+         * with the element name and attributes. This configures
+         * the element with its attributes and sets it up with
+         * its parent container (if any). Nested elements are then
+         * added later as the parser encounters them.
+         *
+         * @param propType Name of the element which caused this handler
+         *            to be created. Must not be <code>null</code>.
+         *
+         * @param attrs Attributes of the element which caused this
+         *              handler to be created. Must not be <code>null</code>.
+         *
+         * @exception SAXParseException in case of error, such as a
+         *            BuildException being thrown during configuration.
+         */
+        public void init(String propType, AttributeList attrs) throws SAXParseException {
+            Class parentClass = parent.getClass();
+            IntrospectionHelper ih =
+                IntrospectionHelper.getHelper(parentClass);
+
+            try {
+                String elementName = propType.toLowerCase(Locale.US);
+                if (parent instanceof UnknownElement) {
+                    UnknownElement uc = new UnknownElement(elementName);
+                    uc.setProject(helperImpl.project);
+                    ((UnknownElement) parent).addChild(uc);
+                    child = uc;
+                } else {
+                    child = ih.createElement(helperImpl.project, parent, elementName);
+                }
+
+                helperImpl.configureId(child, attrs);
+
+                childWrapper = new RuntimeConfigurable(child, propType);
+                childWrapper.setAttributes(attrs);
+                parentWrapper.addChild(childWrapper);
+            } catch (BuildException exc) {
+                throw new SAXParseException(exc.getMessage(), helperImpl.locator, exc);
+            }
+        }
+
+        /**
+         * Adds text to the element, using the wrapper.
+         *
+         * @param buf A character array of the text within the element.
+         *            Will not be <code>null</code>.
+         * @param start The start element in the array.
+         * @param count The number of characters to read from the array.
+         */
+        public void characters(char[] buf, int start, int count) {
+            childWrapper.addText(buf, start, count);
+        }
+
+        /**
+         * Handles the start of an element within this one. Task containers
+         * will always use a task handler, and all other elements
+         * will always use another nested element handler.
+         *
+         * @param name The name of the element being started.
+         *            Will not be <code>null</code>.
+         * @param attrs Attributes of the element being started.
+         *              Will not be <code>null</code>.
+         *
+         * @exception SAXParseException if an error occurs when initialising
+         *                              the appropriate child handler
+         */
+        public void startElement(String name, AttributeList attrs) throws SAXParseException {
+            if (child instanceof TaskContainer) {
+                // taskcontainer nested element can contain other tasks - no other
+                // nested elements possible
+                new TaskHandler(helperImpl, this, (TaskContainer) child,
+                    childWrapper, target).init(name, attrs);
+            } else {
+                new NestedElementHandler(helperImpl, this, child,
+                    childWrapper, target).init(name, attrs);
+            }
+        }
+    }
+
+    /**
+     * Handler for all data types directly subordinate to project or target.
+     */
+    static class DataTypeHandler extends AbstractHandler {
+        /** Parent target, if any. */
+        private Target target;
+        /** The element being configured. */
+        private Object element;
+        /** Wrapper for this element, if it's part of a target. */
+        private RuntimeConfigurable wrapper = null;
+
+        /**
+         * Constructor with a target specified.
+         *
+         * @param parentHandler The handler which should be restored to the
+         *                      parser at the end of the element.
+         *                      Must not be <code>null</code>.
+         *
+         * @param target The parent target of this element.
+         *               Must not be <code>null</code>.
+         */
+        public DataTypeHandler(ProjectHelperImpl helperImpl,
+                               DocumentHandler parentHandler, Target target) {
+            super(helperImpl, parentHandler);
+            this.target = target;
+        }
+
+        /**
+         * Initialisation routine called after handler creation
+         * with the element name and attributes. This configures
+         * the element with its attributes and sets it up with
+         * its parent container (if any). Nested elements are then
+         * added later as the parser encounters them.
+         *
+         * @param propType Name of the element which caused this handler
+         *            to be created. Must not be <code>null</code>.
+         *
+         * @param attrs Attributes of the element which caused this
+         *              handler to be created. Must not be <code>null</code>.
+         *
+         * @exception SAXParseException in case of error, such as a
+         *            BuildException being thrown during configuration.
+         */
+        public void init(String propType, AttributeList attrs) throws SAXParseException {
+            try {
+                element = helperImpl.project.createDataType(propType);
+                if (element == null) {
+                    throw new BuildException("Unknown data type " + propType);
+                }
+
+                wrapper = new RuntimeConfigurable(element, propType);
+                wrapper.setAttributes(attrs);
+                target.addDataType(wrapper);
+            } catch (BuildException exc) {
+                throw new SAXParseException(exc.getMessage(), helperImpl.locator, exc);
+            }
+        }
+
+        /**
+         * Adds text to the using the wrapper.
+         *
+         * @param buf A character array of the text within the element.
+         *            Will not be <code>null</code>.
+         * @param start The start element in the array.
+         * @param count The number of characters to read from the array.
+         *
+         * @see ProjectHelper#addText(Project,Object,char[],int,int)
+         */
+        public void characters(char[] buf, int start, int count) {
+            wrapper.addText(buf, start, count);
+        }
+
+        /**
+         * Handles the start of an element within this one.
+         * This will always use a nested element handler.
+         *
+         * @param name The name of the element being started.
+         *            Will not be <code>null</code>.
+         * @param attrs Attributes of the element being started.
+         *              Will not be <code>null</code>.
+         *
+         * @exception SAXParseException if an error occurs when initialising
+         *                              the child handler
+         */
+        public void startElement(String name, AttributeList attrs) throws SAXParseException {
+            new NestedElementHandler(helperImpl, this, element, wrapper, target).init(name, attrs);
+        }
+    }
+
+    /**
+     * Scans an attribute list for the <code>id</code> attribute and
+     * stores a reference to the target object in the project if an
+     * id is found.
+     * <p>
+     * This method was moved out of the configure method to allow
+     * it to be executed at parse time.
+     *
+     * @see #configure(Object,AttributeList,Project)
+     */
+    private void configureId(Object target, AttributeList attr) {
+        String id = attr.getValue("id");
+        if (id != null) {
+            project.addReference(id, target);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/helper/SingleCheckExecutor.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/helper/SingleCheckExecutor.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/helper/SingleCheckExecutor.java	(revision 16139)
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.helper;
+
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Executor;
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * "Single-check" Target executor implementation.
+ * Differs from {@link DefaultExecutor} in that the dependencies for all
+ * targets are computed together, so that shared dependencies are run just once.
+ * @since Ant 1.6.3
+ */
+public class SingleCheckExecutor implements Executor {
+
+    //inherit doc
+    public void executeTargets(Project project, String[] targetNames)
+        throws BuildException {
+            project.executeSortedTargets(
+                project.topoSort(targetNames, project.getTargets(), false));
+    }
+
+    //inherit doc
+    public Executor getSubProjectExecutor() {
+        return this;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/input/DefaultInputHandler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/input/DefaultInputHandler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/input/DefaultInputHandler.java	(revision 16139)
@@ -0,0 +1,113 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.input;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.KeepAliveInputStream;
+
+/**
+ * Prompts on System.err, reads input from System.in
+ *
+ * @since Ant 1.5
+ */
+public class DefaultInputHandler implements InputHandler {
+
+    /**
+     * Empty no-arg constructor
+     */
+    public DefaultInputHandler() {
+    }
+
+    /**
+     * Prompts and requests input.  May loop until a valid input has
+     * been entered.
+     * @param request the request to handle
+     * @throws BuildException if not possible to read from console
+     */
+    public void handleInput(InputRequest request) throws BuildException {
+        String prompt = getPrompt(request);
+        DataInputStream in = null;
+        try {
+            in =
+                new DataInputStream(new KeepAliveInputStream(getInputStream()));
+            do {
+                System.err.println(prompt);
+                System.err.flush();
+                try {
+                    String input = in.readLine();
+                    request.setInput(input);
+                } catch (IOException e) {
+                    throw new BuildException("Failed to read input from"
+                                             + " Console.", e);
+                }
+            } while (!request.isInputValid());
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    throw new BuildException("Failed to close input.", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Constructs user prompt from a request.
+     *
+     * <p>This implementation adds (choice1,choice2,choice3,...) to the
+     * prompt for <code>MultipleChoiceInputRequest</code>s.</p>
+     *
+     * @param request the request to construct the prompt for.
+     *                Must not be <code>null</code>.
+     * @return the prompt to ask the user
+     */
+    protected String getPrompt(InputRequest request) {
+        String prompt = request.getPrompt();
+        if (request instanceof MultipleChoiceInputRequest) {
+            StringBuffer sb = new StringBuffer(prompt);
+            sb.append("(");
+            Enumeration e =
+                ((MultipleChoiceInputRequest) request).getChoices().elements();
+            boolean first = true;
+            while (e.hasMoreElements()) {
+                if (!first) {
+                    sb.append(",");
+                }
+                sb.append(e.nextElement());
+                first = false;
+            }
+            sb.append(")");
+            prompt = sb.toString();
+        }
+        return prompt;
+    }
+
+    /**
+     * Returns the input stream from which the user input should be read.
+     * @return the input stream from which the user input should be read.
+     */
+    protected InputStream getInputStream() {
+        return System.in;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/input/InputHandler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/input/InputHandler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/input/InputHandler.java	(revision 16139)
@@ -0,0 +1,40 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.input;
+
+/**
+ * Plugin to Ant to handle requests for user input.
+ *
+ * @since Ant 1.5
+ */
+public interface InputHandler {
+
+    /**
+     * Handle the request encapsulated in the argument.
+     *
+     * <p>Precondition: the request.getPrompt will return a non-null
+     * value.</p>
+     *
+     * <p>Postcondition: request.getInput will return a non-null
+     * value, request.isInputValid will return true.</p>
+     * @param request the request to be processed
+     * @throws org.apache.tools.ant.BuildException if the input cannot be read from the console
+     */
+    void handleInput(InputRequest request)
+        throws org.apache.tools.ant.BuildException;
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/input/InputRequest.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/input/InputRequest.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/input/InputRequest.java	(revision 16139)
@@ -0,0 +1,68 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.input;
+
+/**
+ * Encapsulates an input request.
+ *
+ * @since Ant 1.5
+ */
+public class InputRequest {
+    private String prompt;
+    private String input;
+
+    /**
+     * @param prompt The prompt to show to the user.  Must not be null.
+     */
+    public InputRequest(String prompt) {
+        if (prompt == null) {
+            throw new IllegalArgumentException("prompt must not be null");
+        }
+
+        this.prompt = prompt;
+    }
+
+    /**
+     * Retrieves the prompt text.
+     */
+    public String getPrompt() {
+        return prompt;
+    }
+
+    /**
+     * Sets the user provided input.
+     */
+    public void setInput(String input) {
+        this.input = input;
+    }
+
+    /**
+     * Is the user input valid?
+     */
+    public boolean isInputValid() {
+        return true;
+    }
+
+    /**
+     * Retrieves the user input.
+     */
+    public String getInput() {
+        return input;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/input/MultipleChoiceInputRequest.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/input/MultipleChoiceInputRequest.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/input/MultipleChoiceInputRequest.java	(revision 16139)
@@ -0,0 +1,56 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.input;
+
+import java.util.Vector;
+
+/**
+ * Encapsulates an input request.
+ *
+ * @since Ant 1.5
+ */
+public class MultipleChoiceInputRequest extends InputRequest {
+    private Vector choices = new Vector();
+
+    /**
+     * @param prompt The prompt to show to the user.  Must not be null.
+     * @param choices holds all input values that are allowed.
+     *                Must not be null.
+     */
+    public MultipleChoiceInputRequest(String prompt, Vector choices) {
+        super(prompt);
+        if (choices == null) {
+            throw new IllegalArgumentException("choices must not be null");
+        }
+        this.choices = choices;
+    }
+
+    /**
+     * @return The possible values.
+     */
+    public Vector getChoices() {
+        return choices;
+    }
+
+    /**
+     * @return true if the input is one of the allowed values.
+     */
+    public boolean isInputValid() {
+        return choices.contains(getInput());
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/input/PropertyFileInputHandler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/input/PropertyFileInputHandler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/input/PropertyFileInputHandler.java	(revision 16139)
@@ -0,0 +1,89 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.input;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Properties;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Reads input from a property file, the file name is read from the
+ * system property ant.input.properties, the prompt is the key for input.
+ *
+ * @since Ant 1.5
+ */
+public class PropertyFileInputHandler implements InputHandler {
+    private Properties props = null;
+
+    /**
+     * Name of the system property we expect to hold the file name.
+     */
+    public static final String FILE_NAME_KEY = "ant.input.properties";
+
+    /**
+     * Empty no-arg constructor.
+     */
+    public PropertyFileInputHandler() {
+    }
+
+    /**
+     * Picks up the input from a property, using the prompt as the
+     * name of the property.
+     *
+     * @exception BuildException if no property of that name can be found.
+     */
+    public void handleInput(InputRequest request) throws BuildException {
+        readProps();
+
+        Object o = props.get(request.getPrompt());
+        if (o == null) {
+            throw new BuildException("Unable to find input for \'"
+                                     + request.getPrompt() + "\'");
+        }
+        request.setInput(o.toString());
+        if (!request.isInputValid()) {
+            throw new BuildException("Found invalid input " + o
+                                     + " for \'" + request.getPrompt() + "\'");
+        }
+    }
+
+    /**
+     * Reads the properties file if it hasn't already been read.
+     */
+    private synchronized void readProps() throws BuildException {
+        if (props == null) {
+            String propsFile = System.getProperty(FILE_NAME_KEY);
+            if (propsFile == null) {
+                throw new BuildException("System property "
+                                         + FILE_NAME_KEY
+                                         + " for PropertyFileInputHandler not"
+                                         + " set");
+            }
+
+            props = new Properties();
+
+            try {
+                props.load(new FileInputStream(propsFile));
+            } catch (IOException e) {
+                throw new BuildException("Couldn't load " + propsFile, e);
+            }
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/launch/AntMain.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/launch/AntMain.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/launch/AntMain.java	(revision 16139)
@@ -0,0 +1,41 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.launch;
+
+import java.util.Properties;
+
+/**
+ * Interface used to bridge to the actual Main class without any
+ * messy reflection
+ *
+ * @since Ant 1.6
+ */
+public interface AntMain {
+    /**
+     * Start Ant.
+     *
+     * @param args command line args
+     * @param additionalUserProperties properties to set beyond those that
+     *        may be specified on the args list
+     * @param coreLoader - not used
+     *
+     * @since Ant 1.6
+     */
+    void startAnt(String[] args, Properties additionalUserProperties,
+                  ClassLoader coreLoader);
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/launch/LaunchException.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/launch/LaunchException.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/launch/LaunchException.java	(revision 16139)
@@ -0,0 +1,36 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.launch;
+
+/**
+ * Signals an error condition during launching
+ *
+ * @since Ant 1.6
+ */
+public class LaunchException extends Exception {
+
+    /**
+     * Constructs an exception with the given descriptive message.
+     *
+     * @param message A description of or information about the exception.
+     *            Should not be <code>null</code>.
+     */
+    public LaunchException(String message) {
+        super(message);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/launch/Launcher.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/launch/Launcher.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/launch/Launcher.java	(revision 16139)
@@ -0,0 +1,252 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.launch;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.net.MalformedURLException;
+import java.io.File;
+import java.util.StringTokenizer;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+
+/**
+ *  This is a launcher for Ant.
+ *
+ * @since Ant 1.6
+ */
+public class Launcher {
+    /** The Ant Home property */
+    public static final String ANTHOME_PROPERTY = "ant.home";
+
+    /** The Ant Library Directory property */
+    public static final String ANTLIBDIR_PROPERTY = "ant.library.dir";
+
+    /** The directory name of the per-user ant directory */
+    public static final String ANT_PRIVATEDIR = ".ant";
+
+    /**
+     * The location of a per-user library directory
+     */
+    public static final String ANT_PRIVATELIB = "lib";
+
+    /** The location of a per-user library directory */
+    public static final String USER_LIBDIR = ANT_PRIVATEDIR + "/" + ANT_PRIVATELIB;
+
+    /** The startup class that is to be run */
+    public static final String MAIN_CLASS = "org.apache.tools.ant.Main";
+    /**
+     * system property with user home directory
+     */
+    public static final String USER_HOMEDIR = "user.home";
+
+    /**
+     *  Entry point for starting command line Ant
+     *
+     * @param  args commandline arguments
+     */
+    public static void main(String[] args) {
+        try {
+            Launcher launcher = new Launcher();
+            launcher.run(args);
+        } catch (LaunchException e) {
+            System.err.println(e.getMessage());
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+    }
+
+    /**
+      * Add a CLASSPATH or -lib to lib path urls.
+      * @param path        the classpath or lib path to add to the libPathULRLs
+      * @param getJars     if true and a path is a directory, add the jars in
+      *                    the directory to the path urls
+      * @param libPathURLs the list of paths to add to
+      */
+    private void addPath(String path, boolean getJars, List libPathURLs)
+        throws MalformedURLException {
+        StringTokenizer myTokenizer
+            = new StringTokenizer(path, System.getProperty("path.separator"));
+        while (myTokenizer.hasMoreElements()) {
+            String elementName = myTokenizer.nextToken();
+            File element = new File(elementName);
+            if (elementName.indexOf("%") != -1 && !element.exists()) {
+                continue;
+            }
+            if (getJars && element.isDirectory()) {
+                // add any jars in the directory
+                URL[] dirURLs = Locator.getLocationURLs(element);
+                for (int j = 0; j < dirURLs.length; ++j) {
+                    libPathURLs.add(dirURLs[j]);
+                }
+            }
+
+            libPathURLs.add(element.toURL());
+        }
+    }
+
+    /**
+     * Run the launcher to launch Ant
+     *
+     * @param args the command line arguments
+     *
+     * @exception MalformedURLException if the URLs required for the classloader
+     *            cannot be created.
+     */
+    private void run(String[] args) throws LaunchException, MalformedURLException {
+        String antHomeProperty = System.getProperty(ANTHOME_PROPERTY);
+        File antHome = null;
+
+        File sourceJar = Locator.getClassSource(getClass());
+        File jarDir = sourceJar.getParentFile();
+
+        if (antHomeProperty != null) {
+            antHome = new File(antHomeProperty);
+        }
+
+        if (antHome == null || !antHome.exists()) {
+            antHome = jarDir.getParentFile();
+            System.setProperty(ANTHOME_PROPERTY, antHome.getAbsolutePath());
+        }
+
+        if (!antHome.exists()) {
+            throw new LaunchException("Ant home is set incorrectly or "
+                + "ant could not be located");
+        }
+
+        List libPaths = new ArrayList();
+        String cpString = null;
+        List argList = new ArrayList();
+        String[] newArgs;
+        boolean  noUserLib = false;
+        boolean  noClassPath = false;
+
+        for (int i = 0; i < args.length; ++i) {
+            if (args[i].equals("-lib")) {
+                if (i == args.length - 1) {
+                    throw new LaunchException("The -lib argument must "
+                        + "be followed by a library location");
+                }
+                libPaths.add(args[++i]);
+            } else if (args[i].equals("-cp")) {
+                if (i == args.length - 1) {
+                    throw new LaunchException("The -cp argument must "
+                        + "be followed by a classpath expression");
+                }
+                if (cpString != null) {
+                    throw new LaunchException("The -cp argument must "
+                        + "not be repeated");
+                }
+                cpString = args[++i];
+            } else if (args[i].equals("--nouserlib") || args[i].equals("-nouserlib")) {
+                noUserLib = true;
+            } else if (args[i].equals("--noclasspath") || args[i].equals("-noclasspath")) {
+                noClassPath = true;
+            } else {
+                argList.add(args[i]);
+            }
+        }
+
+        //decide whether to copy the existing arg set, or
+        //build a new one from the list of all args excluding the special
+        //operations that only we handle
+
+        if (libPaths.size() == 0 && cpString == null) {
+            newArgs = args;
+        } else {
+            newArgs = (String[]) argList.toArray(new String[0]);
+        }
+
+        List libPathURLs = new ArrayList();
+
+        if (cpString != null && !noClassPath) {
+            addPath(cpString, false, libPathURLs);
+        }
+
+        for (Iterator i = libPaths.iterator(); i.hasNext();) {
+            String libPath = (String) i.next();
+            addPath(libPath, true, libPathURLs);
+        }
+
+        URL[] libJars = (URL[]) libPathURLs.toArray(new URL[0]);
+
+        // Now try and find JAVA_HOME
+        File toolsJar = Locator.getToolsJar();
+
+        // determine ant library directory for system jars: use property
+        // or default using location of ant-launcher.jar
+        File antLibDir = null;
+        String antLibDirProperty = System.getProperty(ANTLIBDIR_PROPERTY);
+        if (antLibDirProperty != null) {
+            antLibDir = new File(antLibDirProperty);
+        }
+        if ((antLibDir == null) || !antLibDir.exists()) {
+            antLibDir = jarDir;
+            System.setProperty(ANTLIBDIR_PROPERTY, antLibDir.getAbsolutePath());
+        }
+        URL[] systemJars = Locator.getLocationURLs(antLibDir);
+
+        File userLibDir
+            = new File(System.getProperty(USER_HOMEDIR),
+                    ANT_PRIVATEDIR + File.separatorChar + ANT_PRIVATELIB);
+
+        URL[] userJars = noUserLib ? new URL[0] : Locator.getLocationURLs(userLibDir);
+
+        int numJars = libJars.length + userJars.length + systemJars.length;
+        if (toolsJar != null) {
+            numJars++;
+        }
+        URL[] jars = new URL[numJars];
+        System.arraycopy(libJars, 0, jars, 0, libJars.length);
+        System.arraycopy(userJars, 0, jars, libJars.length, userJars.length);
+        System.arraycopy(systemJars, 0, jars, userJars.length + libJars.length,
+            systemJars.length);
+
+        if (toolsJar != null) {
+            jars[jars.length - 1] = toolsJar.toURL();
+        }
+
+
+        // now update the class.path property
+        StringBuffer baseClassPath
+            = new StringBuffer(System.getProperty("java.class.path"));
+        if (baseClassPath.charAt(baseClassPath.length() - 1)
+                == File.pathSeparatorChar) {
+            baseClassPath.setLength(baseClassPath.length() - 1);
+        }
+
+        for (int i = 0; i < jars.length; ++i) {
+            baseClassPath.append(File.pathSeparatorChar);
+            baseClassPath.append(Locator.fromURI(jars[i].toString()));
+        }
+
+        System.setProperty("java.class.path", baseClassPath.toString());
+
+        URLClassLoader loader = new URLClassLoader(jars);
+        Thread.currentThread().setContextClassLoader(loader);
+        try {
+            Class mainClass = loader.loadClass(MAIN_CLASS);
+            AntMain main = (AntMain) mainClass.newInstance();
+            main.startAnt(newArgs, null, null);
+        } catch (Throwable t) {
+            t.printStackTrace();
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/launch/Locator.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/launch/Locator.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/launch/Locator.java	(revision 16139)
@@ -0,0 +1,281 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.launch;
+
+import java.net.MalformedURLException;
+
+import java.net.URL;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.text.CharacterIterator;
+import java.text.StringCharacterIterator;
+import java.util.Locale;
+
+/**
+ * The Locator is a utility class which is used to find certain items
+ * in the environment
+ *
+ * @since Ant 1.6
+ */
+public final class Locator {
+    /**
+     * Not instantiable
+     */
+    private Locator() {
+    }
+
+    /**
+     * Find the directory or jar file the class has been loaded from.
+     *
+     * @param c the class whose location is required.
+     * @return the file or jar with the class or null if we cannot
+     *         determine the location.
+     *
+     * @since Ant 1.6
+     */
+    public static File getClassSource(Class c) {
+        String classResource = c.getName().replace('.', '/') + ".class";
+        return getResourceSource(c.getClassLoader(), classResource);
+    }
+
+    /**
+     * Find the directory or jar a give resource has been loaded from.
+     *
+     * @param c the classloader to be consulted for the source
+     * @param resource the resource whose location is required.
+     *
+     * @return the file with the resource source or null if
+     *         we cannot determine the location.
+     *
+     * @since Ant 1.6
+     */
+    public static File getResourceSource(ClassLoader c, String resource) {
+        if (c == null) {
+            c = Locator.class.getClassLoader();
+        }
+
+        URL url = null;
+        if (c == null) {
+            url = ClassLoader.getSystemResource(resource);
+        } else {
+            url = c.getResource(resource);
+        }
+        if (url != null) {
+            String u = url.toString();
+            if (u.startsWith("jar:file:")) {
+                int pling = u.indexOf("!");
+                String jarName = u.substring(4, pling);
+                return new File(fromURI(jarName));
+            } else if (u.startsWith("file:")) {
+                int tail = u.indexOf(resource);
+                String dirName = u.substring(0, tail);
+                return new File(fromURI(dirName));
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Constructs a file path from a <code>file:</code> URI.
+     *
+     * <p>Will be an absolute path if the given URI is absolute.</p>
+     *
+     * <p>Swallows '%' that are not followed by two characters,
+     * doesn't deal with non-ASCII characters.</p>
+     *
+     * @param uri the URI designating a file in the local filesystem.
+     * @return the local file system path for the file.
+     * @since Ant 1.6
+     */
+    public static String fromURI(String uri) {
+        URL url = null;
+        try {
+            url = new URL(uri);
+        } catch (MalformedURLException emYouEarlEx) {
+        }
+        if (url == null || !("file".equals(url.getProtocol()))) {
+            throw new IllegalArgumentException("Can only handle valid file: URIs");
+        }
+        StringBuffer buf = new StringBuffer(url.getHost());
+        if (buf.length() > 0) {
+            buf.insert(0, File.separatorChar).insert(0, File.separatorChar);
+        }
+
+        String file = url.getFile();
+        int queryPos = file.indexOf('?');
+        buf.append((queryPos < 0) ? file : file.substring(0, queryPos));
+
+        uri = buf.toString().replace('/', File.separatorChar);
+
+        if (File.pathSeparatorChar == ';' && uri.startsWith("\\") && uri.length() > 2
+            && Character.isLetter(uri.charAt(1)) && uri.lastIndexOf(':') > -1) {
+            uri = uri.substring(1);
+        }
+
+        String path = decodeUri(uri);
+        return path;
+    }
+
+    /**
+     * Decodes an Uri with % characters.
+     * @param uri String with the uri possibly containing % characters.
+     * @return The decoded Uri
+     */
+    private static String decodeUri(String uri) {
+        if (uri.indexOf('%') == -1)
+        {
+            return uri;
+        }
+        StringBuffer sb = new StringBuffer();
+        CharacterIterator iter = new StringCharacterIterator(uri);
+        for (char c = iter.first(); c != CharacterIterator.DONE;
+             c = iter.next()) {
+            if (c == '%') {
+                char c1 = iter.next();
+                if (c1 != CharacterIterator.DONE) {
+                    int i1 = Character.digit(c1, 16);
+                    char c2 = iter.next();
+                    if (c2 != CharacterIterator.DONE) {
+                        int i2 = Character.digit(c2, 16);
+                        sb.append((char) ((i1 << 4) + i2));
+                    }
+                }
+            } else {
+                sb.append(c);
+            }
+        }
+        String path = sb.toString();
+        return path;
+    }
+    
+    /**
+     * Get the File necessary to load the Sun compiler tools. If the classes
+     * are available to this class, then no additional URL is required and
+     * null is returned. This may be because the classes are explicitly in the
+     * class path or provided by the JVM directly
+     *
+     * @return the tools jar as a File if required, null otherwise
+     */
+    public static File getToolsJar() {
+        // firstly check if the tools jar is already in the classpath
+        boolean toolsJarAvailable = false;
+
+        try {
+            // just check whether this throws an exception
+            Class.forName("com.sun.tools.javac.Main");
+            toolsJarAvailable = true;
+        } catch (Exception e) {
+            try {
+                Class.forName("sun.tools.javac.Main");
+                toolsJarAvailable = true;
+            } catch (Exception e2) {
+                // ignore
+            }
+        }
+
+        if (toolsJarAvailable) {
+            return null;
+        }
+
+        // couldn't find compiler - try to find tools.jar
+        // based on java.home setting
+        String javaHome = System.getProperty("java.home");
+        if (javaHome.toLowerCase(Locale.US).endsWith("jre")) {
+            javaHome = javaHome.substring(0, javaHome.length() - 4);
+        }
+        File toolsJar = new File(javaHome + "/lib/tools.jar");
+        if (!toolsJar.exists()) {
+            System.out.println("Unable to locate tools.jar. "
+                 + "Expected to find it in " + toolsJar.getPath());
+            return null;
+        }
+        return toolsJar;
+    }
+
+    /**
+     * Get an array or URLs representing all of the jar files in the
+     * given location. If the location is a file, it is returned as the only
+     * element of the array. If the location is a directory, it is scanned for
+     * jar files
+     *
+     * @param location the location to scan for Jars
+     *
+     * @return an array of URLs for all jars in the given location.
+     *
+     * @exception MalformedURLException if the URLs for the jars cannot be
+     *            formed
+     */
+    public static URL[] getLocationURLs(File location)
+         throws MalformedURLException {
+        return getLocationURLs(location, new String[]{".jar"});
+    }
+
+    /**
+     * Get an array or URLs representing all of the files of a given set of
+     * extensions in the given location. If the location is a file, it is
+     * returned as the only element of the array. If the location is a
+     * directory, it is scanned for matching files
+     *
+     * @param location the location to scan for files
+     * @param extensions an array of extension that are to match in the
+     *        directory search
+     *
+     * @return an array of URLs of matching files
+     * @exception MalformedURLException if the URLs for the files cannot be
+     *            formed
+     */
+    public static URL[] getLocationURLs(File location,
+                                        final String[] extensions)
+         throws MalformedURLException {
+        URL[] urls = new URL[0];
+
+        if (!location.exists()) {
+            return urls;
+        }
+
+        if (!location.isDirectory()) {
+            urls = new URL[1];
+            String path = location.getPath();
+            for (int i = 0; i < extensions.length; ++i) {
+                if (path.toLowerCase().endsWith(extensions[i])) {
+                    urls[0] = location.toURL();
+                    break;
+                }
+            }
+            return urls;
+        }
+
+        File[] matches = location.listFiles(
+            new FilenameFilter() {
+                public boolean accept(File dir, String name) {
+                    for (int i = 0; i < extensions.length; ++i) {
+                        if (name.toLowerCase().endsWith(extensions[i])) {
+                            return true;
+                        }
+                    }
+                    return false;
+                }
+            });
+
+        urls = new URL[matches.length];
+        for (int i = 0; i < matches.length; ++i) {
+            urls[i] = matches[i].toURL();
+        }
+        return urls;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/listener/AnsiColorLogger.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/listener/AnsiColorLogger.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/listener/AnsiColorLogger.java	(revision 16139)
@@ -0,0 +1,239 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.listener;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Properties;
+import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.Project;
+
+/**
+ * Uses ANSI Color Code Sequences to colorize messages
+ * sent to the console.
+ *
+ * If used with the -logfile option, the output file
+ * will contain all the necessary escape codes to
+ * display the text in colorized mode when displayed
+ * in the console using applications like cat, more,
+ * etc.
+ *
+ * This is designed to work on terminals that support ANSI
+ * color codes.  It works on XTerm, ETerm, Mindterm, etc.
+ * It also works on Win9x (with ANSI.SYS loaded.)
+ *
+ * NOTE:
+ * It doesn't work on WinNT's COMMAND.COM even with
+ * ANSI.SYS loaded.
+ *
+ * The default colors used for differentiating
+ * the message levels can be changed by editing the
+ * /org/apache/tools/ant/listener/defaults.properties
+ * file.
+ * This file contains 5 key/value pairs:
+ * AnsiColorLogger.ERROR_COLOR=2;31
+ * AnsiColorLogger.WARNING_COLOR=2;35
+ * AnsiColorLogger.INFO_COLOR=2;36
+ * AnsiColorLogger.VERBOSE_COLOR=2;32
+ * AnsiColorLogger.DEBUG_COLOR=2;34
+ *
+ * Another option is to pass a system variable named
+ * ant.logger.defaults, with value set to the path of
+ * the file that contains user defined Ansi Color
+ * Codes, to the <B>java</B> command using -D option.
+ *
+ * To change these colors use the following chart:
+ *
+ *      <B>ANSI COLOR LOGGER CONFIGURATION</B>
+ *
+ * Format for AnsiColorLogger.*=
+ *  Attribute;Foreground;Background
+ *
+ *  Attribute is one of the following:
+ *  0 -> Reset All Attributes (return to normal mode)
+ *  1 -> Bright (Usually turns on BOLD)
+ *  2 -> Dim
+ *  3 -> Underline
+ *  5 -> link
+ *  7 -> Reverse
+ *  8 -> Hidden
+ *
+ *  Foreground is one of the following:
+ *  30 -> Black
+ *  31 -> Red
+ *  32 -> Green
+ *  33 -> Yellow
+ *  34 -> Blue
+ *  35 -> Magenta
+ *  36 -> Cyan
+ *  37 -> White
+ *
+ *  Background is one of the following:
+ *  40 -> Black
+ *  41 -> Red
+ *  42 -> Green
+ *  43 -> Yellow
+ *  44 -> Blue
+ *  45 -> Magenta
+ *  46 -> Cyan
+ *  47 -> White
+ *
+ */
+public final class AnsiColorLogger extends DefaultLogger {
+    // private static final int ATTR_NORMAL = 0;
+    // private static final int ATTR_BRIGHT = 1;
+    private static final int ATTR_DIM = 2;
+    // private static final int ATTR_UNDERLINE = 3;
+    // private static final int ATTR_BLINK = 5;
+    // private static final int ATTR_REVERSE = 7;
+    // private static final int ATTR_HIDDEN = 8;
+
+    // private static final int FG_BLACK = 30;
+    private static final int FG_RED = 31;
+    private static final int FG_GREEN = 32;
+    // private static final int FG_YELLOW = 33;
+    private static final int FG_BLUE = 34;
+    private static final int FG_MAGENTA = 35;
+    private static final int FG_CYAN = 36;
+    // private static final int FG_WHITE = 37;
+
+    // private static final int BG_BLACK = 40;
+    // private static final int BG_RED = 41;
+    // private static final int BG_GREEN = 42;
+    // private static final int BG_YELLOW = 44;
+    // private static final int BG_BLUE = 44;
+    // private static final int BG_MAGENTA = 45;
+    // private static final int BG_CYAN = 46;
+    // private static final int BG_WHITE = 47;
+
+    private static final String PREFIX = "\u001b[";
+    private static final String SUFFIX = "m";
+    private static final char SEPARATOR = ';';
+    private static final String END_COLOR = PREFIX + SUFFIX;
+
+    private String errColor
+        = PREFIX + ATTR_DIM + SEPARATOR + FG_RED + SUFFIX;
+    private String warnColor
+        = PREFIX + ATTR_DIM + SEPARATOR + FG_MAGENTA + SUFFIX;
+    private String infoColor
+        = PREFIX + ATTR_DIM + SEPARATOR + FG_CYAN + SUFFIX;
+    private String verboseColor
+        = PREFIX + ATTR_DIM + SEPARATOR + FG_GREEN + SUFFIX;
+    private String debugColor
+        = PREFIX + ATTR_DIM + SEPARATOR + FG_BLUE + SUFFIX;
+
+    private boolean colorsSet = false;
+
+    /**
+     * Set the colors to use from a property file specified by the
+     * special ant property ant.logger.defaults
+     */
+    private final void setColors() {
+        String userColorFile = System.getProperty("ant.logger.defaults");
+        String systemColorFile =
+            "/org/apache/tools/ant/listener/defaults.properties";
+
+        InputStream in = null;
+
+        try {
+            Properties prop = new Properties();
+
+            if (userColorFile != null) {
+                in = new FileInputStream(userColorFile);
+            } else {
+                in = getClass().getResourceAsStream(systemColorFile);
+            }
+
+            if (in != null) {
+                prop.load(in);
+            }
+
+            String err = prop.getProperty("AnsiColorLogger.ERROR_COLOR");
+            String warn = prop.getProperty("AnsiColorLogger.WARNING_COLOR");
+            String info = prop.getProperty("AnsiColorLogger.INFO_COLOR");
+            String verbose = prop.getProperty("AnsiColorLogger.VERBOSE_COLOR");
+            String debug = prop.getProperty("AnsiColorLogger.DEBUG_COLOR");
+            if (err != null) {
+                errColor = PREFIX + err + SUFFIX;
+            }
+            if (warn != null) {
+                warnColor = PREFIX + warn + SUFFIX;
+            }
+            if (info != null) {
+                infoColor = PREFIX + info + SUFFIX;
+            }
+            if (verbose != null) {
+                verboseColor = PREFIX + verbose + SUFFIX;
+            }
+            if (debug != null) {
+                debugColor = PREFIX + debug + SUFFIX;
+            }
+        } catch (IOException ioe) {
+            //Ignore - we will use the defaults.
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    //Ignore - We do not want this to stop the build.
+                }
+            }
+        }
+    }
+
+    /**
+     * @see DefaultLogger#printMessage
+     */
+    protected final void printMessage(final String message,
+                                      final PrintStream stream,
+                                      final int priority) {
+        if (message != null && stream != null) {
+            if (!colorsSet) {
+                setColors();
+                colorsSet = true;
+            }
+
+            final StringBuffer msg = new StringBuffer(message);
+            switch (priority) {
+                case Project.MSG_ERR:
+                    msg.insert(0, errColor);
+                    msg.append(END_COLOR);
+                    break;
+                case Project.MSG_WARN:
+                    msg.insert(0, warnColor);
+                    msg.append(END_COLOR);
+                    break;
+                case Project.MSG_INFO:
+                    msg.insert(0, infoColor);
+                    msg.append(END_COLOR);
+                    break;
+                case Project.MSG_VERBOSE:
+                    msg.insert(0, verboseColor);
+                    msg.append(END_COLOR);
+                    break;
+                case Project.MSG_DEBUG:
+                    msg.insert(0, debugColor);
+                    msg.append(END_COLOR);
+                    break;
+            }
+            final String strmessage = msg.toString();
+            stream.println(strmessage);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/listener/CommonsLoggingListener.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/listener/CommonsLoggingListener.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/listener/CommonsLoggingListener.java	(revision 16139)
@@ -0,0 +1,301 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.listener;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogConfigurationException;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.BuildLogger;
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.UnknownElement;
+
+import java.io.PrintStream;
+
+/**
+ * Jakarta Commons Logging listener.
+ * Note: do not use the SimpleLog as your logger implementation as it
+ * causes an infinite loop since it writes to System.err, which Ant traps
+ * and reroutes to the logger/listener layer.
+ *
+ * The following names are used for the log:
+ *  org.apache.tools.ant.Project.PROJECT_NAME  - for project events
+ *  org.apache.tools.ant.Target.TARGET_NAME - for target events
+ *  TASK_CLASS_NAME.TARGET_NAME - for events in individual targets.
+ *
+ * In all target and project names we replace "." and " " with "-".
+ *
+ * TODO: we should use the advanced context logging features (and expose them
+ * in c-l first :-)
+ * TODO: this is _very_ inefficient. Switching the out and tracking the logs
+ * can be optimized a lot - but may require few more changes to the core.
+ *
+ * @since Ant 1.5
+ */
+public class CommonsLoggingListener implements BuildListener, BuildLogger {
+
+    /** Indicates if the listener was initialized. */
+    private boolean initialized = false;
+
+    private LogFactory logFactory;
+
+    /**
+     * Construct the listener and make sure that a LogFactory
+     * can be obtained.
+     */
+    public CommonsLoggingListener() {
+    }
+
+    private Log getLog(String cat, String suffix) {
+        if (suffix != null) {
+            suffix = suffix.replace('.', '-');
+            suffix = suffix.replace(' ', '-');
+            cat = cat + "." + suffix;
+        }
+        PrintStream tmpOut = System.out;
+        PrintStream tmpErr = System.err;
+        System.setOut(out);
+        System.setErr(err);
+
+        if (!initialized) {
+            try {
+                logFactory = LogFactory.getFactory();
+            } catch (LogConfigurationException e) {
+                e.printStackTrace(System.err);
+                return null;
+            }
+        }
+
+        initialized = true;
+        Log log = logFactory.getInstance(cat);
+        System.setOut(tmpOut);
+        System.setErr(tmpErr);
+        return log;
+    }
+
+    /**
+     * @see BuildListener#buildStarted
+     */
+    public void buildStarted(BuildEvent event) {
+        String categoryString = "org.apache.tools.ant.Project";
+        Log log = getLog(categoryString, null);
+
+        if (initialized) {
+            realLog(log, "Build started.", Project.MSG_INFO, null);
+        }
+    }
+
+    /**
+     * @see BuildListener#buildFinished
+     */
+    public void buildFinished(BuildEvent event) {
+        if (initialized) {
+            String categoryString = "org.apache.tools.ant.Project";
+            Log log = getLog(categoryString, event.getProject().getName());
+
+            if (event.getException() == null) {
+                realLog(log, "Build finished.", Project.MSG_INFO, null);
+            } else {
+                realLog(log, "Build finished with error.", Project.MSG_ERR,
+                        event.getException());
+            }
+        }
+    }
+
+    /**
+     * @see BuildListener#targetStarted
+     */
+    public void targetStarted(BuildEvent event) {
+        if (initialized) {
+            Log log = getLog("org.apache.tools.ant.Target",
+                    event.getTarget().getName());
+            // Since task log category includes target, we don't really
+            // need this message
+            realLog(log, "Start: " + event.getTarget().getName(),
+                    Project.MSG_DEBUG, null);
+        }
+    }
+
+    /**
+     * @see BuildListener#targetFinished
+     */
+    public void targetFinished(BuildEvent event) {
+        if (initialized) {
+            String targetName = event.getTarget().getName();
+            Log log = getLog("org.apache.tools.ant.Target",
+                    event.getTarget().getName());
+            if (event.getException() == null) {
+                realLog(log, "Target end: " + targetName, Project.MSG_DEBUG, null);
+            } else {
+                realLog(log, "Target \"" + targetName
+                        + "\" finished with error.", Project.MSG_ERR,
+                        event.getException());
+            }
+        }
+    }
+
+    /**
+     * @see BuildListener#taskStarted
+     */
+    public void taskStarted(BuildEvent event) {
+        if (initialized) {
+            Task task = event.getTask();
+            Object real = task;
+            if (task instanceof UnknownElement) {
+                Object realObj = ((UnknownElement) task).getTask();
+                if (realObj != null) {
+                    real = realObj;
+                }
+            }
+            Log log = getLog(real.getClass().getName(), null);
+            if (log.isTraceEnabled()) {
+                realLog(log, "Task \"" + task.getTaskName() + "\" started ",
+                        Project.MSG_VERBOSE, null);
+            }
+        }
+    }
+
+    /**
+     * @see BuildListener#taskFinished
+     */
+    public void taskFinished(BuildEvent event) {
+        if (initialized) {
+            Task task = event.getTask();
+            Object real = task;
+            if (task instanceof UnknownElement) {
+                Object realObj = ((UnknownElement) task).getTask();
+                if (realObj != null) {
+                    real = realObj;
+                }
+            }
+            Log log = getLog(real.getClass().getName(), null);
+            if (event.getException() == null) {
+                if (log.isTraceEnabled()) {
+                    realLog(log, "Task \"" + task.getTaskName() + "\" finished.",
+                            Project.MSG_VERBOSE, null);
+                }
+            } else {
+                realLog(log, "Task \"" + task.getTaskName()
+                        + "\" finished with error.", Project.MSG_ERR,
+                        event.getException());
+            }
+        }
+    }
+
+
+    /**
+     * @see BuildListener#messageLogged
+     */
+    public void messageLogged(BuildEvent event) {
+        if (initialized) {
+            Object categoryObject = event.getTask();
+            String categoryString = null;
+            String categoryDetail = null;
+
+            if (categoryObject == null) {
+                categoryObject = event.getTarget();
+                if (categoryObject == null) {
+                    categoryObject = event.getProject();
+                    categoryString = "org.apache.tools.ant.Project";
+                    categoryDetail = event.getProject().getName();
+                } else {
+                    categoryString = "org.apache.tools.ant.Target";
+                    categoryDetail = event.getTarget().getName();
+                }
+            } else {
+                // It's a task - append the target
+                if (event.getTarget() != null) {
+                    categoryString = categoryObject.getClass().getName();
+                    categoryDetail = event.getTarget().getName();
+                } else {
+                    categoryString = categoryObject.getClass().getName();
+                }
+
+            }
+
+            Log log = getLog(categoryString, categoryDetail);
+            int priority = event.getPriority();
+            String message = event.getMessage();
+            realLog(log, message, priority , null);
+        }
+    }
+
+    private void realLog(Log log, String message, int priority, Throwable t) {
+        PrintStream tmpOut = System.out;
+        PrintStream tmpErr = System.err;
+        System.setOut(out);
+        System.setErr(err);
+        switch (priority) {
+            case Project.MSG_ERR:
+                if (t == null) {
+                    log.error(message);
+                } else {
+                    log.error(message, t);
+                }
+                break;
+            case Project.MSG_WARN:
+                if (t == null) {
+                    log.warn(message);
+                } else {
+                    log.warn(message, t);
+                }
+                break;
+            case Project.MSG_INFO:
+                if (t == null) {
+                    log.info(message);
+                } else {
+                    log.info(message, t);
+                }
+                break;
+            case Project.MSG_VERBOSE:
+                log.debug(message);
+                break;
+            case Project.MSG_DEBUG:
+                log.debug(message);
+                break;
+            default:
+                log.error(message);
+                break;
+        }
+        System.setOut(tmpOut);
+        System.setErr(tmpErr);
+    }
+
+    PrintStream out = System.out;
+    PrintStream err = System.err;
+
+    public void setMessageOutputLevel(int level) {
+        // Use the logger config
+    }
+
+    public void setOutputPrintStream(PrintStream output) {
+        this.out = output;
+    }
+
+    public void setEmacsMode(boolean emacsMode) {
+        // Doesn't make sense for c-l. Use the logger config
+    }
+
+    public void setErrorPrintStream(PrintStream err) {
+        this.err = err;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/listener/Log4jListener.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/listener/Log4jListener.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/listener/Log4jListener.java	(revision 16139)
@@ -0,0 +1,166 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.listener;
+
+import org.apache.log4j.Category;
+import org.apache.log4j.helpers.NullEnumeration;
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.Task;
+
+
+/**
+ *  Listener which sends events to Log4j logging system
+ *
+ */
+public class Log4jListener implements BuildListener {
+
+    /** Indicates if the listener was initialized. */
+    private boolean initialized = false;
+
+    /**
+     * Construct the listener and make sure there is a valid appender.
+     */
+    public Log4jListener() {
+        initialized = false;
+        Category cat = Category.getInstance("org.apache.tools.ant");
+        Category rootCat = Category.getRoot();
+        if (!(rootCat.getAllAppenders() instanceof NullEnumeration)) {
+            initialized = true;
+        } else {
+            cat.error("No log4j.properties in build area");
+        }
+    }
+
+    /**
+     * @see BuildListener#buildStarted
+     */
+    public void buildStarted(BuildEvent event) {
+        if (initialized) {
+            Category cat = Category.getInstance(Project.class.getName());
+            cat.info("Build started.");
+        }
+    }
+
+    /**
+     * @see BuildListener#buildFinished
+     */
+    public void buildFinished(BuildEvent event) {
+        if (initialized) {
+            Category cat = Category.getInstance(Project.class.getName());
+            if (event.getException() == null) {
+                cat.info("Build finished.");
+            } else {
+                cat.error("Build finished with error.", event.getException());
+            }
+        }
+    }
+
+    /**
+     * @see BuildListener#targetStarted
+     */
+    public void targetStarted(BuildEvent event) {
+        if (initialized) {
+            Category cat = Category.getInstance(Target.class.getName());
+            cat.info("Target \"" + event.getTarget().getName() + "\" started.");
+        }
+    }
+
+    /**
+     * @see BuildListener#targetFinished
+     */
+    public void targetFinished(BuildEvent event) {
+        if (initialized) {
+            String targetName = event.getTarget().getName();
+            Category cat = Category.getInstance(Target.class.getName());
+            if (event.getException() == null) {
+                cat.info("Target \"" + targetName + "\" finished.");
+            } else {
+                cat.error("Target \"" + targetName
+                    + "\" finished with error.", event.getException());
+            }
+        }
+    }
+
+    /**
+     * @see BuildListener#taskStarted
+     */
+    public void taskStarted(BuildEvent event) {
+        if (initialized) {
+            Task task = event.getTask();
+            Category cat = Category.getInstance(task.getClass().getName());
+            cat.info("Task \"" + task.getTaskName() + "\" started.");
+        }
+    }
+
+    /**
+     * @see BuildListener#taskFinished
+     */
+    public void taskFinished(BuildEvent event) {
+        if (initialized) {
+            Task task = event.getTask();
+            Category cat = Category.getInstance(task.getClass().getName());
+            if (event.getException() == null) {
+                cat.info("Task \"" + task.getTaskName() + "\" finished.");
+            } else {
+                cat.error("Task \"" + task.getTaskName()
+                    + "\" finished with error.", event.getException());
+            }
+        }
+    }
+
+    /**
+     * @see BuildListener#messageLogged
+     */
+    public void messageLogged(BuildEvent event) {
+        if (initialized) {
+            Object categoryObject = event.getTask();
+            if (categoryObject == null) {
+                categoryObject = event.getTarget();
+                if (categoryObject == null) {
+                    categoryObject = event.getProject();
+                }
+            }
+
+            Category cat
+                = Category.getInstance(categoryObject.getClass().getName());
+            switch (event.getPriority()) {
+                case Project.MSG_ERR:
+                    cat.error(event.getMessage());
+                    break;
+                case Project.MSG_WARN:
+                    cat.warn(event.getMessage());
+                    break;
+                case Project.MSG_INFO:
+                    cat.info(event.getMessage());
+                    break;
+                case Project.MSG_VERBOSE:
+                    cat.debug(event.getMessage());
+                    break;
+                case Project.MSG_DEBUG:
+                    cat.debug(event.getMessage());
+                    break;
+                default:
+                    cat.error(event.getMessage());
+                    break;
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/listener/MailLogger.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/listener/MailLogger.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/listener/MailLogger.java	(revision 16139)
@@ -0,0 +1,280 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.listener;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.Properties;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.email.EmailAddress;
+import org.apache.tools.ant.taskdefs.email.Message;
+import org.apache.tools.ant.taskdefs.email.Mailer;
+import org.apache.tools.ant.util.DateUtils;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.mail.MailMessage;
+
+/**
+ *  Buffers log messages from DefaultLogger, and sends an e-mail with the
+ *  results. The following Project properties are used to send the mail.
+ *  <ul>
+ *    <li> MailLogger.mailhost [default: localhost] - Mail server to use</li>
+ *    <li> MailLogger.port [default: 25] - Default port for SMTP </li>
+ *    <li> MailLogger.from [required] - Mail "from" address</li>
+ *    <li> MailLogger.failure.notify [default: true] - Send build failure
+ *    e-mails?</li>
+ *    <li> MailLogger.success.notify [default: true] - Send build success
+ *    e-mails?</li>
+ *    <li> MailLogger.failure.to [required if failure mail to be sent] - Address
+ *    to send failure messages to</li>
+ *    <li> MailLogger.success.to [required if success mail to be sent] - Address
+ *    to send success messages to</li>
+ *    <li> MailLogger.failure.subject [default: "Build Failure"] - Subject of
+ *    failed build</li>
+ *    <li> MailLogger.success.subject [default: "Build Success"] - Subject of
+ *    successful build</li>
+ *  </ul>
+ *  These properties are set using standard Ant property setting mechanisms
+ *  (&lt;property&gt;, command-line -D, etc). Ant properties can be overridden
+ *  by specifying the filename of a properties file in the <i>
+ *  MailLogger.properties.file property</i> . Any properties defined in that
+ *  file will override Ant properties.
+ *
+ *
+ */
+public class MailLogger extends DefaultLogger {
+    /** Buffer in which the message is constructed prior to sending */
+    private StringBuffer buffer = new StringBuffer();
+
+    /**
+     *  Sends an e-mail with the log results.
+     *
+     * @param event the build finished event
+     */
+    public void buildFinished(BuildEvent event) {
+        super.buildFinished(event);
+
+        Project project = event.getProject();
+        Hashtable properties = project.getProperties();
+
+        // overlay specified properties file (if any), which overrides project
+        // settings
+        Properties fileProperties = new Properties();
+        String filename = (String) properties.get("MailLogger.properties.file");
+        if (filename != null) {
+            InputStream is = null;
+            try {
+                is = new FileInputStream(filename);
+                fileProperties.load(is);
+            } catch (IOException ioe) {
+                // ignore because properties file is not required
+            } finally {
+                if (is != null) {
+                    try {
+                        is.close();
+                    } catch (IOException e) {
+                        // ignore
+                    }
+                }
+            }
+        }
+
+        for (Enumeration e = fileProperties.keys(); e.hasMoreElements();) {
+            String key = (String) e.nextElement();
+            String value = fileProperties.getProperty(key);
+            properties.put(key, project.replaceProperties(value));
+        }
+
+        boolean success = (event.getException() == null);
+        String prefix = success ? "success" : "failure";
+
+        try {
+            boolean notify = Project.toBoolean(getValue(properties,
+                    prefix + ".notify", "on"));
+
+            if (!notify) {
+                return;
+            }
+
+            String mailhost = getValue(properties, "mailhost", "localhost");
+            int port = Integer.parseInt(getValue(properties, "port",
+                                        String.valueOf(MailMessage.DEFAULT_PORT)));
+            String user = getValue(properties, "user", "");
+            String password = getValue(properties, "password", "");
+            boolean ssl = Project.toBoolean(getValue(properties,
+                     "ssl", "off"));
+            String from = getValue(properties, "from", null);
+            String replytoList = getValue(properties, "replyto", "");
+            String toList = getValue(properties, prefix + ".to", null);
+            String subject = getValue(properties, prefix + ".subject",
+                    (success) ? "Build Success" : "Build Failure");
+            if (user.equals("") && password.equals("") && !ssl) {
+                sendMail(mailhost, port,  from, replytoList, toList,
+                         subject, buffer.substring(0));
+            } else {
+                sendMimeMail(event.getProject(), mailhost, port, user,
+                             password, ssl, from, replytoList, toList,
+                             subject, buffer.substring(0));
+            }
+        } catch (Exception e) {
+            System.out.println("MailLogger failed to send e-mail!");
+            e.printStackTrace(System.err);
+        }
+    }
+
+
+    /**
+     *  Receives and buffers log messages.
+     *
+     * @param message the message being logger
+     */
+    protected void log(String message) {
+        buffer.append(message).append(StringUtils.LINE_SEP);
+    }
+
+
+    /**
+     *  Gets the value of a property.
+     *
+     * @param  properties     Properties to obtain value from
+     * @param  name           suffix of property name. "MailLogger." will be
+     *      prepended internally.
+     * @param  defaultValue   value returned if not present in the properties.
+     *      Set to null to make required.
+     * @return                The value of the property, or default value.
+     * @exception  Exception  thrown if no default value is specified and the
+     *      property is not present in properties.
+     */
+    private String getValue(Hashtable properties, String name,
+                            String defaultValue) throws Exception {
+        String propertyName = "MailLogger." + name;
+        String value = (String) properties.get(propertyName);
+
+        if (value == null) {
+            value = defaultValue;
+        }
+
+        if (value == null) {
+            throw new Exception("Missing required parameter: " + propertyName);
+        }
+
+        return value;
+    }
+
+
+    /**
+     *  Send the mail
+     * @param  mailhost         mail server
+     * @param  port             mail server port number
+     * @param  from             from address
+     * @param  replyToList      comma-separated replyto list
+     * @param  toList           comma-separated recipient list
+     * @param  subject          mail subject
+     * @param  message          mail body
+     * @exception  IOException  thrown if sending message fails
+     */
+    private void sendMail(String mailhost, int port, String from, String replyToList, String toList,
+                          String subject, String message) throws IOException {
+        MailMessage mailMessage = new MailMessage(mailhost, port);
+        mailMessage.setHeader("Date", DateUtils.getDateForHeader());
+
+        mailMessage.from(from);
+        if (!replyToList.equals("")) {
+            StringTokenizer t = new StringTokenizer(replyToList, ", ", false);
+            while (t.hasMoreTokens()) {
+                mailMessage.replyto(t.nextToken());
+            }
+        }
+        StringTokenizer t = new StringTokenizer(toList, ", ", false);
+        while (t.hasMoreTokens()) {
+            mailMessage.to(t.nextToken());
+        }
+
+        mailMessage.setSubject(subject);
+
+        PrintStream ps = mailMessage.getPrintStream();
+        ps.println(message);
+
+        mailMessage.sendAndClose();
+    }
+    /**
+     *  Send the mail  (MimeMail)
+     * @param  project          current ant project
+     * @param  host             mail server
+     * @param  port             mail server port number
+     * @param  user             user name for SMTP auth
+     * @param  password         password for SMTP auth
+     * @param  ssl              if true send message over SSL
+     * @param  from             from address
+     * @param  replyToString    comma-separated replyto list
+     * @param  toString         comma-separated recipient list
+     * @param  subject          mail subject
+     * @param  message          mail body
+     */
+    private void sendMimeMail(Project project, String host, int port,
+                              String user, String password, boolean ssl,
+                              String from, String replyToString,
+                              String toString, String subject,
+                              String message)  {
+        // convert the replyTo string into a vector of emailaddresses
+        Mailer mailer = null;
+            try {
+                mailer =
+                    (Mailer) Class.forName("org.apache.tools.ant.taskdefs.email.MimeMailer")
+                    .newInstance();
+            } catch (Throwable e) {
+                log("Failed to initialise MIME mail: " + e.getMessage());
+                return;
+            }
+        Vector replyToList = vectorizeEmailAddresses(replyToString);
+        mailer.setHost(host);
+        mailer.setPort(port);
+        mailer.setUser(user);
+        mailer.setPassword(password);
+        mailer.setSSL(ssl);
+        Message mymessage = new Message(message);
+        mymessage.setProject(project);
+        mailer.setMessage(mymessage);
+        mailer.setFrom(new EmailAddress(from));
+        mailer.setReplyToList(replyToList);
+        Vector toList = vectorizeEmailAddresses(toString);
+        mailer.setToList(toList);
+        mailer.setCcList(new Vector());
+        mailer.setBccList(new Vector());
+        mailer.setFiles(new Vector());
+        mailer.setSubject(subject);
+        mailer.send();
+    }
+    private Vector vectorizeEmailAddresses(String listString) {
+        Vector emailList = new Vector();
+        StringTokenizer tokens = new StringTokenizer(listString, ",");
+        while (tokens.hasMoreTokens()) {
+            emailList.addElement(new EmailAddress(tokens.nextToken()));
+        }
+        return emailList;
+    }
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/listener/defaults.properties
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/listener/defaults.properties	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/listener/defaults.properties	(revision 16139)
@@ -0,0 +1,43 @@
+####################################################
+#
+#           ANSI COLOR LOGGER CONFIGURATION
+#
+# Format for AnsiColorLogger.*=
+#  Attribute;Foreground;Background
+#
+#  Attribute is one of the following:
+#  0 -> Reset All Attributes (return to normal mode)
+#  1 -> Bright (Usually turns on BOLD)
+#  2 -> Dim
+#  3 -> Underline
+#  5 -> link
+#  7 -> Reverse
+#  8 -> Hidden
+#  
+#  Foreground is one of the following:
+#  30 -> Black
+#  31 -> Red
+#  32 -> Green
+#  33 -> Yellow
+#  34 -> Blue
+#  35 -> Magenta
+#  36 -> Cyan
+#  37 -> White
+#
+#  Background is one of the following:
+#  40 -> Black
+#  41 -> Red
+#  42 -> Green
+#  43 -> Yellow
+#  44 -> Blue
+#  45 -> Magenta
+#  46 -> Cyan
+#  47 -> White
+#
+####################################################
+
+AnsiColorLogger.ERROR_COLOR=2;31
+AnsiColorLogger.WARNING_COLOR=2;35
+AnsiColorLogger.INFO_COLOR=2;36
+AnsiColorLogger.VERBOSE_COLOR=2;32
+AnsiColorLogger.DEBUG_COLOR=2;34
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/loader/AntClassLoader2.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/loader/AntClassLoader2.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/loader/AntClassLoader2.java	(revision 16139)
@@ -0,0 +1,301 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.loader;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.Project;
+import java.util.jar.Manifest;
+import java.util.jar.JarFile;
+import java.util.zip.ZipFile;
+import java.util.jar.Attributes;
+import java.util.jar.Attributes.Name;
+import java.net.URL;
+import java.net.MalformedURLException;
+import java.util.zip.ZipEntry;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.StringTokenizer;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * An implementation of the AntClassLoader suitable for use on post JDK 1.1
+ * platforms
+ *
+ */
+public class AntClassLoader2 extends AntClassLoader {
+    /** Instance of a utility class to use for file operations. */
+    private FileUtils fileUtils;
+
+    /** Static map of jar file/time to manifiest class-path entries */
+    private static Map pathMap = Collections.synchronizedMap(new HashMap());
+
+    /**
+     * Constructor
+     */
+    public AntClassLoader2() {
+        fileUtils = FileUtils.newFileUtils();
+    }
+
+    /**
+     * Define a class given its bytes
+     *
+     * @param container the container from which the class data has been read
+     *                  may be a directory or a jar/zip file.
+     *
+     * @param classData the bytecode data for the class
+     * @param className the name of the class
+     *
+     * @return the Class instance created from the given data
+     *
+     * @throws IOException if the class data cannot be read.
+     */
+    protected Class defineClassFromData(File container, byte[] classData,
+                                        String className) throws IOException {
+
+        definePackage(container, className);
+        return defineClass(className, classData, 0, classData.length,
+                           Project.class.getProtectionDomain());
+
+    }
+
+    /**
+     * Get the manifest from the given jar, if it is indeed a jar and it has a
+     * manifest
+     *
+     * @param container the File from which a manifest is required.
+     *
+     * @return the jar's manifest or null is the container is not a jar or it
+     *         has no manifest.
+     *
+     * @exception IOException if the manifest cannot be read.
+     */
+    private Manifest getJarManifest(File container) throws IOException {
+        if (container.isDirectory()) {
+            return null;
+        }
+        JarFile jarFile = null;
+        try {
+            jarFile = new JarFile(container);
+            return jarFile.getManifest();
+        } finally {
+            if (jarFile != null) {
+                jarFile.close();
+            }
+        }
+    }
+
+    /**
+     * Define the package information associated with a class.
+     *
+     * @param container the file containing the class definition.
+     * @param className the class name of for which the package information
+     *        is to be determined.
+     *
+     * @exception IOException if the package information cannot be read from the
+     *            container.
+     */
+    protected void definePackage(File container, String className)
+        throws IOException {
+        int classIndex = className.lastIndexOf('.');
+        if (classIndex == -1) {
+            return;
+        }
+
+        String packageName = className.substring(0, classIndex);
+        if (getPackage(packageName) != null) {
+            // already defined
+            return;
+        }
+
+        // define the package now
+        Manifest manifest = getJarManifest(container);
+
+        if (manifest == null) {
+            definePackage(packageName, null, null, null, null, null,
+                          null, null);
+        } else {
+            definePackage(container, packageName, manifest);
+        }
+    }
+
+    /**
+     * Define the package information when the class comes from a
+     * jar with a manifest
+     *
+     * @param container the jar file containing the manifest
+     * @param packageName the name of the package being defined.
+     * @param manifest the jar's manifest
+     */
+    protected void definePackage(File container, String packageName,
+                                 Manifest manifest) {
+        String sectionName = packageName.replace('.', '/') + "/";
+
+        String specificationTitle = null;
+        String specificationVendor = null;
+        String specificationVersion = null;
+        String implementationTitle = null;
+        String implementationVendor = null;
+        String implementationVersion = null;
+        String sealedString = null;
+        URL sealBase = null;
+
+        Attributes sectionAttributes = manifest.getAttributes(sectionName);
+        if (sectionAttributes != null) {
+            specificationTitle
+                = sectionAttributes.getValue(Name.SPECIFICATION_TITLE);
+            specificationVendor
+                = sectionAttributes.getValue(Name.SPECIFICATION_VENDOR);
+            specificationVersion
+                = sectionAttributes.getValue(Name.SPECIFICATION_VERSION);
+            implementationTitle
+                = sectionAttributes.getValue(Name.IMPLEMENTATION_TITLE);
+            implementationVendor
+                = sectionAttributes.getValue(Name.IMPLEMENTATION_VENDOR);
+            implementationVersion
+                = sectionAttributes.getValue(Name.IMPLEMENTATION_VERSION);
+            sealedString
+                = sectionAttributes.getValue(Name.SEALED);
+        }
+
+        Attributes mainAttributes = manifest.getMainAttributes();
+        if (mainAttributes != null) {
+            if (specificationTitle == null) {
+                specificationTitle
+                    = mainAttributes.getValue(Name.SPECIFICATION_TITLE);
+            }
+            if (specificationVendor == null) {
+                specificationVendor
+                    = mainAttributes.getValue(Name.SPECIFICATION_VENDOR);
+            }
+            if (specificationVersion == null) {
+                specificationVersion
+                    = mainAttributes.getValue(Name.SPECIFICATION_VERSION);
+            }
+            if (implementationTitle == null) {
+                implementationTitle
+                    = mainAttributes.getValue(Name.IMPLEMENTATION_TITLE);
+            }
+            if (implementationVendor == null) {
+                implementationVendor
+                    = mainAttributes.getValue(Name.IMPLEMENTATION_VENDOR);
+            }
+            if (implementationVersion == null) {
+                implementationVersion
+                    = mainAttributes.getValue(Name.IMPLEMENTATION_VERSION);
+            }
+            if (sealedString == null) {
+                sealedString
+                    = mainAttributes.getValue(Name.SEALED);
+            }
+        }
+
+        if (sealedString != null && sealedString.equalsIgnoreCase("true")) {
+            try {
+                sealBase = new URL("file:" + container.getPath());
+            } catch (MalformedURLException e) {
+                // ignore
+            }
+        }
+
+        definePackage(packageName, specificationTitle, specificationVersion,
+                      specificationVendor, implementationTitle,
+                      implementationVersion, implementationVendor, sealBase);
+    }
+
+
+    /**
+     * Add a file to the path. This classloader reads the manifest, if
+     * available, and adds any additional class path jars specified in the
+     * manifest.
+     *
+     * @param pathComponent the file which is to be added to the path for
+     *                      this class loader
+     *
+     * @throws IOException if data needed from the file cannot be read.
+     */
+    protected void addPathFile(File pathComponent) throws IOException {
+        super.addPathFile(pathComponent);
+
+        if (pathComponent.isDirectory()) {
+            return;
+        }
+
+        String absPathPlusTimeAndLength =
+            pathComponent.getAbsolutePath() + pathComponent.lastModified() + "-"
+            + pathComponent.length();
+        String classpath = (String) pathMap.get(absPathPlusTimeAndLength);
+        if (classpath == null) {
+            ZipFile jarFile = null;
+            InputStream manifestStream = null;
+            try {
+                jarFile = new ZipFile(pathComponent);
+                manifestStream
+                    = jarFile.getInputStream(new ZipEntry("META-INF/MANIFEST.MF"));
+
+                if (manifestStream == null) {
+                    return;
+                }
+                Reader manifestReader
+                    = new InputStreamReader(manifestStream, "UTF-8");
+                org.apache.tools.ant.taskdefs.Manifest manifest
+                    = new org.apache.tools.ant.taskdefs.Manifest(manifestReader);
+                classpath
+                    = manifest.getMainSection().getAttributeValue("Class-Path");
+
+            } catch (org.apache.tools.ant.taskdefs.ManifestException e) {
+                // ignore
+            } finally {
+                if (manifestStream != null) {
+                    manifestStream.close();
+                }
+                if (jarFile != null) {
+                    jarFile.close();
+                }
+            }
+            if (classpath == null) {
+                classpath = "";
+            }
+            pathMap.put(absPathPlusTimeAndLength, classpath);
+        }
+
+        if (!"".equals(classpath)) {
+            URL baseURL = fileUtils.getFileURL(pathComponent);
+            StringTokenizer st = new StringTokenizer(classpath);
+            while (st.hasMoreTokens()) {
+                String classpathElement = st.nextToken();
+                URL libraryURL = new URL(baseURL, classpathElement);
+                if (!libraryURL.getProtocol().equals("file")) {
+                    log("Skipping jar library " + classpathElement
+                        + " since only relative URLs are supported by this"
+                        + " loader", Project.MSG_VERBOSE);
+                    continue;
+                }
+                File libraryFile = new File(libraryURL.getFile());
+                if (libraryFile.exists() && !isInPath(libraryFile)) {
+                    addPathFile(libraryFile);
+                }
+            }
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/AbstractCvsTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/AbstractCvsTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/AbstractCvsTask.java	(revision 16139)
@@ -0,0 +1,813 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Environment;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * original Cvs.java 1.20
+ *
+ *  NOTE: This implementation has been moved here from Cvs.java with
+ *  the addition of some accessors for extensibility.  Another task
+ *  can extend this with some customized output processing.
+ *
+ * @since Ant 1.5
+ */
+public abstract class AbstractCvsTask extends Task {
+    /**
+     * Default compression level to use, if compression is enabled via
+     * setCompression( true ).
+     */
+    public static final int DEFAULT_COMPRESSION_LEVEL = 3;
+    private static final int MAXIMUM_COMRESSION_LEVEL = 9;
+
+    private Commandline cmd = new Commandline();
+
+    /** list of Commandline children */
+    private Vector vecCommandlines = new Vector();
+
+    /**
+     * the CVSROOT variable.
+     */
+    private String cvsRoot;
+
+    /**
+     * the CVS_RSH variable.
+     */
+    private String cvsRsh;
+
+    /**
+     * the package/module to check out.
+     */
+    private String cvsPackage;
+    /**
+     * the tag
+     */
+    private String tag;
+    /**
+     * the default command.
+     */
+    private static final String DEFAULT_COMMAND = "checkout";
+    /**
+     * the CVS command to execute.
+     */
+    private String command = null;
+
+    /**
+     * suppress information messages.
+     */
+    private boolean quiet = false;
+
+    /**
+     * suppress all messages.
+     */
+    private boolean reallyquiet = false;
+
+    /**
+     * compression level to use.
+     */
+    private int compression = 0;
+
+    /**
+     * report only, don't change any files.
+     */
+    private boolean noexec = false;
+
+    /**
+     * CVS port
+     */
+    private int port = 0;
+
+    /**
+     * CVS password file
+     */
+    private File passFile = null;
+
+    /**
+     * the directory where the checked out files should be placed.
+     */
+    private File dest;
+
+    /** whether or not to append stdout/stderr to existing files */
+    private boolean append = false;
+
+    /**
+     * the file to direct standard output from the command.
+     */
+    private File output;
+
+    /**
+     * the file to direct standard error from the command.
+     */
+    private File error;
+
+    /**
+     * If true it will stop the build if cvs exits with error.
+     * Default is false. (Iulian)
+     */
+    private boolean failOnError = false;
+
+    /**
+     * Create accessors for the following, to allow different handling of
+     * the output.
+     */
+    private ExecuteStreamHandler executeStreamHandler;
+    private OutputStream outputStream;
+    private OutputStream errorStream;
+
+    /** empty no-arg constructor*/
+    public AbstractCvsTask() {
+        super();
+    }
+
+    /**
+     * sets the handler
+     * @param handler a handler able of processing the output and error streams from the cvs exe
+     */
+    public void setExecuteStreamHandler(ExecuteStreamHandler handler) {
+        this.executeStreamHandler = handler;
+    }
+
+    /**
+     * find the handler and instantiate it if it does not exist yet
+     * @return handler for output and error streams
+     */
+    protected ExecuteStreamHandler getExecuteStreamHandler() {
+
+        if (this.executeStreamHandler == null) {
+            setExecuteStreamHandler(new PumpStreamHandler(getOutputStream(),
+                                                          getErrorStream()));
+        }
+
+        return this.executeStreamHandler;
+    }
+
+    /**
+     * sets a stream to which the output from the cvs executable should be sent
+     * @param outputStream stream to which the stdout from cvs should go
+     */
+    protected void setOutputStream(OutputStream outputStream) {
+
+        this.outputStream = outputStream;
+    }
+
+    /**
+     * access the stream to which the stdout from cvs should go
+     * if this stream has already been set, it will be returned
+     * if the stream has not yet been set, if the attribute output
+     * has been set, the output stream will go to the output file
+     * otherwise the output will go to ant's logging system
+     * @return output stream to which cvs' stdout should go to
+     */
+    protected OutputStream getOutputStream() {
+
+        if (this.outputStream == null) {
+
+            if (output != null) {
+                try {
+                    setOutputStream(new PrintStream(
+                                        new BufferedOutputStream(
+                                            new FileOutputStream(output
+                                                                 .getPath(),
+                                                                 append))));
+                } catch (IOException e) {
+                    throw new BuildException(e, getLocation());
+                }
+            } else {
+                setOutputStream(new LogOutputStream(this, Project.MSG_INFO));
+            }
+        }
+
+        return this.outputStream;
+    }
+
+    /**
+     * sets a stream to which the stderr from the cvs exe should go
+     * @param errorStream an output stream willing to process stderr
+     */
+    protected void setErrorStream(OutputStream errorStream) {
+
+        this.errorStream = errorStream;
+    }
+
+    /**
+     * access the stream to which the stderr from cvs should go
+     * if this stream has already been set, it will be returned
+     * if the stream has not yet been set, if the attribute error
+     * has been set, the output stream will go to the file denoted by the error attribute
+     * otherwise the stderr output will go to ant's logging system
+     * @return output stream to which cvs' stderr should go to
+     */
+    protected OutputStream getErrorStream() {
+
+        if (this.errorStream == null) {
+
+            if (error != null) {
+
+                try {
+                    setErrorStream(new PrintStream(
+                                       new BufferedOutputStream(
+                                           new FileOutputStream(error.getPath(),
+                                                                append))));
+                } catch (IOException e) {
+                    throw new BuildException(e, getLocation());
+                }
+            } else {
+                setErrorStream(new LogOutputStream(this, Project.MSG_WARN));
+            }
+        }
+
+        return this.errorStream;
+    }
+
+    /**
+     * Sets up the environment for toExecute and then runs it.
+     * @param toExecute the command line to execute
+     * @throws BuildException if failonError is set to true and the cvs command fails
+     */
+    protected void runCommand(Commandline toExecute) throws BuildException {
+        // XXX: we should use JCVS (www.ice.com/JCVS) instead of
+        // command line execution so that we don't rely on having
+        // native CVS stuff around (SM)
+
+        // We can't do it ourselves as jCVS is GPLed, a third party task
+        // outside of jakarta repositories would be possible though (SB).
+
+        Environment env = new Environment();
+
+        if (port > 0) {
+            Environment.Variable var = new Environment.Variable();
+            var.setKey("CVS_CLIENT_PORT");
+            var.setValue(String.valueOf(port));
+            env.addVariable(var);
+        }
+
+        /**
+         * Need a better cross platform integration with <cvspass>, so
+         * use the same filename.
+         */
+        if (passFile == null) {
+
+            File defaultPassFile = new File(
+                System.getProperty("cygwin.user.home",
+                    System.getProperty("user.home"))
+                + File.separatorChar + ".cvspass");
+
+            if (defaultPassFile.exists()) {
+                this.setPassfile(defaultPassFile);
+            }
+        }
+
+        if (passFile != null) {
+            if (passFile.isFile() && passFile.canRead()) {
+                Environment.Variable var = new Environment.Variable();
+                var.setKey("CVS_PASSFILE");
+                var.setValue(String.valueOf(passFile));
+                env.addVariable(var);
+                log("Using cvs passfile: " + String.valueOf(passFile),
+                    Project.MSG_INFO);
+            } else if (!passFile.canRead()) {
+                log("cvs passfile: " + String.valueOf(passFile)
+                    + " ignored as it is not readable",
+                    Project.MSG_WARN);
+            } else {
+                log("cvs passfile: " + String.valueOf(passFile)
+                    + " ignored as it is not a file",
+                    Project.MSG_WARN);
+            }
+        }
+
+        if (cvsRsh != null) {
+            Environment.Variable var = new Environment.Variable();
+            var.setKey("CVS_RSH");
+            var.setValue(String.valueOf(cvsRsh));
+            env.addVariable(var);
+        }
+
+        //
+        // Just call the getExecuteStreamHandler() and let it handle
+        //     the semantics of instantiation or retrieval.
+        //
+        Execute exe = new Execute(getExecuteStreamHandler(), null);
+
+        exe.setAntRun(getProject());
+        if (dest == null) {
+            dest = getProject().getBaseDir();
+        }
+
+        if (!dest.exists()) {
+            dest.mkdirs();
+        }
+
+        exe.setWorkingDirectory(dest);
+        exe.setCommandline(toExecute.getCommandline());
+        exe.setEnvironment(env.getVariables());
+
+        try {
+            String actualCommandLine = executeToString(exe);
+            log(actualCommandLine, Project.MSG_VERBOSE);
+            int retCode = exe.execute();
+            log("retCode=" + retCode, Project.MSG_DEBUG);
+            /*Throw an exception if cvs exited with error. (Iulian)*/
+            if (failOnError && Execute.isFailure(retCode)) {
+                throw new BuildException("cvs exited with error code "
+                                         + retCode
+                                         + StringUtils.LINE_SEP
+                                         + "Command line was ["
+                                         + actualCommandLine + "]", getLocation());
+            }
+        } catch (IOException e) {
+            if (failOnError) {
+                throw new BuildException(e, getLocation());
+            } else {
+                log("Caught exception: " + e.getMessage(), Project.MSG_WARN);
+            }
+        } catch (BuildException e) {
+            if (failOnError) {
+                throw(e);
+            } else {
+                Throwable t = e.getException();
+                if (t == null) {
+                    t = e;
+                }
+                log("Caught exception: " + t.getMessage(), Project.MSG_WARN);
+            }
+        } catch (Exception e) {
+            if (failOnError) {
+                throw new BuildException(e, getLocation());
+            } else {
+                log("Caught exception: " + e.getMessage(), Project.MSG_WARN);
+            }
+        }
+    }
+
+    /**
+     * do the work
+     * @throws BuildException if failonerror is set to true and the cvs command fails.
+     */
+    public void execute() throws BuildException {
+
+        String savedCommand = getCommand();
+
+        if (this.getCommand() == null && vecCommandlines.size() == 0) {
+            // re-implement legacy behaviour:
+            this.setCommand(AbstractCvsTask.DEFAULT_COMMAND);
+        }
+
+        String c = this.getCommand();
+        Commandline cloned = null;
+        if (c != null) {
+            cloned = (Commandline) cmd.clone();
+            cloned.createArgument(true).setLine(c);
+            this.addConfiguredCommandline(cloned, true);
+        }
+
+        try {
+            for (int i = 0; i < vecCommandlines.size(); i++) {
+                this.runCommand((Commandline) vecCommandlines.elementAt(i));
+            }
+        } finally {
+            if (cloned != null) {
+                removeCommandline(cloned);
+            }
+            setCommand(savedCommand);
+
+            if (outputStream != null) {
+                try {
+                    outputStream.close();
+                } catch (IOException e) {
+                    //ignore
+                }
+            }
+            if (errorStream != null) {
+                try {
+                    errorStream.close();
+                } catch (IOException e) {
+                    //ignore
+                }
+            }
+        }
+    }
+
+    private String executeToString(Execute execute) {
+
+        StringBuffer stringBuffer =
+            new StringBuffer(Commandline.describeCommand(execute
+                                                         .getCommandline()));
+
+        String newLine = StringUtils.LINE_SEP;
+        String[] variableArray = execute.getEnvironment();
+
+        if (variableArray != null) {
+            stringBuffer.append(newLine);
+            stringBuffer.append(newLine);
+            stringBuffer.append("environment:");
+            stringBuffer.append(newLine);
+            for (int z = 0; z < variableArray.length; z++) {
+                stringBuffer.append(newLine);
+                stringBuffer.append("\t");
+                stringBuffer.append(variableArray[z]);
+            }
+        }
+
+        return stringBuffer.toString();
+    }
+
+    /**
+     * The CVSROOT variable.
+     *
+     * @param root the CVSROOT variable
+     */
+    public void setCvsRoot(String root) {
+
+        // Check if not real cvsroot => set it to null
+        if (root != null) {
+            if (root.trim().equals("")) {
+                root = null;
+            }
+        }
+
+        this.cvsRoot = root;
+    }
+
+    /**
+     * access the CVSROOT variable
+     * @return CVSROOT
+     */
+    public String getCvsRoot() {
+
+        return this.cvsRoot;
+    }
+
+    /**
+     * The CVS_RSH variable.
+     *
+     * @param rsh the CVS_RSH variable
+     */
+    public void setCvsRsh(String rsh) {
+        // Check if not real cvsrsh => set it to null
+        if (rsh != null) {
+            if (rsh.trim().equals("")) {
+                rsh = null;
+            }
+        }
+
+        this.cvsRsh = rsh;
+    }
+
+    /**
+     * access the CVS_RSH variable
+     * @return the CVS_RSH variable
+     */
+    public String getCvsRsh() {
+
+        return this.cvsRsh;
+    }
+
+    /**
+     * Port used by CVS to communicate with the server.
+     *
+     * @param port port of CVS
+     */
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    /**
+     * access the port of CVS
+     * @return the port of CVS
+     */
+    public int getPort() {
+
+        return this.port;
+    }
+
+    /**
+     * Password file to read passwords from.
+     *
+     * @param passFile password file to read passwords from
+     */
+    public void setPassfile(File passFile) {
+        this.passFile = passFile;
+    }
+
+    /**
+     * find the password file
+     * @return password file
+     */
+    public File getPassFile() {
+
+        return this.passFile;
+    }
+
+    /**
+     * The directory where the checked out files should be placed.
+     *
+     * <p>Note that this is different from CVS's -d command line
+     * switch as Ant will never shorten pathnames to avoid empty
+     * directories.</p>
+     *
+     * @param dest directory where the checked out files should be placed
+     */
+    public void setDest(File dest) {
+        this.dest = dest;
+    }
+
+    /**
+     * get the file where the checked out files should be placed
+     *
+     * @return directory where the checked out files should be placed
+     */
+    public File getDest() {
+
+        return this.dest;
+    }
+
+    /**
+     * The package/module to operate upon.
+     *
+     * @param p package or module to operate upon
+     */
+    public void setPackage(String p) {
+        this.cvsPackage = p;
+    }
+
+    /**
+     * access the package or module to operate upon
+     *
+     * @return package/module
+     */
+    public String getPackage() {
+
+        return this.cvsPackage;
+    }
+    /**
+     * tag or branch
+     * @return tag or branch
+     * @since ant 1.6.1
+     */
+    public String getTag() {
+        return tag;
+    }
+
+    /**
+     * The tag of the package/module to operate upon.
+     * @param p tag
+     */
+    public void setTag(String p) {
+        // Check if not real tag => set it to null
+        if (p != null && p.trim().length() > 0) {
+            tag = p;
+            addCommandArgument("-r" + p);
+        }
+    }
+
+    /**
+     * This needs to be public to allow configuration
+     *      of commands externally.
+     * @param arg command argument
+     */
+    public void addCommandArgument(String arg) {
+        this.addCommandArgument(cmd, arg);
+    }
+
+    /**
+     * This method adds a command line argument to an external command.
+     *
+     * I do not understand what this method does in this class ???
+     * particularly not why it is public ????
+     * AntoineLL July 23d 2003
+     *
+     * @param c  command line to which one argument should be added
+     * @param arg argument to add
+     */
+    public void addCommandArgument(Commandline c, String arg) {
+        c.createArgument().setValue(arg);
+    }
+
+
+    /**
+     * Use the most recent revision no later than the given date.
+     * @param p a date as string in a format that the CVS executable can understand
+     * see man cvs
+     */
+    public void setDate(String p) {
+        if (p != null && p.trim().length() > 0) {
+            addCommandArgument("-D");
+            addCommandArgument(p);
+        }
+    }
+
+    /**
+     * The CVS command to execute.
+     *
+     * This should be deprecated, it is better to use the Commandline class ?
+     * AntoineLL July 23d 2003
+     *
+     * @param c a command as string
+     */
+    public void setCommand(String c) {
+        this.command = c;
+    }
+    /**
+     * accessor to a command line as string
+     *
+     * This should be deprecated
+     * AntoineLL July 23d 2003
+     *
+     * @return command line as string
+     */
+    public String getCommand() {
+        return this.command;
+    }
+
+    /**
+     * If true, suppress informational messages.
+     * @param q  if true, suppress informational messages
+     */
+    public void setQuiet(boolean q) {
+        quiet = q;
+    }
+
+    /**
+     * If true, suppress all messages.
+     * @param q  if true, suppress all messages
+     * @since Ant 1.6
+     */
+    public void setReallyquiet(boolean q) {
+        reallyquiet = q;
+    }
+
+
+    /**
+     * If true, report only and don't change any files.
+     *
+     * @param ne if true, report only and do not change any files.
+     */
+    public void setNoexec(boolean ne) {
+        noexec = ne;
+    }
+
+    /**
+     * The file to direct standard output from the command.
+     * @param output a file to which stdout should go
+     */
+    public void setOutput(File output) {
+        this.output = output;
+    }
+
+    /**
+     * The file to direct standard error from the command.
+     *
+     * @param error a file to which stderr should go
+     */
+    public void setError(File error) {
+        this.error = error;
+    }
+
+    /**
+     * Whether to append output/error when redirecting to a file.
+     * @param value true indicated you want to append
+     */
+    public void setAppend(boolean value) {
+        this.append = value;
+    }
+
+    /**
+     * Stop the build process if the command exits with
+     * a return code other than 0.
+     * Defaults to false.
+     * @param failOnError stop the build process if the command exits with
+     * a return code other than 0
+     */
+    public void setFailOnError(boolean failOnError) {
+        this.failOnError = failOnError;
+    }
+
+    /**
+     * Configure a commandline element for things like cvsRoot, quiet, etc.
+     * @param c the command line which will be configured
+     * if the commandline is initially null, the function is a noop
+     * otherwise the function append to the commandline arguments concerning
+     * <ul>
+     * <li>
+     * cvs package
+     * </li>
+     * <li>
+     * compression
+     * </li>
+     * <li>
+     * quiet or reallyquiet
+     * </li>
+     * <li>cvsroot</li>
+     * <li>noexec</li>
+     * </ul>
+     */
+    protected void configureCommandline(Commandline c) {
+        if (c == null) {
+            return;
+        }
+        c.setExecutable("cvs");
+        if (cvsPackage != null) {
+            c.createArgument().setLine(cvsPackage);
+        }
+        if (this.compression > 0 && this.compression <= MAXIMUM_COMRESSION_LEVEL) {
+            c.createArgument(true).setValue("-z" + this.compression);
+        }
+        if (quiet && !reallyquiet) {
+            c.createArgument(true).setValue("-q");
+        }
+        if (reallyquiet) {
+            c.createArgument(true).setValue("-Q");
+        }
+        if (noexec) {
+            c.createArgument(true).setValue("-n");
+        }
+        if (cvsRoot != null) {
+            c.createArgument(true).setLine("-d" + cvsRoot);
+        }
+    }
+
+    /**
+     * remove a particular command from a vector of command lines
+     * @param c command line which should be removed
+     */
+    protected void removeCommandline(Commandline c) {
+        vecCommandlines.removeElement(c);
+    }
+
+    /**
+     * Adds direct command-line to execute.
+     * @param c command line to execute
+     */
+    public void addConfiguredCommandline(Commandline c) {
+        this.addConfiguredCommandline(c, false);
+    }
+
+    /**
+     * Configures and adds the given Commandline.
+     * @param c commandline to insert
+     * @param insertAtStart If true, c is
+     * inserted at the beginning of the vector of command lines
+    */
+    public void addConfiguredCommandline(Commandline c,
+                                         boolean insertAtStart) {
+        if (c == null) {
+            return;
+        }
+        this.configureCommandline(c);
+        if (insertAtStart) {
+            vecCommandlines.insertElementAt(c, 0);
+        } else {
+            vecCommandlines.addElement(c);
+        }
+    }
+
+    /**
+    * If set to a value 1-9 it adds -zN to the cvs command line, else
+    * it disables compression.
+     * @param level compression level 1 to 9
+    */
+    public void setCompressionLevel(int level) {
+        this.compression = level;
+    }
+
+    /**
+     * If true, this is the same as compressionlevel="3".
+     *
+     * @param usecomp If true, turns on compression using default
+     * level, AbstractCvsTask.DEFAULT_COMPRESSION_LEVEL.
+     */
+    public void setCompression(boolean usecomp) {
+        setCompressionLevel(usecomp
+            ? AbstractCvsTask.DEFAULT_COMPRESSION_LEVEL : 0);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Ant.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Ant.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Ant.java	(revision 16139)
@@ -0,0 +1,894 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Vector;
+import java.util.Set;
+import java.util.HashSet;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.Executor;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.helper.SingleCheckExecutor;
+import org.apache.tools.ant.types.PropertySet;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Build a sub-project.
+ *
+ *  <pre>
+ *  &lt;target name=&quot;foo&quot; depends=&quot;init&quot;&gt;
+ *    &lt;ant antfile=&quot;build.xml&quot; target=&quot;bar&quot; &gt;
+ *      &lt;property name=&quot;property1&quot; value=&quot;aaaaa&quot; /&gt;
+ *      &lt;property name=&quot;foo&quot; value=&quot;baz&quot; /&gt;
+ *    &lt;/ant&gt;</span>
+ *  &lt;/target&gt;</span>
+ *
+ *  &lt;target name=&quot;bar&quot; depends=&quot;init&quot;&gt;
+ *    &lt;echo message=&quot;prop is ${property1} ${foo}&quot; /&gt;
+ *  &lt;/target&gt;
+ * </pre>
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="control"
+ */
+public class Ant extends Task {
+
+    /** the basedir where is executed the build file */
+    private File dir = null;
+
+    /**
+     * the build.xml file (can be absolute) in this case dir will be
+     * ignored
+     */
+    private String antFile = null;
+
+    /** the output */
+    private String output  = null;
+
+    /** should we inherit properties from the parent ? */
+    private boolean inheritAll = true;
+
+    /** should we inherit references from the parent ? */
+    private boolean inheritRefs = false;
+
+    /** the properties to pass to the new project */
+    private Vector properties = new Vector();
+
+    /** the references to pass to the new project */
+    private Vector references = new Vector();
+
+    /** the temporary project created to run the build file */
+    private Project newProject;
+
+    /** The stream to which output is to be written. */
+    private PrintStream out = null;
+
+    /** the sets of properties to pass to the new project */
+    private Vector propertySets = new Vector();
+
+    /** the targets to call on the new project */
+    private Vector targets = new Vector();
+
+    /** whether the target attribute was specified **/
+    private boolean targetAttributeSet = false;
+
+    /**
+     * If true, pass all properties to the new Ant project.
+     * Defaults to true.
+     * @param value if true pass all properties to the new Ant project.
+     */
+    public void setInheritAll(boolean value) {
+        inheritAll = value;
+    }
+
+    /**
+     * If true, pass all references to the new Ant project.
+     * Defaults to false.
+     * @param value if true, pass all references to the new Ant project
+     */
+    public void setInheritRefs(boolean value) {
+        inheritRefs = value;
+    }
+
+    /**
+     * Creates a Project instance for the project to call.
+     */
+    public void init() {
+        newProject = new Project();
+        newProject.setDefaultInputStream(getProject().getDefaultInputStream());
+        newProject.setJavaVersionProperty();
+    }
+
+    /**
+     * Called in execute or createProperty if newProject is null.
+     *
+     * <p>This can happen if the same instance of this task is run
+     * twice as newProject is set to null at the end of execute (to
+     * save memory and help the GC).</p>
+     * <p>calls init() again</p>
+     *
+     */
+    private void reinit() {
+        init();
+    }
+
+    /**
+     * Attaches the build listeners of the current project to the new
+     * project, configures a possible logfile, transfers task and
+     * data-type definitions, transfers properties (either all or just
+     * the ones specified as user properties to the current project,
+     * depending on inheritall), transfers the input handler.
+     */
+    private void initializeProject() {
+        newProject.setInputHandler(getProject().getInputHandler());
+
+        Iterator iter = getBuildListeners();
+        while (iter.hasNext()) {
+            newProject.addBuildListener((BuildListener) iter.next());
+        }
+
+        if (output != null) {
+            File outfile = null;
+            if (dir != null) {
+                outfile = FileUtils.newFileUtils().resolveFile(dir, output);
+            } else {
+                outfile = getProject().resolveFile(output);
+            }
+            try {
+                out = new PrintStream(new FileOutputStream(outfile));
+                DefaultLogger logger = new DefaultLogger();
+                logger.setMessageOutputLevel(Project.MSG_INFO);
+                logger.setOutputPrintStream(out);
+                logger.setErrorPrintStream(out);
+                newProject.addBuildListener(logger);
+            } catch (IOException ex) {
+                log("Ant: Can't set output to " + output);
+            }
+        }
+
+        getProject().initSubProject(newProject);
+
+        // set user-defined properties
+        getProject().copyUserProperties(newProject);
+
+        if (!inheritAll) {
+           // set Java built-in properties separately,
+           // b/c we won't inherit them.
+           newProject.setSystemProperties();
+
+        } else {
+            // set all properties from calling project
+            addAlmostAll(getProject().getProperties());
+        }
+
+        Enumeration e = propertySets.elements();
+        while (e.hasMoreElements()) {
+            PropertySet ps = (PropertySet) e.nextElement();
+            addAlmostAll(ps.getProperties());
+        }
+    }
+
+    /**
+     * @see Task#handleOutput(String)
+     *
+     * @since Ant 1.5
+     */
+    public void handleOutput(String output) {
+        if (newProject != null) {
+            newProject.demuxOutput(output, false);
+        } else {
+            super.handleOutput(output);
+        }
+    }
+
+    /**
+     * @see Task#handleInput(byte[], int, int)
+     *
+     * @since Ant 1.6
+     */
+    public int handleInput(byte[] buffer, int offset, int length)
+        throws IOException {
+        if (newProject != null) {
+            return newProject.demuxInput(buffer, offset, length);
+        } else {
+            return super.handleInput(buffer, offset, length);
+        }
+    }
+
+    /**
+     * @see Task#handleFlush(String)
+     *
+     * @since Ant 1.5.2
+     */
+    public void handleFlush(String output) {
+        if (newProject != null) {
+            newProject.demuxFlush(output, false);
+        } else {
+            super.handleFlush(output);
+        }
+    }
+
+    /**
+     * @see Task#handleErrorOutput(String)
+     *
+     * @since Ant 1.5
+     */
+    public void handleErrorOutput(String output) {
+        if (newProject != null) {
+            newProject.demuxOutput(output, true);
+        } else {
+            super.handleErrorOutput(output);
+        }
+    }
+
+    /**
+     * @see Task#handleErrorFlush(String)
+     *
+     * @since Ant 1.5.2
+     */
+    public void handleErrorFlush(String output) {
+        if (newProject != null) {
+            newProject.demuxFlush(output, true);
+        } else {
+            super.handleErrorFlush(output);
+        }
+    }
+
+    /**
+     * Do the execution.
+     * @throws BuildException if a target tries to call itself;
+     * probably also if a BuildException is thrown by the new project.
+     */
+    public void execute(String targetName, String targetAddress) throws BuildException {
+        File savedDir = dir;
+        String savedAntFile = antFile;
+        Vector locals = new Vector(targets);
+        try {
+            if (newProject == null) {
+                reinit();
+            }
+
+            if ((dir == null) && (inheritAll)) {
+                dir = getProject().getBaseDir();
+            }
+
+            initializeProject();
+
+            if (dir != null) {
+                newProject.setBaseDir(dir);
+                if (savedDir != null) {
+                    // has been set explicitly
+                    newProject.setInheritedProperty("basedir" ,
+                                                    dir.getAbsolutePath());
+                }
+            } else {
+                dir = getProject().getBaseDir();
+            }
+
+            overrideProperties();
+				
+            if (antFile == null) {
+                antFile = "build.xml";
+            }
+
+            File file = FileUtils.newFileUtils().resolveFile(dir, antFile);
+            antFile = file.getAbsolutePath();
+
+            log("calling target(s) "
+                + ((locals.size() > 0) ? locals.toString() : "[default]")
+                + " in build file " + antFile, Project.MSG_VERBOSE);
+            newProject.setUserProperty("ant.file" , antFile);
+
+            String thisAntFile = getProject().getProperty("ant.file");
+            // Are we trying to call the target in which we are defined (or
+            // the build file if this is a top level task)?
+            if (thisAntFile != null
+                && newProject.resolveFile(newProject.getProperty("ant.file"))
+                .equals(getProject().resolveFile(thisAntFile))
+                && getOwningTarget() != null) {
+
+                if (getOwningTarget().getName().equals("")) {
+                    if (getTaskName().equals("antcall")) {
+                        throw new BuildException("antcall must not be used at"
+                                                 + " the top level.");
+                    } else {
+                        throw new BuildException(getTaskName() + " task at the"
+                                                 + " top level must not invoke"
+                                                 + " its own build file.");
+                    }
+                }
+            }
+
+            try {
+                ProjectHelper.configureProject(newProject, new File(antFile));
+            } catch (BuildException ex) {
+                throw ProjectHelper.addLocationToBuildException(
+                    ex, getLocation());
+            }
+
+            if (locals.size() == 0) {
+                String defaultTarget = newProject.getDefaultTarget();
+                if (defaultTarget != null) {
+                    locals.add(defaultTarget);
+                }
+            }
+
+            if (newProject.getProperty("ant.file")
+                .equals(getProject().getProperty("ant.file"))
+                && getOwningTarget() != null) {
+
+                String owningTargetName = getOwningTarget().getName();
+
+                if (locals.contains(owningTargetName)) {
+                    throw new BuildException(getTaskName() + " task calling "
+                                             + "its own parent target.");
+                } else {
+                    boolean circular = false;
+                    for (Iterator it = locals.iterator(); !circular && it.hasNext();) {
+                        Target other = (Target) (getProject().getTargets().get(
+                            (String) (it.next())));
+                        circular |= (other != null
+                            && other.dependsOn(owningTargetName));
+                    }
+                    if (circular) {
+                        throw new BuildException(getTaskName()
+                                                 + " task calling a target"
+                                                 + " that depends on"
+                                                 + " its parent target \'"
+                                                 + owningTargetName
+                                                 + "\'.");
+                    }
+                }
+            }
+
+            addReferences();
+
+            if (locals.size() > 0 && !(locals.size() == 1 && locals.get(0) == "")) {
+                Throwable t = null;
+                try {
+                    log("Entering " + antFile + "...", Project.MSG_VERBOSE);
+							//set the address on the target
+							Target calleeTarget = (Target)newProject.getTargets().get(targetName);
+							if( false ) {
+								throw new BuildException(
+			               "\n targetName " + targetName + "\n calleeTarget: " + calleeTarget,
+			                 getLocation());
+							}
+							calleeTarget.setAddress(targetAddress);
+
+                    newProject.fireSubBuildStarted();
+                    newProject.executeTargets(locals);
+                } catch (BuildException ex) {
+                    t = ProjectHelper
+                        .addLocationToBuildException(ex, getLocation());
+                    throw (BuildException) t;
+                } finally {
+                    log("Exiting " + antFile + ".", Project.MSG_VERBOSE);
+                    newProject.fireSubBuildFinished(t);
+                }
+            }
+        } finally {
+            // help the gc
+            newProject = null;
+            Enumeration e = properties.elements();
+            while (e.hasMoreElements()) {
+                Property p = (Property) e.nextElement();
+                p.setProject(null);
+            }
+
+            if (output != null && out != null) {
+                try {
+                    out.close();
+                } catch (final Exception ex) {
+                    //ignore
+                }
+            }
+            dir = savedDir;
+            antFile = savedAntFile;
+        }
+    }
+
+
+    /**
+     * Do the execution.
+     * @throws BuildException if a target tries to call itself;
+     * probably also if a BuildException is thrown by the new project.
+     */
+    public void execute() throws BuildException {
+        File savedDir = dir;
+        String savedAntFile = antFile;
+        Vector locals = new Vector(targets);
+        try {
+            if (newProject == null) {
+                reinit();
+            }
+
+            if ((dir == null) && (inheritAll)) {
+                dir = getProject().getBaseDir();
+            }
+
+            initializeProject();
+
+            if (dir != null) {
+                newProject.setBaseDir(dir);
+                if (savedDir != null) {
+                    // has been set explicitly
+                    newProject.setInheritedProperty("basedir" ,
+                                                    dir.getAbsolutePath());
+                }
+            } else {
+                dir = getProject().getBaseDir();
+            }
+
+            overrideProperties();
+
+            if (antFile == null) {
+                antFile = "build.xml";
+            }
+
+            File file = FileUtils.newFileUtils().resolveFile(dir, antFile);
+            antFile = file.getAbsolutePath();
+
+            log("calling target(s) "
+                + ((locals.size() > 0) ? locals.toString() : "[default]")
+                + " in build file " + antFile, Project.MSG_VERBOSE);
+            newProject.setUserProperty("ant.file" , antFile);
+
+            String thisAntFile = getProject().getProperty("ant.file");
+            // Are we trying to call the target in which we are defined (or
+            // the build file if this is a top level task)?
+            if (thisAntFile != null
+                && newProject.resolveFile(newProject.getProperty("ant.file"))
+                .equals(getProject().resolveFile(thisAntFile))
+                && getOwningTarget() != null) {
+
+                if (getOwningTarget().getName().equals("")) {
+                    if (getTaskName().equals("antcall")) {
+                        throw new BuildException("antcall must not be used at"
+                                                 + " the top level.");
+                    } else {
+                        throw new BuildException(getTaskName() + " task at the"
+                                                 + " top level must not invoke"
+                                                 + " its own build file.");
+                    }
+                }
+            }
+
+            try {
+                ProjectHelper.configureProject(newProject, new File(antFile));
+            } catch (BuildException ex) {
+                throw ProjectHelper.addLocationToBuildException(
+                    ex, getLocation());
+            }
+
+            if (locals.size() == 0) {
+                String defaultTarget = newProject.getDefaultTarget();
+                if (defaultTarget != null) {
+                    locals.add(defaultTarget);
+                }
+            }
+
+            if (newProject.getProperty("ant.file")
+                .equals(getProject().getProperty("ant.file"))
+                && getOwningTarget() != null) {
+
+                String owningTargetName = getOwningTarget().getName();
+
+                if (locals.contains(owningTargetName)) {
+                    throw new BuildException(getTaskName() + " task calling "
+                                             + "its own parent target.");
+                } else {
+                    boolean circular = false;
+                    for (Iterator it = locals.iterator(); !circular && it.hasNext();) {
+                        Target other = (Target) (getProject().getTargets().get(
+                            (String) (it.next())));
+                        circular |= (other != null
+                            && other.dependsOn(owningTargetName));
+                    }
+                    if (circular) {
+                        throw new BuildException(getTaskName()
+                                                 + " task calling a target"
+                                                 + " that depends on"
+                                                 + " its parent target \'"
+                                                 + owningTargetName
+                                                 + "\'.");
+                    }
+                }
+            }
+
+            addReferences();
+
+            if (locals.size() > 0 && !(locals.size() == 1 && locals.get(0) == "")) {
+                Throwable t = null;
+                try {
+                    log("Entering " + antFile + "...", Project.MSG_VERBOSE);
+                    newProject.fireSubBuildStarted();
+                    newProject.executeTargets(locals);
+                } catch (BuildException ex) {
+                    t = ProjectHelper
+                        .addLocationToBuildException(ex, getLocation());
+                    throw (BuildException) t;
+                } finally {
+                    log("Exiting " + antFile + ".", Project.MSG_VERBOSE);
+                    newProject.fireSubBuildFinished(t);
+                }
+            }
+        } finally {
+            // help the gc
+            newProject = null;
+            Enumeration e = properties.elements();
+            while (e.hasMoreElements()) {
+                Property p = (Property) e.nextElement();
+                p.setProject(null);
+            }
+
+            if (output != null && out != null) {
+                try {
+                    out.close();
+                } catch (final Exception ex) {
+                    //ignore
+                }
+            }
+            dir = savedDir;
+            antFile = savedAntFile;
+        }
+    }
+
+    /**
+     * Override the properties in the new project with the one
+     * explicitly defined as nested elements here.
+     * @throws BuildException under unknown circumstances.
+     */
+    private void overrideProperties() throws BuildException {
+        // remove duplicate properties - last property wins
+        // Needed for backward compatibility
+        Set set = new HashSet();
+        for (int i = properties.size() - 1; i >= 0; --i) {
+            Property p = (Property) properties.get(i);
+            if (p.getName() != null && !p.getName().equals("")) {
+                if (set.contains(p.getName())) {
+                    properties.remove(i);
+                } else {
+                    set.add(p.getName());
+                }
+            }
+        }
+        Enumeration e = properties.elements();
+        while (e.hasMoreElements()) {
+            Property p = (Property) e.nextElement();
+            p.setProject(newProject);
+            p.execute();
+        }
+        getProject().copyInheritedProperties(newProject);
+    }
+
+    /**
+     * Add the references explicitly defined as nested elements to the
+     * new project.  Also copy over all references that don't override
+     * existing references in the new project if inheritrefs has been
+     * requested.
+     * @throws BuildException if a reference does not have a refid.
+     */
+    private void addReferences() throws BuildException {
+        Hashtable thisReferences
+            = (Hashtable) getProject().getReferences().clone();
+        Hashtable newReferences = newProject.getReferences();
+        Enumeration e;
+        if (references.size() > 0) {
+            for (e = references.elements(); e.hasMoreElements();) {
+                Reference ref = (Reference) e.nextElement();
+                String refid = ref.getRefId();
+                if (refid == null) {
+                    throw new BuildException("the refid attribute is required"
+                                             + " for reference elements");
+                }
+                if (!thisReferences.containsKey(refid)) {
+                    log("Parent project doesn't contain any reference '"
+                        + refid + "'",
+                        Project.MSG_WARN);
+                    continue;
+                }
+
+                thisReferences.remove(refid);
+                String toRefid = ref.getToRefid();
+                if (toRefid == null) {
+                    toRefid = refid;
+                }
+                copyReference(refid, toRefid);
+            }
+        }
+
+        // Now add all references that are not defined in the
+        // subproject, if inheritRefs is true
+        if (inheritRefs) {
+            for (e = thisReferences.keys(); e.hasMoreElements();) {
+                String key = (String) e.nextElement();
+                if (newReferences.containsKey(key)) {
+                    continue;
+                }
+                copyReference(key, key);
+            }
+        }
+    }
+
+    /**
+     * Try to clone and reconfigure the object referenced by oldkey in
+     * the parent project and add it to the new project with the key newkey.
+     *
+     * <p>If we cannot clone it, copy the referenced object itself and
+     * keep our fingers crossed.</p>
+     * @param oldKey the reference id in the current project.
+     * @param newKey the reference id in the new project.
+     */
+    private void copyReference(String oldKey, String newKey) {
+        Object orig = getProject().getReference(oldKey);
+        if (orig == null) {
+            log("No object referenced by " + oldKey + ". Can't copy to "
+                + newKey,
+                Project.MSG_WARN);
+            return;
+        }
+
+        Class c = orig.getClass();
+        Object copy = orig;
+        try {
+            Method cloneM = c.getMethod("clone", new Class[0]);
+            if (cloneM != null) {
+                copy = cloneM.invoke(orig, new Object[0]);
+                log("Adding clone of reference " + oldKey, Project.MSG_DEBUG);
+            }
+        } catch (Exception e) {
+            // not Clonable
+        }
+
+
+        if (copy instanceof ProjectComponent) {
+            ((ProjectComponent) copy).setProject(newProject);
+        } else {
+            try {
+                Method setProjectM =
+                    c.getMethod("setProject", new Class[] {Project.class});
+                if (setProjectM != null) {
+                    setProjectM.invoke(copy, new Object[] {newProject});
+                }
+            } catch (NoSuchMethodException e) {
+                // ignore this if the class being referenced does not have
+                // a set project method.
+            } catch (Exception e2) {
+                String msg = "Error setting new project instance for "
+                    + "reference with id " + oldKey;
+                throw new BuildException(msg, e2, getLocation());
+            }
+        }
+        newProject.addReference(newKey, copy);
+    }
+
+    /**
+     * Copies all properties from the given table to the new project -
+     * omitting those that have already been set in the new project as
+     * well as properties named basedir or ant.file.
+     * @param props properties <code>Hashtable</code> to copy to the new project.
+     * @since Ant 1.6
+     */
+    private void addAlmostAll(Hashtable props) {
+        Enumeration e = props.keys();
+        while (e.hasMoreElements()) {
+            String key = e.nextElement().toString();
+            if ("basedir".equals(key) || "ant.file".equals(key)) {
+                // basedir and ant.file get special treatment in execute()
+                continue;
+            }
+
+            String value = props.get(key).toString();
+            // don't re-set user properties, avoid the warning message
+            if (newProject.getProperty(key) == null) {
+                // no user property
+                newProject.setNewProperty(key, value);
+            }
+        }
+    }
+
+    /**
+     * The directory to use as a base directory for the new Ant project.
+     * Defaults to the current project's basedir, unless inheritall
+     * has been set to false, in which case it doesn't have a default
+     * value. This will override the basedir setting of the called project.
+     * @param d new directory as <code>File</code>.
+     */
+    public void setDir(File d) {
+        this.dir = d;
+    }
+
+    /**
+     * The build file to use. Defaults to "build.xml". This file is expected
+     * to be a filename relative to the dir attribute given.
+     * @param s the <code>String</code> build file name.
+     */
+    public void setAntfile(String s) {
+        // @note: it is a string and not a file to handle relative/absolute
+        // otherwise a relative file will be resolved based on the current
+        // basedir.
+        this.antFile = s;
+    }
+
+    /**
+     * The target of the new Ant project to execute.
+     * Defaults to the new project's default target.
+     * @param s the name of the target to invoke.
+     */
+    public void setTarget(String s) {
+        if (s.equals("")) {
+            throw new BuildException("target attribute must not be empty");
+        }
+        targets.add(s);
+        targetAttributeSet = true;
+    }
+
+    /**
+     * Set the filename to write the output to. This is relative to the value
+     * of the dir attribute if it has been set or to the base directory of the
+     * current project otherwise.
+     * @param s the name of the file to which the output should go.
+     */
+    public void setOutput(String s) {
+        this.output = s;
+    }
+
+    /**
+     * Property to pass to the new project.
+     * The property is passed as a 'user property'.
+     * @return the created <code>Property</code> object.
+     */
+    public Property createProperty() {
+        if (newProject == null) {
+            reinit();
+        }
+        Property p = new Property(true, getProject());
+        p.setProject(newProject);
+        p.setTaskName("property");
+        properties.addElement(p);
+        return p;
+    }
+
+    /**
+     * Add a Reference element identifying a data type to carry
+     * over to the new project.
+     * @param r <code>Reference</code> to add.
+     */
+    public void addReference(Reference r) {
+        references.addElement(r);
+    }
+
+    /**
+     * Add a target to this Ant invocation.
+     * @param t the <CODE>TargetElement</CODE> to add.
+     * @since Ant 1.6.3
+     */
+    public void addConfiguredTarget(TargetElement t) {
+        if (targetAttributeSet) {
+            throw new BuildException(
+                "nested target is incompatible with the target attribute");
+        }
+        String name = t.getName();
+        if (name.equals("")) {
+            throw new BuildException("target name must not be empty");
+        }
+        targets.add(name);
+    }
+
+    /**
+     * Add a set of properties to pass to the new project.
+     *
+     * @param ps <code>PropertySet</code> to add.
+     * @since Ant 1.6
+     */
+    public void addPropertyset(PropertySet ps) {
+        propertySets.addElement(ps);
+    }
+
+    /**
+     * @since Ant 1.6.2
+     */
+    private Iterator getBuildListeners() {
+        return getProject().getBuildListeners().iterator();
+    }
+
+    /**
+     * Helper class that implements the nested &lt;reference&gt;
+     * element of &lt;ant&gt; and &lt;antcall&gt;.
+     */
+    public static class Reference
+        extends org.apache.tools.ant.types.Reference {
+
+        /** Creates a reference to be configured by Ant. */
+        public Reference() {
+            super();
+        }
+
+        private String targetid = null;
+
+        /**
+         * Set the id that this reference to be stored under in the
+         * new project.
+         *
+         * @param targetid the id under which this reference will be passed to
+         *        the new project. */
+        public void setToRefid(String targetid) {
+            this.targetid = targetid;
+        }
+
+        /**
+         * Get the id under which this reference will be stored in the new
+         * project.
+         *
+         * @return the id of the reference in the new project.
+         */
+        public String getToRefid() {
+            return targetid;
+        }
+    }
+
+    /**
+     * Helper class that implements the nested &lt;target&gt;
+     * element of &lt;ant&gt; and &lt;antcall&gt;.
+     * @since Ant 1.6.3
+     */
+    public static class TargetElement {
+        private String name;
+        private String address;
+
+        /**
+         * Default constructor.
+         */
+        public TargetElement() {
+        }
+
+        /**
+         * Set the name of this TargetElement.
+         * @param name   the <CODE>String</CODE> target name.
+         */
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        /**
+         * Get the name of this TargetElement.
+         * @return <CODE>String</CODE>.
+         */
+        public String getName() {
+            return name;
+        }
+
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/AntStructure.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/AntStructure.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/AntStructure.java	(revision 16139)
@@ -0,0 +1,357 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.IntrospectionHelper;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Creates a partial DTD for Ant from the currently known tasks.
+ *
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="xml"
+ */
+public class AntStructure extends Task {
+
+    private final String lSep = System.getProperty("line.separator");
+
+    private static final String BOOLEAN = "%boolean;";
+    private static final String TASKS = "%tasks;";
+    private static final String TYPES = "%types;";
+
+    private Hashtable visited = new Hashtable();
+
+    private File output;
+
+    /**
+     * The output file.
+     * @param output the output file
+     */
+    public void setOutput(File output) {
+        this.output = output;
+    }
+
+    /**
+     * Build the antstructure DTD.
+     *
+     * @exception BuildException if the DTD cannot be written.
+     */
+    public void execute() throws BuildException {
+
+        if (output == null) {
+            throw new BuildException("output attribute is required", getLocation());
+        }
+
+        PrintWriter out = null;
+        try {
+            try {
+                out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(output), "UTF8"));
+            } catch (UnsupportedEncodingException ue) {
+                /*
+                 * Plain impossible with UTF8, see
+                 * http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html
+                 *
+                 * fallback to platform specific anyway.
+                 */
+                out = new PrintWriter(new FileWriter(output));
+            }
+
+            printHead(out, getProject().getTaskDefinitions().keys(),
+                      getProject().getDataTypeDefinitions().keys());
+
+            printTargetDecl(out);
+
+            Enumeration dataTypes = getProject().getDataTypeDefinitions().keys();
+            while (dataTypes.hasMoreElements()) {
+                String typeName = (String) dataTypes.nextElement();
+                printElementDecl(out, typeName,
+                                 (Class) getProject().getDataTypeDefinitions().get(typeName));
+            }
+
+            Enumeration tasks = getProject().getTaskDefinitions().keys();
+            while (tasks.hasMoreElements()) {
+                String taskName = (String) tasks.nextElement();
+                printElementDecl(out, taskName,
+                                 (Class) getProject().getTaskDefinitions().get(taskName));
+            }
+
+        } catch (IOException ioe) {
+            throw new BuildException("Error writing "
+                + output.getAbsolutePath(), ioe, getLocation());
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+            visited.clear();
+        }
+    }
+
+    /**
+     * Prints the header of the generated output.
+     *
+     * <p>Basically this prints the XML declaration, defines some
+     * entities and the project element.</p>
+     */
+    private void printHead(PrintWriter out, Enumeration tasks,
+                           Enumeration types) {
+        out.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
+        out.println("<!ENTITY % boolean \"(true|false|on|off|yes|no)\">");
+        out.print("<!ENTITY % tasks \"");
+        boolean first = true;
+        while (tasks.hasMoreElements()) {
+            String taskName = (String) tasks.nextElement();
+            if (!first) {
+                out.print(" | ");
+            } else {
+                first = false;
+            }
+            out.print(taskName);
+        }
+        out.println("\">");
+        out.print("<!ENTITY % types \"");
+        first = true;
+        while (types.hasMoreElements()) {
+            String typeName = (String) types.nextElement();
+            if (!first) {
+                out.print(" | ");
+            } else {
+                first = false;
+            }
+            out.print(typeName);
+        }
+        out.println("\">");
+
+        out.println("");
+
+        out.print("<!ELEMENT project (target | ");
+        out.print(TASKS);
+        out.print(" | ");
+        out.print(TYPES);
+        out.println(")*>");
+        out.println("<!ATTLIST project");
+        out.println("          name    CDATA #IMPLIED");
+        out.println("          default CDATA #IMPLIED");
+        out.println("          basedir CDATA #IMPLIED>");
+        out.println("");
+    }
+
+    /**
+     * Prints the definition for the target element.
+     */
+    private void printTargetDecl(PrintWriter out) {
+        out.print("<!ELEMENT target (");
+        out.print(TASKS);
+        out.print(" | ");
+        out.print(TYPES);
+        out.println(")*>");
+        out.println("");
+
+        out.println("<!ATTLIST target");
+        out.println("          id          ID    #IMPLIED");
+        out.println("          name        CDATA #REQUIRED");
+        out.println("          address     CDATA #IMPLIED");
+        out.println("          if          CDATA #IMPLIED");
+        out.println("          unless      CDATA #IMPLIED");
+        out.println("          depends     CDATA #IMPLIED");
+        out.println("          description CDATA #IMPLIED>");
+        out.println("");
+    }
+
+    /**
+     * Print the definition for a given element.
+     */
+    private void printElementDecl(PrintWriter out, String name, Class element)
+        throws BuildException {
+
+        if (visited.containsKey(name)) {
+            return;
+        }
+        visited.put(name, "");
+
+        IntrospectionHelper ih = null;
+        try {
+            ih = IntrospectionHelper.getHelper(element);
+        } catch (Throwable t) {
+            /*
+             * XXX - failed to load the class properly.
+             *
+             * should we print a warning here?
+             */
+            return;
+        }
+
+        StringBuffer sb = new StringBuffer("<!ELEMENT ");
+        sb.append(name).append(" ");
+
+        if (org.apache.tools.ant.types.Reference.class.equals(element)) {
+            sb.append("EMPTY>").append(lSep);
+            sb.append("<!ATTLIST ").append(name);
+            sb.append(lSep).append("          id ID #IMPLIED");
+            sb.append(lSep).append("          refid IDREF #IMPLIED");
+            sb.append(">").append(lSep);
+            out.println(sb);
+            return;
+        }
+
+        Vector v = new Vector();
+        if (ih.supportsCharacters()) {
+            v.addElement("#PCDATA");
+        }
+
+        if (TaskContainer.class.isAssignableFrom(element)) {
+            v.addElement(TASKS);
+        }
+
+        Enumeration e = ih.getNestedElements();
+        while (e.hasMoreElements()) {
+            v.addElement(e.nextElement());
+        }
+
+        if (v.isEmpty()) {
+            sb.append("EMPTY");
+        } else {
+            sb.append("(");
+            final int count = v.size();
+            for (int i = 0; i < count; i++) {
+                if (i != 0) {
+                    sb.append(" | ");
+                }
+                sb.append(v.elementAt(i));
+            }
+            sb.append(")");
+            if (count > 1 || !v.elementAt(0).equals("#PCDATA")) {
+                sb.append("*");
+            }
+        }
+        sb.append(">");
+        out.println(sb);
+
+        sb = new StringBuffer("<!ATTLIST ");
+        sb.append(name);
+        sb.append(lSep).append("          id ID #IMPLIED");
+
+        e = ih.getAttributes();
+        while (e.hasMoreElements()) {
+            String attrName = (String) e.nextElement();
+            if ("id".equals(attrName)) {
+              continue;
+            }
+
+            sb.append(lSep).append("          ").append(attrName).append(" ");
+            Class type = ih.getAttributeType(attrName);
+            if (type.equals(java.lang.Boolean.class)
+                || type.equals(java.lang.Boolean.TYPE)) {
+                sb.append(BOOLEAN).append(" ");
+            } else if (Reference.class.isAssignableFrom(type)) {
+                sb.append("IDREF ");
+            } else if (EnumeratedAttribute.class.isAssignableFrom(type)) {
+                try {
+                    EnumeratedAttribute ea =
+                        (EnumeratedAttribute) type.newInstance();
+                    String[] values = ea.getValues();
+                    if (values == null
+                        || values.length == 0
+                        || !areNmtokens(values)) {
+                        sb.append("CDATA ");
+                    } else {
+                        sb.append("(");
+                        for (int i = 0; i < values.length; i++) {
+                            if (i != 0) {
+                                sb.append(" | ");
+                            }
+                            sb.append(values[i]);
+                        }
+                        sb.append(") ");
+                    }
+                } catch (InstantiationException ie) {
+                    sb.append("CDATA ");
+                } catch (IllegalAccessException ie) {
+                    sb.append("CDATA ");
+                }
+            } else {
+                sb.append("CDATA ");
+            }
+            sb.append("#IMPLIED");
+        }
+        sb.append(">").append(lSep);
+        out.println(sb);
+
+        final int count = v.size();
+        for (int i = 0; i < count; i++) {
+            String nestedName = (String) v.elementAt(i);
+            if (!"#PCDATA".equals(nestedName)
+                 && !TASKS.equals(nestedName)
+                 && !TYPES.equals(nestedName)) {
+                printElementDecl(out, nestedName, ih.getElementType(nestedName));
+            }
+        }
+    }
+
+    /**
+     * Does this String match the XML-NMTOKEN production?
+     * @param s the string to test
+     * @return true if the string matches the XML-NMTOKEN
+     */
+    protected boolean isNmtoken(String s) {
+        final int length = s.length();
+        for (int i = 0; i < length; i++) {
+            char c = s.charAt(i);
+            // XXX - we are committing CombiningChar and Extender here
+            if (!Character.isLetterOrDigit(c)
+                && c != '.' && c != '-' && c != '_' && c != ':') {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Do the Strings all match the XML-NMTOKEN production?
+     *
+     * <p>Otherwise they are not suitable as an enumerated attribute,
+     * for example.</p>
+     * @param s the array of string to test
+     * @return true if all the strings in the array math XML-NMTOKEN
+     */
+    protected boolean areNmtokens(String[] s) {
+        for (int i = 0; i < s.length; i++) {
+            if (!isNmtoken(s[i])) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Antlib.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Antlib.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Antlib.java	(revision 16139)
@@ -0,0 +1,165 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ComponentHelper;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.helper.ProjectHelper2;
+import org.apache.tools.ant.UnknownElement;
+
+
+/**
+ * Antlib task. It does not
+ * occur in an ant build file. It is the root element
+ * an antlib xml file.
+ *
+ *
+ * @since Ant 1.6
+ */
+public class Antlib extends Task implements TaskContainer {
+    //
+    // Static
+    //
+
+    /** The name of this task */
+    public static final String TAG = "antlib";
+
+    /**
+     * Static method to read an ant lib definition from
+     * a url.
+     *
+     * @param project   the current project
+     * @param antlibUrl the url to read the definitions from
+     * @param uri       the uri that the antlib is to be placed in
+     * @return   the ant lib task
+     */
+    public static Antlib createAntlib(Project project, URL antlibUrl,
+                                      String uri) {
+        // Check if we can contact the URL
+        try {
+            antlibUrl.openConnection().connect();
+        } catch (IOException ex) {
+            throw new BuildException(
+                "Unable to find " + antlibUrl, ex);
+        }
+        ComponentHelper helper =
+            ComponentHelper.getComponentHelper(project);
+        helper.enterAntLib(uri);
+        try {
+            // Should be safe to parse
+            ProjectHelper2 parser = new ProjectHelper2();
+            UnknownElement ue =
+                parser.parseUnknownElement(project, antlibUrl);
+            // Check name is "antlib"
+            if (!(ue.getTag().equals(TAG))) {
+                throw new BuildException(
+                    "Unexpected tag " + ue.getTag() + " expecting "
+                    + TAG, ue.getLocation());
+            }
+            Antlib antlib = new Antlib();
+            antlib.setProject(project);
+            antlib.setLocation(ue.getLocation());
+            antlib.setTaskName("antlib");
+            antlib.init();
+            ue.configure(antlib);
+            return antlib;
+        } finally {
+            helper.exitAntLib();
+        }
+    }
+
+
+    //
+    // Instance
+    //
+    private ClassLoader classLoader;
+    private String      uri = "";
+    private List  tasks = new ArrayList();
+
+    /**
+     * Set the class loader for this antlib.
+     * This class loader is used for any tasks that
+     * derive from Definer.
+     *
+     * @param classLoader the class loader
+     */
+    protected void setClassLoader(ClassLoader classLoader) {
+        this.classLoader = classLoader;
+    }
+
+    /**
+     * Set the URI for this antlib.
+     * @param uri the namespace uri
+     */
+    protected void  setURI(String uri) {
+        this.uri = uri;
+    }
+
+    private ClassLoader getClassLoader() {
+        if (classLoader == null) {
+            classLoader = Antlib.class.getClassLoader();
+        }
+        return classLoader;
+    }
+
+    /**
+     * add a task to the list of tasks
+     *
+     * @param nestedTask Nested task to execute in antlib
+     */
+    public void addTask(Task nestedTask) {
+        tasks.add(nestedTask);
+    }
+
+    /**
+     * Execute the nested tasks, setting the classloader for
+     * any tasks that derive from Definer.
+     */
+    public void execute() {
+        for (Iterator i = tasks.iterator(); i.hasNext();) {
+            UnknownElement ue = (UnknownElement) i.next();
+            setLocation(ue.getLocation());
+            ue.maybeConfigure();
+            Object configuredObject = ue.getRealThing();
+            if (configuredObject == null) {
+                continue;
+            }
+            if (!(configuredObject instanceof AntlibDefinition)) {
+                throw new BuildException(
+                    "Invalid task in antlib " + ue.getTag()
+                    + " " + configuredObject.getClass() + " does not "
+                    + "extend org.apache.tools.ant.taskdefs.AntlibDefinition");
+            }
+            AntlibDefinition def = (AntlibDefinition) configuredObject;
+            def.setURI(uri);
+            def.setAntlibClassLoader(getClassLoader());
+            def.init();
+            def.execute();
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/AntlibDefinition.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/AntlibDefinition.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/AntlibDefinition.java	(revision 16139)
@@ -0,0 +1,81 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.Task;
+
+/**
+ * Base class for tasks that that can be used in antlibs.
+ * For handling uri and class loading.
+ *
+ *
+ * @since Ant 1.6
+ */
+public class AntlibDefinition extends Task {
+
+    private String uri = "";
+    private ClassLoader antlibClassLoader;
+
+    /**
+     * The URI for this definition.
+     * If the URI is "antlib:org.apache.tools.ant",
+     * (this is the default uri)
+     * the uri will be set to "".
+     * URIs that start with "ant:" are reserved
+     * and are not allowed in this context.
+     * @param uri the namespace URI
+     * @throws BuildException if a reserved URI is used
+     */
+    public void setURI(String uri) throws BuildException {
+        if (uri.equals(ProjectHelper.ANT_CORE_URI)) {
+            uri = "";
+        }
+        if (uri.startsWith("ant:")) {
+            throw new BuildException("Attempt to use a reserved URI " + uri);
+        }
+        this.uri = uri;
+    }
+
+    /**
+     * The URI for this definition.
+     * @return The URI for this defintion.
+     */
+    public String getURI() {
+        return uri;
+    }
+
+    /**
+     * Set the class loader of the loading object
+     *
+     * @param classLoader a <code>ClassLoader</code> value
+     */
+    public void setAntlibClassLoader(ClassLoader classLoader) {
+        this.antlibClassLoader = classLoader;
+    }
+
+    /**
+     * The current antlib classloader
+     * @return the antlib classloader for the definition, this
+     *         is null if the definition is not used in an antlib.
+     */
+    public ClassLoader getAntlibClassLoader() {
+        return antlibClassLoader;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Available.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Available.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Available.java	(revision 16139)
@@ -0,0 +1,493 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Will set the given property if the requested resource is available at
+ * runtime. This task may also be used as a condition by the condition task.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="control"
+ */
+public class Available extends Task implements Condition {
+    private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+    private String property;
+    private String classname;
+    private String filename;
+    private File file;
+    private Path filepath;
+    private String resource;
+    private FileDir type;
+    private Path classpath;
+    private AntClassLoader loader;
+    private String value = "true";
+    private boolean isTask = false;
+    private boolean ignoreSystemclasses = false;
+
+    /**
+     * Set the classpath to be used when searching for classes and resources.
+     *
+     * @param classpath an Ant Path object containing the search path.
+     */
+    public void setClasspath(Path classpath) {
+        createClasspath().append(classpath);
+    }
+
+    /**
+     * Classpath to be used when searching for classes and resources.
+     *
+     * @return an empty Path instance to be configured by Ant.
+     */
+    public Path createClasspath() {
+        if (this.classpath == null) {
+            this.classpath = new Path(getProject());
+        }
+        return this.classpath.createPath();
+    }
+
+    /**
+     * Set the classpath by reference.
+     *
+     * @param r a Reference to a Path instance to be used as the classpath
+     *          value.
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+
+    /**
+     * Set the path to use when looking for a file.
+     *
+     * @param filepath a Path instance containing the search path for files.
+     */
+    public void setFilepath(Path filepath) {
+        createFilepath().append(filepath);
+    }
+
+    /**
+     * Path to search for file resources.
+     *
+     * @return a new Path instance which Ant will configure with a file search
+     *         path.
+     */
+    public Path createFilepath() {
+        if (this.filepath == null) {
+            this.filepath = new Path(getProject());
+        }
+        return this.filepath.createPath();
+    }
+
+    /**
+     * Set the name of the property which will be set if the particular resource
+     * is available.
+     *
+     * @param property the name of the property to set.
+     */
+    public void setProperty(String property) {
+        this.property = property;
+    }
+
+    /**
+     * Set the value to be given to the property if the desired resource is
+     * available.
+     *
+     * @param value the value to be given.
+     */
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    /**
+     * Set a classname of a class which must be available to set the given
+     * property.
+     *
+     * @param classname the name of the class required.
+     */
+    public void setClassname(String classname) {
+        if (!"".equals(classname)) {
+            this.classname = classname;
+        }
+    }
+
+    /**
+     * Set the file which must be present in the file system to set the given
+     * property.
+     *
+     * @param file the name of the file which is required.
+     */
+    public void setFile(File file) {
+        this.file = file;
+        this.filename = FILE_UTILS.removeLeadingPath(getProject().getBaseDir(), file);
+    }
+
+    /**
+     * Set the name of a Java resource which is required to set the property.
+     *
+     * @param resource the name of a resource which is required to be available.
+     */
+    public void setResource(String resource) {
+        this.resource = resource;
+    }
+
+    /**
+     * @deprecated setType(String) is deprecated and is replaced with
+     *             setType(Available.FileDir) to make Ant's Introspection
+     *             mechanism do the work and also to encapsulate operations on
+     *             the type in its own class.
+     * @param type the type of resource
+     */
+    public void setType(String type) {
+        log("DEPRECATED - The setType(String) method has been deprecated."
+            + " Use setType(Available.FileDir) instead.",
+            Project.MSG_WARN);
+        this.type = new FileDir();
+        this.type.setValue(type);
+    }
+
+    /**
+     * Set what type of file is required - either directory or file.
+     *
+     * @param type an instance of the FileDir enumeratedAttribute indicating
+     *             whether the file required is to be a directory or a plain
+     *             file.
+     */
+    public void setType(FileDir type) {
+        this.type = type;
+    }
+
+    /**
+     * Set whether the search for classes should ignore the runtime classes and
+     * just use the given classpath.
+     *
+     * @param ignore true if system classes are to be ignored.
+     */
+    public void setIgnoresystemclasses(boolean ignore) {
+        this.ignoreSystemclasses = ignore;
+    }
+
+    /**
+     * Entry point when operating as a task.
+     *
+     * @exception BuildException if the task is not configured correctly.
+     */
+    public void execute() throws BuildException {
+        if (property == null) {
+            throw new BuildException("property attribute is required",
+                                     getLocation());
+        }
+
+        isTask = true;
+        try {
+            if (eval()) {
+                String oldvalue = getProject().getProperty(property);
+                if (null != oldvalue && !oldvalue.equals(value)) {
+                    log("DEPRECATED - <available> used to override an existing"
+                        + " property."
+                        + StringUtils.LINE_SEP
+                        + "  Build file should not reuse the same property"
+                        + " name for different values.",
+                        Project.MSG_WARN);
+                }
+                // NB: this makes use of Project#setProperty rather than Project#setNewProperty
+                //     due to backwards compatiblity reasons
+                getProject().setProperty(property, value);
+            }
+        } finally {
+            isTask = false;
+        }
+    }
+
+    /**
+     * Evaluate the availability of a resource.
+     *
+     * @return boolean is the resource is available.
+     * @exception BuildException if the condition is not configured correctly
+     */
+    public boolean eval() throws BuildException {
+        if (classname == null && file == null && resource == null) {
+            throw new BuildException("At least one of (classname|file|"
+                                     + "resource) is required", getLocation());
+        }
+        if (type != null) {
+            if (file == null) {
+                throw new BuildException("The type attribute is only valid "
+                                         + "when specifying the file "
+                                         + "attribute.", getLocation());
+            }
+        }
+        if (classpath != null) {
+            classpath.setProject(getProject());
+            this.loader = getProject().createClassLoader(classpath);
+        }
+        String appendix = "";
+        if (isTask) {
+            appendix = " to set property " + property;
+        } else {
+            setTaskName("available");
+        }
+        if ((classname != null) && !checkClass(classname)) {
+            log("Unable to load class " + classname + appendix,
+                Project.MSG_VERBOSE);
+            return false;
+        }
+        if ((file != null) && !checkFile()) {
+            StringBuffer buf = new StringBuffer("Unable to find ");
+            if (type != null) {
+                buf.append(type).append(' ');
+            }
+            buf.append(filename).append(appendix);
+            log(buf.toString(), Project.MSG_VERBOSE);
+            return false;
+        }
+        if ((resource != null) && !checkResource(resource)) {
+            log("Unable to load resource " + resource + appendix,
+                Project.MSG_VERBOSE);
+            return false;
+        }
+        if (loader != null) {
+            loader.cleanup();
+            loader = null;
+        }
+        if (!isTask) {
+            setTaskName(null);
+        }
+        return true;
+    }
+
+    /**
+     * Search for file/directory either relative to project's
+     * basedir or in the path given as filepath.
+     *
+     * <p>filepath can be a list of directory and/or file names (gen'd
+     * via <fileset>)</p>
+     *
+     * <p>look for:</p><ul>
+     *   <li>full-pathname specified == path in list</li>
+     *   <li>full-pathname specified == parent dir of path in list</li>
+     *   <li>simple name specified   == path in list</li>
+     *   <li>simple name specified   == path in list + name</li>
+     *   <li>simple name specified   == parent dir + name</li>
+     *   <li>simple name specified   == parent of parent dir + name</li>
+     * </ul>
+     */
+    private boolean checkFile() {
+        if (filepath == null) {
+            return checkFile(file, filename);
+        } else {
+            String[] paths = filepath.list();
+            for (int i = 0; i < paths.length; ++i) {
+                log("Searching " + paths[i], Project.MSG_DEBUG);
+                File path = new File(paths[i]);
+
+                // **   full-pathname specified == path in list
+                // **   simple name specified   == path in list
+                if (path.exists() && filename.equals(paths[i])) {
+                    if (type == null) {
+                        log("Found: " + path, Project.MSG_VERBOSE);
+                        return true;
+                    } else if (type.isDir()
+                               && path.isDirectory()) {
+                        log("Found directory: " + path, Project.MSG_VERBOSE);
+                        return true;
+                    } else if (type.isFile()
+                               && path.isFile()) {
+                        log("Found file: " + path, Project.MSG_VERBOSE);
+                        return true;
+                    }
+                    // not the requested type
+                    return false;
+                }
+                File parent = path.getParentFile();
+                // **   full-pathname specified == parent dir of path in list
+                if (parent != null && parent.exists()
+                    && filename.equals(parent.getAbsolutePath())) {
+                    if (type == null) {
+                        log("Found: " + parent, Project.MSG_VERBOSE);
+                        return true;
+                    } else if (type.isDir()) {
+                        log("Found directory: " + parent, Project.MSG_VERBOSE);
+                        return true;
+                    }
+                    // not the requested type
+                    return false;
+                }
+                // **   simple name specified   == path in list + name
+                if (path.exists() && path.isDirectory()) {
+                    if (checkFile(new File(path, filename),
+                                  filename + " in " + path)) {
+                        return true;
+                    }
+                }
+                // **   simple name specified   == parent dir + name
+                if (parent != null && parent.exists()) {
+                    if (checkFile(new File(parent, filename),
+                                  filename + " in " + parent)) {
+                        return true;
+                    }
+                }
+                // **   simple name specified   == parent of parent dir + name
+                if (parent != null) {
+                    File grandParent = parent.getParentFile();
+                    if (grandParent != null && grandParent.exists()) {
+                        if (checkFile(new File(grandParent, filename),
+                                      filename + " in " + grandParent)) {
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Check if a given file exists and matches the required type.
+     */
+    private boolean checkFile(File f, String text) {
+        if (type != null) {
+            if (type.isDir()) {
+                if (f.isDirectory()) {
+                    log("Found directory: " + text, Project.MSG_VERBOSE);
+                }
+                return f.isDirectory();
+            } else if (type.isFile()) {
+                if (f.isFile()) {
+                    log("Found file: " + text, Project.MSG_VERBOSE);
+                }
+                return f.isFile();
+            }
+        }
+        if (f.exists()) {
+            log("Found: " + text, Project.MSG_VERBOSE);
+        }
+        return f.exists();
+    }
+
+    /**
+     * Check if a given resource can be loaded.
+     */
+    private boolean checkResource(String resource) {
+        if (loader != null) {
+            return (loader.getResourceAsStream(resource) != null);
+        } else {
+            ClassLoader cL = this.getClass().getClassLoader();
+            if (cL != null) {
+                return (cL.getResourceAsStream(resource) != null);
+            } else {
+                return
+                    (ClassLoader.getSystemResourceAsStream(resource) != null);
+            }
+        }
+    }
+
+    /**
+     * Check if a given class can be loaded.
+     */
+    private boolean checkClass(String classname) {
+        try {
+            Class requiredClass = null;
+            if (ignoreSystemclasses) {
+                loader = getProject().createClassLoader(classpath);
+                loader.setParentFirst(false);
+                loader.addJavaLibraries();
+                if (loader != null) {
+                    try {
+                        requiredClass = loader.findClass(classname);
+                    } catch (SecurityException se) {
+                        // class found but restricted name; this is
+                        // actually the case we're looking for in JDK 1.3+,
+                        // so catch the exception and return
+                        return true;
+                    }
+                } else {
+                    return false;
+                }
+            } else if (loader != null) {
+                requiredClass = loader.loadClass(classname);
+            } else {
+                ClassLoader l = this.getClass().getClassLoader();
+                // Can return null to represent the bootstrap class loader.
+                // see API docs of Class.getClassLoader.
+                if (l != null) {
+                    requiredClass = Class.forName(classname, true, l);
+                } else {
+                    requiredClass = Class.forName(classname);
+                }
+            }
+            return true;
+        } catch (ClassNotFoundException e) {
+            log("class \"" + classname + "\" was not found",
+                Project.MSG_DEBUG);
+            return false;
+        } catch (NoClassDefFoundError e) {
+            log("Could not load dependent class \"" + e.getMessage()
+                + "\" for class \"" + classname + "\"",
+                Project.MSG_DEBUG);
+            return false;
+        }
+    }
+
+    /**
+     * EnumeratedAttribute covering the file types to be checked for, either
+     * file or dir.
+     */
+    public static class FileDir extends EnumeratedAttribute {
+
+        private static final String[] VALUES = {"file", "dir"};
+
+        /**
+         * @see EnumeratedAttribute#getValues
+         */
+        public String[] getValues() {
+            return VALUES;
+        }
+
+        /**
+         * Indicate if the value specifies a directory.
+         *
+         * @return true if the value specifies a directory.
+         */
+        public boolean isDir() {
+            return "dir".equalsIgnoreCase(getValue());
+        }
+
+        /**
+         * Indicate if the value specifies a file.
+         *
+         * @return true if the value specifies a file.
+         */
+        public boolean isFile() {
+            return "file".equalsIgnoreCase(getValue());
+        }
+
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/BUnzip2.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/BUnzip2.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/BUnzip2.java	(revision 16139)
@@ -0,0 +1,110 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.bzip2.CBZip2InputStream;
+
+/**
+ * Expands a file that has been compressed with the BZIP2
+ * algorithm. Normally used to compress non-compressed archives such
+ * as TAR files.
+ *
+ *
+ * @since Ant 1.5
+ *
+ * @ant.task category="packaging"
+ */
+
+public class BUnzip2 extends Unpack {
+
+    private static final String DEFAULT_EXTENSION = ".bz2";
+
+    protected String getDefaultExtension() {
+        return DEFAULT_EXTENSION;
+    }
+
+    protected void extract() {
+        if (source.lastModified() > dest.lastModified()) {
+            log("Expanding " + source.getAbsolutePath() + " to "
+                + dest.getAbsolutePath());
+
+            FileOutputStream out = null;
+            CBZip2InputStream zIn = null;
+            FileInputStream fis = null;
+            BufferedInputStream bis = null;
+            try {
+                out = new FileOutputStream(dest);
+                fis = new FileInputStream(source);
+                bis = new BufferedInputStream(fis);
+                int b = bis.read();
+                if (b != 'B') {
+                    throw new BuildException("Invalid bz2 file.", getLocation());
+                }
+                b = bis.read();
+                if (b != 'Z') {
+                    throw new BuildException("Invalid bz2 file.", getLocation());
+                }
+                zIn = new CBZip2InputStream(bis);
+                byte[] buffer = new byte[8 * 1024];
+                int count = 0;
+                do {
+                    out.write(buffer, 0, count);
+                    count = zIn.read(buffer, 0, buffer.length);
+                } while (count != -1);
+            } catch (IOException ioe) {
+                String msg = "Problem expanding bzip2 " + ioe.getMessage();
+                throw new BuildException(msg, ioe, getLocation());
+            } finally {
+                if (bis != null) {
+                    try {
+                        bis.close();
+                    } catch (IOException ioex) {
+                        // ignore
+                    }
+                }
+                if (fis != null) {
+                    try {
+                        fis.close();
+                    } catch (IOException ioex) {
+                        // ignore
+                    }
+                }
+                if (out != null) {
+                    try {
+                        out.close();
+                    } catch (IOException ioex) {
+                        // ignore
+                    }
+                }
+                if (zIn != null) {
+                    try {
+                        zIn.close();
+                    } catch (IOException ioex) {
+                        // ignore
+                    }
+                }
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/BZip2.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/BZip2.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/BZip2.java	(revision 16139)
@@ -0,0 +1,61 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.bzip2.CBZip2OutputStream;
+
+/**
+ * Compresses a file with the BZIP2 algorithm. Normally used to compress
+ * non-compressed archives such as TAR files.
+ *
+ *
+ * @since Ant 1.5
+ *
+ * @ant.task category="packaging"
+ */
+
+public class BZip2 extends Pack {
+    protected void pack() {
+        CBZip2OutputStream zOut = null;
+        try {
+            BufferedOutputStream bos =
+                new BufferedOutputStream(new FileOutputStream(zipFile));
+            bos.write('B');
+            bos.write('Z');
+            zOut = new CBZip2OutputStream(bos);
+            zipFile(source, zOut);
+        } catch (IOException ioe) {
+            String msg = "Problem creating bzip2 " + ioe.getMessage();
+            throw new BuildException(msg, ioe, getLocation());
+        } finally {
+            if (zOut != null) {
+                try {
+                    // close up
+                    zOut.close();
+                } catch (IOException e) {
+                    //ignore
+                }
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Basename.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Basename.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Basename.java	(revision 16139)
@@ -0,0 +1,109 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Sets a property to the base name of a specified file, optionally minus a
+ * suffix.
+ *
+ * This task can accept the following attributes:
+ * <ul>
+ * <li>file
+ * <li>property
+ * <li>suffix
+ * </ul>
+ * The <b>file</b> and <b>property</b> attributes are required. The
+ * <b>suffix</b> attribute can be specified either with or without
+ * the &quot;.&quot;, and the result will be the same (ie., the
+ * returned file name will be minus the .suffix).
+ * <p>
+ * When this task executes, it will set the specified property to the
+ * value of the last element in the specified file. If file is a
+ * directory, the basename will be the last directory element. If file
+ * is a full-path filename, the basename will be the simple file name.
+ * If a suffix is specified, and the specified file ends in that suffix,
+ * the basename will be the simple file name without the suffix.
+ *
+ *
+ *
+ * @since Ant 1.5
+ *
+ * @ant.task category="property"
+ */
+
+public class Basename extends Task {
+    private File file;
+    private String property;
+    private String suffix;
+
+    /**
+     * file or directory to get base name from
+     * @param file file or directory to get base name from
+     */
+    public void setFile(File file) {
+        this.file = file;
+    }
+
+    /**
+    * Property to set base name to.
+     * @param property name of property
+    */
+    public void setProperty(String property) {
+        this.property  = property;
+    }
+
+    /**
+    * Optional suffix to remove from base name.
+     * @param suffix suffix to remove from base name
+    */
+    public void setSuffix(String suffix) {
+        this.suffix = suffix;
+    }
+
+    /**
+     * do the work
+     * @throws BuildException if required attributes are not supplied
+     * property and attribute are required attributes
+     */
+    public void execute() throws BuildException {
+        if (property == null) {
+            throw new BuildException("property attribute required", getLocation());
+        }
+        if (file == null) {
+            throw new BuildException("file attribute required", getLocation());
+        }
+        String value = file.getName();
+        if (suffix != null && value.endsWith(suffix)) {
+            // if the suffix does not starts with a '.' and the
+            // char preceding the suffix is a '.', we assume the user
+            // wants to remove the '.' as well (see docs)
+            int pos = value.length() - suffix.length();
+            if (pos > 0 && suffix.charAt(0) != '.'
+                && value.charAt(pos - 1) == '.') {
+                pos--;
+            }
+            value = value.substring(0, pos);
+        }
+        getProject().setNewProperty(property, value);
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/BuildNumber.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/BuildNumber.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/BuildNumber.java	(revision 16139)
@@ -0,0 +1,201 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Properties;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Read, increment, and write a build number in a file
+ * It will first
+ * attempt to read a build number from a file, then set the property
+ * "build.number" to the value that was read in (or 0 if no such value). Then
+ * it will increment the build number by one and write it back out into the
+ * file.
+ *
+ * @since Ant 1.5
+ * @ant.task name="buildnumber"
+ */
+public class BuildNumber
+     extends Task {
+    /**
+     * The name of the property in which the build number is stored.
+     */
+    private static final String DEFAULT_PROPERTY_NAME = "build.number";
+
+    /** The default filename to use if no file specified.  */
+    private static final String DEFAULT_FILENAME = DEFAULT_PROPERTY_NAME;
+
+    /** The File in which the build number is stored.  */
+    private File myFile;
+
+
+    /**
+     * The file in which the build number is stored. Defaults to
+     * "build.number" if not specified.
+     *
+     * @param file the file in which build number is stored.
+     */
+    public void setFile(final File file) {
+        myFile = file;
+    }
+
+
+    /**
+     * Run task.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute()
+         throws BuildException {
+        File savedFile = myFile; // may be altered in validate
+
+        validate();
+
+        final Properties properties = loadProperties();
+        final int buildNumber = getBuildNumber(properties);
+
+        properties.put(DEFAULT_PROPERTY_NAME,
+            String.valueOf(buildNumber + 1));
+
+        // Write the properties file back out
+        FileOutputStream output = null;
+
+        try {
+            output = new FileOutputStream(myFile);
+
+            final String header = "Build Number for ANT. Do not edit!";
+
+            properties.store(output, header);
+        } catch (final IOException ioe) {
+            final String message = "Error while writing " + myFile;
+
+            throw new BuildException(message, ioe);
+        } finally {
+            if (null != output) {
+                try {
+                    output.close();
+                } catch (final IOException ioe) {
+                    getProject().log("error closing output stream " + ioe, Project.MSG_ERR);
+                }
+            }
+            myFile = savedFile;
+        }
+
+        //Finally set the property
+        getProject().setNewProperty(DEFAULT_PROPERTY_NAME,
+            String.valueOf(buildNumber));
+    }
+
+
+    /**
+     * Utility method to retrieve build number from properties object.
+     *
+     * @param properties the properties to retrieve build number from
+     * @return the build number or if no number in properties object
+     * @throws BuildException if build.number property is not an integer
+     */
+    private int getBuildNumber(final Properties properties)
+         throws BuildException {
+        final String buildNumber =
+            properties.getProperty(DEFAULT_PROPERTY_NAME, "0").trim();
+
+        // Try parsing the line into an integer.
+        try {
+            return Integer.parseInt(buildNumber);
+        } catch (final NumberFormatException nfe) {
+            final String message =
+                myFile + " contains a non integer build number: " + buildNumber;
+
+            throw new BuildException(message, nfe);
+        }
+    }
+
+
+    /**
+     * Utility method to load properties from file.
+     *
+     * @return the loaded properties
+     * @throws BuildException
+     */
+    private Properties loadProperties()
+         throws BuildException {
+        FileInputStream input = null;
+
+        try {
+            final Properties properties = new Properties();
+
+            input = new FileInputStream(myFile);
+            properties.load(input);
+            return properties;
+        } catch (final IOException ioe) {
+            throw new BuildException(ioe);
+        } finally {
+            if (null != input) {
+                try {
+                    input.close();
+                } catch (final IOException ioe) {
+                    getProject().log("error closing input stream " + ioe, Project.MSG_ERR);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Validate that the task parameters are valid.
+     *
+     * @throws BuildException if parameters are invalid
+     */
+    private void validate()
+         throws BuildException {
+        if (null == myFile) {
+            myFile = getProject().resolveFile(DEFAULT_FILENAME);
+        }
+
+        if (!myFile.exists()) {
+            try {
+                FileUtils.newFileUtils().createNewFile(myFile);
+            } catch (final IOException ioe) {
+                final String message =
+                    myFile + " doesn't exist and new file can't be created.";
+
+                throw new BuildException(message, ioe);
+            }
+        }
+
+        if (!myFile.canRead()) {
+            final String message = "Unable to read from " + myFile + ".";
+
+            throw new BuildException(message);
+        }
+
+        if (!myFile.canWrite()) {
+            final String message = "Unable to write to " + myFile + ".";
+
+            throw new BuildException(message);
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/CVSPass.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/CVSPass.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/CVSPass.java	(revision 16139)
@@ -0,0 +1,170 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Adds an new entry to a CVS password file.
+ *
+ *
+ * @since Ant 1.4
+ *
+ * @ant.task category="scm"
+ */
+public class CVSPass extends Task {
+    /** CVS Root */
+    private String cvsRoot = null;
+    /** Password file to add password to */
+    private File passFile = null;
+    /** Password to add to file */
+    private String password = null;
+
+    /** Array contain char conversion data */
+    private final char[] shifts = {
+          0,   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,
+        114, 120,  53,  79,  96, 109,  72, 108,  70,  64,  76,  67, 116,  74,  68,  87,
+        111,  52,  75, 119,  49,  34,  82,  81,  95,  65, 112,  86, 118, 110, 122, 105,
+         41,  57,  83,  43,  46, 102,  40,  89,  38, 103,  45,  50,  42, 123,  91,  35,
+        125,  55,  54,  66, 124, 126,  59,  47,  92,  71, 115,  78,  88, 107, 106,  56,
+         36, 121, 117, 104, 101, 100,  69,  73,  99,  63,  94,  93,  39,  37,  61,  48,
+         58, 113,  32,  90,  44,  98,  60,  51,  33,  97,  62,  77,  84,  80,  85, 223,
+        225, 216, 187, 166, 229, 189, 222, 188, 141, 249, 148, 200, 184, 136, 248, 190,
+        199, 170, 181, 204, 138, 232, 218, 183, 255, 234, 220, 247, 213, 203, 226, 193,
+        174, 172, 228, 252, 217, 201, 131, 230, 197, 211, 145, 238, 161, 179, 160, 212,
+        207, 221, 254, 173, 202, 146, 224, 151, 140, 196, 205, 130, 135, 133, 143, 246,
+        192, 159, 244, 239, 185, 168, 215, 144, 139, 165, 180, 157, 147, 186, 214, 176,
+        227, 231, 219, 169, 175, 156, 206, 198, 129, 164, 150, 210, 154, 177, 134, 127,
+        182, 128, 158, 208, 162, 132, 167, 209, 149, 241, 153, 251, 237, 236, 171, 195,
+        243, 233, 253, 240, 194, 250, 191, 155, 142, 137, 245, 235, 163, 242, 178, 152
+    };
+
+    /**
+     * Create a CVS task using the default cvspass file location.
+     */
+    public CVSPass() {
+        passFile = new File(
+            System.getProperty("cygwin.user.home",
+                System.getProperty("user.home"))
+            + File.separatorChar + ".cvspass");
+    }
+
+    /**
+     * Does the work.
+     *
+     * @exception BuildException if something goes wrong with the build
+     */
+    public final void execute() throws BuildException {
+        if (cvsRoot == null) {
+            throw new BuildException("cvsroot is required");
+        }
+        if (password == null) {
+            throw new BuildException("password is required");
+        }
+
+        log("cvsRoot: " + cvsRoot, Project.MSG_DEBUG);
+        log("password: " + password, Project.MSG_DEBUG);
+        log("passFile: " + passFile, Project.MSG_DEBUG);
+
+        BufferedReader reader = null;
+        PrintWriter writer = null;
+        try {
+            StringBuffer buf = new StringBuffer();
+
+            if (passFile.exists()) {
+                reader = new BufferedReader(new FileReader(passFile));
+
+                String line = null;
+
+                while ((line = reader.readLine()) != null) {
+                    if (!line.startsWith(cvsRoot)) {
+                        buf.append(line).append(StringUtils.LINE_SEP);
+                    }
+                }
+            }
+
+            String pwdfile = buf.toString() + cvsRoot + " A"
+                + mangle(password);
+
+            log("Writing -> " + pwdfile , Project.MSG_DEBUG);
+
+            writer = new PrintWriter(new FileWriter(passFile));
+
+            writer.println(pwdfile);
+        } catch (IOException e) {
+            throw new BuildException(e);
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+            if (writer != null) {
+                writer.close();
+            }
+        }
+    }
+
+    private final String mangle(String password) {
+        StringBuffer buf = new StringBuffer();
+        for (int i = 0; i < password.length(); i++) {
+            buf.append(shifts[password.charAt(i)]);
+        }
+        return buf.toString();
+    }
+
+    /**
+     * The CVS repository to add an entry for.
+     *
+     * @param cvsRoot the CVS repository
+     */
+    public void setCvsroot(String cvsRoot) {
+        this.cvsRoot = cvsRoot;
+    }
+
+    /**
+     * Password file to add the entry to.
+     *
+     * @param passFile the password file.
+     */
+    public void setPassfile(File passFile) {
+        this.passFile = passFile;
+    }
+
+    /**
+     * Password to be added to the password file.
+     *
+     * @param password the password.
+     */
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/CallTarget.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/CallTarget.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/CallTarget.java	(revision 16139)
@@ -0,0 +1,236 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.IOException;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.PropertySet;
+
+/**
+ * Call another target in the same project.
+ *
+ *  <pre>
+ *    &lt;target name="foo"&gt;
+ *      &lt;antcall target="bar"&gt;
+ *        &lt;param name="property1" value="aaaaa" /&gt;
+ *        &lt;param name="foo" value="baz" /&gt;
+ *       &lt;/antcall&gt;
+ *    &lt;/target&gt;
+ *
+ *    &lt;target name="bar" depends="init"&gt;
+ *      &lt;echo message="prop is ${property1} ${foo}" /&gt;
+ *    &lt;/target&gt;
+ * </pre>
+ *
+ * <p>This only works as expected if neither property1 nor foo are
+ * defined in the project itself.
+ *
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task name="antcall" category="control"
+ */
+public class CallTarget extends Task {
+
+    private Ant callee;
+    // must match the default value of Ant#inheritAll
+    private boolean inheritAll = true;
+    // must match the default value of Ant#inheritRefs
+    private boolean inheritRefs = false;
+
+    private boolean targetSet = false;
+
+    /**
+     * If true, pass all properties to the new Ant project.
+     * Defaults to true.
+     * @param inherit <code>boolean</code> flag.
+     */
+    public void setInheritAll(boolean inherit) {
+       inheritAll = inherit;
+    }
+
+    /**
+     * If true, pass all references to the new Ant project.
+     * Defaults to false.
+     * @param inheritRefs <code>boolean</code> flag.
+     */
+    public void setInheritRefs(boolean inheritRefs) {
+        this.inheritRefs = inheritRefs;
+    }
+
+    /**
+     * Initialize this task by creating new instance of the ant task and
+     * configuring it by calling its own init method.
+     */
+    public void init() {
+        callee = (Ant) getProject().createTask("ant");
+        callee.setOwningTarget(getOwningTarget());
+        callee.setTaskName(getTaskName());
+        callee.setLocation(getLocation());
+        callee.init();
+    }
+
+    /**
+     * Delegate the work to the ant task instance, after setting it up.
+     * @throws BuildException on validation failure or if the target didn't
+     * execute.
+     */
+    public void execute() throws BuildException {
+        if (callee == null) {
+            init();
+        }
+        if (!targetSet) {
+            throw new BuildException(
+                "Attribute target or at least one nested target is required.",
+                 getLocation());
+        }
+        callee.setAntfile(getProject().getProperty("ant.file"));
+        callee.setInheritAll(inheritAll);
+        callee.setInheritRefs(inheritRefs);
+        callee.execute();
+    }
+
+    /**
+     * Create a new Property to pass to the invoked target(s).
+     * @return a <code>Property</code> object.
+     */
+    public Property createParam() {
+        if (callee == null) {
+            init();
+        }
+        return callee.createProperty();
+    }
+
+    /**
+     * Reference element identifying a data type to carry
+     * over to the invoked target.
+     * @param r the specified <code>Ant.Reference</code>.
+     * @since Ant 1.5
+     */
+    public void addReference(Ant.Reference r) {
+        if (callee == null) {
+            init();
+        }
+        callee.addReference(r);
+    }
+
+    /**
+     * Set of properties to pass to the new project.
+     * @param ps the <code>PropertySet</code> to pass.
+     * @since Ant 1.6
+     */
+    public void addPropertyset(PropertySet ps) {
+        if (callee == null) {
+            init();
+        }
+        callee.addPropertyset(ps);
+    }
+
+    /**
+     * Set target to execute.
+     * @param target the name of the target to execute.
+     */
+    public void setTarget(String target) {
+        if (callee == null) {
+            init();
+        }
+        callee.setTarget(target);
+        targetSet = true;
+    }
+
+    /**
+     * Add a target to the list of targets to invoke.
+     * @param t <code>Ant.TargetElement</code> representing the target.
+     * @since Ant 1.6.3
+     */
+    public void addConfiguredTarget(Ant.TargetElement t) {
+        if (callee == null) {
+            init();
+        }
+        callee.addConfiguredTarget(t);
+        targetSet = true;
+    }
+
+    /**
+     * @see Task#handleOutput(String)
+     *
+     * @since Ant 1.5
+     */
+    public void handleOutput(String output) {
+        if (callee != null) {
+            callee.handleOutput(output);
+        } else {
+            super.handleOutput(output);
+        }
+    }
+
+    /**
+     * @see Task#handleInput(byte[], int, int)
+     *
+     * @since Ant 1.6
+     */
+    public int handleInput(byte[] buffer, int offset, int length)
+        throws IOException {
+        if (callee != null) {
+            return callee.handleInput(buffer, offset, length);
+        } else {
+            return super.handleInput(buffer, offset, length);
+        }
+    }
+
+    /**
+     * @see Task#handleFlush(String)
+     *
+     * @since Ant 1.5.2
+     */
+    public void handleFlush(String output) {
+        if (callee != null) {
+            callee.handleFlush(output);
+        } else {
+            super.handleFlush(output);
+        }
+    }
+
+    /**
+     * @see Task#handleErrorOutput(String)
+     *
+     * @since Ant 1.5
+     */
+    public void handleErrorOutput(String output) {
+        if (callee != null) {
+            callee.handleErrorOutput(output);
+        } else {
+            super.handleErrorOutput(output);
+        }
+    }
+
+    /**
+     * @see Task#handleErrorFlush(String)
+     *
+     * @since Ant 1.5.2
+     */
+    public void handleErrorFlush(String output) {
+        if (callee != null) {
+            callee.handleErrorFlush(output);
+        } else {
+            super.handleErrorFlush(output);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Checksum.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Checksum.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Checksum.java	(revision 16139)
@@ -0,0 +1,566 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Vector;
+import java.util.Hashtable;
+import java.util.Enumeration;
+import java.util.Set;
+import java.util.Arrays;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * Used to create or verify file checksums.
+ *
+ *
+ * @since Ant 1.5
+ *
+ * @ant.task category="control"
+ */
+public class Checksum extends MatchingTask implements Condition {
+
+    /**
+     * File for which checksum is to be calculated.
+     */
+    private File file = null;
+
+    /**
+     * Root directory in which the checksum files will be written.
+     * If not specified, the checksum files will be written
+     * in the same directory as each file.
+     */
+    private File todir;
+
+    /**
+     * MessageDigest algorithm to be used.
+     */
+    private String algorithm = "MD5";
+    /**
+     * MessageDigest Algorithm provider
+     */
+    private String provider = null;
+    /**
+     * File Extension that is be to used to create or identify
+     * destination file
+     */
+    private String fileext;
+    /**
+     * Holds generated checksum and gets set as a Project Property.
+     */
+    private String property;
+    /**
+     * Holds checksums for all files (both calculated and cached on disk).
+     * Key:   java.util.File (source file)
+     * Value: java.lang.String (digest)
+     */
+    private Map allDigests = new HashMap();
+    /**
+     * Holds relative file names for all files (always with a forward slash).
+     * This is used to calculate the total hash.
+     * Key:   java.util.File (source file)
+     * Value: java.lang.String (relative file name)
+     */
+    private Map relativeFilePaths = new HashMap();
+    /**
+     * Property where totalChecksum gets set.
+     */
+    private String totalproperty;
+    /**
+     * Whether or not to create a new file.
+     * Defaults to <code>false</code>.
+     */
+    private boolean forceOverwrite;
+    /**
+     * Contains the result of a checksum verification. ("true" or "false")
+     */
+    private String verifyProperty;
+    /**
+     * Vector to hold source file sets.
+     */
+    private Vector filesets = new Vector();
+    /**
+     * Stores SourceFile, DestFile pairs and SourceFile, Property String pairs.
+     */
+    private Hashtable includeFileMap = new Hashtable();
+    /**
+     * Message Digest instance
+     */
+    private MessageDigest messageDigest;
+    /**
+     * is this task being used as a nested condition element?
+     */
+    private boolean isCondition;
+    /**
+     * Size of the read buffer to use.
+     */
+    private int readBufferSize = 8 * 1024;
+
+    /**
+     * Sets the file for which the checksum is to be calculated.
+     */
+    public void setFile(File file) {
+        this.file = file;
+    }
+
+    /**
+     * Sets the root directory where checksum files will be
+     * written/read
+     *
+     * @since Ant 1.6
+     */
+    public void setTodir(File todir) {
+        this.todir = todir;
+    }
+
+    /**
+     * Specifies the algorithm to be used to compute the checksum.
+     * Defaults to "MD5". Other popular algorithms like "SHA" may be used as well.
+     */
+    public void setAlgorithm(String algorithm) {
+        this.algorithm = algorithm;
+    }
+
+    /**
+     * Sets the MessageDigest algorithm provider to be used
+     * to calculate the checksum.
+     */
+    public void setProvider(String provider) {
+        this.provider = provider;
+    }
+
+    /**
+     * Sets the file extension that is be to used to
+     * create or identify destination file.
+     */
+    public void setFileext(String fileext) {
+        this.fileext = fileext;
+    }
+
+    /**
+     * Sets the property to hold the generated checksum.
+     */
+    public void setProperty(String property) {
+        this.property = property;
+    }
+
+    /**
+     * Sets the property to hold the generated total checksum
+     * for all files.
+     *
+     * @since Ant 1.6
+     */
+    public void setTotalproperty(String totalproperty) {
+        this.totalproperty = totalproperty;
+    }
+
+    /**
+     * Sets the verify property.  This project property holds
+     * the result of a checksum verification - "true" or "false"
+     */
+    public void setVerifyproperty(String verifyProperty) {
+        this.verifyProperty = verifyProperty;
+    }
+
+    /**
+     * Whether or not to overwrite existing file irrespective of
+     * whether it is newer than
+     * the source file.  Defaults to false.
+     */
+    public void setForceOverwrite(boolean forceOverwrite) {
+        this.forceOverwrite = forceOverwrite;
+    }
+
+    /**
+     * The size of the read buffer to use.
+     */
+    public void setReadBufferSize(int size) {
+        this.readBufferSize = size;
+    }
+
+    /**
+     * Files to generate checksums for.
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * Calculate the checksum(s).
+     */
+    public void execute() throws BuildException {
+        isCondition = false;
+        boolean value = validateAndExecute();
+        if (verifyProperty != null) {
+            getProject().setNewProperty(verifyProperty,
+                                new Boolean(value).toString());
+        }
+    }
+
+    /**
+     * Calculate the checksum(s)
+     *
+     * @return Returns true if the checksum verification test passed,
+     * false otherwise.
+     */
+    public boolean eval() throws BuildException {
+        isCondition = true;
+        return validateAndExecute();
+    }
+
+    /**
+     * Validate attributes and get down to business.
+     */
+    private boolean validateAndExecute() throws BuildException {
+        String savedFileExt = fileext;
+
+        if (file == null && filesets.size() == 0) {
+            throw new BuildException(
+                "Specify at least one source - a file or a fileset.");
+        }
+
+        if (file != null && file.exists() && file.isDirectory()) {
+            throw new BuildException(
+                "Checksum cannot be generated for directories");
+        }
+
+        if (file != null && totalproperty != null) {
+            throw new BuildException(
+                "File and Totalproperty cannot co-exist.");
+        }
+
+        if (property != null && fileext != null) {
+            throw new BuildException(
+                "Property and FileExt cannot co-exist.");
+        }
+
+        if (property != null) {
+            if (forceOverwrite) {
+                throw new BuildException(
+                    "ForceOverwrite cannot be used when Property is specified");
+            }
+
+            if (file != null) {
+                if (filesets.size() > 0) {
+                    throw new BuildException("Multiple files cannot be used "
+                        + "when Property is specified");
+                }
+            } else {
+                if (filesets.size() > 1) {
+                    throw new BuildException("Multiple files cannot be used "
+                        + "when Property is specified");
+                }
+            }
+        }
+
+        if (verifyProperty != null) {
+            isCondition = true;
+        }
+
+        if (verifyProperty != null && forceOverwrite) {
+            throw new BuildException(
+                "VerifyProperty and ForceOverwrite cannot co-exist.");
+        }
+
+        if (isCondition && forceOverwrite) {
+            throw new BuildException("ForceOverwrite cannot be used when "
+                + "conditions are being used.");
+        }
+
+        messageDigest = null;
+        if (provider != null) {
+            try {
+                messageDigest = MessageDigest.getInstance(algorithm, provider);
+            } catch (NoSuchAlgorithmException noalgo) {
+                throw new BuildException(noalgo, getLocation());
+            } catch (NoSuchProviderException noprovider) {
+                throw new BuildException(noprovider, getLocation());
+            }
+        } else {
+            try {
+                messageDigest = MessageDigest.getInstance(algorithm);
+            } catch (NoSuchAlgorithmException noalgo) {
+                throw new BuildException(noalgo, getLocation());
+            }
+        }
+
+        if (messageDigest == null) {
+            throw new BuildException("Unable to create Message Digest",
+                                     getLocation());
+        }
+
+        if (fileext == null) {
+            fileext = "." + algorithm;
+        } else if (fileext.trim().length() == 0) {
+            throw new BuildException(
+                "File extension when specified must not be an empty string");
+        }
+
+        try {
+            int sizeofFileSet = filesets.size();
+            for (int i = 0; i < sizeofFileSet; i++) {
+                FileSet fs = (FileSet) filesets.elementAt(i);
+                DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+                String[] srcFiles = ds.getIncludedFiles();
+                for (int j = 0; j < srcFiles.length; j++) {
+                    File src = new File(fs.getDir(getProject()), srcFiles[j]);
+                    if (totalproperty != null || todir != null) {
+                        // Use '/' to calculate digest based on file name.
+                        // This is required in order to get the same result
+                        // on different platforms.
+                        String relativePath = srcFiles[j].replace(File.separatorChar, '/');
+                        relativeFilePaths.put(src, relativePath);
+                    }
+                    addToIncludeFileMap(src);
+                }
+            }
+
+            addToIncludeFileMap(file);
+
+            return generateChecksums();
+        } finally {
+            fileext = savedFileExt;
+            includeFileMap.clear();
+        }
+    }
+
+    /**
+     * Add key-value pair to the hashtable upon which
+     * to later operate upon.
+     */
+    private void addToIncludeFileMap(File file) throws BuildException {
+        if (file != null) {
+            if (file.exists()) {
+                if (property == null) {
+                    File checksumFile = getChecksumFile(file);
+                    if (forceOverwrite || isCondition
+                        || (file.lastModified() > checksumFile.lastModified())) {
+                        includeFileMap.put(file, checksumFile);
+                    } else {
+                        log(file + " omitted as " + checksumFile + " is up to date.",
+                            Project.MSG_VERBOSE);
+                        if (totalproperty != null) {
+                            // Read the checksum from disk.
+                            String checksum = null;
+                            try {
+                                BufferedReader diskChecksumReader
+                                    = new BufferedReader(new FileReader(checksumFile));
+                                checksum = diskChecksumReader.readLine();
+                            } catch (IOException e) {
+                                throw new BuildException("Couldn't read checksum file "
+                                    + checksumFile, e);
+                            }
+                            byte[] digest = decodeHex(checksum.toCharArray());
+                            allDigests.put(file, digest);
+                        }
+                    }
+                } else {
+                    includeFileMap.put(file, property);
+                }
+            } else {
+                String message = "Could not find file "
+                                 + file.getAbsolutePath()
+                                 + " to generate checksum for.";
+                log(message);
+                throw new BuildException(message, getLocation());
+            }
+        }
+    }
+
+    private File getChecksumFile(File file) {
+        File directory;
+        if (todir != null) {
+            // A separate directory was explicitly declared
+            String path = (String) relativeFilePaths.get(file);
+            directory = new File(todir, path).getParentFile();
+            // Create the directory, as it might not exist.
+            directory.mkdirs();
+        } else {
+            // Just use the same directory as the file itself.
+            // This directory will exist
+            directory = file.getParentFile();
+        }
+        File checksumFile = new File(directory, file.getName() + fileext);
+        return checksumFile;
+    }
+
+    /**
+     * Generate checksum(s) using the message digest created earlier.
+     */
+    private boolean generateChecksums() throws BuildException {
+        boolean checksumMatches = true;
+        FileInputStream fis = null;
+        FileOutputStream fos = null;
+        byte[] buf = new byte[readBufferSize];
+        try {
+            for (Enumeration e = includeFileMap.keys(); e.hasMoreElements();) {
+                messageDigest.reset();
+                File src = (File) e.nextElement();
+                if (!isCondition) {
+                    log("Calculating " + algorithm + " checksum for " + src, Project.MSG_VERBOSE);
+                }
+                fis = new FileInputStream(src);
+                DigestInputStream dis = new DigestInputStream(fis,
+                                                              messageDigest);
+                while (dis.read(buf, 0, readBufferSize) != -1) {
+                    ;
+                }
+                dis.close();
+                fis.close();
+                fis = null;
+                byte[] fileDigest = messageDigest.digest ();
+                if (totalproperty != null) {
+                    allDigests.put(src, fileDigest);
+                }
+                String checksum = createDigestString(fileDigest);
+                //can either be a property name string or a file
+                Object destination = includeFileMap.get(src);
+                if (destination instanceof java.lang.String) {
+                    String prop = (String) destination;
+                    if (isCondition) {
+                        checksumMatches
+                            = checksumMatches && checksum.equals(property);
+                    } else {
+                        getProject().setNewProperty(prop, checksum);
+                    }
+                } else if (destination instanceof java.io.File) {
+                    if (isCondition) {
+                        File existingFile = (File) destination;
+                        if (existingFile.exists()) {
+                            fis = new FileInputStream(existingFile);
+                            InputStreamReader isr = new InputStreamReader(fis);
+                            BufferedReader br = new BufferedReader(isr);
+                            String suppliedChecksum = br.readLine();
+                            fis.close();
+                            fis = null;
+                            br.close();
+                            isr.close();
+                            checksumMatches = checksumMatches
+                                && checksum.equals(suppliedChecksum);
+                        } else {
+                            checksumMatches = false;
+                        }
+                    } else {
+                        File dest = (File) destination;
+                        fos = new FileOutputStream(dest);
+                        fos.write(checksum.getBytes());
+                        fos.close();
+                        fos = null;
+                    }
+                }
+            }
+            if (totalproperty != null) {
+                // Calculate the total checksum
+                // Convert the keys (source files) into a sorted array.
+                Set keys = allDigests.keySet();
+                Object[] keyArray = keys.toArray();
+                // File is Comparable, so sorting is trivial
+                Arrays.sort(keyArray);
+                // Loop over the checksums and generate a total hash.
+                messageDigest.reset();
+                for (int i = 0; i < keyArray.length; i++) {
+                    File src = (File) keyArray[i];
+
+                    // Add the digest for the file content
+                    byte[] digest = (byte[]) allDigests.get(src);
+                    messageDigest.update(digest);
+
+                    // Add the file path
+                    String fileName = (String) relativeFilePaths.get(src);
+                    messageDigest.update(fileName.getBytes());
+                }
+                String totalChecksum = createDigestString(messageDigest.digest());
+                getProject().setNewProperty(totalproperty, totalChecksum);
+            }
+        } catch (Exception e) {
+            throw new BuildException(e, getLocation());
+        } finally {
+            if (fis != null) {
+                try {
+                    fis.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+            if (fos != null) {
+                try {
+                    fos.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+        return checksumMatches;
+    }
+
+    private String createDigestString(byte[] fileDigest) {
+        StringBuffer checksumSb = new StringBuffer();
+        for (int i = 0; i < fileDigest.length; i++) {
+            String hexStr = Integer.toHexString(0x00ff & fileDigest[i]);
+            if (hexStr.length() < 2) {
+                checksumSb.append("0");
+            }
+            checksumSb.append(hexStr);
+        }
+        return checksumSb.toString();
+    }
+
+    /**
+     * Converts an array of characters representing hexadecimal values into an
+     * array of bytes of those same values. The returned array will be half the
+     * length of the passed array, as it takes two characters to represent any
+     * given byte. An exception is thrown if the passed char array has an odd
+     * number of elements.
+     *
+     * NOTE: This code is copied from jakarta-commons codec.
+     */
+    public static byte[] decodeHex(char[] data) throws BuildException {
+        int l = data.length;
+
+        if ((l & 0x01) != 0) {
+            throw new BuildException("odd number of characters.");
+        }
+
+        byte[] out = new byte[l >> 1];
+
+        // two characters form the hex value.
+        for (int i = 0, j = 0; j < l; i++) {
+            int f = Character.digit(data[j++], 16) << 4;
+            f = f | Character.digit(data[j++], 16);
+            out[i] = (byte) (f & 0xFF);
+        }
+
+        return out;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Chmod.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Chmod.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Chmod.java	(revision 16139)
@@ -0,0 +1,226 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.PatternSet;
+
+/**
+ * Chmod equivalent for unix-like environments.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="filesystem"
+ * @todo Refactor so it does not extend from ExecuteOn and then turn around
+ *       and unsupport several attributes.
+ */
+public class Chmod extends ExecuteOn {
+
+    private FileSet defaultSet = new FileSet();
+    private boolean defaultSetDefined = false;
+    private boolean havePerm = false;
+
+    /**
+     * Chmod task for setting file and directory permissions.
+     */
+    public Chmod() {
+        super.setExecutable("chmod");
+        super.setParallel(true);
+        super.setSkipEmptyFilesets(true);
+    }
+
+    /**
+     * @see org.apache.tools.ant.ProjectComponent#setProject
+     */
+    public void setProject(Project project) {
+        super.setProject(project);
+        defaultSet.setProject(project);
+    }
+
+    /**
+     * The file or single directory of which the permissions must be changed.
+     * @param src
+     */
+    public void setFile(File src) {
+        FileSet fs = new FileSet();
+        fs.setFile(src);
+        addFileset(fs);
+    }
+
+    /**
+     * The directory which holds the files whose permissions must be changed.
+     * @param src
+     */
+    public void setDir(File src) {
+        defaultSet.setDir(src);
+    }
+
+    /**
+     * The new permissions.
+     * @param perm
+     */
+    public void setPerm(String perm) {
+        createArg().setValue(perm);
+        havePerm = true;
+    }
+
+    /**
+     * Add a name entry on the include list.
+     */
+    public PatternSet.NameEntry createInclude() {
+        defaultSetDefined = true;
+        return defaultSet.createInclude();
+    }
+
+    /**
+     * Add a name entry on the exclude list.
+     */
+    public PatternSet.NameEntry createExclude() {
+        defaultSetDefined = true;
+        return defaultSet.createExclude();
+    }
+
+    /**
+     * Add a set of patterns.
+     */
+    public PatternSet createPatternSet() {
+        defaultSetDefined = true;
+        return defaultSet.createPatternSet();
+    }
+
+    /**
+     * Sets the set of include patterns. Patterns may be separated by a comma
+     * or a space.
+     *
+     * @param includes the string containing the include patterns
+     */
+    public void setIncludes(String includes) {
+        defaultSetDefined = true;
+        defaultSet.setIncludes(includes);
+    }
+
+    /**
+     * Sets the set of exclude patterns. Patterns may be separated by a comma
+     * or a space.
+     *
+     * @param excludes the string containing the exclude patterns
+     */
+    public void setExcludes(String excludes) {
+        defaultSetDefined = true;
+        defaultSet.setExcludes(excludes);
+    }
+
+    /**
+     * Sets whether default exclusions should be used or not.
+     *
+     * @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
+     *                           should be used, "false"|"off"|"no" when they
+     *                           shouldn't be used.
+     */
+    public void setDefaultexcludes(boolean useDefaultExcludes) {
+        defaultSetDefined = true;
+        defaultSet.setDefaultexcludes(useDefaultExcludes);
+    }
+
+    protected void checkConfiguration() {
+        if (!havePerm) {
+            throw new BuildException("Required attribute perm not set in chmod",
+                                     getLocation());
+        }
+
+        if (defaultSetDefined && defaultSet.getDir(getProject()) != null) {
+            addFileset(defaultSet);
+        }
+        super.checkConfiguration();
+    }
+
+    public void execute() throws BuildException {
+        /*
+         * In Ant 1.1, <chmod dir="foo" /> means, change the permissions
+         * of directory foo, not anything inside of it.  This is the case the
+         * second branch of the if statement below catches for backwards
+         * compatibility.
+         */
+        if (defaultSetDefined || defaultSet.getDir(getProject()) == null) {
+            try {
+                super.execute();
+            } finally {
+                if (defaultSetDefined && defaultSet.getDir(getProject()) != null) {
+                    filesets.removeElement(defaultSet);
+                }
+            }
+        } else if (isValidOs()) {
+            // we are chmodding the given directory
+            Execute execute = prepareExec();
+            Commandline cloned = (Commandline) cmdl.clone();
+            cloned.createArgument().setValue(defaultSet.getDir(getProject())
+                                             .getPath());
+            try {
+                execute.setCommandline(cloned.getCommandline());
+                runExecute(execute);
+            } catch (IOException e) {
+                throw new BuildException("Execute failed: " + e, e, getLocation());
+            } finally {
+                // close the output file if required
+                logFlush();
+            }
+        }
+    }
+
+    /**
+     * @ant.attribute ignore="true"
+     */
+    public void setExecutable(String e) {
+        throw new BuildException(getTaskType()
+            + " doesn\'t support the executable attribute", getLocation());
+    }
+
+    /**
+     * @ant.attribute ignore="true"
+     */
+    public void setCommand(Commandline cmdl) {
+        throw new BuildException(getTaskType()
+            + " doesn\'t support the command attribute", getLocation());
+    }
+
+    /**
+     * @ant.attribute ignore="true"
+     */
+    public void setSkipEmptyFilesets(boolean skip) {
+        throw new BuildException(getTaskType()
+            + " doesn\'t support the skipemptyfileset attribute", getLocation());
+    }
+
+    /**
+     * @ant.attribute ignore="true"
+     */
+    public void setAddsourcefile(boolean b) {
+        throw new BuildException(getTaskType()
+            + " doesn\'t support the addsourcefile attribute", getLocation());
+    }
+
+    protected boolean isValidOs() {
+        return Os.isFamily("unix") && super.isValidOs();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Concat.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Concat.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Concat.java	(revision 16139)
@@ -0,0 +1,973 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
+import org.apache.tools.ant.types.FileList;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * This class contains the 'concat' task, used to concatenate a series
+ * of files into a single stream. The destination of this stream may
+ * be the system console, or a file. The following is a sample
+ * invocation:
+ *
+ * <pre>
+ * &lt;concat destfile=&quot;${build.dir}/index.xml&quot;
+ *   append=&quot;false&quot;&gt;
+ *
+ *   &lt;fileset dir=&quot;${xml.root.dir}&quot;
+ *     includes=&quot;*.xml&quot; /&gt;
+ *
+ * &lt;/concat&gt;
+ * </pre>
+ *
+ */
+public class Concat extends Task {
+
+    // The size of buffers to be used
+    private static final int BUFFER_SIZE = 8192;
+
+    // Attributes.
+
+    /**
+     * The destination of the stream. If <code>null</code>, the system
+     * console is used.
+     */
+    private File destinationFile = null;
+
+    /**
+     * Whether or not the stream should be appended if the destination file
+     * exists.
+     * Defaults to <code>false</code>.
+     */
+    private boolean append = false;
+
+    /**
+     * Stores the input file encoding.
+     */
+    private String encoding = null;
+
+    /** Stores the output file encoding. */
+    private String outputEncoding = null;
+
+    /** Stores the binary attribute */
+    private boolean binary = false;
+
+    // Child elements.
+
+    /**
+     * This buffer stores the text within the 'concat' element.
+     */
+    private StringBuffer textBuffer;
+
+    /**
+     * Stores a collection of file sets and/or file lists, used to
+     * select multiple files for concatenation.
+     */
+    private Vector sources = new Vector();
+
+    /** for filtering the concatenated */
+    private Vector        filterChains = null;
+    /** ignore dates on input files */
+    private boolean       forceOverwrite = true;
+    /** String to place at the start of the concatented stream */
+    private TextElement   footer;
+    /** String to place at the end of the concatented stream */
+    private TextElement   header;
+    /** add missing line.separator to files **/
+    private boolean       fixLastLine = false;
+    /** endofline for fixlast line */
+    private String       eolString = System.getProperty("line.separator");
+    /** outputwriter */
+    private Writer       outputWriter = null;
+
+    /** internal variable - used to collect the source files from sources */
+    private Vector        sourceFiles = new Vector();
+
+    /** 1.1 utilities and copy utilities */
+    private static FileUtils     fileUtils = FileUtils.newFileUtils();
+
+    // Attribute setters.
+
+    /**
+     * Sets the destination file, or uses the console if not specified.
+     * @param destinationFile the destination file
+     */
+    public void setDestfile(File destinationFile) {
+        this.destinationFile = destinationFile;
+    }
+
+    /**
+     * Sets the behavior when the destination file exists. If set to
+     * <code>true</code> the stream data will be appended to the
+     * existing file, otherwise the existing file will be
+     * overwritten. Defaults to <code>false</code>.
+     * @param append if true append to the file.
+     */
+    public void setAppend(boolean append) {
+        this.append = append;
+    }
+
+    /**
+     * Sets the character encoding
+     * @param encoding the encoding of the input stream and unless
+     *        outputencoding is set, the outputstream.
+     */
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+        if (outputEncoding == null) {
+            outputEncoding = encoding;
+        }
+    }
+
+    /**
+     * Sets the character encoding for outputting
+     * @param outputEncoding the encoding for the output file
+     * @since Ant 1.6
+     */
+    public void setOutputEncoding(String outputEncoding) {
+        this.outputEncoding = outputEncoding;
+    }
+
+    /**
+     * Force overwrite existing destination file
+     * @param force if true always overwrite, otherwise only overwrite
+     *              if the output file is older any of the input files.
+     * @since Ant 1.6
+     */
+    public void setForce(boolean force) {
+        this.forceOverwrite = force;
+    }
+
+    // Nested element creators.
+
+    /**
+     * Path of files to concatenate.
+     * @return the path used for concatenating
+     * @since Ant 1.6
+     */
+     public Path createPath() {
+        Path path = new Path(getProject());
+        sources.addElement(path);
+        return path;
+    }
+
+    /**
+     * Set of files to concatenate.
+     * @param set the set of files
+     */
+    public void addFileset(FileSet set) {
+        sources.addElement(set);
+    }
+
+    /**
+     * List of files to concatenate.
+     * @param list the list of files
+     */
+    public void addFilelist(FileList list) {
+        sources.addElement(list);
+    }
+
+    /**
+     * Adds a FilterChain.
+     * @param filterChain a filterchain to filter the concatenated input
+     * @since Ant 1.6
+     */
+    public void addFilterChain(FilterChain filterChain) {
+        if (filterChains == null) {
+            filterChains = new Vector();
+        }
+        filterChains.addElement(filterChain);
+    }
+
+    /**
+     * This method adds text which appears in the 'concat' element.
+     * @param text the text to be concated.
+     */
+    public void addText(String text) {
+        if (textBuffer == null) {
+            // Initialize to the size of the first text fragment, with
+            // the hopes that it's the only one.
+            textBuffer = new StringBuffer(text.length());
+        }
+
+        // Append the fragment -- we defer property replacement until
+        // later just in case we get a partial property in a fragment.
+        textBuffer.append(text);
+    }
+
+    /**
+     * Add a header to the concatenated output
+     * @param header the header
+     * @since Ant 1.6
+     */
+    public void addHeader(TextElement header) {
+        this.header = header;
+    }
+
+    /**
+     * Add a footer to the concatenated output
+     * @param footer the footer
+     * @since Ant 1.6
+     */
+    public void addFooter(TextElement footer) {
+        this.footer = footer;
+    }
+
+    /**
+     * Append line.separator to files that do not end
+     * with a line.separator, default false.
+     * @param fixLastLine if true make sure each input file has
+     *                    new line on the concatenated stream
+     * @since Ant 1.6
+     */
+    public void setFixLastLine(boolean fixLastLine) {
+        this.fixLastLine = fixLastLine;
+    }
+
+    /**
+     * Specify the end of line to find and to add if
+     * not present at end of each input file. This attribute
+     * is used in conjunction with fixlastline.
+     * @param crlf the type of new line to add -
+     *              cr, mac, lf, unix, crlf, or dos
+     * @since Ant 1.6
+     */
+    public void setEol(FixCRLF.CrLf crlf) {
+        String s = crlf.getValue();
+        if (s.equals("cr") || s.equals("mac")) {
+            eolString = "\r";
+        } else if (s.equals("lf") || s.equals("unix")) {
+            eolString = "\n";
+        } else if (s.equals("crlf") || s.equals("dos")) {
+            eolString = "\r\n";
+        }
+    }
+
+    /**
+     * set the output writer, this is to allow
+     * concat to be used as a nested element
+     * @param outputWriter the output writer
+     * @since Ant 1.6
+     */
+    public void setWriter(Writer outputWriter) {
+        this.outputWriter = outputWriter;
+    }
+
+    /**
+     * set the binary attribute.
+     * if true, concat will concatenate the files
+     * byte for byte. This mode does not allow
+     * any filtering, or other modifications
+     * to the input streams.
+     * The default value is false.
+     * @since ant 1.6.2
+     * @param binary if true, enable binary mode
+     */
+    public void setBinary(boolean binary) {
+        this.binary = binary;
+    }
+
+    /**
+     * This method checks the attributes and performs the concatenation.
+     */
+    private void checkAndExecute() {
+
+        // treat empty nested text as no text
+        sanitizeText();
+
+        // if binary check if incompatible attributes are used
+        if (binary) {
+            if (destinationFile == null) {
+                throw new BuildException(
+                    "DestFile attribute is required for binary concatenation");
+            }
+
+            if (textBuffer != null) {
+                throw new BuildException(
+                    "Nested text is incompatible with binary concatenation");
+            }
+            if (encoding != null || outputEncoding != null) {
+                throw new BuildException(
+                    "Seting input or output encoding is incompatible with binary"
+                    + " concatenation");
+            }
+            if (filterChains != null) {
+                throw new BuildException(
+                    "Setting filters is incompatible with binary concatenation");
+            }
+            if (fixLastLine) {
+                throw new BuildException(
+                    "Setting fixlastline is incompatible with binary concatenation");
+            }
+            if (header != null || footer != null) {
+                throw new BuildException(
+                    "Nested header or footer is incompatible with binary concatenation");
+            }
+        }
+
+        if (destinationFile != null && outputWriter != null) {
+            throw new BuildException(
+                "Cannot specify both a destination file and an output writer");
+        }
+
+        // Sanity check our inputs.
+        if (sources.size() == 0 && textBuffer == null) {
+            // Nothing to concatenate!
+            throw new BuildException(
+                "At least one file must be provided, or some text.");
+        }
+
+        // If using filesets, disallow inline text. This is similar to
+        // using GNU 'cat' with file arguments -- stdin is simply
+        // ignored.
+        if (sources.size() > 0 && textBuffer != null) {
+            throw new BuildException(
+                "Cannot include inline text when using filesets.");
+        }
+
+        // Iterate thru the sources - paths, filesets and filelists
+        for (Enumeration e = sources.elements(); e.hasMoreElements();) {
+            Object o = e.nextElement();
+            if (o instanceof Path) {
+                Path path = (Path) o;
+                checkAddFiles(null, path.list());
+
+            } else if (o instanceof FileSet) {
+                FileSet fileSet = (FileSet) o;
+                DirectoryScanner scanner =
+                    fileSet.getDirectoryScanner(getProject());
+                checkAddFiles(fileSet.getDir(getProject()),
+                              scanner.getIncludedFiles());
+
+            } else if (o instanceof FileList) {
+                FileList fileList = (FileList) o;
+                checkAddFiles(fileList.getDir(getProject()),
+                              fileList.getFiles(getProject()));
+            }
+        }
+
+        // check if the files are outofdate
+        if (destinationFile != null && !forceOverwrite
+            && (sourceFiles.size() > 0) && destinationFile.exists()) {
+            boolean outofdate = false;
+            for (int i = 0; i < sourceFiles.size(); ++i) {
+                File file = (File) sourceFiles.elementAt(i);
+                if (file.lastModified() > destinationFile.lastModified()) {
+                    outofdate = true;
+                    break;
+                }
+            }
+            if (!outofdate) {
+                log(destinationFile + " is up-to-date.", Project.MSG_VERBOSE);
+                return; // no need to do anything
+            }
+        }
+
+        // Do nothing if all the sources are not present
+        // And textBuffer is null
+        if (textBuffer == null && sourceFiles.size() == 0
+            && header == null && footer == null) {
+            log("No existing files and no nested text, doing nothing",
+                Project.MSG_INFO);
+            return;
+        }
+
+        if (binary) {
+            binaryCat();
+        } else {
+            cat();
+        }
+    }
+
+    /**
+     * execute the concat task.
+     */
+    public void execute() {
+        try {
+            checkAndExecute();
+        } finally {
+            resetTask();
+        }
+    }
+
+    /**
+     * Reset state to default.
+     */
+    public void reset() {
+        append = false;
+        forceOverwrite = true;
+        destinationFile = null;
+        encoding = null;
+        outputEncoding = null;
+        fixLastLine = false;
+        sources.removeAllElements();
+        sourceFiles.removeAllElements();
+        filterChains = null;
+        footer = null;
+        header = null;
+    }
+
+    /**
+     * reset the used variables to allow the same task
+     * instance to be used again.
+     */
+    private void resetTask() {
+        sourceFiles.clear();
+    }
+
+    private void checkAddFiles(File base, String[] filenames) {
+        for (int i = 0; i < filenames.length; ++i) {
+            File file = new File(base, filenames[i]);
+            if (!file.exists()) {
+                log("File " + file + " does not exist.", Project.MSG_ERR);
+                continue;
+            }
+            if (destinationFile != null
+                && fileUtils.fileNameEquals(destinationFile, file)) {
+                throw new BuildException("Input file \""
+                                         + file + "\" "
+                                         + "is the same as the output file.");
+            }
+            sourceFiles.addElement(file);
+        }
+    }
+
+    /** perform the binary concatenation */
+    private void binaryCat() {
+        log("Binary concatenation of " + sourceFiles.size()
+            + " files to " + destinationFile);
+        FileOutputStream out = null;
+        FileInputStream in = null;
+        byte[] buffer = new byte[8 * 1024];
+        try {
+            try {
+                out = new FileOutputStream(destinationFile);
+            } catch (Exception t) {
+                throw new BuildException(
+                    "Unable to open " + destinationFile
+                    + " for writing", t);
+            }
+            for (Iterator i = sourceFiles.iterator(); i.hasNext();) {
+                File sourceFile = (File) i.next();
+                try {
+                    in = new FileInputStream(sourceFile);
+                } catch (Exception t) {
+                    throw new BuildException(
+                        "Unable to open input file " + sourceFile,
+                        t);
+                }
+                int count = 0;
+                do {
+                    try {
+                        count = in.read(buffer, 0, buffer.length);
+                    } catch (Exception t) {
+                        throw new BuildException(
+                            "Unable to read from " + sourceFile, t);
+                    }
+                    try {
+                        if (count > 0) {
+                            out.write(buffer, 0, count);
+                        }
+                    } catch (Exception t) {
+                        throw new BuildException(
+                            "Unable to write to " + destinationFile, t);
+                    }
+                } while (count > 0);
+
+                try {
+                    in.close();
+                } catch (Exception t) {
+                    throw new BuildException(
+                        "Unable to close " + sourceFile, t);
+                }
+                in = null;
+            }
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (Throwable t) {
+                    // Ignore
+                }
+            }
+            if (out != null) {
+                try {
+                    out.close();
+                } catch (Exception ex) {
+                    throw new BuildException(
+                        "Unable to close " + destinationFile, ex);
+                }
+            }
+        }
+    }
+
+    /** perform the concatenation */
+    private void cat() {
+        OutputStream os = null;
+        Reader       reader = null;
+        char[]       buffer = new char[BUFFER_SIZE];
+
+        try {
+
+            PrintWriter writer = null;
+
+            if (outputWriter != null) {
+                writer = new PrintWriter(outputWriter);
+            } else {
+                if (destinationFile == null) {
+                    // Log using WARN so it displays in 'quiet' mode.
+                    os = new LogOutputStream(this, Project.MSG_WARN);
+                } else {
+                    // ensure that the parent dir of dest file exists
+                    File parent = fileUtils.getParentFile(destinationFile);
+                    if (!parent.exists()) {
+                        parent.mkdirs();
+                    }
+
+                    os = new FileOutputStream(destinationFile.getAbsolutePath(),
+                                              append);
+                }
+
+                if (outputEncoding == null) {
+                    writer = new PrintWriter(
+                        new BufferedWriter(
+                            new OutputStreamWriter(os)));
+                } else {
+                    writer = new PrintWriter(
+                        new BufferedWriter(
+                            new OutputStreamWriter(os, outputEncoding)));
+                }
+            }
+
+            if (header != null) {
+                if (header.getFiltering()) {
+                    concatenate(
+                        buffer, writer, new StringReader(header.getValue()));
+                } else {
+                    writer.print(header.getValue());
+                }
+            }
+
+            if (textBuffer != null) {
+                reader = new StringReader(
+                    getProject().replaceProperties(textBuffer.substring(0)));
+            } else {
+                reader =  new MultiReader();
+            }
+
+            concatenate(buffer, writer, reader);
+
+            if (footer != null) {
+                if (footer.getFiltering()) {
+                    concatenate(
+                        buffer, writer, new StringReader(footer.getValue()));
+                } else {
+                    writer.print(footer.getValue());
+                }
+            }
+
+            writer.flush();
+            if (os != null) {
+                os.flush();
+            }
+
+        } catch (IOException ioex) {
+            throw new BuildException("Error while concatenating: "
+                                     + ioex.getMessage(), ioex);
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException ignore) {
+                    // ignore
+                }
+            }
+            if (os != null) {
+                try {
+                    os.close();
+                } catch (IOException ignore) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+
+    /** Concatenate a single reader to the writer using buffer */
+    private void concatenate(char[] buffer, Writer writer, Reader in)
+        throws IOException {
+        if (filterChains != null) {
+            ChainReaderHelper helper = new ChainReaderHelper();
+            helper.setBufferSize(BUFFER_SIZE);
+            helper.setPrimaryReader(in);
+            helper.setFilterChains(filterChains);
+            helper.setProject(getProject());
+            in = new BufferedReader(helper.getAssembledReader());
+        }
+
+        while (true) {
+            int nRead = in.read(buffer, 0, buffer.length);
+            if (nRead == -1) {
+                break;
+            }
+            writer.write(buffer, 0, nRead);
+        }
+
+        writer.flush();
+    }
+
+    /**
+     * Treat empty nested text as no text.
+     *
+     * <p>Depending on the XML parser, addText may have been called
+     * for &quot;ignorable whitespace&quot; as well.</p>
+     */
+    private void sanitizeText() {
+        if (textBuffer != null) {
+            if (textBuffer.substring(0).trim().length() == 0) {
+                textBuffer = null;
+            }
+        }
+    }
+
+    /**
+     * sub element points to a file or contains text
+     */
+    public static class TextElement extends ProjectComponent {
+        private String   value = "";
+        private boolean  trimLeading = false;
+        private boolean  trim = false;
+        private boolean  filtering = true;
+        private String   encoding = null;
+
+        /**
+         * whether to filter the text in this element
+         * or not.
+         *
+         * @param filtering true if the text should be filtered.
+         *                  the default value is true.
+         */
+        public void setFiltering(boolean filtering) {
+            this.filtering = filtering;
+        }
+
+        /** return the filtering attribute */
+        private boolean getFiltering() {
+            return filtering;
+        }
+
+        /**
+         * The encoding of the text element
+         *
+         * @param encoding the name of the charset used to encode
+         */
+        public void setEncoding(String encoding) {
+            this.encoding = encoding;
+        }
+
+        /**
+         * set the text using a file
+         * @param file the file to use
+         * @throws BuildException if the file does not exist, or cannot be
+         *                        read
+         */
+        public void setFile(File file) {
+            // non-existing files are not allowed
+            if (!file.exists()) {
+                throw new BuildException("File " + file + " does not exist.");
+            }
+
+            BufferedReader reader = null;
+            try {
+                if (this.encoding == null) {
+                    reader = new BufferedReader(new FileReader(file));
+                } else {
+                    reader = new BufferedReader(
+                        new InputStreamReader(new FileInputStream(file),
+                                              this.encoding));
+                }
+                value = fileUtils.readFully(reader);
+            } catch (IOException ex) {
+                throw new BuildException(ex);
+            } finally {
+                if (reader != null) {
+                    try {
+                        reader.close();
+                    } catch (Throwable t) {
+                        // ignore
+                    }
+                }
+            }
+        }
+
+        /**
+         * set the text using inline
+         * @param value the text to place inline
+         */
+        public void addText(String value) {
+            this.value += getProject().replaceProperties(value);
+        }
+
+        /**
+         * s:^\s*:: on each line of input
+         * @param strip if true do the trim
+         */
+        public void setTrimLeading(boolean strip) {
+            this.trimLeading = strip;
+        }
+
+        /**
+         * whether to call text.trim()
+         * @param trim if true trim the text
+         */
+        public void setTrim(boolean trim) {
+            this.trim = trim;
+        }
+
+        /**
+         * @return the text, after possible trimming
+         */
+        public String getValue() {
+            if (value == null) {
+                value = "";
+            }
+            if (value.trim().length() == 0) {
+                value = "";
+            }
+            if (trimLeading) {
+                char[] current = value.toCharArray();
+                StringBuffer b = new StringBuffer(current.length);
+                boolean startOfLine = true;
+                int pos = 0;
+                while (pos < current.length) {
+                    char ch = current[pos++];
+                    if (startOfLine) {
+                        if (ch == ' ' || ch == '\t') {
+                            continue;
+                        }
+                        startOfLine = false;
+                    }
+                    b.append(ch);
+                    if (ch == '\n' || ch == '\r') {
+                        startOfLine = true;
+                    }
+                }
+                value = b.toString();
+            }
+            if (trim) {
+                value = value.trim();
+            }
+            return value;
+        }
+    }
+
+    /**
+     * This class reads from each of the source files in turn.
+     * The concatentated result can then be filtered as
+     * a single stream.
+     */
+    private class MultiReader extends Reader {
+        private int    pos = 0;
+        private Reader reader = null;
+        private int    lastPos = 0;
+        private char[] lastChars = new char[eolString.length()];
+        private boolean needAddSeparator = false;
+
+        private Reader getReader() throws IOException {
+            if (reader == null) {
+                log("Concating file " + sourceFiles.elementAt(pos),
+                    Project.MSG_VERBOSE);
+                if (encoding == null) {
+                    reader = new BufferedReader(
+                        new FileReader((File) sourceFiles.elementAt(pos)));
+                } else {
+                    // invoke the zoo of io readers
+                    reader = new BufferedReader(
+                        new InputStreamReader(
+                            new FileInputStream(
+                                (File) sourceFiles.elementAt(pos)),
+                            encoding));
+                }
+                for (int i = 0; i < lastChars.length; ++i) {
+                    lastChars[i] = 0;
+                }
+            }
+            return reader;
+        }
+
+        /**
+         * Read a character from the current reader object. Advance
+         * to the next if the reader is finished.
+         * @return the character read, -1 for EOF on the last reader.
+         * @exception IOException - possibly thrown by the read for a reader
+         *            object.
+         */
+        public int read() throws IOException {
+            if (needAddSeparator) {
+                int ret = eolString.charAt(lastPos++);
+                if (lastPos >= eolString.length()) {
+                    lastPos = 0;
+                    needAddSeparator = false;
+                }
+                return ret;
+            }
+
+            while (pos < sourceFiles.size()) {
+                int ch = getReader().read();
+                if (ch == -1) {
+                    reader.close();
+                    reader = null;
+                    if (fixLastLine && isMissingEndOfLine()) {
+                        needAddSeparator = true;
+                        lastPos = 0;
+                    }
+                } else {
+                    addLastChar((char) ch);
+                    return ch;
+                }
+                pos++;
+            }
+            return -1;
+        }
+
+        /**
+         * Read into the buffer <code>cbuf</code>.
+         * @param cbuf The array to be read into.
+         * @param off The offset.
+         * @param len The length to read.
+         * @exception IOException - possibly thrown by the reads to the
+         *            reader objects.
+         */
+        public int read(char[] cbuf, int off, int len)
+            throws IOException {
+
+            int amountRead = 0;
+            while (pos < sourceFiles.size() || (needAddSeparator)) {
+                if (needAddSeparator) {
+                    cbuf[off] = eolString.charAt(lastPos++);
+                    if (lastPos >= eolString.length()) {
+                        lastPos = 0;
+                        needAddSeparator = false;
+                        pos++;
+                    }
+                    len--;
+                    off++;
+                    amountRead++;
+                    if (len == 0) {
+                        return amountRead;
+                    }
+                    continue;
+                }
+                int nRead = getReader().read(cbuf, off, len);
+                if (nRead == -1 || nRead == 0) {
+                    reader.close();
+                    reader = null;
+                    if (fixLastLine && isMissingEndOfLine()) {
+                        needAddSeparator = true;
+                        lastPos = 0;
+                    } else {
+                        pos++;
+                    }
+                } else {
+                    if (fixLastLine) {
+                        for (int i = nRead;
+                                 i > (nRead - lastChars.length);
+                                 --i) {
+                            if (i <= 0) {
+                                break;
+                            }
+                            addLastChar(cbuf[off + i - 1]);
+                        }
+                    }
+                    len -= nRead;
+                    off += nRead;
+                    amountRead += nRead;
+                    if (len == 0) {
+                        return amountRead;
+                    }
+                }
+            }
+            if (amountRead == 0) {
+                return -1;
+            } else {
+                return amountRead;
+            }
+        }
+
+        /**
+         * Close the current reader
+         */
+        public void close() throws IOException {
+            if (reader != null) {
+                reader.close();
+            }
+        }
+        /**
+         * if checking for end of line at end of file
+         * add a character to the lastchars buffer
+         */
+        private void addLastChar(char ch) {
+            for (int i = lastChars.length - 2; i >= 0; --i) {
+                lastChars[i] = lastChars[i + 1];
+            }
+            lastChars[lastChars.length - 1] = ch;
+        }
+
+        /**
+         * return true if the lastchars buffer does
+         * not contain the lineseparator
+         */
+        private boolean isMissingEndOfLine() {
+            for (int i = 0; i < lastChars.length; ++i) {
+                if (lastChars[i] != eolString.charAt(i)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ConditionTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ConditionTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ConditionTask.java	(revision 16139)
@@ -0,0 +1,107 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.taskdefs.condition.ConditionBase;
+
+/**
+ * Task to set a property conditionally using &lt;uptodate&gt;, &lt;available&gt;,
+ * and many other supported conditions.
+ *
+ * <p>This task supports boolean logic as well as pluggable conditions
+ * to decide, whether a property should be set.</p>
+ *
+ * <p>This task does not extend Task to take advantage of
+ * ConditionBase.</p>
+ *
+ *
+ * @since Ant 1.4
+ *
+ * @ant.task category="control"
+ */
+public class ConditionTask extends ConditionBase {
+
+    private String property = null;
+    private String value = "true";
+    private String alternative = null;
+
+    /**
+     * The name of the property to set. Required.
+     * @param p the name of the property
+     * @since Ant 1.4
+     */
+    public void setProperty(String p) {
+        property = p;
+    }
+
+    /**
+     * The value for the property to set, if condition evaluates to true.
+     * Defaults to "true".
+     * @param v the value of the property
+     * @since Ant 1.4
+     */
+    public void setValue(String v) {
+        value = v;
+    }
+
+    /**
+     * The value for the property to set, if condition evaluates to false.
+     * If this attribute is not specified, the property will not be set.
+     * @param e the alternate value of the property.
+     * @since Ant 1.6.3
+     */
+    public void setElse(String e) {
+        alternative = e;
+    }
+
+    /**
+     * See whether our nested condition holds and set the property.
+     *
+     * @since Ant 1.4
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+        if (countConditions() > 1) {
+            throw new BuildException("You must not nest more than one "
+                + "condition into <condition>");
+        }
+        if (countConditions() < 1) {
+            throw new BuildException("You must nest a condition into "
+                + "<condition>");
+        }
+        if (property == null) {
+            throw new BuildException("The property attribute is required.");
+        }
+        Condition c = (Condition) getConditions().nextElement();
+        if (c.eval()) {
+            log("Condition true; setting " + property + " to " + value,
+                Project.MSG_DEBUG);
+            getProject().setNewProperty(property, value);
+        } else if (alternative != null) {
+            log("Condition false; setting " + property + " to " + alternative,
+                Project.MSG_DEBUG);
+            getProject().setNewProperty(property, alternative);
+        } else {
+            log("Condition false; not setting " + property,
+                Project.MSG_DEBUG);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Copy.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Copy.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Copy.java	(revision 16139)
@@ -0,0 +1,672 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.types.FilterSet;
+import org.apache.tools.ant.types.FilterSetCollection;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.FlatFileNameMapper;
+import org.apache.tools.ant.util.IdentityMapper;
+import org.apache.tools.ant.util.SourceFileScanner;
+
+/**
+ * Copies a file or directory to a new file
+ * or directory.  Files are only copied if the source file is newer
+ * than the destination file, or when the destination file does not
+ * exist.  It is possible to explicitly overwrite existing files.</p>
+ *
+ * <p>This implementation is based on Arnout Kuiper's initial design
+ * document, the following mailing list discussions, and the
+ * copyfile/copydir tasks.</p>
+ *
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task category="filesystem"
+ */
+public class Copy extends Task {
+    protected File file = null;     // the source file
+    protected File destFile = null; // the destination file
+    protected File destDir = null;  // the destination directory
+    protected Vector filesets = new Vector();
+
+    private boolean enableMultipleMappings = false;
+    protected boolean filtering = false;
+    protected boolean preserveLastModified = false;
+    protected boolean forceOverwrite = false;
+    protected boolean flatten = false;
+    protected int verbosity = Project.MSG_VERBOSE;
+    protected boolean includeEmpty = true;
+    protected boolean failonerror = true;
+
+    protected Hashtable fileCopyMap = new Hashtable();
+    protected Hashtable dirCopyMap = new Hashtable();
+    protected Hashtable completeDirMap = new Hashtable();
+
+    protected Mapper mapperElement = null;
+    protected FileUtils fileUtils;
+    private Vector filterChains = new Vector();
+    private Vector filterSets = new Vector();
+    private String inputEncoding = null;
+    private String outputEncoding = null;
+    private long granularity = 0;
+
+    /**
+     * Copy task constructor.
+     */
+    public Copy() {
+        fileUtils = FileUtils.newFileUtils();
+        granularity = fileUtils.getFileTimestampGranularity();
+    }
+
+    /**
+     * @return the fileutils object
+     */
+    protected FileUtils getFileUtils() {
+        return fileUtils;
+    }
+
+    /**
+     * Sets a single source file to copy.
+     * @param file the file to copy
+     */
+    public void setFile(File file) {
+        this.file = file;
+    }
+
+    /**
+     * Sets the destination file.
+     * @param destFile the file to copy to
+     */
+    public void setTofile(File destFile) {
+        this.destFile = destFile;
+    }
+
+    /**
+     * Sets the destination directory.
+     * @param destDir the destination directory
+     */
+    public void setTodir(File destDir) {
+        this.destDir = destDir;
+    }
+
+    /**
+     * Adds a FilterChain.
+     * @return a filter chain object
+     */
+    public FilterChain createFilterChain() {
+        FilterChain filterChain = new FilterChain();
+        filterChains.addElement(filterChain);
+        return filterChain;
+    }
+
+    /**
+     * Adds a filterset.
+     * @return a filter set object
+     */
+    public FilterSet createFilterSet() {
+        FilterSet filterSet = new FilterSet();
+        filterSets.addElement(filterSet);
+        return filterSet;
+    }
+
+    /**
+     * Give the copied files the same last modified time as the original files.
+     * @param preserve a boolean string
+     * @deprecated setPreserveLastModified(String) has been deprecated and
+     *             replaced with setPreserveLastModified(boolean) to
+     *             consistently let the Introspection mechanism work.
+     */
+    public void setPreserveLastModified(String preserve) {
+        setPreserveLastModified(Project.toBoolean(preserve));
+    }
+
+    /**
+     * Give the copied files the same last modified time as the original files.
+     * @param preserve if true perverse the modified time, default is false
+     */
+    public void setPreserveLastModified(boolean preserve) {
+        preserveLastModified = preserve;
+    }
+
+    /**
+     * Whether to give the copied files the same last modified time as
+     * the original files.
+     * @return the preserveLastModified attribute
+     * @since 1.32, Ant 1.5
+     */
+    public boolean getPreserveLastModified() {
+        return preserveLastModified;
+    }
+
+    /**
+     * Get the filtersets being applied to this operation.
+     *
+     * @return a vector of FilterSet objects
+     */
+    protected Vector getFilterSets() {
+        return filterSets;
+    }
+
+    /**
+     * Get the filterchains being applied to this operation.
+     *
+     * @return a vector of FilterChain objects
+     */
+    protected Vector getFilterChains() {
+        return filterChains;
+    }
+
+    /**
+     * If true, enables filtering.
+     * @param filtering if true enable filtering, default is false
+     */
+    public void setFiltering(boolean filtering) {
+        this.filtering = filtering;
+    }
+
+    /**
+     * Overwrite any existing destination file(s).
+     * @param overwrite if true force overwriting of destination file(s)
+     *                  even if the destination file(s) are younger than
+     *                  the corresponding source file. Default is false.
+     */
+    public void setOverwrite(boolean overwrite) {
+        this.forceOverwrite = overwrite;
+    }
+
+    /**
+     * When copying directory trees, the files can be "flattened"
+     * into a single directory.  If there are multiple files with
+     * the same name in the source directory tree, only the first
+     * file will be copied into the "flattened" directory, unless
+     * the forceoverwrite attribute is true.
+     * @param flatten if true flatten the destination directory. Default
+     *                is false.
+     */
+    public void setFlatten(boolean flatten) {
+        this.flatten = flatten;
+    }
+
+    /**
+     * Used to force listing of all names of copied files.
+     * @param verbose output the names of copied files. Default is false.
+     */
+    public void setVerbose(boolean verbose) {
+        if (verbose) {
+            this.verbosity = Project.MSG_INFO;
+        } else {
+            this.verbosity = Project.MSG_VERBOSE;
+        }
+    }
+
+    /**
+     * Used to copy empty directories.
+     * @param includeEmpty if true copy empty directories. Default is true.
+     */
+    public void setIncludeEmptyDirs(boolean includeEmpty) {
+        this.includeEmpty = includeEmpty;
+    }
+
+    /**
+     * Attribute to handle mappers that return multiple
+     * mappings for a given source path.
+     * @param enableMultipleMappings If true the task will
+     *        copy to all the mappings for a given source path, if
+     *        false, only the first file or directory is
+     *        processed.
+     *        By default, this setting is false to provide backward
+     *        compatibility with earlier releases.
+     * @since 1.6
+     */
+    public void setEnableMultipleMappings(boolean enableMultipleMappings) {
+        this.enableMultipleMappings = enableMultipleMappings;
+    }
+
+    /**
+     * @return the value of the enableMultipleMapping attribute
+     */
+    public boolean isEnableMultipleMapping() {
+        return enableMultipleMappings;
+    }
+
+    /**
+     * If false, note errors to the output but keep going.
+     * @param failonerror true or false
+     */
+    public void setFailOnError(boolean failonerror) {
+        this.failonerror = failonerror;
+    }
+
+    /**
+     * Adds a set of files to copy.
+     * @param set a set of files to copy
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * Defines the mapper to map source to destination files.
+     * @return a mapper to be configured
+     * @exception BuildException if more than one mapper is defined
+     */
+    public Mapper createMapper() throws BuildException {
+        if (mapperElement != null) {
+            throw new BuildException("Cannot define more than one mapper",
+                                     getLocation());
+        }
+        mapperElement = new Mapper(getProject());
+        return mapperElement;
+    }
+
+    /**
+     * A nested filenamemapper
+     * @param fileNameMapper the mapper to add
+     * @since Ant 1.6.3
+     */
+    public void add(FileNameMapper fileNameMapper) {
+        createMapper().add(fileNameMapper);
+    }
+
+
+    /**
+     * Sets the character encoding
+     * @param encoding the character encoding
+     * @since 1.32, Ant 1.5
+     */
+    public void setEncoding(String encoding) {
+        this.inputEncoding = encoding;
+        if (outputEncoding == null) {
+            outputEncoding = encoding;
+        }
+    }
+
+    /**
+     * @return the character encoding, <code>null</code> if not set.
+     *
+     * @since 1.32, Ant 1.5
+     */
+    public String getEncoding() {
+        return inputEncoding;
+    }
+
+    /**
+     * Sets the character encoding for output files.
+     * @param encoding the character encoding
+     * @since Ant 1.6
+     */
+    public void setOutputEncoding(String encoding) {
+        this.outputEncoding = encoding;
+    }
+
+    /**
+     * @return the character encoding for output files,
+     * <code>null</code> if not set.
+     *
+     * @since Ant 1.6
+     */
+    public String getOutputEncoding() {
+        return outputEncoding;
+    }
+
+    /**
+     * The number of milliseconds leeway to give before deciding a
+     * target is out of date.
+     *
+     * <p>Default is 0 milliseconds, or 2 seconds on DOS systems.</p>
+     *
+     * @since Ant 1.6.2
+     */
+    public void setGranularity(long granularity) {
+        this.granularity = granularity;
+    }
+
+    /**
+     * Performs the copy operation.
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+        File savedFile = file; // may be altered in validateAttributes
+        File savedDestFile = destFile;
+        File savedDestDir = destDir;
+        FileSet savedFileSet = null;
+        if (file == null && destFile != null && filesets.size() == 1) {
+            // will be removed in validateAttributes
+            savedFileSet = (FileSet) filesets.elementAt(0);
+        }
+
+        // make sure we don't have an illegal set of options
+        validateAttributes();
+
+        try {
+
+            // deal with the single file
+            if (file != null) {
+                if (file.exists()) {
+                    if (destFile == null) {
+                        destFile = new File(destDir, file.getName());
+                    }
+
+                    if (forceOverwrite || !destFile.exists()
+                        || (file.lastModified() - granularity
+                                > destFile.lastModified())) {
+                        fileCopyMap.put(file.getAbsolutePath(),
+                                        new String[] {destFile.getAbsolutePath()});
+                    } else {
+                        log(file + " omitted as " + destFile
+                            + " is up to date.", Project.MSG_VERBOSE);
+                    }
+                } else {
+                    String message = "Warning: Could not find file "
+                        + file.getAbsolutePath() + " to copy.";
+                    if (!failonerror) {
+                        log(message);
+                    } else {
+                        throw new BuildException(message);
+                    }
+                }
+            }
+
+            // deal with the filesets
+            for (int i = 0; i < filesets.size(); i++) {
+                FileSet fs = (FileSet) filesets.elementAt(i);
+                DirectoryScanner ds = null;
+                try {
+                    ds = fs.getDirectoryScanner(getProject());
+                } catch (BuildException e) {
+                    if (failonerror
+                        || !e.getMessage().endsWith(" not found.")) {
+                        throw e;
+                    } else {
+                        log("Warning: " + e.getMessage());
+                        continue;
+                    }
+                }
+
+                File fromDir = fs.getDir(getProject());
+
+                String[] srcFiles = ds.getIncludedFiles();
+                String[] srcDirs = ds.getIncludedDirectories();
+                boolean isEverythingIncluded = ds.isEverythingIncluded()
+                    && (!fs.hasSelectors() && !fs.hasPatterns());
+                if (isEverythingIncluded
+                    && !flatten && mapperElement == null) {
+                    completeDirMap.put(fromDir, destDir);
+                }
+                scan(fromDir, destDir, srcFiles, srcDirs);
+            }
+
+            // do all the copy operations now...
+            try {
+                doFileOperations();
+            } catch (BuildException e) {
+                if (!failonerror) {
+                    log("Warning: " + e.getMessage(), Project.MSG_ERR);
+                } else {
+                    throw e;
+                }
+            }
+        } finally {
+            // clean up again, so this instance can be used a second
+            // time
+            file = savedFile;
+            destFile = savedDestFile;
+            destDir = savedDestDir;
+            if (savedFileSet != null) {
+                filesets.insertElementAt(savedFileSet, 0);
+            }
+
+            fileCopyMap.clear();
+            dirCopyMap.clear();
+            completeDirMap.clear();
+        }
+    }
+
+    /************************************************************************
+     **  protected and private methods
+     ************************************************************************/
+
+    /**
+     * Ensure we have a consistent and legal set of attributes, and set
+     * any internal flags necessary based on different combinations
+     * of attributes.
+     * @exception BuildException if an error occurs
+     */
+    protected void validateAttributes() throws BuildException {
+        if (file == null && filesets.size() == 0) {
+            throw new BuildException("Specify at least one source "
+                                     + "- a file or a fileset.");
+        }
+
+        if (destFile != null && destDir != null) {
+            throw new BuildException("Only one of tofile and todir "
+                                     + "may be set.");
+        }
+
+        if (destFile == null && destDir == null) {
+            throw new BuildException("One of tofile or todir must be set.");
+        }
+
+        if (file != null && file.exists() && file.isDirectory()) {
+            throw new BuildException("Use a fileset to copy directories.");
+        }
+
+        if (destFile != null && filesets.size() > 0) {
+            if (filesets.size() > 1) {
+                throw new BuildException(
+                                         "Cannot concatenate multiple files into a single file.");
+            } else {
+                FileSet fs = (FileSet) filesets.elementAt(0);
+                DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+                String[] srcFiles = ds.getIncludedFiles();
+
+                if (srcFiles.length == 0) {
+                    throw new BuildException(
+                                             "Cannot perform operation from directory to file.");
+                } else if (srcFiles.length == 1) {
+                    if (file == null) {
+                        file = new File(ds.getBasedir(), srcFiles[0]);
+                        filesets.removeElementAt(0);
+                    } else {
+                        throw new BuildException("Cannot concatenate multiple "
+                                                 + "files into a single file.");
+                    }
+                } else {
+                    throw new BuildException("Cannot concatenate multiple "
+                                             + "files into a single file.");
+                }
+            }
+        }
+
+        if (destFile != null) {
+            destDir = fileUtils.getParentFile(destFile);
+        }
+
+    }
+
+    /**
+     * Compares source files to destination files to see if they should be
+     * copied.
+     *
+     * @param fromDir  The source directory
+     * @param toDir    The destination directory
+     * @param files    A list of files to copy
+     * @param dirs     A list of directories to copy
+     */
+    protected void scan(File fromDir, File toDir, String[] files,
+                        String[] dirs) {
+        FileNameMapper mapper = null;
+        if (mapperElement != null) {
+            mapper = mapperElement.getImplementation();
+        } else if (flatten) {
+            mapper = new FlatFileNameMapper();
+        } else {
+            mapper = new IdentityMapper();
+        }
+
+        buildMap(fromDir, toDir, files, mapper, fileCopyMap);
+
+        if (includeEmpty) {
+            buildMap(fromDir, toDir, dirs, mapper, dirCopyMap);
+        }
+    }
+
+    /**
+     * Add to a map of files/directories to copy
+     *
+     * @param fromDir the source directory
+     * @param toDir   the destination directory
+     * @param names   a list of filenames
+     * @param mapper  a <code>FileNameMapper</code> value
+     * @param map     a map of source file to array of destination files
+     */
+    protected void buildMap(File fromDir, File toDir, String[] names,
+                            FileNameMapper mapper, Hashtable map) {
+
+        String[] toCopy = null;
+        if (forceOverwrite) {
+            Vector v = new Vector();
+            for (int i = 0; i < names.length; i++) {
+                if (mapper.mapFileName(names[i]) != null) {
+                    v.addElement(names[i]);
+                }
+            }
+            toCopy = new String[v.size()];
+            v.copyInto(toCopy);
+        } else {
+            SourceFileScanner ds = new SourceFileScanner(this);
+            toCopy = ds.restrict(names, fromDir, toDir, mapper, granularity);
+        }
+
+        for (int i = 0; i < toCopy.length; i++) {
+            File src = new File(fromDir, toCopy[i]);
+
+            String[] mappedFiles = mapper.mapFileName(toCopy[i]);
+
+            if (!enableMultipleMappings) {
+                map.put(src.getAbsolutePath(),
+                        new String[] {new File(toDir, mappedFiles[0]).getAbsolutePath()});
+            } else {
+                // reuse the array created by the mapper
+                for (int k = 0; k < mappedFiles.length; k++) {
+                    mappedFiles[k] = new File(toDir, mappedFiles[k]).getAbsolutePath();
+                }
+
+                map.put(src.getAbsolutePath(), mappedFiles);
+            }
+        }
+    }
+
+    /**
+     * Actually does the file (and possibly empty directory) copies.
+     * This is a good method for subclasses to override.
+     */
+    protected void doFileOperations() {
+        if (fileCopyMap.size() > 0) {
+            log("Copying " + fileCopyMap.size()
+                + " file" + (fileCopyMap.size() == 1 ? "" : "s")
+                + " to " + destDir.getAbsolutePath());
+
+            Enumeration e = fileCopyMap.keys();
+            while (e.hasMoreElements()) {
+                String fromFile = (String) e.nextElement();
+                String[] toFiles = (String[]) fileCopyMap.get(fromFile);
+
+                for (int i = 0; i < toFiles.length; i++) {
+                    String toFile = toFiles[i];
+
+                    if (fromFile.equals(toFile)) {
+                        log("Skipping self-copy of " + fromFile, verbosity);
+                        continue;
+                    }
+
+                    try {
+                        log("Copying " + fromFile + " to " + toFile, verbosity);
+
+                        FilterSetCollection executionFilters =
+                            new FilterSetCollection();
+                        if (filtering) {
+                            executionFilters
+                                .addFilterSet(getProject().getGlobalFilterSet());
+                        }
+                        for (Enumeration filterEnum = filterSets.elements();
+                            filterEnum.hasMoreElements();) {
+                            executionFilters
+                                .addFilterSet((FilterSet) filterEnum.nextElement());
+                        }
+                        fileUtils.copyFile(fromFile, toFile, executionFilters,
+                                           filterChains, forceOverwrite,
+                                           preserveLastModified, inputEncoding,
+                                           outputEncoding, getProject());
+                    } catch (IOException ioe) {
+                        String msg = "Failed to copy " + fromFile + " to " + toFile
+                            + " due to " + ioe.getMessage();
+                        File targetFile = new File(toFile);
+                        if (targetFile.exists() && !targetFile.delete()) {
+                            msg += " and I couldn't delete the corrupt " + toFile;
+                        }
+                        throw new BuildException(msg, ioe, getLocation());
+                    }
+                }
+            }
+        }
+
+        if (includeEmpty) {
+            Enumeration e = dirCopyMap.elements();
+            int createCount = 0;
+            while (e.hasMoreElements()) {
+                String[] dirs = (String[]) e.nextElement();
+                for (int i = 0; i < dirs.length; i++) {
+                    File d = new File(dirs[i]);
+                    if (!d.exists()) {
+                        if (!d.mkdirs()) {
+                            log("Unable to create directory "
+                                + d.getAbsolutePath(), Project.MSG_ERR);
+                        } else {
+                            createCount++;
+                        }
+                    }
+                }
+            }
+            if (createCount > 0) {
+                log("Copied " + dirCopyMap.size()
+                    + " empty director"
+                    + (dirCopyMap.size() == 1 ? "y" : "ies")
+                    + " to " + createCount
+                    + " empty director"
+                    + (createCount == 1 ? "y" : "ies") + " under "
+                    + destDir.getAbsolutePath());
+            }
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Copydir.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Copydir.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Copydir.java	(revision 16139)
@@ -0,0 +1,143 @@
+/*
+ * Copyright  2000,2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+
+/**
+ * Copies a directory.
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @deprecated The copydir task is deprecated since Ant 1.2.  Use copy instead.
+ */
+
+public class Copydir extends MatchingTask {
+
+    private File srcDir;
+    private File destDir;
+    private boolean filtering = false;
+    private boolean flatten = false;
+    private boolean forceOverwrite = false;
+    private Hashtable filecopyList = new Hashtable();
+
+    /**
+     * The src attribute
+     *
+     * @param src the source file
+     */
+    public void setSrc(File src) {
+        srcDir = src;
+    }
+
+    /**
+     * The dest attribute
+     *
+     * @param dest the destination file
+     */
+    public void setDest(File dest) {
+        destDir = dest;
+    }
+
+    public void setFiltering(boolean filter) {
+        filtering = filter;
+    }
+
+    public void setFlatten(boolean flatten) {
+        this.flatten = flatten;
+    }
+
+    public void setForceoverwrite(boolean force) {
+        forceOverwrite = force;
+    }
+
+    public void execute() throws BuildException {
+        log("DEPRECATED - The copydir task is deprecated.  Use copy instead.");
+
+        if (srcDir == null) {
+            throw new BuildException("src attribute must be set!",
+                                     getLocation());
+        }
+
+        if (!srcDir.exists()) {
+            throw new BuildException("srcdir " + srcDir.toString()
+                                     + " does not exist!", getLocation());
+        }
+
+        if (destDir == null) {
+            throw new BuildException("The dest attribute must be set.",
+                                     getLocation());
+        }
+
+        if (srcDir.equals(destDir)) {
+            log("Warning: src == dest", Project.MSG_WARN);
+        }
+
+        DirectoryScanner ds = super.getDirectoryScanner(srcDir);
+
+        try {
+            String[] files = ds.getIncludedFiles();
+            scanDir(srcDir, destDir, files);
+            if (filecopyList.size() > 0) {
+                log("Copying " + filecopyList.size() + " file"
+                    + (filecopyList.size() == 1 ? "" : "s")
+                    + " to " + destDir.getAbsolutePath());
+                Enumeration e = filecopyList.keys();
+                while (e.hasMoreElements()) {
+                    String fromFile = (String) e.nextElement();
+                    String toFile = (String) filecopyList.get(fromFile);
+                    try {
+                        getProject().copyFile(fromFile, toFile, filtering,
+                                         forceOverwrite);
+                    } catch (IOException ioe) {
+                        String msg = "Failed to copy " + fromFile + " to "
+                            + toFile + " due to " + ioe.getMessage();
+                        throw new BuildException(msg, ioe, getLocation());
+                    }
+                }
+            }
+        } finally {
+            filecopyList.clear();
+        }
+    }
+
+    private void scanDir(File from, File to, String[] files) {
+        for (int i = 0; i < files.length; i++) {
+            String filename = files[i];
+            File srcFile = new File(from, filename);
+            File destFile;
+            if (flatten) {
+                destFile = new File(to, new File(filename).getName());
+            } else {
+                destFile = new File(to, filename);
+            }
+            if (forceOverwrite
+                || (srcFile.lastModified() > destFile.lastModified())) {
+                filecopyList.put(srcFile.getAbsolutePath(),
+                                 destFile.getAbsolutePath());
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Copyfile.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Copyfile.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Copyfile.java	(revision 16139)
@@ -0,0 +1,92 @@
+/*
+ * Copyright  2000,2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Copies a file.
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @deprecated The copyfile task is deprecated since Ant 1.2.  Use
+ * copy instead.
+ */
+
+public class Copyfile extends Task {
+
+    private File srcFile;
+    private File destFile;
+    private boolean filtering = false;
+    private boolean forceOverwrite = false;
+
+    public void setSrc(File src) {
+        srcFile = src;
+    }
+
+    public void setForceoverwrite(boolean force) {
+        forceOverwrite = force;
+    }
+
+    public void setDest(File dest) {
+        destFile = dest;
+    }
+
+    public void setFiltering(String filter) {
+        filtering = Project.toBoolean(filter);
+    }
+
+    public void execute() throws BuildException {
+        log("DEPRECATED - The copyfile task is deprecated.  Use copy instead.");
+
+        if (srcFile == null) {
+            throw new BuildException("The src attribute must be present.",
+                                     getLocation());
+        }
+
+        if (!srcFile.exists()) {
+            throw new BuildException("src " + srcFile.toString()
+                                     + " does not exist.", getLocation());
+        }
+
+        if (destFile == null) {
+            throw new BuildException("The dest attribute must be present.",
+                                     getLocation());
+        }
+
+        if (srcFile.equals(destFile)) {
+            log("Warning: src == dest", Project.MSG_WARN);
+        }
+
+        if (forceOverwrite
+            || srcFile.lastModified() > destFile.lastModified()) {
+            try {
+                getProject().copyFile(srcFile, destFile, filtering, forceOverwrite);
+            } catch (IOException ioe) {
+                String msg = "Error copying file: " + srcFile.getAbsolutePath()
+                    + " due to " + ioe.getMessage();
+                throw new BuildException(msg);
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Cvs.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Cvs.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Cvs.java	(revision 16139)
@@ -0,0 +1,40 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+/**
+ * Performs operations on a CVS repository.
+ *
+ * original 1.20
+ *
+ *  NOTE: This implementation has been moved to AbstractCvsTask with
+ *  the addition of some accessors for extensibility.
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="scm"
+ */
+public class Cvs extends AbstractCvsTask {
+
+    /**
+     * CVS Task - now implemented by the Abstract CVS Task base class
+     */
+    public Cvs() {
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/DefBase.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/DefBase.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/DefBase.java	(revision 16139)
@@ -0,0 +1,159 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.ClasspathUtils;
+
+/**
+ * Base class for Definitions
+ * handling uri and class loading.
+ * (This was part of Definer)
+ *
+ * @since Ant 1.6
+ */
+public abstract class DefBase extends AntlibDefinition {
+    private ClassLoader createdLoader;
+    private ClasspathUtils.Delegate cpDelegate;
+
+    /**
+     * @param reverseLoader if true a delegated loader will take precedence over
+     *                      the parent
+     * @deprecated stop using this attribute
+     * @ant.attribute ignore="true"
+     */
+    public void setReverseLoader(boolean reverseLoader) {
+        getDelegate().setReverseLoader(reverseLoader);
+        log("The reverseloader attribute is DEPRECATED. It will be removed",
+            Project.MSG_WARN);
+    }
+
+    /**
+     * @return the classpath for this definition
+     */
+    public Path getClasspath() {
+        return getDelegate().getClasspath();
+    }
+
+    /**
+     * @return the reverse loader attribute of the classpath delegate.
+     */
+    public boolean isReverseLoader() {
+        return getDelegate().isReverseLoader();
+    }
+
+    /**
+     * Returns the loader id of the class path Delegate.
+     * @return the loader id
+     */
+    public String getLoaderId() {
+        return getDelegate().getClassLoadId();
+    }
+
+    /**
+     * Returns the class path id of the class path delegate.
+     * @return the class path id
+     */
+    public String getClasspathId() {
+        return getDelegate().getClassLoadId();
+    }
+
+    /**
+     * Set the classpath to be used when searching for component being defined
+     *
+     * @param classpath an Ant Path object containing the classpath.
+     */
+    public void setClasspath(Path classpath) {
+        getDelegate().setClasspath(classpath);
+    }
+
+    /**
+     * Create the classpath to be used when searching for component being
+     * defined
+     * @return the classpath of the this definition
+     */
+    public Path createClasspath() {
+        return getDelegate().createClasspath();
+    }
+
+    /**
+     * reference to a classpath to use when loading the files.
+     * To actually share the same loader, set loaderref as well
+     * @param r the reference to the classpath
+     */
+    public void setClasspathRef(Reference r) {
+        getDelegate().setClasspathref(r);
+    }
+
+    /**
+     * Use the reference to locate the loader. If the loader is not
+     * found, taskdef will use the specified classpath and register it
+     * with the specified name.
+     *
+     * This allow multiple taskdef/typedef to use the same class loader,
+     * so they can be used together. It eliminate the need to
+     * put them in the CLASSPATH.
+     *
+     * @param r the reference to locate the loader.
+     * @since Ant 1.5
+     */
+    public void setLoaderRef(Reference r) {
+        getDelegate().setLoaderRef(r);
+    }
+
+    /**
+     * create a classloader for this definition
+     * @return the classloader from the cpDelegate
+     */
+    protected ClassLoader createLoader() {
+        if (getAntlibClassLoader() != null && cpDelegate == null) {
+            return getAntlibClassLoader();
+        }
+        if (cpDelegate == null) {
+            cpDelegate = ClasspathUtils.getDelegate(this);
+        }
+        if (createdLoader == null) {
+            createdLoader = cpDelegate.getClassLoader();
+            // need to load Task via system classloader or the new
+            // task we want to define will never be a Task but always
+            // be wrapped into a TaskAdapter.
+            ((AntClassLoader) createdLoader)
+                .addSystemPackageRoot("org.apache.tools.ant");
+        }
+        return createdLoader;
+    }
+
+    /**
+     * @see org.apache.tools.ant.Task#init()
+     * @since Ant 1.6
+     */
+    public void init() throws BuildException {
+        super.init();
+    }
+
+    private ClasspathUtils.Delegate getDelegate() {
+        if (cpDelegate == null) {
+            cpDelegate = ClasspathUtils.getDelegate(this);
+        }
+        return cpDelegate;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/DefaultExcludes.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/DefaultExcludes.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/DefaultExcludes.java	(revision 16139)
@@ -0,0 +1,111 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+
+/**
+ * Alters the default excludes for the <strong>entire</strong> build..
+ *
+ *
+ * @since Ant 1.6
+ *
+ * @ant.task category="utility"
+ */
+public class DefaultExcludes extends Task {
+    private String add = "";
+    private String remove = "";
+    private boolean defaultrequested = false;
+    private boolean echo = false;
+
+    // by default, messages are always displayed
+    private int logLevel = Project.MSG_WARN;
+
+    /**
+     * Does the work.
+     *
+     * @exception BuildException if something goes wrong with the build
+     */
+    public void execute() throws BuildException {
+        if (!defaultrequested && add.equals("") && remove.equals("") && !echo) {
+            throw new BuildException("<defaultexcludes> task must set "
+                + "at least one attribute (echo=\"false\""
+                + " doesn't count since that is the default");
+        }
+        if (defaultrequested) {
+            DirectoryScanner.resetDefaultExcludes();
+        }
+        if (!add.equals("")) {
+            DirectoryScanner.addDefaultExclude(add);
+        }
+        if (!remove.equals("")) {
+            DirectoryScanner.removeDefaultExclude(remove);
+        }
+        if (echo) {
+            StringBuffer message
+                = new StringBuffer("Current Default Excludes:\n");
+            String[] excludes = DirectoryScanner.getDefaultExcludes();
+            for (int i = 0; i < excludes.length; i++) {
+                message.append("  " + excludes[i] + "\n");
+            }
+            log(message.toString(), logLevel);
+        }
+    }
+
+    /**
+     * go back to standard default patterns
+     *
+     * @param def if true go back to default patterns
+     */
+    public void setDefault(boolean def) {
+        defaultrequested = def;
+    }
+    /**
+     * Pattern to add to the default excludes
+     *
+     * @param add Sets the value for the pattern to exclude.
+     */
+    public void setAdd(String add) {
+        this.add = add;
+    }
+
+     /**
+     * Pattern to remove from the default excludes.
+     *
+     * @param remove Sets the value for the pattern that
+     *            should no longer be excluded.
+     */
+    public void setRemove(String remove) {
+        this.remove = remove;
+    }
+
+    /**
+     * If true, echo the default excludes.
+     *
+     * @param echo whether or not to echo the contents of
+     *             the default excludes.
+     */
+    public void setEcho(boolean echo) {
+        this.echo = echo;
+    }
+
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Definer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Definer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Definer.java	(revision 16139)
@@ -0,0 +1,509 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.NoSuchElementException;
+import java.util.Properties;
+
+import org.apache.tools.ant.AntTypeDefinition;
+import org.apache.tools.ant.ComponentHelper;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+/**
+ * Base class for Taskdef and Typedef - handles all
+ * the attributes for Typedef. The uri and class
+ * handling is handled by DefBase
+ *
+ * @since Ant 1.4
+ */
+public abstract class Definer extends DefBase {
+    private static class ResourceStack extends ThreadLocal {
+        public Object initialValue() {
+            return new HashMap();
+        }
+        Map getStack() {
+            return (Map) get();
+        }
+    }
+    private static ResourceStack resourceStack = new ResourceStack();
+    private String name;
+    private String classname;
+    private File file;
+    private String resource;
+
+    private   int    format = Format.PROPERTIES;
+    private   boolean definerSet = false;
+    private   int         onError = OnError.FAIL;
+    private   String      adapter;
+    private   String      adaptTo;
+
+    private   Class       adapterClass;
+    private   Class       adaptToClass;
+
+    /**
+     * Enumerated type for onError attribute
+     *
+     * @see EnumeratedAttribute
+     */
+    public static class OnError extends EnumeratedAttribute {
+        /** Enumerated values */
+        public static final int  FAIL = 0, REPORT = 1, IGNORE = 2;
+        /**
+         * Constructor
+         */
+        public OnError() {
+            super();
+        }
+
+        /**
+         * Constructor using a string.
+         * @param value the value of the attribute
+         */
+        public OnError(String value) {
+            setValue(value);
+        }
+
+        /**
+         * get the values
+         * @return an array of the allowed values for this attribute.
+         */
+        public String[] getValues() {
+            return new String[] {"fail", "report", "ignore"};
+        }
+    }
+
+    /**
+     * Enumerated type for format attribute
+     *
+     * @see EnumeratedAttribute
+     */
+    public static class Format extends EnumeratedAttribute {
+        /** Enumerated values */
+        public static final int PROPERTIES = 0, XML = 1;
+
+        /**
+         * get the values
+         * @return an array of the allowed values for this attribute.
+         */
+        public String[] getValues() {
+            return new String[] {"properties", "xml"};
+        }
+    }
+
+    /**
+     * What to do if there is an error in loading the class.
+     * <dl>
+     *   <li>error - throw build exception</li>
+     *   <li>report - output at warning level</li>
+     *   <li>ignore - output at debug level</li>
+     * </dl>
+     *
+     * @param onError an <code>OnError</code> value
+     */
+    public void setOnError(OnError onError) {
+        this.onError = onError.getIndex();
+    }
+
+    /**
+     * Sets the format of the file or resource
+     * @param format the enumerated value - xml or properties
+     */
+    public void setFormat(Format format) {
+        this.format = format.getIndex();
+    }
+
+    /**
+     * @return the name for this definition
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * @return the file containing definitions
+     */
+    public File getFile() {
+        return file;
+    }
+
+    /**
+     * @return the resource containing definitions
+     */
+    public String getResource() {
+        return resource;
+    }
+
+
+    /**
+     * Run the definition.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+        ClassLoader al = createLoader();
+
+        if (!definerSet) {
+            throw new BuildException(
+                "name, file or resource attribute of "
+                + getTaskName() + " is undefined", getLocation());
+        }
+
+        if (name != null) {
+            if (classname == null) {
+                throw new BuildException(
+                    "classname attribute of " + getTaskName() + " element "
+                    + "is undefined", getLocation());
+            }
+            addDefinition(al, name, classname);
+        } else {
+            if (classname != null) {
+                String msg = "You must not specify classname "
+                    + "together with file or resource.";
+                throw new BuildException(msg, getLocation());
+            }
+            Enumeration/*<URL>*/ urls = null;
+            if (file != null) {
+                final URL url = fileToURL();
+                if (url == null) {
+                    return;
+                }
+                urls = new Enumeration() {
+                    private boolean more = true;
+                    public boolean hasMoreElements() {
+                        return more;
+                    }
+                    public Object nextElement() throws NoSuchElementException {
+                        if (more) {
+                            more = false;
+                            return url;
+                        } else {
+                            throw new NoSuchElementException();
+                        }
+                    }
+                };
+            } else {
+                urls = resourceToURLs(al);
+            }
+
+            while (urls.hasMoreElements()) {
+                URL url = (URL) urls.nextElement();
+
+                int format = this.format;
+                if (url.toString().toLowerCase(Locale.US).endsWith(".xml")) {
+                    format = Format.XML;
+                }
+
+                if (format == Format.PROPERTIES) {
+                    loadProperties(al, url);
+                    break;
+                } else {
+                    if (resourceStack.getStack().get(url) != null) {
+                        log("Warning: Recursive loading of " + url
+                            + " ignored"
+                            + " at " + getLocation()
+                            + " originally loaded at "
+                            + resourceStack.getStack().get(url),
+                            Project.MSG_WARN);
+                    } else {
+                        try {
+                            resourceStack.getStack().put(url, getLocation());
+                            loadAntlib(al, url);
+                        } finally {
+                            resourceStack.getStack().remove(url);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    private URL fileToURL() {
+        if (!(file.exists())) {
+            log("File " + file + " does not exist", Project.MSG_WARN);
+            return null;
+        }
+        if (!(file.isFile())) {
+            log("File " + file + " is not a file", Project.MSG_WARN);
+            return null;
+        }
+        try {
+            return file.toURL();
+        } catch (Exception ex) {
+            log("File " + file + " cannot use as URL: "
+                + ex.toString(), Project.MSG_WARN);
+            return null;
+        }
+    }
+
+    private Enumeration/*<URL>*/ resourceToURLs(ClassLoader classLoader) {
+        Enumeration ret;
+        try {
+            ret = classLoader.getResources(resource);
+        } catch (IOException e) {
+            throw new BuildException(
+                "Could not fetch resources named " + resource,
+                e, getLocation());
+        }
+        if (!ret.hasMoreElements()) {
+            if (onError != OnError.IGNORE) {
+                log("Could not load definitions from resource "
+                    + resource + ". It could not be found.",
+                    Project.MSG_WARN);
+            }
+        }
+        return ret;
+    }
+
+    /**
+     * Load type definitions as properties from a url.
+     *
+     * @param al the classloader to use
+     * @param url the url to get the definitions from
+     */
+    protected void loadProperties(ClassLoader al, URL url) {
+        InputStream is = null;
+        try {
+            is = url.openStream();
+            if (is == null) {
+                log("Could not load definitions from " + url,
+                    Project.MSG_WARN);
+                return;
+            }
+            Properties props = new Properties();
+            props.load(is);
+            Enumeration keys = props.keys();
+            while (keys.hasMoreElements()) {
+                name = ((String) keys.nextElement());
+                classname = props.getProperty(name);
+                addDefinition(al, name, classname);
+            }
+        } catch (IOException ex) {
+            throw new BuildException(ex, getLocation());
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    /**
+     * Load an antlib from a url.
+     *
+     * @param classLoader the classloader to use.
+     * @param url the url to load the definitions from.
+     */
+    private void loadAntlib(ClassLoader classLoader, URL url) {
+        try {
+            Antlib antlib = Antlib.createAntlib(getProject(), url, getURI());
+            antlib.setClassLoader(classLoader);
+            antlib.setURI(getURI());
+            antlib.perform();
+        } catch (BuildException ex) {
+            throw ProjectHelper.addLocationToBuildException(
+                ex, getLocation());
+        }
+    }
+
+    /**
+     * Name of the property file  to load
+     * ant name/classname pairs from.
+     * @param file the file
+     */
+    public void setFile(File file) {
+        if (definerSet) {
+            tooManyDefinitions();
+        }
+        definerSet = true;
+        this.file = file;
+    }
+
+    /**
+     * Name of the property resource to load
+     * ant name/classname pairs from.
+     * @param res the resource to use
+     */
+    public void setResource(String res) {
+        if (definerSet) {
+            tooManyDefinitions();
+        }
+        definerSet = true;
+        this.resource = res;
+    }
+
+    /**
+     * Name of the definition
+     * @param name the name of the definition
+     */
+    public void setName(String name) {
+        if (definerSet) {
+            tooManyDefinitions();
+        }
+        definerSet = true;
+        this.name = name;
+    }
+
+    /**
+     * Returns the classname of the object we are defining.
+     * May be <code>null</code>.
+     * @return the class name
+     */
+    public String getClassname() {
+        return classname;
+    }
+
+    /**
+     * The full class name of the object being defined.
+     * Required, unless file or resource have
+     * been specified.
+     * @param classname the name of the class
+     */
+    public void setClassname(String classname) {
+        this.classname = classname;
+    }
+
+    /**
+     * Set the class name of the adapter class.
+     * An adapter class is used to proxy the
+     * definition class. It is used if the
+     * definition class is not assignable to
+     * the adaptto class, or if the adaptto
+     * class is not present.
+     *
+     * @param adapter the name of the adapter class
+     */
+
+    public void setAdapter(String adapter) {
+        this.adapter = adapter;
+    }
+
+    /**
+     * Set the adapter class.
+     *
+     * @param adapterClass the class to use to adapt the definition class
+     */
+    protected void setAdapterClass(Class adapterClass) {
+        this.adapterClass = adapterClass;
+    }
+
+    /**
+     * Set the classname of the class that the definition
+     * must be compatible with, either directly or
+     * by use of the adapter class.
+     *
+     * @param adaptTo the name of the adaptto class
+     */
+    public void setAdaptTo(String adaptTo) {
+        this.adaptTo = adaptTo;
+    }
+
+    /**
+     * Set the class for adaptToClass, to be
+     * used by derived classes, used instead of
+     * the adaptTo attribute.
+     *
+     * @param adaptToClass the class for adapto.
+     */
+    protected void setAdaptToClass(Class adaptToClass) {
+        this.adaptToClass = adaptToClass;
+    }
+
+
+    /**
+     * Add a definition using the attributes of Definer
+     *
+     * @param al the ClassLoader to use
+     * @param name the name of the definition
+     * @param classname the classname of the definition
+     * @exception BuildException if an error occurs
+     */
+    protected void addDefinition(ClassLoader al, String name, String classname)
+        throws BuildException {
+        Class cl = null;
+        try {
+            try {
+                name = ProjectHelper.genComponentName(getURI(), name);
+
+                if (onError != OnError.IGNORE) {
+                    cl = Class.forName(classname, true, al);
+                }
+
+                if (adapter != null) {
+                    adapterClass = Class.forName(adapter, true, al);
+                }
+
+                if (adaptTo != null) {
+                    adaptToClass = Class.forName(adaptTo, true, al);
+                }
+
+                AntTypeDefinition def = new AntTypeDefinition();
+                def.setName(name);
+                def.setClassName(classname);
+                def.setClass(cl);
+                def.setAdapterClass(adapterClass);
+                def.setAdaptToClass(adaptToClass);
+                def.setClassLoader(al);
+                if (cl != null) {
+                    def.checkClass(getProject());
+                }
+                ComponentHelper.getComponentHelper(getProject())
+                    .addDataTypeDefinition(def);
+            } catch (ClassNotFoundException cnfe) {
+                String msg = getTaskName() + " class " + classname
+                    + " cannot be found";
+                throw new BuildException(msg, cnfe, getLocation());
+            } catch (NoClassDefFoundError ncdfe) {
+                String msg = getTaskName() + " A class needed by class "
+                    + classname + " cannot be found: " + ncdfe.getMessage();
+                throw new BuildException(msg, ncdfe, getLocation());
+            }
+        } catch (BuildException ex) {
+            switch (onError) {
+                case OnError.FAIL:
+                    throw ex;
+                case OnError.REPORT:
+                    log(ex.getLocation() + "Warning: " + ex.getMessage(),
+                        Project.MSG_WARN);
+                    break;
+                default:
+                    log(ex.getLocation() + ex.getMessage(),
+                        Project.MSG_DEBUG);
+            }
+        }
+    }
+
+    private void tooManyDefinitions() {
+        throw new BuildException(
+            "Only one of the attributes name,file,resource"
+            + " can be set", getLocation());
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Delete.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Delete.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Delete.java	(revision 16139)
@@ -0,0 +1,671 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.selectors.AndSelector;
+import org.apache.tools.ant.types.selectors.ContainsRegexpSelector;
+import org.apache.tools.ant.types.selectors.ContainsSelector;
+import org.apache.tools.ant.types.selectors.DateSelector;
+import org.apache.tools.ant.types.selectors.DependSelector;
+import org.apache.tools.ant.types.selectors.DepthSelector;
+import org.apache.tools.ant.types.selectors.ExtendSelector;
+import org.apache.tools.ant.types.selectors.FilenameSelector;
+import org.apache.tools.ant.types.selectors.MajoritySelector;
+import org.apache.tools.ant.types.selectors.NoneSelector;
+import org.apache.tools.ant.types.selectors.NotSelector;
+import org.apache.tools.ant.types.selectors.OrSelector;
+import org.apache.tools.ant.types.selectors.PresentSelector;
+import org.apache.tools.ant.types.selectors.SelectSelector;
+import org.apache.tools.ant.types.selectors.SizeSelector;
+import org.apache.tools.ant.types.selectors.FileSelector;
+import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector;
+
+/**
+ * Deletes a file or directory, or set of files defined by a fileset.
+ * The original delete task would delete a file, or a set of files
+ * using the include/exclude syntax.  The deltree task would delete a
+ * directory tree.  This task combines the functionality of these two
+ * originally distinct tasks.
+ * <p>Currently Delete extends MatchingTask.  This is intended <i>only</i>
+ * to provide backwards compatibility for a release.  The future position
+ * is to use nested filesets exclusively.</p>
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task category="filesystem"
+ */
+public class Delete extends MatchingTask {
+    private static final int DELETE_RETRY_SLEEP_MILLIS = 10;
+    protected File file = null;
+    protected File dir = null;
+    protected Vector filesets = new Vector();
+    protected boolean usedMatchingTask = false;
+    // by default, remove matching empty dirs
+    protected boolean includeEmpty = false;
+
+    private int verbosity = Project.MSG_VERBOSE;
+    private boolean quiet = false;
+    private boolean failonerror = true;
+    private boolean deleteOnExit = false;
+
+    /**
+     * Set the name of a single file to be removed.
+     *
+     * @param file the file to be deleted
+     */
+    public void setFile(File file) {
+        this.file = file;
+    }
+
+    /**
+     * Set the directory from which files are to be deleted
+     *
+     * @param dir the directory path.
+     */
+    public void setDir(File dir) {
+        this.dir = dir;
+    }
+
+    /**
+     * If true, list all names of deleted files.
+     *
+     * @param verbose "true" or "on"
+     */
+    public void setVerbose(boolean verbose) {
+        if (verbose) {
+            this.verbosity = Project.MSG_INFO;
+        } else {
+            this.verbosity = Project.MSG_VERBOSE;
+        }
+    }
+
+    /**
+     * If true and the file does not exist, do not display a diagnostic
+     * message or modify the exit status to reflect an error.
+     * This means that if a file or directory cannot be deleted,
+     * then no error is reported. This setting emulates the
+     * -f option to the Unix &quot;rm&quot; command.
+     * Default is false meaning things are &quot;noisy&quot;
+     * @param quiet "true" or "on"
+     */
+    public void setQuiet(boolean quiet) {
+        this.quiet = quiet;
+        if (quiet) {
+            this.failonerror = false;
+        }
+    }
+
+    /**
+     * If false, note errors but continue.
+     *
+     * @param failonerror true or false
+     */
+     public void setFailOnError(boolean failonerror) {
+         this.failonerror = failonerror;
+     }
+
+    /**
+     * If true, on failure to delete, note the error and set
+     * the deleteonexit flag, and continue
+     *
+     * @param deleteOnExit true or false
+     */
+     public void setDeleteOnExit(boolean deleteOnExit) {
+         this.deleteOnExit = deleteOnExit;
+     }
+
+
+    /**
+     * If true, delete empty directories.
+     * @param includeEmpty if true delete empty directories (only
+     *                     for filesets). Default is false.
+     */
+    public void setIncludeEmptyDirs(boolean includeEmpty) {
+        this.includeEmpty = includeEmpty;
+    }
+
+   /**
+    * Adds a set of files to be deleted.
+    * @param set the set of files to be deleted
+    */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * add a name entry on the include list
+     * @return a NameEntry object to be configured
+     */
+    public PatternSet.NameEntry createInclude() {
+        usedMatchingTask = true;
+        return super.createInclude();
+    }
+
+    /**
+     * add a name entry on the include files list
+     * @return an NameEntry object to be configured
+     */
+    public PatternSet.NameEntry createIncludesFile() {
+        usedMatchingTask = true;
+        return super.createIncludesFile();
+    }
+
+    /**
+     * add a name entry on the exclude list
+     * @return an NameEntry object to be configured
+     */
+    public PatternSet.NameEntry createExclude() {
+        usedMatchingTask = true;
+        return super.createExclude();
+    }
+
+    /**
+     * add a name entry on the include files list
+     * @return an NameEntry object to be configured
+     */
+    public PatternSet.NameEntry createExcludesFile() {
+        usedMatchingTask = true;
+        return super.createExcludesFile();
+    }
+
+    /**
+     * add a set of patterns
+     * @return PatternSet object to be configured
+     */
+    public PatternSet createPatternSet() {
+        usedMatchingTask = true;
+        return super.createPatternSet();
+    }
+
+    /**
+     * Sets the set of include patterns. Patterns may be separated by a comma
+     * or a space.
+     *
+     * @param includes the string containing the include patterns
+     */
+    public void setIncludes(String includes) {
+        usedMatchingTask = true;
+        super.setIncludes(includes);
+    }
+
+    /**
+     * Sets the set of exclude patterns. Patterns may be separated by a comma
+     * or a space.
+     *
+     * @param excludes the string containing the exclude patterns
+     */
+    public void setExcludes(String excludes) {
+        usedMatchingTask = true;
+        super.setExcludes(excludes);
+    }
+
+    /**
+     * Sets whether default exclusions should be used or not.
+     *
+     * @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
+     *                           should be used, "false"|"off"|"no" when they
+     *                           shouldn't be used.
+     */
+    public void setDefaultexcludes(boolean useDefaultExcludes) {
+        usedMatchingTask = true;
+        super.setDefaultexcludes(useDefaultExcludes);
+    }
+
+    /**
+     * Sets the name of the file containing the includes patterns.
+     *
+     * @param includesfile A string containing the filename to fetch
+     * the include patterns from.
+     */
+    public void setIncludesfile(File includesfile) {
+        usedMatchingTask = true;
+        super.setIncludesfile(includesfile);
+    }
+
+    /**
+     * Sets the name of the file containing the includes patterns.
+     *
+     * @param excludesfile A string containing the filename to fetch
+     * the include patterns from.
+     */
+    public void setExcludesfile(File excludesfile) {
+        usedMatchingTask = true;
+        super.setExcludesfile(excludesfile);
+    }
+
+    /**
+     * Sets case sensitivity of the file system
+     *
+     * @param isCaseSensitive "true"|"on"|"yes" if file system is case
+     *                           sensitive, "false"|"off"|"no" when not.
+     */
+    public void setCaseSensitive(boolean isCaseSensitive) {
+        usedMatchingTask = true;
+        super.setCaseSensitive(isCaseSensitive);
+    }
+
+    /**
+     * Sets whether or not symbolic links should be followed.
+     *
+     * @param followSymlinks whether or not symbolic links should be followed
+     */
+    public void setFollowSymlinks(boolean followSymlinks) {
+        usedMatchingTask = true;
+        super.setFollowSymlinks(followSymlinks);
+    }
+
+    /**
+     * add a "Select" selector entry on the selector list
+     * @param selector the selector to be added
+     */
+    public void addSelector(SelectSelector selector) {
+        usedMatchingTask = true;
+        super.addSelector(selector);
+    }
+
+    /**
+     * add an "And" selector entry on the selector list
+     * @param selector the selector to be added
+     */
+    public void addAnd(AndSelector selector) {
+        usedMatchingTask = true;
+        super.addAnd(selector);
+    }
+
+    /**
+     * add an "Or" selector entry on the selector list
+     * @param selector the selector to be added
+     */
+    public void addOr(OrSelector selector) {
+        usedMatchingTask = true;
+        super.addOr(selector);
+    }
+
+    /**
+     * add a "Not" selector entry on the selector list
+     * @param selector the selector to be added
+     */
+    public void addNot(NotSelector selector) {
+        usedMatchingTask = true;
+        super.addNot(selector);
+    }
+
+    /**
+     * add a "None" selector entry on the selector list
+     * @param selector the selector to be added
+     */
+    public void addNone(NoneSelector selector) {
+        usedMatchingTask = true;
+        super.addNone(selector);
+    }
+
+    /**
+     * add a majority selector entry on the selector list
+     * @param selector the selector to be added
+     */
+    public void addMajority(MajoritySelector selector) {
+        usedMatchingTask = true;
+        super.addMajority(selector);
+    }
+
+    /**
+     * add a selector date entry on the selector list
+     * @param selector the selector to be added
+     */
+    public void addDate(DateSelector selector) {
+        usedMatchingTask = true;
+        super.addDate(selector);
+    }
+
+    /**
+     * add a selector size entry on the selector list
+     * @param selector the selector to be added
+     */
+    public void addSize(SizeSelector selector) {
+        usedMatchingTask = true;
+        super.addSize(selector);
+    }
+
+    /**
+     * add a selector filename entry on the selector list
+     * @param selector the selector to be added
+     */
+    public void addFilename(FilenameSelector selector) {
+        usedMatchingTask = true;
+        super.addFilename(selector);
+    }
+
+    /**
+     * add an extended selector entry on the selector list
+     * @param selector the selector to be added
+     */
+    public void addCustom(ExtendSelector selector) {
+        usedMatchingTask = true;
+        super.addCustom(selector);
+    }
+
+    /**
+     * add a contains selector entry on the selector list
+     * @param selector the selector to be added
+     */
+    public void addContains(ContainsSelector selector) {
+        usedMatchingTask = true;
+        super.addContains(selector);
+    }
+
+    /**
+     * add a present selector entry on the selector list
+     * @param selector the selector to be added
+     */
+    public void addPresent(PresentSelector selector) {
+        usedMatchingTask = true;
+        super.addPresent(selector);
+    }
+
+    /**
+     * add a depth selector entry on the selector list
+     * @param selector the selector to be added
+     */
+    public void addDepth(DepthSelector selector) {
+        usedMatchingTask = true;
+        super.addDepth(selector);
+    }
+
+    /**
+     * add a depends selector entry on the selector list
+     * @param selector the selector to be added
+     */
+    public void addDepend(DependSelector selector) {
+        usedMatchingTask = true;
+        super.addDepend(selector);
+    }
+
+    /**
+     * add a regular expression selector entry on the selector list
+     * @param selector the selector to be added
+     */
+    public void addContainsRegexp(ContainsRegexpSelector selector) {
+        usedMatchingTask = true;
+        super.addContainsRegexp(selector);
+    }
+
+    /**
+     * add the modified selector
+     * @param selector the selector to add
+     * @since ant 1.6
+     */
+    public void addModified(ModifiedSelector selector) {
+        usedMatchingTask = true;
+        super.addModified(selector);
+    }
+
+    /**
+     * add an arbitrary selector
+     * @param selector the selector to be added
+     * @since Ant 1.6
+     */
+    public void add(FileSelector selector) {
+        usedMatchingTask = true;
+        super.add(selector);
+    }
+
+    /**
+     * Delete the file(s).
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+        if (usedMatchingTask) {
+            log("DEPRECATED - Use of the implicit FileSet is deprecated.  "
+                + "Use a nested fileset element instead.");
+        }
+
+        if (file == null && dir == null && filesets.size() == 0) {
+            throw new BuildException("At least one of the file or dir "
+                                     + "attributes, or a fileset element, "
+                                     + "must be set.");
+        }
+
+        if (quiet && failonerror) {
+            throw new BuildException("quiet and failonerror cannot both be "
+                                     + "set to true", getLocation());
+        }
+
+
+        // delete the single file
+        if (file != null) {
+            if (file.exists()) {
+                if (file.isDirectory()) {
+                    log("Directory " + file.getAbsolutePath()
+                        + " cannot be removed using the file attribute.  "
+                        + "Use dir instead.");
+                } else {
+                    log("Deleting: " + file.getAbsolutePath());
+
+                    if (!delete(file)) {
+                        String message = "Unable to delete file "
+                            + file.getAbsolutePath();
+                        if (failonerror) {
+                            throw new BuildException(message);
+                        } else {
+                            log(message, quiet ? Project.MSG_VERBOSE
+                                               : Project.MSG_WARN);
+                        }
+                    }
+                }
+            } else {
+                log("Could not find file " + file.getAbsolutePath()
+                    + " to delete.",
+                    Project.MSG_VERBOSE);
+            }
+        }
+
+        // delete the directory
+        if (dir != null && dir.exists() && dir.isDirectory()
+            && !usedMatchingTask) {
+            /*
+               If verbosity is MSG_VERBOSE, that mean we are doing
+               regular logging (backwards as that sounds).  In that
+               case, we want to print one message about deleting the
+               top of the directory tree.  Otherwise, the removeDir
+               method will handle messages for _all_ directories.
+             */
+            if (verbosity == Project.MSG_VERBOSE) {
+                log("Deleting directory " + dir.getAbsolutePath());
+            }
+            removeDir(dir);
+        }
+
+        // delete the files in the filesets
+        for (int i = 0; i < filesets.size(); i++) {
+            FileSet fs = (FileSet) filesets.elementAt(i);
+            try {
+                DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+                String[] files = ds.getIncludedFiles();
+                String[] dirs = ds.getIncludedDirectories();
+                removeFiles(fs.getDir(getProject()), files, dirs);
+            } catch (BuildException be) {
+                // directory doesn't exist or is not readable
+                if (failonerror) {
+                    throw be;
+                } else {
+                    log(be.getMessage(),
+                        quiet ? Project.MSG_VERBOSE : Project.MSG_WARN);
+                }
+            }
+        }
+
+        // delete the files from the default fileset
+        if (usedMatchingTask && dir != null) {
+            try {
+                DirectoryScanner ds = super.getDirectoryScanner(dir);
+                String[] files = ds.getIncludedFiles();
+                String[] dirs = ds.getIncludedDirectories();
+                removeFiles(dir, files, dirs);
+            } catch (BuildException be) {
+                // directory doesn't exist or is not readable
+                if (failonerror) {
+                    throw be;
+                } else {
+                    log(be.getMessage(),
+                        quiet ? Project.MSG_VERBOSE : Project.MSG_WARN);
+                }
+            }
+        }
+    }
+
+//************************************************************************
+//  protected and private methods
+//************************************************************************
+    /**
+     * Accommodate Windows bug encountered in both Sun and IBM JDKs.
+     * Others possible. If the delete does not work, call System.gc(),
+     * wait a little and try again.
+     */
+    private boolean delete(File f) {
+        if (!f.delete()) {
+            if (Os.isFamily("windows")) {
+                System.gc();
+            }
+            try {
+                Thread.sleep(DELETE_RETRY_SLEEP_MILLIS);
+            } catch (InterruptedException ex) {
+                // Ignore Exception
+            }
+            if (!f.delete()) {
+                if (deleteOnExit) {
+                    int level = quiet ? Project.MSG_VERBOSE : Project.MSG_INFO;
+                    log("Failed to delete " + f + ", calling deleteOnExit."
+                        + " This attempts to delete the file when the ant jvm"
+                        + " has exited and might not succeed."
+                        , level);
+                    f.deleteOnExit();
+                    return true;
+                }
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Delete a directory
+     *
+     * @param d the directory to delete
+     */
+    protected void removeDir(File d) {
+        String[] list = d.list();
+        if (list == null) {
+            list = new String[0];
+        }
+        for (int i = 0; i < list.length; i++) {
+            String s = list[i];
+            File f = new File(d, s);
+            if (f.isDirectory()) {
+                removeDir(f);
+            } else {
+                log("Deleting " + f.getAbsolutePath(), verbosity);
+                if (!delete(f)) {
+                    String message = "Unable to delete file "
+                        + f.getAbsolutePath();
+                    if (failonerror) {
+                        throw new BuildException(message);
+                    } else {
+                        log(message,
+                            quiet ? Project.MSG_VERBOSE : Project.MSG_WARN);
+                    }
+                }
+            }
+        }
+        log("Deleting directory " + d.getAbsolutePath(), verbosity);
+        if (!delete(d)) {
+            String message = "Unable to delete directory "
+                + dir.getAbsolutePath();
+            if (failonerror) {
+                throw new BuildException(message);
+            } else {
+                log(message,
+                    quiet ? Project.MSG_VERBOSE : Project.MSG_WARN);
+            }
+        }
+    }
+
+    /**
+     * remove an array of files in a directory, and a list of subdirectories
+     * which will only be deleted if 'includeEmpty' is true
+     * @param d directory to work from
+     * @param files array of files to delete; can be of zero length
+     * @param dirs array of directories to delete; can of zero length
+     */
+    protected void removeFiles(File d, String[] files, String[] dirs) {
+        if (files.length > 0) {
+            log("Deleting " + files.length + " files from "
+                + d.getAbsolutePath());
+            for (int j = 0; j < files.length; j++) {
+                File f = new File(d, files[j]);
+                log("Deleting " + f.getAbsolutePath(), verbosity);
+                if (!delete(f)) {
+                    String message = "Unable to delete file "
+                        + f.getAbsolutePath();
+                    if (failonerror) {
+                        throw new BuildException(message);
+                    } else {
+                        log(message,
+                            quiet ? Project.MSG_VERBOSE : Project.MSG_WARN);
+                    }
+                }
+            }
+        }
+
+        if (dirs.length > 0 && includeEmpty) {
+            int dirCount = 0;
+            for (int j = dirs.length - 1; j >= 0; j--) {
+                File currDir = new File(d, dirs[j]);
+                String[] dirFiles = currDir.list();
+                if (dirFiles == null || dirFiles.length == 0) {
+                    log("Deleting " + currDir.getAbsolutePath(), verbosity);
+                    if (!delete(currDir)) {
+                        String message = "Unable to delete directory "
+                                + currDir.getAbsolutePath();
+                        if (failonerror) {
+                            throw new BuildException(message);
+                        } else {
+                            log(message,
+                                quiet ? Project.MSG_VERBOSE : Project.MSG_WARN);
+                        }
+                    } else {
+                        dirCount++;
+                    }
+                }
+            }
+
+            if (dirCount > 0) {
+                log("Deleted " + dirCount + " director"
+                    + (dirCount == 1 ? "y" : "ies")
+                    + " from " + d.getAbsolutePath());
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Deltree.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Deltree.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Deltree.java	(revision 16139)
@@ -0,0 +1,111 @@
+/*
+ * Copyright  2000,2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ *
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @deprecated The deltree task is deprecated since Ant 1.2.  Use
+ * delete instead.
+ */
+
+public class Deltree extends Task {
+
+    private File dir;
+
+    /**
+     * Set the directory to be deleted
+     *
+     * @param dir the root of the tree to be removed.
+     */
+    public void setDir(File dir) {
+        this.dir = dir;
+    }
+
+    /**
+     * Do the work.
+     *
+     * @exception BuildException if the task is not configured correctly or
+     * the tree cannot be removed.
+     */
+    public void execute() throws BuildException {
+        log("DEPRECATED - The deltree task is deprecated.  "
+            + "Use delete instead.");
+
+        if (dir == null) {
+            throw new BuildException("dir attribute must be set!", getLocation());
+        }
+
+        if (dir.exists()) {
+            if (!dir.isDirectory()) {
+                if (!dir.delete()) {
+                    throw new BuildException("Unable to delete directory "
+                                             + dir.getAbsolutePath(),
+                                             getLocation());
+                }
+                return;
+            }
+
+            log("Deleting: " + dir.getAbsolutePath());
+
+            try {
+                removeDir(dir);
+            } catch (IOException ioe) {
+                String msg = "Unable to delete " + dir.getAbsolutePath();
+                throw new BuildException(msg, getLocation());
+            }
+        }
+    }
+
+    private void removeDir(File dir) throws IOException {
+
+        // check to make sure that the given dir isn't a symlink
+        // the comparison of absolute path and canonical path
+        // catches this
+
+        //        if (dir.getCanonicalPath().equals(dir.getAbsolutePath())) {
+        // (costin) It will not work if /home/costin is symlink to
+        // /da0/home/costin ( taz for example )
+        String[] list = dir.list();
+        for (int i = 0; i < list.length; i++) {
+            String s = list[i];
+            File f = new File(dir, s);
+            if (f.isDirectory()) {
+                removeDir(f);
+            } else {
+                if (!f.delete()) {
+                    throw new BuildException("Unable to delete file "
+                                             + f.getAbsolutePath());
+                }
+            }
+        }
+        if (!dir.delete()) {
+            throw new BuildException("Unable to delete directory "
+                                     + dir.getAbsolutePath());
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/DependSet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/DependSet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/DependSet.java	(revision 16139)
@@ -0,0 +1,275 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileList;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Examines and removes out of date target files.  If any of the target files
+ * are out of date with respect to any of the source files, all target
+ * files are removed.  This is useful where dependencies cannot be
+ * computed (for example, dynamically interpreted parameters or files
+ * that need to stay in synch but are not directly linked) or where
+ * the ant task in question could compute them but does not (for
+ * example, the linked DTD for an XML file using the style task).
+ *
+ * nested arguments:
+ * <ul>
+ * <li>srcfileset     (fileset describing the source files to examine)
+ * <li>srcfilelist    (filelist describing the source files to examine)
+ * <li>targetfileset  (fileset describing the target files to examine)
+ * <li>targetfilelist (filelist describing the target files to examine)
+ * </ul>
+ * At least one instance of either a fileset or filelist for both source and
+ * target are required.
+ * <p>
+ * This task will examine each of the source files against each of the target
+ * files. If any target files are out of date with respect to any of the source
+ * files, all targets are removed. If any files named in a (src or target)
+ * filelist do not exist, all targets are removed.
+ * Hint: If missing files should be ignored, specify them as include patterns
+ * in filesets, rather than using filelists.
+ * </p><p>
+ * This task attempts to optimize speed of dependency checking.  It will stop
+ * after the first out of date file is found and remove all targets, rather
+ * than exhaustively checking every source vs target combination unnecessarily.
+ * </p><p>
+ * Example uses:
+ * <ul><li>
+ * Record the fact that an XML file must be up to date
+ * with respect to its XSD (Schema file), even though the XML file
+ * itself includes no reference to its XSD.
+ * </li><li>
+ * Record the fact that an XSL stylesheet includes other
+ * sub-stylesheets
+ * </li><li>
+ * Record the fact that java files must be recompiled if the ant build
+ * file changes
+ * </li></ul>
+ *
+ * @ant.task category="filesystem"
+ * @since Ant 1.4
+ */
+public class DependSet extends MatchingTask {
+
+    private static final FileUtils     FILE_UTILS = FileUtils.getFileUtils();
+
+    private Vector sourceFileSets  = new Vector();
+    private Vector sourceFileLists = new Vector();
+    private Vector targetFileSets  = new Vector();
+    private Vector targetFileLists = new Vector();
+
+    /**
+     * Creates a new DependSet Task.
+     **/
+    public DependSet() {
+    } //-- DependSet
+
+    /**
+     * Add a set of source files.
+     * @param fs the FileSet to add.
+     */
+    public void addSrcfileset(FileSet fs) {
+        sourceFileSets.addElement(fs);
+    }
+
+    /**
+     * Add a list of source files.
+     * @param fl the FileList to add.
+     */
+    public void addSrcfilelist(FileList fl) {
+        sourceFileLists.addElement(fl);
+    }
+
+    /**
+     * Add a set of target files.
+     * @param fs the FileSet to add.
+     */
+    public void addTargetfileset(FileSet fs) {
+        targetFileSets.addElement(fs);
+    }
+
+    /**
+     * Add a list of target files.
+     * @param fl the FileList to add.
+     */
+    public void addTargetfilelist(FileList fl) {
+        targetFileLists.addElement(fl);
+    }
+
+    /**
+     * Executes the task.
+     * @throws BuildException if errors occur.
+     */
+    public void execute() throws BuildException {
+
+        if ((sourceFileSets.size() == 0) && (sourceFileLists.size() == 0)) {
+          throw new BuildException("At least one <srcfileset> or <srcfilelist>"
+                                   + " element must be set");
+        }
+        if ((targetFileSets.size() == 0) && (targetFileLists.size() == 0)) {
+          throw new BuildException("At least one <targetfileset> or"
+                                   + " <targetfilelist> element must be set");
+        }
+        long now = (new Date()).getTime();
+        /*
+          We have to munge the time to allow for the filesystem time
+          granularity.
+        */
+        now += FILE_UTILS.getFileTimestampGranularity();
+
+        // Grab all the target files specified via filesets:
+        Vector allTargets = new Vector();
+        long oldestTargetTime = 0;
+        File oldestTarget = null;
+        Enumeration enumTargetSets = targetFileSets.elements();
+        while (enumTargetSets.hasMoreElements()) {
+
+           FileSet targetFS          = (FileSet) enumTargetSets.nextElement();
+           if (!targetFS.getDir(getProject()).exists()) {
+               // this is the same as if it was empty, no target files found
+               continue;
+           }
+           DirectoryScanner targetDS = targetFS.getDirectoryScanner(getProject());
+           String[] targetFiles      = targetDS.getIncludedFiles();
+
+           for (int i = 0; i < targetFiles.length; i++) {
+
+              File dest = new File(targetFS.getDir(getProject()), targetFiles[i]);
+              allTargets.addElement(dest);
+
+              if (dest.lastModified() > now) {
+                 log("Warning: " + targetFiles[i] + " modified in the future.",
+                     Project.MSG_WARN);
+              }
+              if (oldestTarget == null
+                || dest.lastModified() < oldestTargetTime) {
+                  oldestTargetTime = dest.lastModified();
+                  oldestTarget = dest;
+              }
+           }
+        }
+        // Grab all the target files specified via filelists:
+        boolean upToDate = true;
+        Enumeration enumTargetLists = targetFileLists.elements();
+        while (enumTargetLists.hasMoreElements()) {
+
+           FileList targetFL    = (FileList) enumTargetLists.nextElement();
+           String[] targetFiles = targetFL.getFiles(getProject());
+
+           for (int i = 0; i < targetFiles.length; i++) {
+
+              File dest = new File(targetFL.getDir(getProject()), targetFiles[i]);
+              if (!dest.exists()) {
+                 log(targetFiles[i] + " does not exist.", Project.MSG_VERBOSE);
+                 upToDate = false;
+                 continue;
+              } else {
+                 allTargets.addElement(dest);
+              }
+              if (dest.lastModified() > now) {
+                 log("Warning: " + targetFiles[i] + " modified in the future.",
+                     Project.MSG_WARN);
+              }
+              if (oldestTarget == null
+                  || dest.lastModified() < oldestTargetTime) {
+                  oldestTargetTime = dest.lastModified();
+                  oldestTarget = dest;
+              }
+           }
+        }
+        if (oldestTarget != null) {
+            log(oldestTarget + " is oldest target file", Project.MSG_VERBOSE);
+        } else {
+            // no target files, then we cannot remove any target files and
+            // skip the following tests right away
+            upToDate = false;
+        }
+        // Check targets vs source files specified via filelists:
+        if (upToDate) {
+           Enumeration enumSourceLists = sourceFileLists.elements();
+           while (upToDate && enumSourceLists.hasMoreElements()) {
+
+              FileList sourceFL    = (FileList) enumSourceLists.nextElement();
+              String[] sourceFiles = sourceFL.getFiles(getProject());
+
+              for (int i = 0; upToDate && i < sourceFiles.length; i++) {
+                 File src = new File(sourceFL.getDir(getProject()), sourceFiles[i]);
+
+                 if (src.lastModified() > now) {
+                    log("Warning: " + sourceFiles[i]
+                        + " modified in the future.", Project.MSG_WARN);
+                 }
+                 if (!src.exists()) {
+                    log(sourceFiles[i] + " does not exist.",
+                        Project.MSG_VERBOSE);
+                    upToDate = false;
+                    break;
+                 }
+                 if (src.lastModified() > oldestTargetTime) {
+                    upToDate = false;
+                    log(oldestTarget + " is out of date with respect to "
+                        + sourceFiles[i], Project.MSG_VERBOSE);
+                 }
+              }
+           }
+        }
+        // Check targets vs source files specified via filesets:
+        if (upToDate) {
+           Enumeration enumSourceSets = sourceFileSets.elements();
+           while (upToDate && enumSourceSets.hasMoreElements()) {
+
+              FileSet sourceFS          = (FileSet) enumSourceSets.nextElement();
+              DirectoryScanner sourceDS = sourceFS.getDirectoryScanner(getProject());
+              String[] sourceFiles      = sourceDS.getIncludedFiles();
+
+              for (int i = 0; upToDate && i < sourceFiles.length; i++) {
+                 File src = new File(sourceFS.getDir(getProject()), sourceFiles[i]);
+
+                 if (src.lastModified() > now) {
+                    log("Warning: " + sourceFiles[i]
+                        + " modified in the future.", Project.MSG_WARN);
+                 }
+                 if (src.lastModified() > oldestTargetTime) {
+                    upToDate = false;
+                    log(oldestTarget + " is out of date with respect to "
+                        + sourceFiles[i], Project.MSG_VERBOSE);
+                 }
+              }
+           }
+        }
+        if (!upToDate) {
+           log("Deleting all target files. ", Project.MSG_VERBOSE);
+           for (Enumeration e = allTargets.elements(); e.hasMoreElements();) {
+              File fileToRemove = (File) e.nextElement();
+              log("Deleting file " + fileToRemove.getAbsolutePath(),
+                  Project.MSG_VERBOSE);
+              fileToRemove.delete();
+           }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Dirname.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Dirname.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Dirname.java	(revision 16139)
@@ -0,0 +1,80 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Determines the directory name of the specified file.
+ *
+ * This task can accept the following attributes:
+ * <ul>
+ * <li>file
+ * <li>property
+ * </ul>
+ * Both <b>file</b> and <b>property</b> are required.
+ * <p>
+ * When this task executes, it will set the specified property to the
+ * value of the specified file up to, but not including, the last path
+ * element. If file is a file, the directory will be the current
+ * directory.
+ *
+ *
+ *
+ * @since Ant 1.5
+ *
+ * @ant.task category="property"
+ */
+
+public class Dirname extends Task {
+    private File file;
+    private String property;
+
+    /**
+     * Path to take the dirname of.
+     * @param file
+     */
+    public void setFile(File file) {
+        this.file = file;
+    }
+
+    /**
+     * The name of the property to set.
+     * @param property
+     */
+    public void setProperty(String property) {
+        this.property = property;
+    }
+
+
+    // The method executing the task
+    public void execute() throws BuildException {
+        if (property == null) {
+            throw new BuildException("property attribute required", getLocation());
+        }
+        if (file == null) {
+            throw new BuildException("file attribute required", getLocation());
+        } else {
+            String value = file.getParent();
+            getProject().setNewProperty(property, value);
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Ear.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Ear.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Ear.java	(revision 16139)
@@ -0,0 +1,135 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.ZipFileSet;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.zip.ZipOutputStream;
+
+/**
+ * Creates a EAR archive. Based on WAR task
+ *
+ *
+ * @since Ant 1.4
+ *
+ * @ant.task category="packaging"
+ */
+public class Ear extends Jar {
+
+    private File deploymentDescriptor;
+    private boolean descriptorAdded;
+    private static final FileUtils fu = FileUtils.newFileUtils();
+
+    /**
+     * Create an Ear task.
+     */
+    public Ear() {
+        super();
+        archiveType = "ear";
+        emptyBehavior = "create";
+    }
+
+    /**
+     * @deprecated Use setDestFile(destfile) instead
+     */
+    public void setEarfile(File earFile) {
+        setDestFile(earFile);
+    }
+
+    /**
+     * File to incorporate as application.xml.
+     */
+    public void setAppxml(File descr) {
+        deploymentDescriptor = descr;
+        if (!deploymentDescriptor.exists()) {
+            throw new BuildException("Deployment descriptor: "
+                                     + deploymentDescriptor
+                                     + " does not exist.");
+        }
+
+        // Create a ZipFileSet for this file, and pass it up.
+        ZipFileSet fs = new ZipFileSet();
+        fs.setFile(deploymentDescriptor);
+        fs.setFullpath("META-INF/application.xml");
+        super.addFileset(fs);
+    }
+
+
+    /**
+     * Adds zipfileset.
+     *
+     * @param fs zipfileset to add
+     */
+    public void addArchives(ZipFileSet fs) {
+        // We just set the prefix for this fileset, and pass it up.
+        // Do we need to do this? LH
+        fs.setPrefix("/");
+        super.addFileset(fs);
+    }
+
+
+    protected void initZipOutputStream(ZipOutputStream zOut)
+        throws IOException, BuildException {
+        // If no webxml file is specified, it's an error.
+        if (deploymentDescriptor == null && !isInUpdateMode()) {
+            throw new BuildException("appxml attribute is required", getLocation());
+        }
+
+        super.initZipOutputStream(zOut);
+    }
+
+    /**
+     * Overridden from Zip class to deal with application.xml
+     */
+    protected void zipFile(File file, ZipOutputStream zOut, String vPath,
+                           int mode)
+        throws IOException {
+        // If the file being added is META-INF/application.xml, we
+        // warn if it's not the one specified in the "appxml"
+        // attribute - or if it's being added twice, meaning the same
+        // file is specified by the "appxml" attribute and in a
+        // <fileset> element.
+        if (vPath.equalsIgnoreCase("META-INF/application.xml"))  {
+            if (deploymentDescriptor == null
+                || !fu.fileNameEquals(deploymentDescriptor, file)
+                || descriptorAdded) {
+                log("Warning: selected " + archiveType
+                    + " files include a META-INF/application.xml which will"
+                    + " be ignored (please use appxml attribute to "
+                    + archiveType + " task)", Project.MSG_WARN);
+            } else {
+                super.zipFile(file, zOut, vPath, mode);
+                descriptorAdded = true;
+            }
+        } else {
+            super.zipFile(file, zOut, vPath, mode);
+        }
+    }
+
+    /**
+     * Make sure we don't think we already have a application.xml next
+     * time this task gets executed.
+     */
+    protected void cleanUp() {
+        descriptorAdded = false;
+        super.cleanUp();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Echo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Echo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Echo.java	(revision 16139)
@@ -0,0 +1,147 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+/**
+ * Writes a message to the Ant logging facilities.
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="utility"
+ */
+public class Echo extends Task {
+    protected String message = ""; // required
+    protected File file = null;
+    protected boolean append = false;
+
+    // by default, messages are always displayed
+    protected int logLevel = Project.MSG_WARN;
+
+    /**
+     * Does the work.
+     *
+     * @exception BuildException if something goes wrong with the build
+     */
+    public void execute() throws BuildException {
+        if (file == null) {
+            log(message, logLevel);
+        } else {
+            FileWriter out = null;
+            try {
+                out = new FileWriter(file.getAbsolutePath(), append);
+                out.write(message, 0, message.length());
+            } catch (IOException ioe) {
+                throw new BuildException(ioe, getLocation());
+            } finally {
+                if (out != null) {
+                    try {
+                        out.close();
+                    } catch (IOException ioex) {
+                        //ignore
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Message to write.
+     *
+     * @param msg Sets the value for the message variable.
+     */
+    public void setMessage(String msg) {
+        this.message = msg;
+    }
+
+    /**
+     * File to write to.
+     * @param file the file to write to, if not set, echo to
+     *             standard output
+     */
+    public void setFile(File file) {
+        this.file = file;
+    }
+
+    /**
+     * If true, append to existing file.
+     * @param append if true, append to existing file, default
+     *               is false.
+     */
+    public void setAppend(boolean append) {
+        this.append = append;
+    }
+
+    /**
+     * Set a multiline message.
+     * @param msg the CDATA text to append to the output text
+     */
+    public void addText(String msg) {
+        message += getProject().replaceProperties(msg);
+    }
+
+    /**
+     * Set the logging level. Level should be one of
+     * <ul>
+     *  <li>error</li>
+     *  <li>warning</li>
+     *  <li>info</li>
+     *  <li>verbose</li>
+     *  <li>debug</li>
+     * </ul>
+     * <p>The default is &quot;warning&quot; to ensure that messages are
+     * displayed by default when using the -quiet command line option.</p>
+     * @param echoLevel the logging level
+     */
+    public void setLevel(EchoLevel echoLevel) {
+        String option = echoLevel.getValue();
+        if (option.equals("error")) {
+            logLevel = Project.MSG_ERR;
+        } else if (option.equals("warning")) {
+            logLevel = Project.MSG_WARN;
+        } else if (option.equals("info")) {
+            logLevel = Project.MSG_INFO;
+        } else if (option.equals("verbose")) {
+            logLevel = Project.MSG_VERBOSE;
+        } else {
+            // must be "debug"
+            logLevel = Project.MSG_DEBUG;
+        }
+    }
+
+    /**
+     * The enumerated values for the level attribute.
+     */
+    public static class EchoLevel extends EnumeratedAttribute {
+        /**
+         * @see EnumeratedAttribute#getValues
+         * @return the strings allowed for the level attribute
+         */
+        public String[] getValues() {
+            return new String[] {"error", "warning", "info",
+                                 "verbose", "debug"};
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Exec.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Exec.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Exec.java	(revision 16139)
@@ -0,0 +1,231 @@
+/*
+ * Copyright  2000,2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Executes a given command if the os platform is appropriate.
+ *
+ * <p><strong>As of Ant 1.2, this class is no longer the
+ * implementation of Ant's &lt;exec&gt; task - it is considered to be
+ * dead code by the Ant developers and is unmaintained.  Don't use
+ * it.</strong></p>
+
+ *
+ * @deprecated delegate to {@link org.apache.tools.ant.taskdefs.Execute Execute}
+ *             instead.
+ */
+public class Exec extends Task {
+    private String os;
+    private String out;
+    private File dir;
+    private String command;
+    protected PrintWriter fos = null;
+    private boolean failOnError = false;
+
+    public Exec() {
+        System.err.println("As of Ant 1.2 released in October 2000, "
+            + "the Exec class");
+        System.err.println("is considered to be dead code by the Ant "
+            + "developers and is unmaintained.");
+        System.err.println("Don\'t use it!");
+    }
+
+    public void execute() throws BuildException {
+        run(command);
+    }
+
+    protected int run(String command) throws BuildException {
+
+        int err = -1; // assume the worst
+
+        // test if os match
+        String myos = System.getProperty("os.name");
+        log("Myos = " + myos, Project.MSG_VERBOSE);
+        if ((os != null) && (os.indexOf(myos) < 0)) {
+            // this command will be executed only on the specified OS
+            log("Not found in " + os, Project.MSG_VERBOSE);
+            return 0;
+        }
+
+        // default directory to the project's base directory
+        if (dir == null) {
+          dir = getProject().getBaseDir();
+        }
+
+        if (myos.toLowerCase().indexOf("windows") >= 0) {
+            if (!dir.equals(getProject().resolveFile("."))) {
+                if (myos.toLowerCase().indexOf("nt") >= 0) {
+                    command = "cmd /c cd " + dir + " && " + command;
+                } else {
+                    String ant = getProject().getProperty("ant.home");
+                    if (ant == null) {
+                        throw new BuildException("Property 'ant.home' not "
+                            + "found", getLocation());
+                    }
+
+                    String antRun = getProject().resolveFile(ant + "/bin/antRun.bat").toString();
+                    command = antRun + " " + dir + " " + command;
+                }
+            }
+        } else {
+            String ant = getProject().getProperty("ant.home");
+            if (ant == null) {
+              throw new BuildException("Property 'ant.home' not found",
+                                       getLocation());
+            }
+            String antRun = getProject().resolveFile(ant + "/bin/antRun").toString();
+
+            command = antRun + " " + dir + " " + command;
+        }
+
+        try {
+            // show the command
+            log(command, Project.MSG_VERBOSE);
+
+            // exec command on system runtime
+            Process proc = Runtime.getRuntime().exec(command);
+
+            if (out != null)  {
+                fos = new PrintWriter(new FileWriter(out));
+                log("Output redirected to " + out, Project.MSG_VERBOSE);
+            }
+
+            // copy input and error to the output stream
+            StreamPumper inputPumper =
+                new StreamPumper(proc.getInputStream(), Project.MSG_INFO);
+            StreamPumper errorPumper =
+                new StreamPumper(proc.getErrorStream(), Project.MSG_WARN);
+
+            // starts pumping away the generated output/error
+            inputPumper.start();
+            errorPumper.start();
+
+            // Wait for everything to finish
+            proc.waitFor();
+            inputPumper.join();
+            errorPumper.join();
+            proc.destroy();
+
+            // close the output file if required
+            logFlush();
+
+            // check its exit value
+            err = proc.exitValue();
+            if (err != 0) {
+                if (failOnError) {
+                    throw new BuildException("Exec returned: " + err, getLocation());
+                } else {
+                    log("Result: " + err, Project.MSG_ERR);
+                }
+            }
+        } catch (IOException ioe) {
+            throw new BuildException("Error exec: " + command, ioe, getLocation());
+        } catch (InterruptedException ex) {
+            //ignore
+        }
+
+        return err;
+    }
+
+    public void setDir(String d) {
+        this.dir = getProject().resolveFile(d);
+    }
+
+    public void setOs(String os) {
+        this.os = os;
+    }
+
+    public void setCommand(String command) {
+        this.command = command;
+    }
+
+    public void setOutput(String out) {
+        this.out = out;
+    }
+
+    public void setFailonerror(boolean fail) {
+        failOnError = fail;
+    }
+
+    protected void outputLog(String line, int messageLevel) {
+        if (fos == null) {
+            log(line, messageLevel);
+        } else {
+            fos.println(line);
+        }
+    }
+
+    protected void logFlush() {
+        if (fos != null) {
+          fos.close();
+        }
+    }
+
+    // Inner class for continually pumping the input stream during
+    // Process's runtime.
+    class StreamPumper extends Thread {
+        private BufferedReader din;
+        private int messageLevel;
+        private boolean endOfStream = false;
+        private int SLEEP_TIME = 5;
+
+        public StreamPumper(InputStream is, int messageLevel) {
+            this.din = new BufferedReader(new InputStreamReader(is));
+            this.messageLevel = messageLevel;
+        }
+
+        public void pumpStream() throws IOException {
+            if (!endOfStream) {
+                String line = din.readLine();
+
+                if (line != null) {
+                    outputLog(line, messageLevel);
+                } else {
+                    endOfStream = true;
+                }
+            }
+        }
+
+        public void run() {
+            try {
+                try {
+                    while (!endOfStream) {
+                        pumpStream();
+                        sleep(SLEEP_TIME);
+                    }
+                } catch (InterruptedException ie) {
+                    //ignore
+                }
+                din.close();
+            } catch (IOException ioe) {
+                // ignore
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ExecTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ExecTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ExecTask.java	(revision 16139)
@@ -0,0 +1,664 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Environment;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.RedirectorElement;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Executes a given command if the os platform is appropriate.
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task category="control"
+ */
+public class ExecTask extends Task {
+
+    private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+    private String os;
+
+    private File dir;
+    protected boolean failOnError = false;
+    protected boolean newEnvironment = false;
+    private Long timeout = null;
+    private Environment env = new Environment();
+    protected Commandline cmdl = new Commandline();
+    private String resultProperty;
+    private boolean failIfExecFails = true;
+    private String executable;
+    private boolean resolveExecutable = false;
+    private boolean searchPath = false;
+    private boolean spawn = false;
+    private boolean incompatibleWithSpawn = false;
+
+    //include locally for screening purposes
+    private String inputString;
+    private File input;
+    private File output;
+    private File error;
+
+    protected Redirector redirector = new Redirector(this);
+    protected RedirectorElement redirectorElement;
+
+    /**
+     * Controls whether the VM (1.3 and above) is used to execute the
+     * command
+     */
+    private boolean vmLauncher = true;
+
+    /**
+     * Set whether or not you want the process to be spawned.
+     * Default is false.
+     * @param spawn if true you do not want Ant to wait for the end of the process.
+     * @since Ant 1.6
+     */
+    public void setSpawn(boolean spawn) {
+        this.spawn = spawn;
+    }
+
+    /**
+     * Set the timeout in milliseconds after which the process will be killed.
+     *
+     * @param value timeout in milliseconds.
+     *
+     * @since Ant 1.5
+     */
+    public void setTimeout(Long value) {
+        timeout = value;
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Set the timeout in milliseconds after which the process will be killed.
+     *
+     * @param value timeout in milliseconds.
+     */
+    public void setTimeout(Integer value) {
+        setTimeout(
+            (Long) ((value == null) ? null : new Long(value.intValue())));
+    }
+
+    /**
+     * Set the name of the executable program.
+     * @param value the name of the executable program.
+     */
+    public void setExecutable(String value) {
+        this.executable = value;
+        cmdl.setExecutable(value);
+    }
+
+    /**
+     * Set the working directory of the process.
+     * @param d the working directory of the process.
+     */
+    public void setDir(File d) {
+        this.dir = d;
+    }
+
+    /**
+     * List of operating systems on which the command may be executed.
+     * @param os list of operating systems on which the command may be executed.
+     */
+    public void setOs(String os) {
+        this.os = os;
+    }
+
+    /**
+     * Sets a command line.
+     * @param cmdl command line.
+     * @ant.attribute ignore="true"
+     */
+    public void setCommand(Commandline cmdl) {
+        log("The command attribute is deprecated. "
+            + "Please use the executable attribute and nested arg elements.",
+            Project.MSG_WARN);
+        this.cmdl = cmdl;
+    }
+
+    /**
+     * File the output of the process is redirected to. If error is not
+     * redirected, it too will appear in the output.
+     *
+     * @param out name of a file to which output should be sent.
+     */
+    public void setOutput(File out) {
+        this.output = out;
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Set the input file to use for the task.
+     *
+     * @param input name of a file from which to get input.
+     */
+    public void setInput(File input) {
+        if (inputString != null) {
+            throw new BuildException("The \"input\" and \"inputstring\" "
+                + "attributes cannot both be specified");
+        }
+        this.input = input;
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Set the string to use as input.
+     *
+     * @param inputString the string which is used as the input source.
+     */
+    public void setInputString(String inputString) {
+        if (input != null) {
+            throw new BuildException("The \"input\" and \"inputstring\" "
+                + "attributes cannot both be specified");
+        }
+        this.inputString = inputString;
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Controls whether error output of exec is logged. This is only useful when
+     * output is being redirected and error output is desired in the Ant log.
+     *
+     * @param logError set to true to log error output in the normal ant log.
+     */
+    public void setLogError(boolean logError) {
+        redirector.setLogError(logError);
+        incompatibleWithSpawn |= logError;
+    }
+
+    /**
+     * Set the File to which the error stream of the process should be redirected.
+     *
+     * @param error a file to which stderr should be sent.
+     *
+     * @since Ant 1.6
+     */
+    public void setError(File error) {
+        this.error = error;
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Sets the property name whose value should be set to the output of
+     * the process.
+     *
+     * @param outputProp name of property.
+     */
+    public void setOutputproperty(String outputProp) {
+        redirector.setOutputProperty(outputProp);
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Sets the name of the property whose value should be set to the error of
+     * the process.
+     *
+     * @param errorProperty name of property.
+     *
+     * @since Ant 1.6
+     */
+    public void setErrorProperty(String errorProperty) {
+        redirector.setErrorProperty(errorProperty);
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Fail if the command exits with a non-zero return code.
+     *
+     * @param fail if true fail the command on non-zero return code.
+     */
+    public void setFailonerror(boolean fail) {
+        failOnError = fail;
+        incompatibleWithSpawn |= fail;
+    }
+
+    /**
+     * Do not propagate old environment when new environment variables are specified.
+     *
+     * @param newenv if true, do not propagate old environment
+     * when new environment variables are specified.
+     */
+    public void setNewenvironment(boolean newenv) {
+        newEnvironment = newenv;
+    }
+
+    /**
+     * Set whether to attempt to resolve the executable to a file.
+     *
+     * @param resolveExecutable if true, attempt to resolve the
+     * path of the executable.
+     */
+    public void setResolveExecutable(boolean resolveExecutable) {
+        this.resolveExecutable = resolveExecutable;
+    }
+
+    /**
+     * Set whether to search nested, then
+     * system PATH environment variables for the executable.
+     *
+     * @param searchPath if true, search PATHs.
+     */
+    public void setSearchPath(boolean searchPath) {
+        this.searchPath = searchPath;
+    }
+
+    /**
+     * Indicates whether to attempt to resolve the executable to a
+     * file.
+     *
+     * @since Ant 1.6
+     */
+    public boolean getResolveExecutable() {
+        return resolveExecutable;
+    }
+
+    /**
+     * Add an environment variable to the launched process.
+     *
+     * @param var new environment variable.
+     */
+    public void addEnv(Environment.Variable var) {
+        env.addVariable(var);
+    }
+
+    /**
+     * Adds a command-line argument.
+     *
+     * @return new command line argument created.
+     */
+    public Commandline.Argument createArg() {
+        return cmdl.createArgument();
+    }
+
+    /**
+     * Sets the name of a property in which the return code of the
+     * command should be stored. Only of interest if failonerror=false.
+     *
+     * @since Ant 1.5
+     *
+     * @param resultProperty name of property.
+     */
+    public void setResultProperty(String resultProperty) {
+        this.resultProperty = resultProperty;
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Helper method to set result property to the
+     * passed in value if appropriate.
+     *
+     * @param result value desired for the result property value.
+     */
+    protected void maybeSetResultPropertyValue(int result) {
+        if (resultProperty != null) {
+            String res = Integer.toString(result);
+            getProject().setNewProperty(resultProperty, res);
+        }
+    }
+
+    /**
+     * Set whether to stop the build if program cannot be started.
+     * Defaults to true.
+     *
+     * @param flag stop the build if program cannot be started.
+     *
+     * @since Ant 1.5
+     */
+    public void setFailIfExecutionFails(boolean flag) {
+        failIfExecFails = flag;
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Set whether output should be appended to or overwrite an existing file.
+     * Defaults to false.
+     *
+     * @param append if true append is desired.
+     *
+     * @since 1.30, Ant 1.5
+     */
+    public void setAppend(boolean append) {
+        redirector.setAppend(append);
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Add a <code>RedirectorElement</code> to this task.
+     *
+     * @param redirectorElement   <code>RedirectorElement</code>.
+     * @since Ant 1.6.2
+     */
+    public void addConfiguredRedirector(RedirectorElement redirectorElement) {
+        if (this.redirectorElement != null) {
+            throw new BuildException("cannot have > 1 nested <redirector>s");
+        }
+        this.redirectorElement = redirectorElement;
+        incompatibleWithSpawn = true;
+    }
+
+
+    /**
+     * The method attempts to figure out where the executable is so that we can feed
+     * the full path. We first try basedir, then the exec dir, and then
+     * fallback to the straight executable name (i.e. on the path).
+     *
+     * @param exec the name of the executable.
+     * @param mustSearchPath if true, the executable will be looked up in
+     * the PATH environment and the absolute path is returned.
+     *
+     * @return the executable as a full path if it can be determined.
+     *
+     * @since Ant 1.6
+     */
+    protected String resolveExecutable(String exec, boolean mustSearchPath) {
+        if (!resolveExecutable) {
+            return exec;
+        }
+        // try to find the executable
+        File executableFile = getProject().resolveFile(exec);
+        if (executableFile.exists()) {
+            return executableFile.getAbsolutePath();
+        }
+        // now try to resolve against the dir if given
+        if (dir != null) {
+            executableFile = FILE_UTILS.resolveFile(dir, exec);
+            if (executableFile.exists()) {
+                return executableFile.getAbsolutePath();
+            }
+        }
+        // couldn't find it - must be on path
+        if (mustSearchPath) {
+            Path p = null;
+            String[] environment = env.getVariables();
+            if (environment != null) {
+                for (int i = 0; i < environment.length; i++) {
+                    if (isPath(environment[i])) {
+                        p = new Path(getProject(), environment[i].substring(5));
+                        break;
+                    }
+                }
+            }
+            if (p == null) {
+                Vector envVars = Execute.getProcEnvironment();
+                Enumeration e = envVars.elements();
+                while (e.hasMoreElements()) {
+                    String line = (String) e.nextElement();
+                    if (isPath(line)) {
+                        p = new Path(getProject(), line.substring(5));
+                        break;
+                    }
+                }
+            }
+            if (p != null) {
+                String[] dirs = p.list();
+                for (int i = 0; i < dirs.length; i++) {
+                    executableFile
+                        = FILE_UTILS.resolveFile(new File(dirs[i]), exec);
+                    if (executableFile.exists()) {
+                        return executableFile.getAbsolutePath();
+                    }
+                }
+            }
+        }
+        // mustSearchPath is false, or no PATH or not found - keep our
+        // fingers crossed.
+        return exec;
+    }
+
+    /**
+     * Do the work.
+     *
+     * @throws BuildException in a number of circumstances:
+     * <ul>
+     * <li>if failIfExecFails is set to true and the process cannot be started</li>
+     * <li>the java13command launcher can send build exceptions</li>
+     * <li>this list is not exhaustive or limitative</li>
+     * </ul>
+     */
+    public void execute() throws BuildException {
+        // Quick fail if this is not a valid OS for the command
+        if (!isValidOs()) {
+            return;
+        }
+        File savedDir = dir; // possibly altered in prepareExec
+        cmdl.setExecutable(resolveExecutable(executable, searchPath));
+        checkConfiguration();
+        try {
+            runExec(prepareExec());
+        } finally {
+            dir = savedDir;
+        }
+    }
+
+    /**
+     * Has the user set all necessary attributes?
+     * @throws BuildException if there are missing required parameters.
+     */
+    protected void checkConfiguration() throws BuildException {
+        if (cmdl.getExecutable() == null) {
+            throw new BuildException("no executable specified", getLocation());
+        }
+        if (dir != null && !dir.exists()) {
+            throw new BuildException("The directory you specified does not "
+                                     + "exist");
+        }
+        if (dir != null && !dir.isDirectory()) {
+            throw new BuildException("The directory you specified is not a "
+                                     + "directory");
+        }
+        if (spawn && incompatibleWithSpawn) {
+            getProject().log("spawn does not allow attributes related to input, "
+            + "output, error, result", Project.MSG_ERR);
+            getProject().log("spawn also does not allow timeout", Project.MSG_ERR);
+            getProject().log("finally, spawn is not compatible "
+                + "with a nested I/O <redirector>", Project.MSG_ERR);
+            throw new BuildException("You have used an attribute "
+                + "or nested element which is not compatible with spawn");
+        }
+        setupRedirector();
+    }
+
+    /**
+     * Set up properties on the redirector that we needed to store locally.
+     */
+    protected void setupRedirector() {
+        redirector.setInput(input);
+        redirector.setInputString(inputString);
+        redirector.setOutput(output);
+        redirector.setError(error);
+    }
+
+    /**
+     * Is this the OS the user wanted?
+     * @return boolean.
+     * <ul>
+     * <li>
+     * <code>true</code> if the os under which Ant is running is
+     * matches one os in the os attribute
+     * or if the os attribute is null</li>
+     * <li><code>false</code> otherwise.</li>
+     * </ul>
+     */
+    protected boolean isValidOs() {
+        // test if os match
+        String myos = System.getProperty("os.name");
+        log("Current OS is " + myos, Project.MSG_VERBOSE);
+        if ((os != null) && (os.indexOf(myos) < 0)) {
+            // this command will be executed only on the specified OS
+            log("This OS, " + myos
+                + " was not found in the specified list of valid OSes: " + os,
+                Project.MSG_VERBOSE);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Set whether to launch new process with VM, otherwise use the OS's shell.
+     * Default value is true.
+     * @param vmLauncher true if we want to launch new process with VM,
+     * false if we want to use the OS's shell.
+     */
+    public void setVMLauncher(boolean vmLauncher) {
+        this.vmLauncher = vmLauncher;
+    }
+
+    /**
+     * Create an Execute instance with the correct working directory set.
+     *
+     * @return an instance of the Execute class.
+     *
+     * @throws BuildException under unknown circumstances.
+     */
+    protected Execute prepareExec() throws BuildException {
+        // default directory to the project's base directory
+        if (dir == null) {
+            dir = getProject().getBaseDir();
+        }
+        if (redirectorElement != null) {
+            redirectorElement.configure(redirector);
+        }
+        Execute exe = new Execute(createHandler(), createWatchdog());
+        exe.setAntRun(getProject());
+        exe.setWorkingDirectory(dir);
+        exe.setVMLauncher(vmLauncher);
+        exe.setSpawn(spawn);
+        String[] environment = env.getVariables();
+        if (environment != null) {
+            for (int i = 0; i < environment.length; i++) {
+                log("Setting environment variable: " + environment[i],
+                    Project.MSG_VERBOSE);
+            }
+        }
+        exe.setNewenvironment(newEnvironment);
+        exe.setEnvironment(environment);
+        return exe;
+    }
+
+    /**
+     * A Utility method for this classes and subclasses to run an
+     * Execute instance (an external command).
+     *
+     * @param exe instance of the execute class.
+     *
+     * @throws IOException in case of problem to attach to the stdin/stdout/stderr
+     * streams of the process.
+     */
+    protected final void runExecute(Execute exe) throws IOException {
+        int returnCode = -1; // assume the worst
+
+        if (!spawn) {
+            returnCode = exe.execute();
+
+            //test for and handle a forced process death
+            if (exe.killedProcess()) {
+                String msg = "Timeout: killed the sub-process";
+                if (failOnError) {
+                    throw new BuildException(msg);
+                } else {
+                    log(msg, Project.MSG_WARN);
+                }
+            }
+            maybeSetResultPropertyValue(returnCode);
+            redirector.complete();
+            if (Execute.isFailure(returnCode)) {
+                if (failOnError) {
+                    throw new BuildException(getTaskType() + " returned: "
+                        + returnCode, getLocation());
+                } else {
+                    log("Result: " + returnCode, Project.MSG_ERR);
+                }
+            }
+        } else {
+            exe.spawn();
+        }
+    }
+
+    /**
+     * Run the command using the given Execute instance. This may be
+     * overridden by subclasses.
+     *
+     * @param exe instance of Execute to run.
+     *
+     * @throws BuildException if the new process could not be started
+     * only if failIfExecFails is set to true (the default).
+     */
+    protected void runExec(Execute exe) throws BuildException {
+        // show the command
+        log(cmdl.describeCommand(), Project.MSG_VERBOSE);
+
+        exe.setCommandline(cmdl.getCommandline());
+        try {
+            runExecute(exe);
+        } catch (IOException e) {
+            if (failIfExecFails) {
+                throw new BuildException("Execute failed: " + e.toString(), e,
+                                         getLocation());
+            } else {
+                log("Execute failed: " + e.toString(), Project.MSG_ERR);
+            }
+        } finally {
+            // close the output file if required
+            logFlush();
+        }
+    }
+
+    /**
+     * Create the StreamHandler to use with our Execute instance.
+     *
+     * @return instance of ExecuteStreamHandler.
+     *
+     * @throws BuildException under unknown circumstances.
+     */
+    protected ExecuteStreamHandler createHandler() throws BuildException {
+        return redirector.createHandler();
+    }
+
+    /**
+     * Create the Watchdog to kill a runaway process.
+     *
+     * @return instance of ExecuteWatchdog.
+     *
+     * @throws BuildException under unknown circumstances.
+     */
+    protected ExecuteWatchdog createWatchdog() throws BuildException {
+        return (timeout == null)
+            ? null : new ExecuteWatchdog(timeout.longValue());
+    }
+
+    /**
+     * Flush the output stream - if there is one.
+     */
+    protected void logFlush() {
+    }
+
+    private boolean isPath(String line) {
+        return line.startsWith("PATH=") || line.startsWith("Path=");
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Execute.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Execute.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Execute.java	(revision 16139)
@@ -0,0 +1,1184 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.OutputStream;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Runs an external program.
+ *
+ * @since Ant 1.2
+ *
+ */
+public class Execute {
+
+    /** Invalid exit code. **/
+    public static final int INVALID = Integer.MAX_VALUE;
+
+    private String[] cmdl = null;
+    private String[] env = null;
+    private int exitValue = INVALID;
+    private ExecuteStreamHandler streamHandler;
+    private ExecuteWatchdog watchdog;
+    private File workingDirectory = null;
+    private Project project = null;
+    private boolean newEnvironment = false;
+
+    /** Controls whether the VM is used to launch commands, where possible */
+    private boolean useVMLauncher = true;
+
+    private static String antWorkingDirectory = System.getProperty("user.dir");
+    private static CommandLauncher vmLauncher = null;
+    private static CommandLauncher shellLauncher = null;
+    private static Vector procEnvironment = null;
+    private boolean spawn = false;
+
+    /** Used to destroy processes when the VM exits. */
+    private static ProcessDestroyer processDestroyer = new ProcessDestroyer();
+
+    /**
+     * Builds a command launcher for the OS and JVM we are running under
+     */
+    static {
+        // Try using a JDK 1.3 launcher
+        try {
+            if (Os.isFamily("openvms")) {
+                vmLauncher = new VmsCommandLauncher();
+            } else if (!Os.isFamily("os/2")) {
+                vmLauncher = new Java13CommandLauncher();
+            }
+        } catch (NoSuchMethodException exc) {
+            // Ignore and keep trying
+        }
+
+        if (Os.isFamily("mac") && !Os.isFamily("unix")) {
+            // Mac
+            shellLauncher = new MacCommandLauncher(new CommandLauncher());
+        } else if (Os.isFamily("os/2")) {
+            // OS/2
+            shellLauncher = new OS2CommandLauncher(new CommandLauncher());
+        } else if (Os.isFamily("windows")) {
+            // Windows.  Need to determine which JDK we're running in
+
+            CommandLauncher baseLauncher;
+            if (System.getProperty("java.version").startsWith("1.1")) {
+                // JDK 1.1
+                baseLauncher = new Java11CommandLauncher();
+            } else {
+                // JDK 1.2
+                baseLauncher = new CommandLauncher();
+            }
+
+            if (!Os.isFamily("win9x")) {
+                // Windows XP/2000/NT
+                shellLauncher = new WinNTCommandLauncher(baseLauncher);
+            } else {
+                // Windows 98/95 - need to use an auxiliary script
+                shellLauncher
+                    = new ScriptCommandLauncher("bin/antRun.bat", baseLauncher);
+            }
+        } else if (Os.isFamily("netware")) {
+            // NetWare.  Need to determine which JDK we're running in
+            CommandLauncher baseLauncher;
+            if (System.getProperty("java.version").startsWith("1.1")) {
+                // JDK 1.1
+                baseLauncher = new Java11CommandLauncher();
+            } else {
+                // JDK 1.2
+                baseLauncher = new CommandLauncher();
+            }
+
+            shellLauncher
+                = new PerlScriptCommandLauncher("bin/antRun.pl", baseLauncher);
+        } else if (Os.isFamily("openvms")) {
+            // the vmLauncher already uses the shell
+            shellLauncher = vmLauncher;
+        } else {
+            // Generic
+            shellLauncher = new ScriptCommandLauncher("bin/antRun",
+                new CommandLauncher());
+        }
+    }
+
+    /**
+     * set whether or not you want the process to be spawned
+     * default is not spawned
+     *
+     * @param spawn if true you do not want ant to wait for the end of the process
+     *
+     * @since ant 1.6
+     */
+    public void setSpawn(boolean spawn) {
+        this.spawn = spawn;
+    }
+
+    /**
+     * Find the list of environment variables for this process.
+     *
+     * @return a vector containing the environment variables
+     * the vector elements are strings formatted like variable = value
+     */
+    public static synchronized Vector getProcEnvironment() {
+        if (procEnvironment != null) {
+            return procEnvironment;
+        }
+
+        procEnvironment = new Vector();
+        try {
+            ByteArrayOutputStream out = new ByteArrayOutputStream();
+            Execute exe = new Execute(new PumpStreamHandler(out));
+            exe.setCommandline(getProcEnvCommand());
+            // Make sure we do not recurse forever
+            exe.setNewenvironment(true);
+            int retval = exe.execute();
+            if (retval != 0) {
+                // Just try to use what we got
+            }
+
+            BufferedReader in =
+                new BufferedReader(new StringReader(toString(out)));
+
+            if (Os.isFamily("openvms")) {
+                procEnvironment = addVMSLogicals(procEnvironment, in);
+                return procEnvironment;
+            }
+
+            String var = null;
+            String line, lineSep = System.getProperty("line.separator");
+            while ((line = in.readLine()) != null) {
+                if (line.indexOf('=') == -1) {
+                    // Chunk part of previous env var (UNIX env vars can
+                    // contain embedded new lines).
+                    if (var == null) {
+                        var = lineSep + line;
+                    } else {
+                        var += lineSep + line;
+                    }
+                } else {
+                    // New env var...append the previous one if we have it.
+                    if (var != null) {
+                        procEnvironment.addElement(var);
+                    }
+                    var = line;
+                }
+            }
+            // Since we "look ahead" before adding, there's one last env var.
+            if (var != null) {
+                procEnvironment.addElement(var);
+            }
+        } catch (java.io.IOException exc) {
+            exc.printStackTrace();
+            // Just try to see how much we got
+        }
+        return procEnvironment;
+    }
+
+    private static String[] getProcEnvCommand() {
+        if (Os.isFamily("os/2")) {
+            // OS/2 - use same mechanism as Windows 2000
+            return new String[] {"cmd", "/c", "set" };
+        } else if (Os.isFamily("windows")) {
+            // Determine if we're running under XP/2000/NT or 98/95
+            if (Os.isFamily("win9x")) {
+                // Windows 98/95
+                return new String[] {"command.com", "/c", "set" };
+            } else {
+                // Windows XP/2000/NT/2003
+                return new String[] {"cmd", "/c", "set" };
+            }
+        } else if (Os.isFamily("z/os") || Os.isFamily("unix")) {
+            // On most systems one could use: /bin/sh -c env
+
+            // Some systems have /bin/env, others /usr/bin/env, just try
+            String[] cmd = new String[1];
+            if (new File("/bin/env").canRead()) {
+                cmd[0] = "/bin/env";
+            } else if (new File("/usr/bin/env").canRead()) {
+                cmd[0] = "/usr/bin/env";
+            } else {
+                // rely on PATH
+                cmd[0] = "env";
+            }
+            return cmd;
+        } else if (Os.isFamily("netware") || Os.isFamily("os/400")) {
+            // rely on PATH
+            return new String[] {"env"};
+        } else if (Os.isFamily("openvms")) {
+            return new String[] {"show", "logical"};
+        } else {
+            // MAC OS 9 and previous
+            //TODO: I have no idea how to get it, someone must fix it
+            return null;
+        }
+    }
+
+    /**
+     * ByteArrayOutputStream#toString doesn't seem to work reliably on
+     * OS/390, at least not the way we use it in the execution
+     * context.
+     *
+     * @param bos the output stream that one wants to read
+     * @return the output stream as a string, read with
+     * special encodings in the case of z/os and os/400
+     *
+     * @since Ant 1.5
+     */
+    public static String toString(ByteArrayOutputStream bos) {
+        if (Os.isFamily("z/os")) {
+            try {
+                return bos.toString("Cp1047");
+            } catch (java.io.UnsupportedEncodingException e) {
+                //noop default encoding used
+            }
+        } else if (Os.isFamily("os/400")) {
+            try {
+                return bos.toString("Cp500");
+            } catch (java.io.UnsupportedEncodingException e) {
+                //noop default encoding used
+            }
+        }
+        return bos.toString();
+    }
+
+    /**
+     * Creates a new execute object using <code>PumpStreamHandler</code> for
+     * stream handling.
+     */
+    public Execute() {
+        this(new PumpStreamHandler(), null);
+    }
+
+
+    /**
+     * Creates a new execute object.
+     *
+     * @param streamHandler the stream handler used to handle the input and
+     *        output streams of the subprocess.
+     */
+    public Execute(ExecuteStreamHandler streamHandler) {
+        this(streamHandler, null);
+    }
+
+    /**
+     * Creates a new execute object.
+     *
+     * @param streamHandler the stream handler used to handle the input and
+     *        output streams of the subprocess.
+     * @param watchdog a watchdog for the subprocess or <code>null</code> to
+     *        to disable a timeout for the subprocess.
+     */
+    public Execute(ExecuteStreamHandler streamHandler,
+                   ExecuteWatchdog watchdog) {
+        setStreamHandler(streamHandler);
+        this.watchdog = watchdog;
+    }
+
+    /**
+     * @since Ant 1.6
+     */
+    public void setStreamHandler(ExecuteStreamHandler streamHandler) {
+        this.streamHandler = streamHandler;
+    }
+
+    /**
+     * Returns the commandline used to create a subprocess.
+     *
+     * @return the commandline used to create a subprocess
+     */
+    public String[] getCommandline() {
+        return cmdl;
+    }
+
+
+    /**
+     * Sets the commandline of the subprocess to launch.
+     *
+     * @param commandline the commandline of the subprocess to launch
+     */
+    public void setCommandline(String[] commandline) {
+        cmdl = commandline;
+    }
+
+    /**
+     * Set whether to propagate the default environment or not.
+     *
+     * @param newenv whether to propagate the process environment.
+     */
+    public void setNewenvironment(boolean newenv) {
+        newEnvironment = newenv;
+    }
+
+    /**
+     * Returns the environment used to create a subprocess.
+     *
+     * @return the environment used to create a subprocess
+     */
+    public String[] getEnvironment() {
+        if (env == null || newEnvironment) {
+            return env;
+        }
+        return patchEnvironment();
+    }
+
+
+    /**
+     * Sets the environment variables for the subprocess to launch.
+     *
+     * @param env array of Strings, each element of which has
+     * an environment variable settings in format <em>key=value</em>
+     */
+    public void setEnvironment(String[] env) {
+        this.env = env;
+    }
+
+    /**
+     * Sets the working directory of the process to execute.
+     *
+     * <p>This is emulated using the antRun scripts unless the OS is
+     * Windows NT in which case a cmd.exe is spawned,
+     * or MRJ and setting user.dir works, or JDK 1.3 and there is
+     * official support in java.lang.Runtime.
+     *
+     * @param wd the working directory of the process.
+     */
+    public void setWorkingDirectory(File wd) {
+        if (wd == null || wd.getAbsolutePath().equals(antWorkingDirectory)) {
+            workingDirectory = null;
+        } else {
+            workingDirectory = wd;
+        }
+    }
+
+    /**
+     * Set the name of the antRun script using the project's value.
+     *
+     * @param project the current project.
+     *
+     * @throws BuildException not clear when it is going to throw an exception, but
+     * it is the method's signature
+     */
+    public void setAntRun(Project project) throws BuildException {
+        this.project = project;
+    }
+
+    /**
+     * Launch this execution through the VM, where possible, rather than through
+     * the OS's shell. In some cases and operating systems using the shell will
+     * allow the shell to perform additional processing such as associating an
+     * executable with a script, etc
+     *
+     * @param useVMLauncher true if exec should launch through the VM,
+     *                   false if the shell should be used to launch the
+     *                   command.
+     */
+    public void setVMLauncher(boolean useVMLauncher) {
+        this.useVMLauncher = useVMLauncher;
+    }
+
+    /**
+     * Creates a process that runs a command.
+     *
+     * @param project the Project, only used for logging purposes, may be null.
+     * @param command the command to run
+     * @param env the environment for the command
+     * @param dir the working directory for the command
+     * @param useVM use the built-in exec command for JDK 1.3 if available.
+     * @return the process started
+     * @throws IOException forwarded from the particular launcher used
+     *
+     * @since Ant 1.5
+     */
+    public static Process launch(Project project, String[] command,
+                                 String[] env, File dir, boolean useVM)
+        throws IOException {
+        CommandLauncher launcher
+            = vmLauncher != null ? vmLauncher : shellLauncher;
+        if (!useVM) {
+            launcher = shellLauncher;
+        }
+
+        if (dir != null && !dir.exists()) {
+            throw new BuildException(dir + " doesn't exist.");
+        }
+        return launcher.exec(project, command, env, dir);
+    }
+
+    /**
+     * Runs a process defined by the command line and returns its exit status.
+     *
+     * @return the exit status of the subprocess or <code>INVALID</code>
+     * @exception java.io.IOException The exception is thrown, if launching
+     *            of the subprocess failed
+     */
+    public int execute() throws IOException {
+        if (workingDirectory != null && !workingDirectory.exists()) {
+            throw new BuildException(workingDirectory + " doesn't exist.");
+        }
+        final Process process = launch(project, getCommandline(),
+                                       getEnvironment(), workingDirectory,
+                                       useVMLauncher);
+
+        try {
+            streamHandler.setProcessInputStream(process.getOutputStream());
+            streamHandler.setProcessOutputStream(process.getInputStream());
+            streamHandler.setProcessErrorStream(process.getErrorStream());
+        } catch (IOException e) {
+            process.destroy();
+            throw e;
+        }
+        streamHandler.start();
+
+        try {
+            // add the process to the list of those to destroy if the VM exits
+            //
+            processDestroyer.add(process);
+
+            if (watchdog != null) {
+                watchdog.start(process);
+            }
+            waitFor(process);
+
+            if (watchdog != null) {
+                watchdog.stop();
+            }
+            streamHandler.stop();
+            closeStreams(process);
+
+            if (watchdog != null) {
+                watchdog.checkException();
+            }
+            return getExitValue();
+        } catch (ThreadDeath t) {
+            // #31928: forcibly kill it before continuing.
+            process.destroy();
+            throw t;
+        } finally {
+            // remove the process to the list of those to destroy if the VM exits
+            //
+            processDestroyer.remove(process);
+        }
+    }
+
+    /**
+     * Starts a process defined by the command line.
+     * Ant will not wait for this process, nor log its output
+     *
+     * @throws java.io.IOException The exception is thrown, if launching
+     *            of the subprocess failed
+     * @since ant 1.6
+     */
+    public void spawn() throws IOException {
+        if (workingDirectory != null && !workingDirectory.exists()) {
+            throw new BuildException(workingDirectory + " doesn't exist.");
+        }
+        final Process process = launch(project, getCommandline(),
+                                       getEnvironment(), workingDirectory,
+                                       useVMLauncher);
+        if (Os.isFamily("windows")) {
+            try {
+                Thread.sleep(1000);
+            } catch (InterruptedException e) {
+                project.log("interruption in the sleep after having spawned a process",
+                    Project.MSG_VERBOSE);
+            }
+        }
+
+        OutputStream dummyOut = new OutputStream() {
+            public void write(int b) throws IOException {
+            }
+        };
+
+        ExecuteStreamHandler streamHandler = new PumpStreamHandler(dummyOut);
+        streamHandler.setProcessErrorStream(process.getErrorStream());
+        streamHandler.setProcessOutputStream(process.getInputStream());
+        streamHandler.start();
+        process.getOutputStream().close();
+
+        project.log("spawned process " + process.toString(), Project.MSG_VERBOSE);
+    }
+
+    /**
+     * wait for a given process
+     *
+     * @param process the process one wants to wait for
+     */
+    protected void waitFor(Process process) {
+        try {
+            process.waitFor();
+            setExitValue(process.exitValue());
+        } catch (InterruptedException e) {
+            process.destroy();
+        }
+    }
+
+    /**
+     * set the exit value
+     *
+     * @param value exit value of the process
+     */
+    protected void setExitValue(int value) {
+        exitValue = value;
+    }
+
+    /**
+     * Query the exit value of the process.
+     * @return the exit value or Execute.INVALID if no exit value has
+     * been received
+     */
+    public int getExitValue() {
+        return exitValue;
+    }
+
+    /**
+     * Checks whether <code>exitValue</code> signals a failure on the current
+     * system (OS specific).
+     *
+     * <p><b>Note</b> that this method relies on the conventions of
+     * the OS, it will return false results if the application you are
+     * running doesn't follow these conventions.  One notable
+     * exception is the Java VM provided by HP for OpenVMS - it will
+     * return 0 if successful (like on any other platform), but this
+     * signals a failure on OpenVMS.  So if you execute a new Java VM
+     * on OpenVMS, you cannot trust this method.</p>
+     *
+     * @param exitValue the exit value (return code) to be checked
+     * @return <code>true</code> if <code>exitValue</code> signals a failure
+     */
+    public static boolean isFailure(int exitValue) {
+        if (Os.isFamily("openvms")) {
+            // even exit value signals failure
+            return (exitValue % 2) == 0;
+        } else {
+            // non zero exit value signals failure
+            return exitValue != 0;
+        }
+    }
+
+    /**
+     * test for an untimely death of the process
+     * @return true if a watchdog had to kill the process
+     * @since Ant 1.5
+     */
+    public boolean killedProcess() {
+        return watchdog != null && watchdog.killedProcess();
+    }
+
+    /**
+     * Patch the current environment with the new values from the user.
+     * @return the patched environment
+     */
+    private String[] patchEnvironment() {
+        // On OpenVMS Runtime#exec() doesn't support the environment array,
+        // so we only return the new values which then will be set in
+        // the generated DCL script, inheriting the parent process environment
+        if (Os.isFamily("openvms")) {
+            return env;
+        }
+
+        Vector osEnv = (Vector) getProcEnvironment().clone();
+        for (int i = 0; i < env.length; i++) {
+            int pos = env[i].indexOf('=');
+            // Get key including "="
+            String key = env[i].substring(0, pos + 1);
+            int size = osEnv.size();
+            for (int j = 0; j < size; j++) {
+                if (((String) osEnv.elementAt(j)).startsWith(key)) {
+                    osEnv.removeElementAt(j);
+                    break;
+                }
+            }
+            osEnv.addElement(env[i]);
+        }
+        String[] result = new String[osEnv.size()];
+        osEnv.copyInto(result);
+        return result;
+    }
+
+    /**
+     * A utility method that runs an external command.  Writes the output and
+     * error streams of the command to the project log.
+     *
+     * @param task      The task that the command is part of.  Used for logging
+     * @param cmdline   The command to execute.
+     *
+     * @throws BuildException if the command does not return 0.
+     */
+    public static void runCommand(Task task, String[] cmdline)
+        throws BuildException {
+        try {
+            task.log(Commandline.describeCommand(cmdline),
+                     Project.MSG_VERBOSE);
+            Execute exe = new Execute(new LogStreamHandler(task,
+                                                           Project.MSG_INFO,
+                                                           Project.MSG_ERR));
+            exe.setAntRun(task.getProject());
+            exe.setCommandline(cmdline);
+            int retval = exe.execute();
+            if (isFailure(retval)) {
+                throw new BuildException(cmdline[0]
+                    + " failed with return code " + retval, task.getLocation());
+            }
+        } catch (java.io.IOException exc) {
+            throw new BuildException("Could not launch " + cmdline[0] + ": "
+                + exc, task.getLocation());
+        }
+    }
+
+    /**
+     * Close the streams belonging to the given Process.
+     * @param process   the <CODE>Process</CODE>.
+     */
+    public static void closeStreams(Process process) {
+        try {
+            process.getInputStream().close();
+        } catch (IOException eyeOhEx) {
+        }
+        try {
+            process.getOutputStream().close();
+        } catch (IOException eyeOhEx) {
+        }
+        try {
+            process.getErrorStream().close();
+        } catch (IOException eyeOhEx) {
+        }
+    }
+
+    /**
+     * This method is VMS specific and used by getProcEnvironment().
+     *
+     * Parses VMS logicals from <code>in</code> and adds them to
+     * <code>environment</code>.  <code>in</code> is expected to be the
+     * output of "SHOW LOGICAL".  The method takes care of parsing the output
+     * correctly as well as making sure that a logical defined in multiple
+     * tables only gets added from the highest order table.  Logicals with
+     * multiple equivalence names are mapped to a variable with multiple
+     * values separated by a comma (,).
+     */
+    private static Vector addVMSLogicals(Vector environment, BufferedReader in)
+        throws IOException {
+        HashMap logicals = new HashMap();
+        String logName = null, logValue = null, newLogName;
+        String line = null;
+        while ((line = in.readLine()) != null) {
+            // parse the VMS logicals into required format ("VAR=VAL[,VAL2]")
+            if (line.startsWith("\t=")) {
+                // further equivalence name of previous logical
+                if (logName != null) {
+                    logValue += "," + line.substring(4, line.length() - 1);
+                }
+            } else if (line.startsWith("  \"")) {
+                // new logical?
+                if (logName != null) {
+                    logicals.put(logName, logValue);
+                }
+                int eqIndex = line.indexOf('=');
+                newLogName = line.substring(3, eqIndex - 2);
+                if (logicals.containsKey(newLogName)) {
+                    // already got this logical from a higher order table
+                    logName = null;
+                } else {
+                    logName = newLogName;
+                    logValue = line.substring(eqIndex + 3, line.length() - 1);
+                }
+            }
+        }
+        // Since we "look ahead" before adding, there's one last env var.
+        if (logName != null) {
+            logicals.put(logName, logValue);
+        }
+
+        for (Iterator i = logicals.keySet().iterator(); i.hasNext();) {
+            String logical = (String) i.next();
+            environment.add(logical + "=" + logicals.get(logical));
+        }
+        return environment;
+    }
+
+    /**
+     * A command launcher for a particular JVM/OS platform.  This class is
+     * a general purpose command launcher which can only launch commands in
+     * the current working directory.
+     */
+    private static class CommandLauncher {
+        /**
+         * Launches the given command in a new process.
+         *
+         * @param project       The project that the command is part of
+         * @param cmd           The command to execute
+         * @param env           The environment for the new process.  If null,
+         *                      the environment of the current process is used.
+         * @throws IOException  if attempting to run a command in a specific directory
+         */
+        public Process exec(Project project, String[] cmd, String[] env)
+             throws IOException {
+            if (project != null) {
+                project.log("Execute:CommandLauncher: "
+                    + Commandline.describeCommand(cmd), Project.MSG_DEBUG);
+            }
+            return Runtime.getRuntime().exec(cmd, env);
+        }
+
+        /**
+         * Launches the given command in a new process, in the given working
+         * directory.
+         *
+         * @param project       The project that the command is part of
+         * @param cmd           The command to execute
+         * @param env           The environment for the new process.  If null,
+         *                      the environment of the current process is used.
+         * @param workingDir    The directory to start the command in.  If null,
+         *                      the current directory is used
+         * @throws IOException  if trying to change directory
+         */
+        public Process exec(Project project, String[] cmd, String[] env,
+                            File workingDir) throws IOException {
+            if (workingDir == null) {
+                return exec(project, cmd, env);
+            }
+            throw new IOException("Cannot execute a process in different "
+                + "directory under this JVM");
+        }
+    }
+
+    /**
+     * A command launcher for JDK/JRE 1.1 under Windows.  Fixes quoting problems
+     * in Runtime.exec().  Can only launch commands in the current working
+     * directory
+     */
+    private static class Java11CommandLauncher extends CommandLauncher {
+        /**
+         * Launches the given command in a new process.  Needs to quote
+         * arguments
+         * @param project the ant project
+         * @param cmd the command line to execute as an array of strings
+         * @param env the environment to set as an array of strings
+         * @throws IOException probably forwarded from Runtime#exec
+         */
+        public Process exec(Project project, String[] cmd, String[] env)
+             throws IOException {
+            // Need to quote arguments with spaces, and to escape
+            // quote characters
+            String[] newcmd = new String[cmd.length];
+            for (int i = 0; i < cmd.length; i++) {
+                newcmd[i] = Commandline.quoteArgument(cmd[i]);
+            }
+            if (project != null) {
+                project.log("Execute:Java11CommandLauncher: "
+                    + Commandline.describeCommand(newcmd), Project.MSG_DEBUG);
+            }
+            return Runtime.getRuntime().exec(newcmd, env);
+        }
+    }
+
+    /**
+     * A command launcher for JDK/JRE 1.3 (and higher).  Uses the built-in
+     * Runtime.exec() command
+     */
+    private static class Java13CommandLauncher extends CommandLauncher {
+        public Java13CommandLauncher() throws NoSuchMethodException {
+            // Locate method Runtime.exec(String[] cmdarray,
+            //                            String[] envp, File dir)
+            myExecWithCWD = Runtime.class.getMethod("exec",
+                new Class[] {String[].class, String[].class, File.class});
+        }
+
+        /**
+         * Launches the given command in a new process, in the given working
+         * directory
+         * @param project the ant project
+         * @param cmd the command line to execute as an array of strings
+         * @param env the environment to set as an array of strings
+         * @param workingDir the working directory where the command should run
+         * @throws IOException probably forwarded from Runtime#exec
+         */
+        public Process exec(Project project, String[] cmd, String[] env,
+                            File workingDir) throws IOException {
+            try {
+                if (project != null) {
+                    project.log("Execute:Java13CommandLauncher: "
+                        + Commandline.describeCommand(cmd), Project.MSG_DEBUG);
+                }
+                Object[] arguments = {cmd, env, workingDir};
+                return (Process) myExecWithCWD.invoke(Runtime.getRuntime(),
+                                                     arguments);
+            } catch (InvocationTargetException exc) {
+                Throwable realexc = exc.getTargetException();
+                if (realexc instanceof ThreadDeath) {
+                    throw (ThreadDeath) realexc;
+                } else if (realexc instanceof IOException) {
+                    throw (IOException) realexc;
+                } else {
+                    throw new BuildException("Unable to execute command",
+                                             realexc);
+                }
+            } catch (Exception exc) {
+                // IllegalAccess, IllegalArgument, ClassCast
+                throw new BuildException("Unable to execute command", exc);
+            }
+        }
+
+        private Method myExecWithCWD;
+    }
+
+    /**
+     * A command launcher that proxies another command launcher.
+     *
+     * Sub-classes override exec(args, env, workdir)
+     */
+    private static class CommandLauncherProxy extends CommandLauncher {
+        CommandLauncherProxy(CommandLauncher launcher) {
+            myLauncher = launcher;
+        }
+
+        /**
+         * Launches the given command in a new process.  Delegates this
+         * method to the proxied launcher
+         * @param project the ant project
+         * @param cmd the command line to execute as an array of strings
+         * @param env the environment to set as an array of strings
+         * @throws IOException forwarded from the exec method of the command launcher
+         */
+        public Process exec(Project project, String[] cmd, String[] env)
+            throws IOException {
+            return myLauncher.exec(project, cmd, env);
+        }
+
+        private CommandLauncher myLauncher;
+    }
+
+    /**
+     * A command launcher for OS/2 that uses 'cmd.exe' when launching
+     * commands in directories other than the current working
+     * directory.
+     *
+     * <p>Unlike Windows NT and friends, OS/2's cd doesn't support the
+     * /d switch to change drives and directories in one go.</p>
+     */
+    private static class OS2CommandLauncher extends CommandLauncherProxy {
+        OS2CommandLauncher(CommandLauncher launcher) {
+            super(launcher);
+        }
+
+        /**
+         * Launches the given command in a new process, in the given working
+         * directory.
+         * @param project the ant project
+         * @param cmd the command line to execute as an array of strings
+         * @param env the environment to set as an array of strings
+         * @param workingDir working directory where the command should run
+         * @throws IOException forwarded from the exec method of the command launcher
+         */
+        public Process exec(Project project, String[] cmd, String[] env,
+                            File workingDir) throws IOException {
+            File commandDir = workingDir;
+            if (workingDir == null) {
+                if (project != null) {
+                    commandDir = project.getBaseDir();
+                } else {
+                    return exec(project, cmd, env);
+                }
+            }
+
+            // Use cmd.exe to change to the specified drive and
+            // directory before running the command
+            final int preCmdLength = 7;
+            final String cmdDir = commandDir.getAbsolutePath();
+            String[] newcmd = new String[cmd.length + preCmdLength];
+            newcmd[0] = "cmd";
+            newcmd[1] = "/c";
+            newcmd[2] = cmdDir.substring(0, 2);
+            newcmd[3] = "&&";
+            newcmd[4] = "cd";
+            newcmd[5] = cmdDir.substring(2);
+            newcmd[6] = "&&";
+            System.arraycopy(cmd, 0, newcmd, preCmdLength, cmd.length);
+
+            return exec(project, newcmd, env);
+        }
+    }
+
+    /**
+     * A command launcher for Windows XP/2000/NT that uses 'cmd.exe' when
+     * launching commands in directories other than the current working
+     * directory.
+     */
+    private static class WinNTCommandLauncher extends CommandLauncherProxy {
+        WinNTCommandLauncher(CommandLauncher launcher) {
+            super(launcher);
+        }
+
+        /**
+         * Launches the given command in a new process, in the given working
+         * directory.
+         * @param project the ant project
+         * @param cmd the command line to execute as an array of strings
+         * @param env the environment to set as an array of strings
+         * @param workingDir working directory where the command should run
+         * @throws IOException forwarded from the exec method of the command launcher
+         */
+        public Process exec(Project project, String[] cmd, String[] env,
+                            File workingDir) throws IOException {
+            File commandDir = workingDir;
+            if (workingDir == null) {
+                if (project != null) {
+                    commandDir = project.getBaseDir();
+                } else {
+                    return exec(project, cmd, env);
+                }
+            }
+
+            // Use cmd.exe to change to the specified directory before running
+            // the command
+            final int preCmdLength = 6;
+            String[] newcmd = new String[cmd.length + preCmdLength];
+            newcmd[0] = "cmd";
+            newcmd[1] = "/c";
+            newcmd[2] = "cd";
+            newcmd[3] = "/d";
+            newcmd[4] = commandDir.getAbsolutePath();
+            newcmd[5] = "&&";
+            System.arraycopy(cmd, 0, newcmd, preCmdLength, cmd.length);
+
+            return exec(project, newcmd, env);
+        }
+    }
+
+    /**
+     * A command launcher for Mac that uses a dodgy mechanism to change
+     * working directory before launching commands.
+     */
+    private static class MacCommandLauncher extends CommandLauncherProxy {
+        MacCommandLauncher(CommandLauncher launcher) {
+            super(launcher);
+        }
+
+        /**
+         * Launches the given command in a new process, in the given working
+         * directory
+         * @param project the ant project
+         * @param cmd the command line to execute as an array of strings
+         * @param env the environment to set as an array of strings
+         * @param workingDir working directory where the command should run
+         * @throws IOException forwarded from the exec method of the command launcher
+         */
+        public Process exec(Project project, String[] cmd, String[] env,
+                            File workingDir) throws IOException {
+            if (workingDir == null) {
+                return exec(project, cmd, env);
+            }
+
+            System.getProperties().put("user.dir", workingDir.getAbsolutePath());
+            try {
+                return exec(project, cmd, env);
+            } finally {
+                System.getProperties().put("user.dir", antWorkingDirectory);
+            }
+        }
+    }
+
+    /**
+     * A command launcher that uses an auxiliary script to launch commands
+     * in directories other than the current working directory.
+     */
+    private static class ScriptCommandLauncher extends CommandLauncherProxy {
+        ScriptCommandLauncher(String script, CommandLauncher launcher) {
+            super(launcher);
+            myScript = script;
+        }
+
+        /**
+         * Launches the given command in a new process, in the given working
+         * directory
+         */
+        public Process exec(Project project, String[] cmd, String[] env,
+                            File workingDir) throws IOException {
+            if (project == null) {
+                if (workingDir == null) {
+                    return exec(project, cmd, env);
+                }
+                throw new IOException("Cannot locate antRun script: "
+                    + "No project provided");
+            }
+
+            // Locate the auxiliary script
+            String antHome = project.getProperty("ant.home");
+            if (antHome == null) {
+                throw new IOException("Cannot locate antRun script: "
+                    + "Property 'ant.home' not found");
+            }
+            String antRun = project.resolveFile(antHome + File.separator + myScript).toString();
+
+            // Build the command
+            File commandDir = workingDir;
+            if (workingDir == null && project != null) {
+                commandDir = project.getBaseDir();
+            }
+
+            String[] newcmd = new String[cmd.length + 2];
+            newcmd[0] = antRun;
+            newcmd[1] = commandDir.getAbsolutePath();
+            System.arraycopy(cmd, 0, newcmd, 2, cmd.length);
+
+            return exec(project, newcmd, env);
+        }
+
+        private String myScript;
+    }
+
+    /**
+     * A command launcher that uses an auxiliary perl script to launch commands
+     * in directories other than the current working directory.
+     */
+    private static class PerlScriptCommandLauncher
+        extends CommandLauncherProxy {
+        PerlScriptCommandLauncher(String script, CommandLauncher launcher) {
+            super(launcher);
+            myScript = script;
+        }
+
+        /**
+         * Launches the given command in a new process, in the given working
+         * directory
+         */
+        public Process exec(Project project, String[] cmd, String[] env,
+                            File workingDir) throws IOException {
+            if (project == null) {
+                if (workingDir == null) {
+                    return exec(project, cmd, env);
+                }
+                throw new IOException("Cannot locate antRun script: "
+                    + "No project provided");
+            }
+
+            // Locate the auxiliary script
+            String antHome = project.getProperty("ant.home");
+            if (antHome == null) {
+                throw new IOException("Cannot locate antRun script: "
+                    + "Property 'ant.home' not found");
+            }
+            String antRun = project.resolveFile(antHome + File.separator + myScript).toString();
+
+            // Build the command
+            File commandDir = workingDir;
+            if (workingDir == null && project != null) {
+                commandDir = project.getBaseDir();
+            }
+
+            String[] newcmd = new String[cmd.length + 3];
+            newcmd[0] = "perl";
+            newcmd[1] = antRun;
+            newcmd[2] = commandDir.getAbsolutePath();
+            System.arraycopy(cmd, 0, newcmd, 3, cmd.length);
+
+            return exec(project, newcmd, env);
+        }
+
+        private String myScript;
+    }
+
+    /**
+     * A command launcher for VMS that writes the command to a temporary DCL
+     * script before launching commands.  This is due to limitations of both
+     * the DCL interpreter and the Java VM implementation.
+     */
+    private static class VmsCommandLauncher extends Java13CommandLauncher {
+
+        public VmsCommandLauncher() throws NoSuchMethodException {
+            super();
+        }
+
+        /**
+         * Launches the given command in a new process.
+         */
+        public Process exec(Project project, String[] cmd, String[] env)
+            throws IOException {
+            String[] vmsCmd = {createCommandFile(cmd, env).getPath()};
+            return super.exec(project, vmsCmd, env);
+        }
+
+        /**
+         * Launches the given command in a new process, in the given working
+         * directory.  Note that under Java 1.3.1, 1.4.0 and 1.4.1 on VMS this
+         * method only works if <code>workingDir</code> is null or the logical
+         * JAVA$FORK_SUPPORT_CHDIR needs to be set to TRUE.
+         */
+        public Process exec(Project project, String[] cmd, String[] env,
+                            File workingDir) throws IOException {
+            String[] vmsCmd = {createCommandFile(cmd, env).getPath()};
+            return super.exec(project, vmsCmd, env, workingDir);
+        }
+
+        /*
+         * Writes the command into a temporary DCL script and returns the
+         * corresponding File object.  The script will be deleted on exit.
+         */
+        private File createCommandFile(String[] cmd, String[] env)
+            throws IOException {
+            File script = File.createTempFile("ANT", ".COM");
+            script.deleteOnExit();
+            PrintWriter out = null;
+            try {
+                out = new PrintWriter(new FileWriter(script));
+
+                // add the environment as logicals to the DCL script
+                if (env != null) {
+                    int eqIndex;
+                    for (int i = 1; i < env.length ; i++) {
+                        eqIndex = env[i].indexOf('=');
+                        if (eqIndex != -1) {
+                            out.print("$ DEFINE/NOLOG ");
+                            out.print(env[i].substring(0, eqIndex));
+                            out.print(" \"");
+                            out.print(env[i].substring(eqIndex + 1));
+                            out.println('\"');
+                        }
+                    }
+                }
+
+                out.print("$ " + cmd[0]);
+                for (int i = 1; i < cmd.length ; i++) {
+                    out.println(" -");
+                    out.print(cmd[i]);
+                }
+            } finally {
+                if (out != null) {
+                    out.close();
+                }
+            }
+            return script;
+        }
+
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java	(revision 16139)
@@ -0,0 +1,278 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Permissions;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.TimeoutObserver;
+import org.apache.tools.ant.util.Watchdog;
+
+/**
+ *
+ * @since Ant 1.2
+ */
+public class ExecuteJava implements Runnable, TimeoutObserver {
+
+    private Commandline javaCommand = null;
+    private Path classpath = null;
+    private CommandlineJava.SysProperties sysProperties = null;
+    private Permissions  perm = null;
+    private Method main = null;
+    private Long timeout = null;
+    private Throwable caught = null;
+    private boolean timedOut = false;
+    private Thread thread = null;
+
+    public void setJavaCommand(Commandline javaCommand) {
+        this.javaCommand = javaCommand;
+    }
+
+    /**
+     * Set the classpath to be used when running the Java class
+     *
+     * @param p an Ant Path object containing the classpath.
+     */
+    public void setClasspath(Path p) {
+        classpath = p;
+    }
+
+    public void setSystemProperties(CommandlineJava.SysProperties s) {
+        sysProperties = s;
+    }
+
+    /**
+     * Permissions for the application run.
+     * @since Ant 1.6
+     * @param permissions
+     */
+    public void setPermissions(Permissions permissions) {
+        perm = permissions;
+    }
+
+    /**
+     * All output (System.out as well as System.err) will be written
+     * to this Stream.
+     *
+     * @deprecated manage output at the task level
+     */
+    public void setOutput(PrintStream out) {
+    }
+
+    /**
+     * @since Ant 1.5
+     */
+    public void setTimeout(Long timeout) {
+        this.timeout = timeout;
+    }
+
+    public void execute(Project project) throws BuildException {
+        final String classname = javaCommand.getExecutable();
+
+        AntClassLoader loader = null;
+        try {
+            if (sysProperties != null) {
+                sysProperties.setSystem();
+            }
+
+            final Class[] param = {Class.forName("[Ljava.lang.String;")};
+            Class target = null;
+            if (classpath == null) {
+                target = Class.forName(classname);
+            } else {
+                loader = project.createClassLoader(classpath);
+                loader.setParent(project.getCoreLoader());
+                loader.setParentFirst(false);
+                loader.addJavaLibraries();
+                loader.setIsolated(true);
+                loader.setThreadContextLoader();
+                loader.forceLoadClass(classname);
+                target = Class.forName(classname, true, loader);
+            }
+            main = target.getMethod("main", param);
+            if (main == null) {
+                throw new BuildException("Could not find main() method in "
+                                         + classname);
+            }
+
+            if ((main.getModifiers() & Modifier.STATIC) == 0) {
+                throw new BuildException("main() method in " + classname
+                    + " is not declared static");
+            }
+
+
+            if (timeout == null) {
+                run();
+            } else {
+                thread = new Thread(this, "ExecuteJava");
+                Task currentThreadTask
+                    = project.getThreadTask(Thread.currentThread());
+                project.registerThreadTask(thread, currentThreadTask);
+                // if we run into a timeout, the run-away thread shall not
+                // make the VM run forever - if no timeout occurs, Ant's
+                // main thread will still be there to let the new thread
+                // finish
+                thread.setDaemon(true);
+                Watchdog w = new Watchdog(timeout.longValue());
+                w.addTimeoutObserver(this);
+                synchronized (this) {
+                    thread.start();
+                    w.start();
+                    try {
+                        wait();
+                    } catch (InterruptedException e) {
+                        // ignore
+                    }
+                    if (timedOut) {
+                        project.log("Timeout: sub-process interrupted",
+                                    Project.MSG_WARN);
+                    } else {
+                        thread = null;
+                        w.stop();
+                    }
+                }
+            }
+
+            if (caught != null) {
+                throw caught;
+            }
+
+        } catch (ClassNotFoundException e) {
+            throw new BuildException("Could not find " + classname + "."
+                                     + " Make sure you have it in your"
+                                     + " classpath");
+        } catch (BuildException e) {
+            throw e;
+        } catch (SecurityException e) {
+            throw e;
+        } catch (ThreadDeath e) {
+            throw e;
+        } catch (Throwable e) {
+            throw new BuildException(e);
+        } finally {
+            if (loader != null) {
+                loader.resetThreadContextLoader();
+                loader.cleanup();
+                loader = null;
+            }
+            if (sysProperties != null) {
+                sysProperties.restoreSystem();
+            }
+        }
+    }
+
+    /**
+     * @since Ant 1.5
+     */
+    public void run() {
+        final Object[] argument = {javaCommand.getArguments()};
+        try {
+            if (perm != null) {
+                perm.setSecurityManager();
+            }
+            main.invoke(null, argument);
+        } catch (InvocationTargetException e) {
+            Throwable t = e.getTargetException();
+            if (!(t instanceof InterruptedException)) {
+                caught = t;
+            } /* else { swallow, probably due to timeout } */
+        } catch (Throwable t) {
+            caught = t;
+        } finally {
+            if (perm != null) {
+                perm.restoreSecurityManager();
+            }
+            synchronized (this) {
+                notifyAll();
+            }
+        }
+    }
+
+    /**
+     * @since Ant 1.5
+     */
+    public synchronized void timeoutOccured(Watchdog w) {
+        if (thread != null) {
+            timedOut = true;
+            thread.interrupt();
+        }
+        notifyAll();
+    }
+
+    /**
+     * @since 1.19, Ant 1.5
+     */
+    public synchronized boolean killedProcess() {
+        return timedOut;
+    }
+
+    /**
+     * Runs the Java command in a separate VM, this does not give you
+     * the full flexibility of the Java task, but may be enough for
+     * simple needs.
+     *
+     * @since Ant 1.6.3
+     */
+    public int fork(ProjectComponent pc) throws BuildException {
+        CommandlineJava cmdl = new CommandlineJava();
+        cmdl.setClassname(javaCommand.getExecutable());
+        String[] args = javaCommand.getArguments();
+        for (int i = 0; i < args.length; i++) {
+            cmdl.createArgument().setValue(args[i]);
+        }
+        if (classpath != null) {
+            cmdl.createClasspath(pc.getProject()).append(classpath);
+        }
+        if (sysProperties != null) {
+            cmdl.addSysproperties(sysProperties);
+        }
+
+        Redirector redirector = new Redirector(pc);
+        Execute exe
+            = new Execute(redirector.createHandler(),
+                          timeout == null 
+                          ? null 
+                          : new ExecuteWatchdog(timeout.longValue()));
+        exe.setAntRun(pc.getProject());
+        exe.setCommandline(cmdl.getCommandline());
+
+        try {
+            int rc = exe.execute();
+            redirector.complete();
+            timedOut = exe.killedProcess();
+            return rc;
+        } catch (IOException e) {
+            throw new BuildException(e);
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ExecuteOn.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ExecuteOn.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ExecuteOn.java	(revision 16139)
@@ -0,0 +1,707 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation.
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.AbstractFileSet;
+import org.apache.tools.ant.types.DirSet;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileList;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.SourceFileScanner;
+
+/**
+ * Executes a given command, supplying a set of files as arguments.
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task category="control" name="apply"
+ */
+public class ExecuteOn extends ExecTask {
+
+    protected Vector filesets = new Vector(); // contains AbstractFileSet
+                                              // (both DirSet and FileSet)
+    private Vector filelists = new Vector();
+    private boolean relative = false;
+    private boolean parallel = false;
+    private boolean forwardSlash = false;
+    protected String type = "file";
+    protected Commandline.Marker srcFilePos = null;
+    private boolean skipEmpty = false;
+    protected Commandline.Marker targetFilePos = null;
+    protected Mapper mapperElement = null;
+    protected FileNameMapper mapper = null;
+    protected File destDir = null;
+    private int maxParallel = -1;
+    private boolean addSourceFile = true;
+    private boolean verbose = false;
+    private boolean ignoreMissing = true;
+    private boolean force = false;
+
+    /**
+     * Has &lt;srcfile&gt; been specified before &lt;targetfile&gt;
+     */
+    protected boolean srcIsFirst = true;
+
+    /**
+     * Add a set of files upon which to operate.
+     * @param set the FileSet to add.
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * Add a set of directories upon which to operate.
+     *
+     * @param  set the DirSet to add.
+     *
+     * @since Ant 1.6
+     */
+    public void addDirset(DirSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * Add a list of source files upon which to operate.
+     * @param list the FileList to add.
+     */
+    public void addFilelist(FileList list) {
+        filelists.addElement(list);
+    }
+
+    /**
+     * Set whether the filenames should be passed on the command line as
+     * absolute or relative pathnames. Paths are relative to the base
+     * directory of the corresponding fileset for source files or the
+     * dest attribute for target files.
+     * @param relative whether to pass relative pathnames.
+     */
+    public void setRelative(boolean relative) {
+        this.relative = relative;
+    }
+
+
+    /**
+     * Set whether to execute in parallel mode.
+     * If true, run the command only once, appending all files as arguments.
+     * If false, command will be executed once for every file. Defaults to false.
+     * @param parallel whether to run in parallel.
+     */
+    public void setParallel(boolean parallel) {
+        this.parallel = parallel;
+    }
+
+    /**
+     * Set whether the command works only on files, directories or both.
+     * @param type a FileDirBoth EnumeratedAttribute.
+     */
+    public void setType(FileDirBoth type) {
+        this.type = type.getValue();
+    }
+
+    /**
+     * Set whether empty filesets will be skipped.  If true and
+     * no source files have been found or are newer than their
+     * corresponding target files, the command will not be run.
+     * @param skip whether to skip empty filesets.
+     */
+    public void setSkipEmptyFilesets(boolean skip) {
+        skipEmpty = skip;
+    }
+
+    /**
+     * Specify the directory where target files are to be placed.
+     * @param destDir the File object representing the destination directory.
+     */
+    public void setDest(File destDir) {
+        this.destDir = destDir;
+    }
+
+    /**
+     * Set whether the source and target file names on Windows and OS/2
+     * must use the forward slash as file separator.
+     * @param forwardSlash whether the forward slash will be forced.
+     */
+    public void setForwardslash(boolean forwardSlash) {
+        this.forwardSlash = forwardSlash;
+    }
+
+    /**
+     * Limit the command line length by passing at maximum this many
+     * sourcefiles at once to the command.
+     *
+     * <p>Set to &lt;= 0 for unlimited - this is the default.</p>
+     *
+     * @param max <code>int</code> maximum number of sourcefiles
+     *            passed to the executable.
+     *
+     * @since Ant 1.6
+     */
+    public void setMaxParallel(int max) {
+        maxParallel = max;
+    }
+
+    /**
+     * Set whether to send the source file name on the command line.
+     *
+     * <p>Defaults to <code>true</code>.
+     *
+     * @param b whether to add the source file to the command line.
+     *
+     * @since Ant 1.6
+     */
+    public void setAddsourcefile(boolean b) {
+        addSourceFile = b;
+    }
+
+    /**
+     * Set whether to operate in verbose mode.
+     * If true, a verbose summary will be printed after execution.
+     * @param b whether to operate in verbose mode.
+     *
+     * @since Ant 1.6
+     */
+    public void setVerbose(boolean b) {
+        verbose = b;
+    }
+
+    /**
+     * Set whether to ignore nonexistent files from filelists.
+     * @param b whether to ignore missing files.
+     *
+     * @since Ant 1.6.2
+     */
+    public void setIgnoremissing(boolean b) {
+        ignoreMissing = b;
+    }
+
+    /**
+     * Set whether to bypass timestamp comparisons for target files.
+     * @param b whether to bypass timestamp comparisons.
+     *
+     * @since Ant 1.6.3
+     */
+    public void setForce(boolean b) {
+        force = b;
+    }
+
+    /**
+     * Create a placeholder indicating where on the command line
+     * the name of the source file should be inserted.
+     * @return <code>Commandline.Marker</code>.
+     */
+    public Commandline.Marker createSrcfile() {
+        if (srcFilePos != null) {
+            throw new BuildException(getTaskType() + " doesn\'t support multiple "
+                                     + "srcfile elements.", getLocation());
+        }
+        srcFilePos = cmdl.createMarker();
+        return srcFilePos;
+    }
+
+    /**
+     * Create a placeholder indicating where on the command line
+     * the name of the target file should be inserted.
+     * @return <code>Commandline.Marker</code>.
+     */
+    public Commandline.Marker createTargetfile() {
+        if (targetFilePos != null) {
+            throw new BuildException(getTaskType() + " doesn\'t support multiple "
+                                     + "targetfile elements.", getLocation());
+        }
+        targetFilePos = cmdl.createMarker();
+        srcIsFirst = (srcFilePos != null);
+        return targetFilePos;
+    }
+
+    /**
+     * Create a nested Mapper element to use for mapping
+     * source files to target files.
+     * @return <code>Mapper</code>.
+     * @throws BuildException if more than one mapper is defined.
+     */
+    public Mapper createMapper() throws BuildException {
+        if (mapperElement != null) {
+            throw new BuildException("Cannot define more than one mapper",
+                                     getLocation());
+        }
+        mapperElement = new Mapper(getProject());
+        return mapperElement;
+    }
+
+    /**
+     * Add a nested FileNameMapper.
+     * @param fileNameMapper the mapper to add.
+     * @since Ant 1.6.3
+     */
+    public void add(FileNameMapper fileNameMapper) {
+        createMapper().add(fileNameMapper);
+    }
+
+    /**
+     * Check the configuration of this ExecuteOn instance.
+     */
+    protected void checkConfiguration() {
+//     * @TODO using taskName here is brittle, as a user could override it.
+//     *       this should probably be modified to use the classname instead.
+        if ("execon".equals(getTaskName())) {
+            log("!! execon is deprecated. Use apply instead. !!");
+        }
+        super.checkConfiguration();
+        if (filesets.size() == 0 && filelists.size() == 0) {
+            throw new BuildException("no filesets and no filelists specified",
+                                     getLocation());
+        }
+        if (targetFilePos != null && mapperElement == null) {
+            throw new BuildException("targetfile specified without mapper",
+                                     getLocation());
+        }
+        if (destDir != null && mapperElement == null) {
+            throw new BuildException("dest specified without mapper",
+                                     getLocation());
+        }
+        if (mapperElement != null) {
+            mapper = mapperElement.getImplementation();
+        }
+    }
+
+    /**
+     * Create the ExecuteStreamHandler instance that will be used
+     * during execution.
+     * @return <code>ExecuteStreamHandler</code>.
+     * @throws BuildException on error.
+     */
+    protected ExecuteStreamHandler createHandler() throws BuildException {
+        //if we have a RedirectorElement, return a decoy
+        return (redirectorElement == null)
+            ? super.createHandler() : new PumpStreamHandler();
+    }
+
+    /**
+     * Set up the I/O Redirector.
+     */
+    protected void setupRedirector() {
+        super.setupRedirector();
+        redirector.setAppendProperties(true);
+    }
+
+    /**
+     * Run the specified Execute object.
+     * @param exe the Execute instance representing the external process.
+     * @throws BuildException on error
+     */
+    protected void runExec(Execute exe) throws BuildException {
+        int totalFiles = 0;
+        int totalDirs = 0;
+        boolean haveExecuted = false;
+        try {
+            Vector fileNames = new Vector();
+            Vector baseDirs = new Vector();
+            for (int i = 0; i < filesets.size(); i++) {
+                String currentType = type;
+                AbstractFileSet fs = (AbstractFileSet) filesets.elementAt(i);
+                if (fs instanceof DirSet) {
+                    if (!"dir".equals(type)) {
+                        log("Found a nested dirset but type is " + type + ". "
+                            + "Temporarily switching to type=\"dir\" on the"
+                            + " assumption that you really did mean"
+                            + " <dirset> not <fileset>.", Project.MSG_DEBUG);
+                        currentType = "dir";
+                    }
+                }
+                File base = fs.getDir(getProject());
+
+                DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+
+                if (!"dir".equals(currentType)) {
+                    String[] s = getFiles(base, ds);
+                    for (int j = 0; j < s.length; j++) {
+                        totalFiles++;
+                        fileNames.addElement(s[j]);
+                        baseDirs.addElement(base);
+                    }
+                }
+                if (!"file".equals(currentType)) {
+                    String[] s = getDirs(base, ds);
+                    for (int j = 0; j < s.length; j++) {
+                        totalDirs++;
+                        fileNames.addElement(s[j]);
+                        baseDirs.addElement(base);
+                    }
+                }
+                if (fileNames.size() == 0 && skipEmpty) {
+                    int includedCount
+                        = ((!"dir".equals(currentType))
+                        ? ds.getIncludedFilesCount() : 0)
+                        + ((!"file".equals(currentType))
+                        ? ds.getIncludedDirsCount() : 0);
+
+                    log("Skipping fileset for directory " + base + ". It is "
+                        + ((includedCount > 0) ? "up to date." : "empty."),
+                        Project.MSG_INFO);
+                    continue;
+                }
+                if (!parallel) {
+                    String[] s = new String[fileNames.size()];
+                    fileNames.copyInto(s);
+                    for (int j = 0; j < s.length; j++) {
+                        String[] command = getCommandline(s[j], base);
+                        log(Commandline.describeCommand(command),
+                            Project.MSG_VERBOSE);
+                        exe.setCommandline(command);
+
+                        if (redirectorElement != null) {
+                            setupRedirector();
+                            redirectorElement.configure(redirector, s[j]);
+                        }
+                        if (redirectorElement != null || haveExecuted) {
+                            // need to reset the stream handler to restart
+                            // reading of pipes;
+                            // go ahead and do it always w/ nested redirectors
+                            exe.setStreamHandler(redirector.createHandler());
+                        }
+                        runExecute(exe);
+                        haveExecuted = true;
+                    }
+                    fileNames.removeAllElements();
+                    baseDirs.removeAllElements();
+                }
+            }
+            for (int i = 0; i < filelists.size(); i++) {
+                FileList list = (FileList) filelists.elementAt(i);
+                File base = list.getDir(getProject());
+                String[] names = getFilesAndDirs(list);
+
+                for (int j = 0; j < names.length; j++) {
+                    File f = new File(base, names[j]);
+                    if ((!ignoreMissing) || (f.isFile() && !"dir".equals(type))
+                        || (f.isDirectory() && !"file".equals(type))) {
+
+                        if (ignoreMissing || f.isFile()) {
+                            totalFiles++;
+                        } else {
+                            totalDirs++;
+                        }
+                        fileNames.addElement(names[j]);
+                        baseDirs.addElement(base);
+                    }
+                }
+                if (fileNames.size() == 0 && skipEmpty) {
+                    DirectoryScanner ds = new DirectoryScanner();
+                    ds.setBasedir(base);
+                    ds.setIncludes(list.getFiles(getProject()));
+                    ds.scan();
+                    int includedCount
+                        = ds.getIncludedFilesCount() + ds.getIncludedDirsCount();
+
+                    log("Skipping filelist for directory " + base + ". It is "
+                        + ((includedCount > 0) ? "up to date." : "empty."),
+                        Project.MSG_INFO);
+                    continue;
+                }
+                if (!parallel) {
+                    String[] s = new String[fileNames.size()];
+                    fileNames.copyInto(s);
+                    for (int j = 0; j < s.length; j++) {
+                        String[] command = getCommandline(s[j], base);
+                        log(Commandline.describeCommand(command),
+                            Project.MSG_VERBOSE);
+                        exe.setCommandline(command);
+
+                        if (redirectorElement != null) {
+                            setupRedirector();
+                            redirectorElement.configure(redirector, s[j]);
+                        }
+                        if (redirectorElement != null || haveExecuted) {
+                            // need to reset the stream handler to restart
+                            // reading of pipes;
+                            // go ahead and do it always w/ nested redirectors
+                            exe.setStreamHandler(redirector.createHandler());
+                        }
+                        runExecute(exe);
+                        haveExecuted = true;
+                    }
+                    fileNames.removeAllElements();
+                    baseDirs.removeAllElements();
+                }
+            }
+            if (parallel && (fileNames.size() > 0 || !skipEmpty)) {
+                runParallel(exe, fileNames, baseDirs);
+                haveExecuted = true;
+            }
+            if (haveExecuted) {
+                log("Applied " + cmdl.getExecutable() + " to "
+                    + totalFiles + " file"
+                    + (totalFiles != 1 ? "s" : "") + " and "
+                    + totalDirs + " director"
+                    + (totalDirs != 1 ? "ies" : "y") + ".",
+                    verbose ? Project.MSG_INFO : Project.MSG_VERBOSE);
+            }
+        } catch (IOException e) {
+            throw new BuildException("Execute failed: " + e, e, getLocation());
+        } finally {
+            // close the output file if required
+            logFlush();
+            redirector.setAppendProperties(false);
+            redirector.setProperties();
+        }
+    }
+
+    /**
+     * Construct the command line for parallel execution.
+     *
+     * @param srcFiles The filenames to add to the commandline.
+     * @param baseDirs filenames are relative to this dir.
+     * @return the command line in the form of a String[].
+     */
+    protected String[] getCommandline(String[] srcFiles, File[] baseDirs) {
+        final char fileSeparator = File.separatorChar;
+        Vector targets = new Vector();
+        if (targetFilePos != null) {
+            Hashtable addedFiles = new Hashtable();
+            for (int i = 0; i < srcFiles.length; i++) {
+                String[] subTargets = mapper.mapFileName(srcFiles[i]);
+                if (subTargets != null) {
+                    for (int j = 0; j < subTargets.length; j++) {
+                        String name = null;
+                        if (!relative) {
+                            name = (new File(destDir, subTargets[j])).getAbsolutePath();
+                        } else {
+                            name = subTargets[j];
+                        }
+                        if (forwardSlash && fileSeparator != '/') {
+                            name = name.replace(fileSeparator, '/');
+                        }
+                        if (!addedFiles.contains(name)) {
+                            targets.addElement(name);
+                            addedFiles.put(name, name);
+                        }
+                    }
+                }
+            }
+        }
+        String[] targetFiles = new String[targets.size()];
+        targets.copyInto(targetFiles);
+
+        if (!addSourceFile) {
+            srcFiles = new String[0];
+        }
+        String[] orig = cmdl.getCommandline();
+        String[] result
+            = new String[orig.length + srcFiles.length + targetFiles.length];
+
+        int srcIndex = orig.length;
+        if (srcFilePos != null) {
+            srcIndex = srcFilePos.getPosition();
+        }
+        if (targetFilePos != null) {
+            int targetIndex = targetFilePos.getPosition();
+
+            if (srcIndex < targetIndex
+                || (srcIndex == targetIndex && srcIsFirst)) {
+
+                // 0 --> srcIndex
+                System.arraycopy(orig, 0, result, 0, srcIndex);
+
+                // srcIndex --> targetIndex
+                System.arraycopy(orig, srcIndex, result,
+                                 srcIndex + srcFiles.length,
+                                 targetIndex - srcIndex);
+
+                // targets are already absolute file names
+                System.arraycopy(targetFiles, 0, result,
+                                 targetIndex + srcFiles.length,
+                                 targetFiles.length);
+
+                // targetIndex --> end
+                System.arraycopy(orig, targetIndex, result,
+                    targetIndex + srcFiles.length + targetFiles.length,
+                    orig.length - targetIndex);
+            } else {
+                // 0 --> targetIndex
+                System.arraycopy(orig, 0, result, 0, targetIndex);
+
+                // targets are already absolute file names
+                System.arraycopy(targetFiles, 0, result,
+                                 targetIndex,
+                                 targetFiles.length);
+
+                // targetIndex --> srcIndex
+                System.arraycopy(orig, targetIndex, result,
+                                 targetIndex + targetFiles.length,
+                                 srcIndex - targetIndex);
+
+                // srcIndex --> end
+                System.arraycopy(orig, srcIndex, result,
+                    srcIndex + srcFiles.length + targetFiles.length,
+                    orig.length - srcIndex);
+                srcIndex += targetFiles.length;
+            }
+
+        } else { // no targetFilePos
+
+            // 0 --> srcIndex
+            System.arraycopy(orig, 0, result, 0, srcIndex);
+            // srcIndex --> end
+            System.arraycopy(orig, srcIndex, result,
+                             srcIndex + srcFiles.length,
+                             orig.length - srcIndex);
+        }
+        // fill in source file names
+        for (int i = 0; i < srcFiles.length; i++) {
+            if (!relative) {
+                result[srcIndex + i] =
+                    (new File(baseDirs[i], srcFiles[i])).getAbsolutePath();
+            } else {
+                result[srcIndex + i] = srcFiles[i];
+            }
+            if (forwardSlash && fileSeparator != '/') {
+                result[srcIndex + i] =
+                    result[srcIndex + i].replace(fileSeparator, '/');
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Construct the command line for serial execution.
+     *
+     * @param srcFile The filename to add to the commandline.
+     * @param baseDir filename is relative to this dir.
+     * @return the command line in the form of a String[].
+     */
+    protected String[] getCommandline(String srcFile, File baseDir) {
+        return getCommandline(new String[] {srcFile}, new File[] {baseDir});
+    }
+
+    /**
+     * Return the list of files from this DirectoryScanner that should
+     * be included on the command line.
+     * @param baseDir the File base directory.
+     * @param ds the DirectoryScanner to use for file scanning.
+     * @return a String[] containing the filenames.
+     */
+    protected String[] getFiles(File baseDir, DirectoryScanner ds) {
+        return restrict(ds.getIncludedFiles(), baseDir);
+    }
+
+    /**
+     * Return the list of Directories from this DirectoryScanner that
+     * should be included on the command line.
+     * @param baseDir the File base directory.
+     * @param ds the DirectoryScanner to use for file scanning.
+     * @return a String[] containing the directory names.
+     */
+    protected String[] getDirs(File baseDir, DirectoryScanner ds) {
+        return restrict(ds.getIncludedDirectories(), baseDir);
+    }
+
+    /**
+     * Return the list of files or directories from this FileList that
+     * should be included on the command line.
+     * @param list the FileList to check.
+     * @return a String[] containing the directory names.
+     *
+     * @since Ant 1.6.2
+     */
+    protected String[] getFilesAndDirs(FileList list) {
+        return restrict(list.getFiles(getProject()), list.getDir(getProject()));
+    }
+
+    private String[] restrict(String[] s, File baseDir) {
+        return (mapper == null || force) ? s
+            : new SourceFileScanner(this).restrict(s, baseDir, destDir, mapper);
+    }
+
+    /**
+     * Run the command in "parallel" mode, making sure that at most
+     * maxParallel sourcefiles get passed on the command line.
+     * @param exe the Executable to use.
+     * @param fileNames the Vector of filenames.
+     * @param baseDirs the Vector of base directories corresponding to fileNames.
+     * @throws IOException  on I/O errors.
+     * @throws BuildException on other errors.
+     * @since Ant 1.6
+     */
+    protected void runParallel(Execute exe, Vector fileNames,
+                               Vector baseDirs)
+        throws IOException, BuildException {
+        String[] s = new String[fileNames.size()];
+        fileNames.copyInto(s);
+        File[] b = new File[baseDirs.size()];
+        baseDirs.copyInto(b);
+
+        if (maxParallel <= 0
+            || s.length == 0 /* this is skipEmpty == false */) {
+            String[] command = getCommandline(s, b);
+            log(Commandline.describeCommand(command), Project.MSG_VERBOSE);
+            exe.setCommandline(command);
+            runExecute(exe);
+        } else {
+            int stillToDo = fileNames.size();
+            int currentOffset = 0;
+            while (stillToDo > 0) {
+                int currentAmount = Math.min(stillToDo, maxParallel);
+                String[] cs = new String[currentAmount];
+                System.arraycopy(s, currentOffset, cs, 0, currentAmount);
+                File[] cb = new File[currentAmount];
+                System.arraycopy(b, currentOffset, cb, 0, currentAmount);
+                String[] command = getCommandline(cs, cb);
+                log(Commandline.describeCommand(command), Project.MSG_VERBOSE);
+                exe.setCommandline(command);
+                if (redirectorElement != null) {
+                    setupRedirector();
+                    redirectorElement.configure(redirector, null);
+                }
+                if (redirectorElement != null || currentOffset > 0) {
+                    // need to reset the stream handler to restart
+                    // reading of pipes;
+                    // go ahead and do it always w/ nested redirectors
+                    exe.setStreamHandler(redirector.createHandler());
+                }
+                runExecute(exe);
+
+                stillToDo -= currentAmount;
+                currentOffset += currentAmount;
+            }
+        }
+    }
+
+    /**
+     * Enumerated attribute with the values "file", "dir" and "both"
+     * for the type attribute.
+     */
+    public static class FileDirBoth extends EnumeratedAttribute {
+        /**
+         * @see EnumeratedAttribute#getValues
+         */
+        public String[] getValues() {
+            return new String[] {"file", "dir", "both"};
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ExecuteStreamHandler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ExecuteStreamHandler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ExecuteStreamHandler.java	(revision 16139)
@@ -0,0 +1,63 @@
+/*
+ * Copyright  2000,2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Used by <code>Execute</code> to handle input and output stream of
+ * subprocesses.
+ *
+ * @since Ant 1.2
+ */
+public interface ExecuteStreamHandler {
+
+    /**
+     * Install a handler for the input stream of the subprocess.
+     *
+     * @param os output stream to write to the standard input stream of the
+     *           subprocess
+     */
+    void setProcessInputStream(OutputStream os) throws IOException;
+
+    /**
+     * Install a handler for the error stream of the subprocess.
+     *
+     * @param is input stream to read from the error stream from the subprocess
+     */
+    void setProcessErrorStream(InputStream is) throws IOException;
+
+    /**
+     * Install a handler for the output stream of the subprocess.
+     *
+     * @param is input stream to read from the error stream from the subprocess
+     */
+    void setProcessOutputStream(InputStream is) throws IOException;
+
+    /**
+     * Start handling of the streams.
+     */
+    void start() throws IOException;
+
+    /**
+     * Stop handling of the streams - will not be restarted.
+     */
+    void stop();
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ExecuteWatchdog.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ExecuteWatchdog.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ExecuteWatchdog.java	(revision 16139)
@@ -0,0 +1,173 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.TimeoutObserver;
+import org.apache.tools.ant.util.Watchdog;
+
+/**
+ * Destroys a process running for too long.
+ * For example:
+ * <pre>
+ * ExecuteWatchdog watchdog = new ExecuteWatchdog(30000);
+ * Execute exec = new Execute(myloghandler, watchdog);
+ * exec.setCommandLine(mycmdline);
+ * int exitvalue = exec.execute();
+ * if (Execute.isFailure(exitvalue) &amp;&amp; watchdog.killedProcess()) {
+ *              // it was killed on purpose by the watchdog
+ * }
+ * </pre>
+
+ * @see Execute
+ * @see org.apache.tools.ant.util.Watchdog
+ * @since Ant 1.2
+ */
+public class ExecuteWatchdog implements TimeoutObserver {
+
+    /** the process to execute and watch for duration */
+    private Process process;
+
+    /** say whether or not the watchdog is currently monitoring a process */
+    private boolean watch = false;
+
+    /** exception that might be thrown during the process execution */
+    private Exception caught = null;
+
+    /** say whether or not the process was killed due to running overtime */
+    private boolean     killedProcess = false;
+
+    /** will tell us whether timeout has occurred */
+    private Watchdog watchdog;
+
+    /**
+     * Creates a new watchdog with a given timeout.
+     *
+     * @param timeout the timeout for the process in milliseconds.
+     * It must be greater than 0.
+     */
+    public ExecuteWatchdog(long timeout) {
+        watchdog = new Watchdog(timeout);
+        watchdog.addTimeoutObserver(this);
+    }
+
+    /**
+     * @see #ExecuteWatchdog(long)
+     * @deprecated Use constructor with a long type instead.
+     * (1.4.x compatibility)
+     */
+    public ExecuteWatchdog(int timeout) {
+        this((long) timeout);
+    }
+
+    /**
+     * Watches the given process and terminates it, if it runs for too long.
+     * All information from the previous run are reset.
+     * @param process the process to monitor. It cannot be <tt>null</tt>
+     * @throws IllegalStateException if a process is still being monitored.
+     */
+    public synchronized void start(Process process) {
+        if (process == null) {
+            throw new NullPointerException("process is null.");
+        }
+        if (this.process != null) {
+            throw new IllegalStateException("Already running.");
+        }
+        this.caught = null;
+        this.killedProcess = false;
+        this.watch = true;
+        this.process = process;
+        watchdog.start();
+    }
+
+    /**
+     * Stops the watcher. It will notify all threads possibly waiting
+     * on this object.
+     */
+    public synchronized void stop() {
+        watchdog.stop();
+        watch = false;
+        process = null;
+    }
+
+    /**
+     * Called after watchdog has finished.
+     */
+    public void timeoutOccured(Watchdog w) {
+        try {
+            try {
+                // We must check if the process was not stopped
+                // before being here
+                process.exitValue();
+            } catch (IllegalThreadStateException itse) {
+                // the process is not terminated, if this is really
+                // a timeout and not a manual stop then kill it.
+                if (watch) {
+                    killedProcess = true;
+                    process.destroy();
+                }
+            }
+        } catch (Exception e) {
+            caught = e;
+        } finally {
+            cleanUp();
+        }
+    }
+
+    /**
+     * reset the monitor flag and the process.
+     */
+    protected void cleanUp() {
+        watch = false;
+        process = null;
+    }
+
+    /**
+     * This method will rethrow the exception that was possibly caught during
+     * the run of the process. It will only remains valid once the process has
+     * been terminated either by 'error', timeout or manual intervention.
+     * Information will be discarded once a new process is ran.
+     * @throws  BuildException  a wrapped exception over the one that was
+     * silently swallowed and stored during the process run.
+     */
+    public void checkException() throws BuildException {
+        if (caught != null) {
+            throw new BuildException("Exception in ExecuteWatchdog.run: "
+                                     + caught.getMessage(), caught);
+        }
+    }
+
+    /**
+     * Indicates whether or not the watchdog is still monitoring the process.
+     * @return  <tt>true</tt> if the process is still running, otherwise
+     *          <tt>false</tt>.
+     */
+    public boolean isWatching() {
+        return watch;
+    }
+
+    /**
+     * Indicates whether the last process run was killed on timeout or not.
+     * @return  <tt>true</tt> if the process was killed otherwise
+     *          <tt>false</tt>.
+     */
+    public boolean killedProcess() {
+        return killedProcess;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Exit.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Exit.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Exit.java	(revision 16139)
@@ -0,0 +1,214 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ExitStatusException;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.taskdefs.condition.ConditionBase;
+
+/**
+ * Exits the active build, giving an additional message
+ * if available.
+ *
+ * The <code>if</code> and <code>unless</code> attributes make the
+ * failure conditional -both probe for the named property being defined.
+ * The <code>if</code> tests for the property being defined, the
+ * <code>unless</code> for a property being undefined.
+ *
+ * If both attributes are set, then the test fails only if both tests
+ * are true. i.e.
+ * <pre>fail := defined(ifProperty) && !defined(unlessProperty)</pre>
+ *
+ * A single nested<CODE>&lt;condition&gt;</CODE> element can be specified
+ * instead of using <CODE>if</CODE>/<CODE>unless</CODE> (a combined
+ * effect can be achieved using <CODE>isset</CODE> conditions).
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task name="fail" category="control"
+ */
+public class Exit extends Task {
+
+    private class NestedCondition extends ConditionBase implements Condition {
+        public boolean eval() {
+            if (countConditions() != 1) {
+                throw new BuildException(
+                    "A single nested condition is required.");
+            }
+            return ((Condition)(getConditions().nextElement())).eval();
+        }
+    }
+
+    private String message;
+    private String ifCondition, unlessCondition;
+    private NestedCondition nestedCondition;
+    private Integer status;
+
+    /**
+     * A message giving further information on why the build exited.
+     *
+     * @param value message to output
+     */
+    public void setMessage(String value) {
+        this.message = value;
+    }
+
+    /**
+     * Only fail if a property of the given name exists in the current project.
+     * @param c property name
+     */
+    public void setIf(String c) {
+        ifCondition = c;
+    }
+
+    /**
+     * Only fail if a property of the given name does not
+     * exist in the current project.
+     * @param c property name
+     */
+    public void setUnless(String c) {
+        unlessCondition = c;
+    }
+
+    /**
+     * Set the status code to associate with the thrown Exception.
+     * @param i   the <CODE>int</CODE> status
+     */
+    public void setStatus(int i) {
+        status = new Integer(i);
+    }
+
+    /**
+     * Throw a <CODE>BuildException</CODE> to exit (fail) the build.
+     * If specified, evaluate conditions:
+     * A single nested condition is accepted, but requires that the
+     * <CODE>if</CODE>/<code>unless</code> attributes be omitted.
+     * If the nested condition evaluates to true, or the
+     * ifCondition is true or unlessCondition is false, the build will exit.
+     * The error message is constructed from the text fields, from
+     * the nested condition (if specified), or finally from
+     * the if and unless parameters (if present).
+     * @throws BuildException on error
+     */
+    public void execute() throws BuildException {
+        boolean fail = (nestedConditionPresent()) ? testNestedCondition()
+                     : (testIfCondition() && testUnlessCondition());
+        if (fail) {
+            String text = null;
+            if (message != null && message.trim().length() > 0) {
+                text = message.trim();
+            } else {
+                if (ifCondition != null && ifCondition.length() > 0
+                    && getProject().getProperty(ifCondition) != null) {
+                    text = "if=" + ifCondition;
+                }
+                if (unlessCondition != null && unlessCondition.length() > 0
+                    && getProject().getProperty(unlessCondition) == null) {
+                    if (text == null) {
+                        text = "";
+                    } else {
+                        text += " and ";
+                    }
+                    text += "unless=" + unlessCondition;
+                }
+                if (nestedConditionPresent()) {
+                    text = "condition satisfied";
+                } else {
+                    if (text == null) {
+                        text = "No message";
+                    }
+                }
+            }
+            throw ((status == null) ? new BuildException(text)
+             : new ExitStatusException(text, status.intValue()));
+        }
+    }
+
+    /**
+     * Set a multiline message.
+     * @param msg the message to display
+     */
+    public void addText(String msg) {
+        if (message == null) {
+            message = "";
+        }
+        message += getProject().replaceProperties(msg);
+    }
+
+    /**
+     * Add a condition element.
+     * @return <CODE>ConditionBase</CODE>.
+     * @since Ant 1.6.2
+     */
+    public ConditionBase createCondition() {
+        if (nestedCondition != null) {
+            throw new BuildException("Only one nested condition is allowed.");
+        }
+        nestedCondition = new NestedCondition();
+        return nestedCondition;
+    }
+
+    /**
+     * test the if condition
+     * @return true if there is no if condition, or the named property exists
+     */
+    private boolean testIfCondition() {
+        if (ifCondition == null || "".equals(ifCondition)) {
+            return true;
+        }
+        return getProject().getProperty(ifCondition) != null;
+    }
+
+    /**
+     * test the unless condition
+     * @return true if there is no unless condition,
+     *  or there is a named property but it doesn't exist
+     */
+    private boolean testUnlessCondition() {
+        if (unlessCondition == null || "".equals(unlessCondition)) {
+            return true;
+        }
+        return getProject().getProperty(unlessCondition) == null;
+    }
+
+    /**
+     * test the nested condition
+     * @return true if there is none, or it evaluates to true
+     */
+    private boolean testNestedCondition() {
+        boolean result = nestedConditionPresent();
+
+        if (result && ifCondition != null || unlessCondition != null) {
+            throw new BuildException("Nested conditions "
+                + "not permitted in conjunction with if/unless attributes");
+        }
+
+        return result && nestedCondition.eval();
+    }
+
+    /**
+     * test whether there is a nested condition.
+     * @return <CODE>boolean</CODE>.
+     */
+    private boolean nestedConditionPresent() {
+        return (nestedCondition != null);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Expand.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Expand.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Expand.java	(revision 16139)
@@ -0,0 +1,305 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.zip.ZipEntry;
+import org.apache.tools.zip.ZipFile;
+
+/**
+ * Unzip a file.
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="packaging"
+ *           name="unzip"
+ *           name="unjar"
+ *           name="unwar"
+ */
+public class Expand extends Task {
+    private File dest; //req
+    private File source; // req
+    private boolean overwrite = true;
+    private Vector patternsets = new Vector();
+    private Vector filesets = new Vector();
+
+    private static final String NATIVE_ENCODING = "native-encoding";
+
+    private String encoding = "UTF8";
+
+    /**
+     * Do the work.
+     *
+     * @exception BuildException Thrown in unrecoverable error.
+     */
+    public void execute() throws BuildException {
+        if ("expand".equals(getTaskType())) {
+            log("!! expand is deprecated. Use unzip instead. !!");
+        }
+
+        if (source == null && filesets.size() == 0) {
+            throw new BuildException("src attribute and/or filesets must be "
+                                     + "specified");
+        }
+
+        if (dest == null) {
+            throw new BuildException(
+                "Dest attribute must be specified");
+        }
+
+        if (dest.exists() && !dest.isDirectory()) {
+            throw new BuildException("Dest must be a directory.", getLocation());
+        }
+
+        FileUtils fileUtils = FileUtils.newFileUtils();
+
+        if (source != null) {
+            if (source.isDirectory()) {
+                throw new BuildException("Src must not be a directory."
+                    + " Use nested filesets instead.", getLocation());
+            } else {
+                expandFile(fileUtils, source, dest);
+            }
+        }
+        if (filesets.size() > 0) {
+            for (int j = 0; j < filesets.size(); j++) {
+                FileSet fs = (FileSet) filesets.elementAt(j);
+                DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+                File fromDir = fs.getDir(getProject());
+
+                String[] files = ds.getIncludedFiles();
+                for (int i = 0; i < files.length; ++i) {
+                    File file = new File(fromDir, files[i]);
+                    expandFile(fileUtils, file, dest);
+                }
+            }
+        }
+    }
+
+    /*
+     * This method is to be overridden by extending unarchival tasks.
+     */
+    protected void expandFile(FileUtils fileUtils, File srcF, File dir) {
+        log("Expanding: " + srcF + " into " + dir, Project.MSG_INFO);
+        ZipFile zf = null;
+        try {
+            zf = new ZipFile(srcF, encoding);
+            Enumeration e = zf.getEntries();
+            while (e.hasMoreElements()) {
+                ZipEntry ze = (ZipEntry) e.nextElement();
+                extractFile(fileUtils, srcF, dir, zf.getInputStream(ze),
+                            ze.getName(), new Date(ze.getTime()),
+                            ze.isDirectory());
+            }
+
+            log("expand complete", Project.MSG_VERBOSE);
+        } catch (IOException ioe) {
+            throw new BuildException("Error while expanding " + srcF.getPath(),
+                                     ioe);
+        } finally {
+            if (zf != null) {
+                try {
+                    zf.close();
+                } catch (IOException e) {
+                    //ignore
+                }
+            }
+        }
+    }
+
+    protected void extractFile(FileUtils fileUtils, File srcF, File dir,
+                               InputStream compressedInputStream,
+                               String entryName,
+                               Date entryDate, boolean isDirectory)
+                               throws IOException {
+
+        if (patternsets != null && patternsets.size() > 0) {
+            String name = entryName.replace('/', File.separatorChar)
+                .replace('\\', File.separatorChar);
+            boolean included = false;
+            for (int v = 0; v < patternsets.size(); v++) {
+                PatternSet p = (PatternSet) patternsets.elementAt(v);
+                String[] incls = p.getIncludePatterns(getProject());
+                if (incls == null || incls.length == 0) {
+                    // no include pattern implicitly means includes="**"
+                    incls = new String[] {"**"};
+                }
+
+                for (int w = 0; w < incls.length; w++) {
+                    String pattern = incls[w].replace('/', File.separatorChar)
+                        .replace('\\', File.separatorChar);
+                    if (pattern.endsWith(File.separator)) {
+                        pattern += "**";
+                    }
+
+                    included = SelectorUtils.matchPath(pattern, name);
+                    if (included) {
+                        break;
+                    }
+                }
+
+                if (!included) {
+                    break;
+                }
+
+
+                String[] excls = p.getExcludePatterns(getProject());
+                if (excls != null) {
+                    for (int w = 0; w < excls.length; w++) {
+                        String pattern = excls[w]
+                            .replace('/', File.separatorChar)
+                            .replace('\\', File.separatorChar);
+                        if (pattern.endsWith(File.separator)) {
+                            pattern += "**";
+                        }
+                        included = !(SelectorUtils.matchPath(pattern, name));
+                        if (!included) {
+                            break;
+                        }
+                    }
+                }
+            }
+            if (!included) {
+                //Do not process this file
+                return;
+            }
+        }
+        File f = fileUtils.resolveFile(dir, entryName);
+        try {
+            if (!overwrite && f.exists()
+                && f.lastModified() >= entryDate.getTime()) {
+                log("Skipping " + f + " as it is up-to-date",
+                    Project.MSG_DEBUG);
+                return;
+            }
+
+            log("expanding " + entryName + " to " + f,
+                Project.MSG_VERBOSE);
+            // create intermediary directories - sometimes zip don't add them
+            File dirF = fileUtils.getParentFile(f);
+            if (dirF != null) {
+                dirF.mkdirs();
+            }
+
+            if (isDirectory) {
+                f.mkdirs();
+            } else {
+                byte[] buffer = new byte[1024];
+                int length = 0;
+                FileOutputStream fos = null;
+                try {
+                    fos = new FileOutputStream(f);
+
+                    while ((length =
+                            compressedInputStream.read(buffer)) >= 0) {
+                        fos.write(buffer, 0, length);
+                    }
+
+                    fos.close();
+                    fos = null;
+                } finally {
+                    if (fos != null) {
+                        try {
+                            fos.close();
+                        } catch (IOException e) {
+                            // ignore
+                        }
+                    }
+                }
+            }
+
+            fileUtils.setFileLastModified(f, entryDate.getTime());
+        } catch (FileNotFoundException ex) {
+            log("Unable to expand to file " + f.getPath(), Project.MSG_WARN);
+        }
+
+    }
+
+    /**
+     * Set the destination directory. File will be unzipped into the
+     * destination directory.
+     *
+     * @param d Path to the directory.
+     */
+    public void setDest(File d) {
+        this.dest = d;
+    }
+
+    /**
+     * Set the path to zip-file.
+     *
+     * @param s Path to zip-file.
+     */
+    public void setSrc(File s) {
+        this.source = s;
+    }
+
+    /**
+     * Should we overwrite files in dest, even if they are newer than
+     * the corresponding entries in the archive?
+     */
+    public void setOverwrite(boolean b) {
+        overwrite = b;
+    }
+
+    /**
+     * Add a patternset
+     */
+    public void addPatternset(PatternSet set) {
+        patternsets.addElement(set);
+    }
+
+    /**
+     * Add a fileset
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * Sets the encoding to assume for file names and comments.
+     *
+     * <p>Set to <code>native-encoding</code> if you want your
+     * platform's native encoding, defaults to UTF8.</p>
+     *
+     * @since Ant 1.6
+     */
+    public void setEncoding(String encoding) {
+        if (NATIVE_ENCODING.equals(encoding)) {
+            encoding = null;
+        }
+        this.encoding = encoding;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Filter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Filter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Filter.java	(revision 16139)
@@ -0,0 +1,91 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Sets a token filter that is used by the file copy tasks
+ * to do token substitution. Sets multiple tokens by
+ * reading these from a file.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="filesystem"
+ */
+public class Filter extends Task {
+
+    private String token;
+    private String value;
+    private File filtersFile;
+
+    /**
+     * The token string without @ delimiters.
+     * @param token token to set
+     */
+    public void setToken(String token) {
+        this.token = token;
+    }
+
+    /**
+     * The string that should replace the token during filtered copies.
+     * @param value token replace value
+     */
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    /**
+     * The file from which the filters must be read.
+     * This file must be a formatted as a property file.
+     *
+     * @param filtersFile filter file
+     */
+    public void setFiltersfile(File filtersFile) {
+        this.filtersFile = filtersFile;
+    }
+
+    public void execute() throws BuildException {
+        boolean isFiltersFromFile =
+            filtersFile != null && token == null && value == null;
+        boolean isSingleFilter =
+            filtersFile == null && token != null && value != null;
+
+        if (!isFiltersFromFile && !isSingleFilter) {
+            throw new BuildException("both token and value parameters, or "
+                                     + "only a filtersFile parameter is "
+                                     + "required", getLocation());
+        }
+
+        if (isSingleFilter) {
+            getProject().getGlobalFilterSet().addFilter(token, value);
+        }
+
+        if (isFiltersFromFile) {
+            readFilters();
+        }
+    }
+
+    protected void readFilters() throws BuildException {
+        log("Reading filters from " + filtersFile, Project.MSG_VERBOSE);
+        getProject().getGlobalFilterSet().readFiltersFromFile(filtersFile);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java	(revision 16139)
@@ -0,0 +1,1001 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Converts text source files to local OS formatting conventions, as
+ * well as repair text files damaged by misconfigured or misguided editors or
+ * file transfer programs.
+ * <p>
+ * This task can take the following arguments:
+ * <ul>
+ * <li>srcdir
+ * <li>destdir
+ * <li>include
+ * <li>exclude
+ * <li>cr
+ * <li>eol
+ * <li>tab
+ * <li>eof
+ * <li>encoding
+ * </ul>
+ * Of these arguments, only <b>sourcedir</b> is required.
+ * <p>
+ * When this task executes, it will scan the srcdir based on the include
+ * and exclude properties.
+ * <p>
+ * This version generalises the handling of EOL characters, and allows
+ * for CR-only line endings (which I suspect is the standard on Macs.)
+ * Tab handling has also been generalised to accommodate any tabwidth
+ * from 2 to 80, inclusive.  Importantly, it will leave untouched any
+ * literal TAB characters embedded within string or character constants.
+ * <p>
+ * <em>Warning:</em> do not run on binary files.
+ * <em>Caution:</em> run with care on carefully formatted files.
+ * This may sound obvious, but if you don't specify asis, presume that
+ * your files are going to be modified.  If "tabs" is "add" or "remove",
+ * whitespace characters may be added or removed as necessary.  Similarly,
+ * for CR's - in fact "eol"="crlf" or cr="add" can result in cr
+ * characters being removed in one special case accommodated, i.e.,
+ * CRCRLF is regarded as a single EOL to handle cases where other
+ * programs have converted CRLF into CRCRLF.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="filesystem"
+ */
+
+public class FixCRLF extends MatchingTask {
+
+    private static final int UNDEF = -1;
+    private static final int NOTJAVA = 0;
+    private static final int LOOKING = 1;
+    private static final int IN_CHAR_CONST = 2;
+    private static final int IN_STR_CONST = 3;
+    private static final int IN_SINGLE_COMMENT = 4;
+    private static final int IN_MULTI_COMMENT = 5;
+
+    private static final int ASIS = 0;
+    private static final int CR = 1;
+    private static final int LF = 2;
+    private static final int CRLF = 3;
+    private static final int ADD = 1;
+    private static final int REMOVE = -1;
+    private static final int SPACES = -1;
+    private static final int TABS = 1;
+
+    private static final int INBUFLEN = 8192;
+    private static final int LINEBUFLEN = 200;
+
+    private static final char CTRLZ = '\u001A';
+
+    private int tablength = 8;
+    private String spaces = "        ";
+    private StringBuffer linebuf = new StringBuffer(1024);
+    private StringBuffer linebuf2 = new StringBuffer(1024);
+    private int eol;
+    private String eolstr;
+    private int ctrlz;
+    private int tabs;
+    private boolean javafiles = false;
+    private boolean fixlast = true;
+
+    private File srcDir;
+    private File destDir = null;
+
+    private FileUtils fileUtils = FileUtils.newFileUtils();
+
+    /**
+     * Encoding to assume for the files
+     */
+    private String encoding = null;
+
+    /**
+     * Defaults the properties based on the system type.
+     * <ul><li>Unix: eol="LF" tab="asis" eof="remove"
+     *     <li>Mac: eol="CR" tab="asis" eof="remove"
+     *     <li>DOS: eol="CRLF" tab="asis" eof="asis"</ul>
+     */
+    public FixCRLF () {
+        tabs = ASIS;
+        if (Os.isFamily("mac")) {
+            ctrlz = REMOVE;
+            eol = CR;
+            eolstr = "\r";
+        } else if (Os.isFamily("dos")) {
+            ctrlz = ASIS;
+            eol = CRLF;
+            eolstr = "\r\n";
+        } else {
+            ctrlz = REMOVE;
+            eol = LF;
+            eolstr = "\n";
+        }
+    }
+
+    /**
+     * Set the source dir to find the source text files.
+     */
+    public void setSrcdir(File srcDir) {
+        this.srcDir = srcDir;
+    }
+
+    /**
+     * Set the destination where the fixed files should be placed.
+     * Default is to replace the original file.
+     */
+    public void setDestdir(File destDir) {
+        this.destDir = destDir;
+    }
+
+    /**
+     * Set to true if modifying Java source files.
+     */
+    public void setJavafiles(boolean javafiles) {
+        this.javafiles = javafiles;
+    }
+
+
+    /**
+     * Specify how EndOfLine characters are to be handled.
+     *
+     * @param attr valid values:
+     * <ul>
+     * <li>asis: leave line endings alone
+     * <li>cr: convert line endings to CR
+     * <li>lf: convert line endings to LF
+     * <li>crlf: convert line endings to CRLF
+     * </ul>
+     */
+    public void setEol(CrLf attr) {
+        String option = attr.getValue();
+        if (option.equals("asis")) {
+            eol = ASIS;
+        } else if (option.equals("cr") || option.equals("mac")) {
+            eol = CR;
+            eolstr = "\r";
+        } else if (option.equals("lf") || option.equals("unix")) {
+            eol = LF;
+            eolstr = "\n";
+        } else {
+            // Must be "crlf"
+            eol = CRLF;
+            eolstr = "\r\n";
+        }
+    }
+
+    /**
+     * Specify how carriage return (CR) characters are to be handled.
+     *
+     * @param attr valid values:
+     * <ul>
+     * <li>add: ensure that there is a CR before every LF
+     * <li>asis: leave CR characters alone
+     * <li>remove: remove all CR characters
+     * </ul>
+     *
+     * @deprecated use {@link #setEol setEol} instead.
+     */
+    public void setCr(AddAsisRemove attr) {
+        log("DEPRECATED: The cr attribute has been deprecated,",
+            Project.MSG_WARN);
+        log("Please use the eol attribute instead", Project.MSG_WARN);
+        String option = attr.getValue();
+        CrLf c = new CrLf();
+        if (option.equals("remove")) {
+            c.setValue("lf");
+        } else if (option.equals("asis")) {
+            c.setValue("asis");
+        } else {
+            // must be "add"
+            c.setValue("crlf");
+        }
+        setEol(c);
+    }
+
+    /**
+     * Specify how tab characters are to be handled.
+     *
+     * @param attr valid values:
+     * <ul>
+     * <li>add: convert sequences of spaces which span a tab stop to tabs
+     * <li>asis: leave tab and space characters alone
+     * <li>remove: convert tabs to spaces
+     * </ul>
+     */
+    public void setTab(AddAsisRemove attr) {
+        String option = attr.getValue();
+        if (option.equals("remove")) {
+            tabs = SPACES;
+        } else if (option.equals("asis")) {
+            tabs = ASIS;
+        } else {
+            // must be "add"
+            tabs = TABS;
+        }
+    }
+
+    /**
+     * Specify tab length in characters.
+     *
+     * @param tlength specify the length of tab in spaces,
+     */
+    public void setTablength(int tlength) throws BuildException {
+        if (tlength < 2 || tlength > 80) {
+            throw new BuildException("tablength must be between 2 and 80",
+                                     getLocation());
+        }
+        tablength = tlength;
+        StringBuffer sp = new StringBuffer();
+        for (int i = 0; i < tablength; i++) {
+            sp.append(' ');
+        }
+        spaces = sp.toString();
+    }
+
+    /**
+     * Specify how DOS EOF (control-z) characters are to be handled.
+     *
+     * @param attr valid values:
+     * <ul>
+     * <li>add: ensure that there is an eof at the end of the file
+     * <li>asis: leave eof characters alone
+     * <li>remove: remove any eof character found at the end
+     * </ul>
+     */
+    public void setEof(AddAsisRemove attr) {
+        String option = attr.getValue();
+        if (option.equals("remove")) {
+            ctrlz = REMOVE;
+        } else if (option.equals("asis")) {
+            ctrlz = ASIS;
+        } else {
+            // must be "add"
+            ctrlz = ADD;
+        }
+    }
+
+    /**
+     * Specifies the encoding Ant expects the files to be in -
+     * defaults to the platforms default encoding.
+     */
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * Specify whether a missing EOL will be added
+     * to the final line of a file.
+     */
+    public void setFixlast(boolean fixlast) {
+        this.fixlast = fixlast;
+    }
+
+    /**
+     * Executes the task.
+     */
+    public void execute() throws BuildException {
+        // first off, make sure that we've got a srcdir and destdir
+
+        if (srcDir == null) {
+            throw new BuildException("srcdir attribute must be set!");
+        }
+        if (!srcDir.exists()) {
+            throw new BuildException("srcdir does not exist!");
+        }
+        if (!srcDir.isDirectory()) {
+            throw new BuildException("srcdir is not a directory!");
+        }
+        if (destDir != null) {
+            if (!destDir.exists()) {
+                throw new BuildException("destdir does not exist!");
+            }
+            if (!destDir.isDirectory()) {
+                throw new BuildException("destdir is not a directory!");
+            }
+        }
+
+        // log options used
+        log("options:"
+            + " eol="
+            + (eol == ASIS ? "asis" : eol == CR ? "cr" : eol == LF ? "lf" : "crlf")
+            + " tab=" + (tabs == TABS ? "add" : tabs == ASIS ? "asis" : "remove")
+            + " eof=" + (ctrlz == ADD ? "add" : ctrlz == ASIS ? "asis" : "remove")
+            + " tablength=" + tablength
+            + " encoding=" + (encoding == null ? "default" : encoding),
+            Project.MSG_VERBOSE);
+
+        DirectoryScanner ds = super.getDirectoryScanner(srcDir);
+        String[] files = ds.getIncludedFiles();
+
+        for (int i = 0; i < files.length; i++) {
+            processFile(files[i]);
+        }
+    }
+
+    /**
+     * Creates a Reader reading from a given file an taking the user
+     * defined encoding into account.
+     */
+    private Reader getReader(File f) throws IOException {
+        return (encoding == null) ? new FileReader(f)
+            : new InputStreamReader(new FileInputStream(f), encoding);
+    }
+
+
+    private void processFile(String file) throws BuildException {
+        File srcFile = new File(srcDir, file);
+        File destD = destDir == null ? srcDir : destDir;
+        File tmpFile = null;
+        BufferedWriter outWriter;
+        OneLiner.BufferLine line;
+
+        // read the contents of the file
+        OneLiner lines = new OneLiner(srcFile);
+
+        try {
+            // Set up the output Writer
+            try {
+                tmpFile = fileUtils.createTempFile("fixcrlf", "", null);
+                tmpFile.deleteOnExit();
+                Writer writer = (encoding == null) ? new FileWriter(tmpFile)
+                    : new OutputStreamWriter(new FileOutputStream(tmpFile),
+                                             encoding);
+                outWriter = new BufferedWriter(writer);
+            } catch (IOException e) {
+                throw new BuildException(e);
+            }
+
+            while (lines.hasMoreElements()) {
+                // In-line states
+                int endComment;
+
+                try {
+                    line = (OneLiner.BufferLine) lines.nextElement();
+                } catch (NoSuchElementException e) {
+                    throw new BuildException(e);
+                }
+
+                String lineString = line.getLineString();
+                int linelen = line.length();
+
+                // Note - all of the following processing NOT done for
+                // tabs ASIS
+
+                if (tabs == ASIS) {
+                    // Just copy the body of the line across
+                    try {
+                        outWriter.write(lineString);
+                    } catch (IOException e) {
+                        throw new BuildException(e);
+                    } // end of try-catch
+
+                } else { // (tabs != ASIS)
+
+                    while (line.getNext() < linelen) {
+
+                        switch (lines.getState()) {
+
+                        case NOTJAVA:
+                            notInConstant(line, line.length(), outWriter);
+                            break;
+
+                        case IN_MULTI_COMMENT:
+                            endComment
+                                = lineString.indexOf("*/", line.getNext());
+                            if (endComment >= 0) {
+                                // End of multiLineComment on this line
+                                endComment += 2;  // Include the end token
+                                lines.setState(LOOKING);
+                            } else {
+                                endComment = linelen;
+                            }
+
+                            notInConstant(line, endComment, outWriter);
+                            break;
+
+                        case IN_SINGLE_COMMENT:
+                            notInConstant(line, line.length(), outWriter);
+                            lines.setState(LOOKING);
+                            break;
+
+                        case IN_CHAR_CONST:
+                        case IN_STR_CONST:
+                            // Got here from LOOKING by finding an
+                            // opening "\'" next points to that quote
+                            // character.
+                            // Find the end of the constant.  Watch
+                            // out for backslashes.  Literal tabs are
+                            // left unchanged, and the column is
+                            // adjusted accordingly.
+
+                            int begin = line.getNext();
+                            char terminator = (lines.getState() == IN_STR_CONST
+                                               ? '\"'
+                                               : '\'');
+                            endOfCharConst(line, terminator);
+                            while (line.getNext() < line.getLookahead()) {
+                                if (line.getNextCharInc() == '\t') {
+                                    line.setColumn(line.getColumn()
+                                        + tablength
+                                        - (line.getColumn() % tablength));
+                                } else {
+                                    line.incColumn();
+                                }
+                            }
+
+                            // Now output the substring
+                            try {
+                                outWriter.write(line.substring(begin,
+                                                               line.getNext()));
+                            } catch (IOException e) {
+                                throw new BuildException(e);
+                            }
+
+                            lines.setState(LOOKING);
+
+                            break;
+
+
+                        case LOOKING:
+                            nextStateChange(line);
+                            notInConstant(line, line.getLookahead(), outWriter);
+                            break;
+
+                        } // end of switch (state)
+
+                    } // end of while (line.getNext() < linelen)
+
+                } // end of else (tabs != ASIS)
+
+                if (!("".equals(line.getEol())) || fixlast) {
+                    try {
+                        outWriter.write(eolstr);
+                    } catch (IOException e) {
+                        throw new BuildException(e);
+                    } // end of try-catch
+                } //end if non-blank original eol or fixlast
+
+            } // end of while (lines.hasNext())
+
+            try {
+                // Handle CTRLZ
+                if (ctrlz == ASIS) {
+                    outWriter.write(lines.getEofStr());
+                } else if (ctrlz == ADD) {
+                    outWriter.write(CTRLZ);
+                }
+            } catch (IOException e) {
+                throw new BuildException(e);
+            } finally {
+                try {
+                    outWriter.close();
+                } catch (IOException e) {
+                    throw new BuildException(e);
+                }
+            }
+
+
+            try {
+                lines.close();
+                lines = null;
+            } catch (IOException e) {
+                throw new BuildException("Unable to close source file "
+                                         + srcFile);
+            }
+
+            File destFile = new File(destD, file);
+
+            boolean destIsWrong = true;
+            if (destFile.exists()) {
+                // Compare the destination with the temp file
+                log("destFile exists", Project.MSG_DEBUG);
+                if (!fileUtils.contentEquals(destFile, tmpFile)) {
+                    log(destFile + " is being written", Project.MSG_DEBUG);
+                } else {
+                    log(destFile + " is not written, as the contents "
+                        + "are identical", Project.MSG_DEBUG);
+                    destIsWrong = false;
+                }
+            }
+
+            if (destIsWrong) {
+                fileUtils.rename(tmpFile, destFile);
+                tmpFile = null;
+            }
+
+        } catch (IOException e) {
+            throw new BuildException(e);
+        } finally {
+            try {
+                if (lines != null) {
+                    lines.close();
+                }
+            } catch (IOException io) {
+                log("Error closing " + srcFile, Project.MSG_ERR);
+            } // end of catch
+
+            if (tmpFile != null) {
+                tmpFile.delete();
+            }
+        } // end of finally
+    }
+
+    /**
+     * Scan a BufferLine for the next state changing token: the beginning
+     * of a single or multi-line comment, a character or a string constant.
+     *
+     * As a side-effect, sets the buffer state to the next state, and sets
+     * field lookahead to the first character of the state-changing token, or
+     * to the next eol character.
+     *
+     * @param bufline       BufferLine containing the string
+     *                                 to be processed
+     * @exception org.apache.tools.ant.BuildException
+     *                                 Thrown when end of line is reached
+     *                                 before the terminator is found.
+     */
+    private void nextStateChange(OneLiner.BufferLine bufline)
+        throws BuildException {
+        int eol = bufline.length();
+        int ptr = bufline.getNext();
+
+
+        //  Look for next single or double quote, double slash or slash star
+        while (ptr < eol) {
+            switch (bufline.getChar(ptr++)) {
+            case '\'':
+                bufline.setState(IN_CHAR_CONST);
+                bufline.setLookahead(--ptr);
+                return;
+            case '\"':
+                bufline.setState(IN_STR_CONST);
+                bufline.setLookahead(--ptr);
+                return;
+            case '/':
+                if (ptr < eol) {
+                    if (bufline.getChar(ptr) == '*') {
+                        bufline.setState(IN_MULTI_COMMENT);
+                        bufline.setLookahead(--ptr);
+                        return;
+                    } else if (bufline.getChar(ptr) == '/') {
+                        bufline.setState(IN_SINGLE_COMMENT);
+                        bufline.setLookahead(--ptr);
+                        return;
+                    }
+                }
+                break;
+            } // end of switch (bufline.getChar(ptr++))
+
+        } // end of while (ptr < eol)
+        // Eol is the next token
+        bufline.setLookahead(ptr);
+    }
+
+
+    /**
+     * Scan a BufferLine forward from the 'next' pointer
+     * for the end of a character constant.  Set 'lookahead' pointer to the
+     * character following the terminating quote.
+     *
+     * @param bufline       BufferLine containing the string
+     *                                 to be processed
+     * @param terminator          The constant terminator
+     *
+     * @exception org.apache.tools.ant.BuildException
+     *                                 Thrown when end of line is reached
+     *                                 before the terminator is found.
+     */
+    private void endOfCharConst(OneLiner.BufferLine bufline, char terminator)
+        throws BuildException {
+        int ptr = bufline.getNext();
+        int eol = bufline.length();
+        char c;
+        ptr++;          // skip past initial quote
+        while (ptr < eol) {
+            if ((c = bufline.getChar(ptr++)) == '\\') {
+                ptr++;
+            } else {
+                if (c == terminator) {
+                    bufline.setLookahead(ptr);
+                    return;
+                }
+            }
+        } // end of while (ptr < eol)
+        // Must have fallen through to the end of the line
+        throw new BuildException("endOfCharConst: unterminated char constant");
+    }
+
+
+    /**
+     * Process a BufferLine string which is not part of a string constant.
+     * The start position of the string is given by the 'next' field.
+     * Sets the 'next' and 'column' fields in the BufferLine.
+     *
+     * @param bufline       BufferLine containing the string
+     *                                 to be processed
+     * @param end                  Index just past the end of the
+     *                                 string
+     * @param outWriter Sink for the processed string
+     */
+    private void notInConstant(OneLiner.BufferLine bufline, int end,
+                                BufferedWriter outWriter) {
+        // N.B. both column and string index are zero-based
+        // Process a string not part of a constant;
+        // i.e. convert tabs<->spaces as required
+        // This is NOT called for ASIS tab handling
+        int nextTab;
+        int nextStop;
+        int tabspaces;
+        String line = bufline.substring(bufline.getNext(), end);
+        int place = 0;          // Zero-based
+        int col = bufline.getColumn();  // Zero-based
+
+        // process sequences of white space
+        // first convert all tabs to spaces
+        linebuf = new StringBuffer();
+        while ((nextTab = line.indexOf((int) '\t', place)) >= 0) {
+            linebuf.append(line.substring(place, nextTab)); // copy to the TAB
+            col += nextTab - place;
+            tabspaces = tablength - (col % tablength);
+            linebuf.append(spaces.substring(0, tabspaces));
+            col += tabspaces;
+            place = nextTab + 1;
+        } // end of while
+        linebuf.append(line.substring(place, line.length()));
+        // if converting to spaces, all finished
+        String linestring = new String(linebuf.substring(0));
+        if (tabs == REMOVE) {
+            try {
+                outWriter.write(linestring);
+            } catch (IOException e) {
+                throw new BuildException(e);
+            } // end of try-catch
+        } else { // tabs == ADD
+            int tabCol;
+            linebuf2 = new StringBuffer();
+            place = 0;
+            col = bufline.getColumn();
+            int placediff = col - 0;
+            // for the length of the string, cycle through the tab stop
+            // positions, checking for a space preceded by at least one
+            // other space at the tab stop.  if so replace the longest possible
+            // preceding sequence of spaces with a tab.
+            nextStop = col + (tablength - col % tablength);
+            if (nextStop - col < 2) {
+                linebuf2.append(linestring.substring(
+                                        place, nextStop - placediff));
+                place = nextStop - placediff;
+                nextStop += tablength;
+            }
+
+            for (; nextStop - placediff <= linestring.length();
+                    nextStop += tablength) {
+                for (tabCol = nextStop;
+                             --tabCol - placediff >= place
+                             && linestring.charAt(tabCol - placediff) == ' ';) {
+                    ; // Loop for the side-effects
+                }
+                // tabCol is column index of the last non-space character
+                // before the next tab stop
+                if (nextStop - tabCol > 2) {
+                    linebuf2.append(linestring.substring(
+                                    place, ++tabCol - placediff));
+                    linebuf2.append('\t');
+                } else {
+                    linebuf2.append(linestring.substring(
+                                    place, nextStop - placediff));
+                } // end of else
+
+                place = nextStop - placediff;
+            } // end of for (nextStop ... )
+
+            // pick up that last bit, if any
+            linebuf2.append(linestring.substring(place, linestring.length()));
+
+            try {
+                outWriter.write(linebuf2.substring(0));
+            } catch (IOException e) {
+                throw new BuildException(e);
+            } // end of try-catch
+
+        } // end of else tabs == ADD
+
+        // Set column position as modified by this method
+        bufline.setColumn(bufline.getColumn() + linestring.length());
+        bufline.setNext(end);
+
+    }
+
+
+    class OneLiner implements Enumeration {
+
+        private int state = javafiles ? LOOKING : NOTJAVA;
+
+        private StringBuffer eolStr = new StringBuffer(LINEBUFLEN);
+        private StringBuffer eofStr = new StringBuffer();
+
+        private BufferedReader reader;
+        private StringBuffer line = new StringBuffer();
+        private boolean reachedEof = false;
+        private File srcFile;
+
+        public OneLiner(File srcFile)
+            throws BuildException {
+            this.srcFile = srcFile;
+            try {
+                reader = new BufferedReader
+                        (getReader(srcFile), INBUFLEN);
+                nextLine();
+            } catch (IOException e) {
+                throw new BuildException(srcFile + ": " + e.getMessage(),
+                                         e, getLocation());
+            }
+        }
+
+        protected void nextLine()
+            throws BuildException {
+            int ch = -1;
+            int eolcount = 0;
+
+            eolStr = new StringBuffer();
+            line = new StringBuffer();
+
+            try {
+                ch = reader.read();
+                while (ch != -1 && ch != '\r' && ch != '\n') {
+                    line.append((char) ch);
+                    ch = reader.read();
+                }
+
+                if (ch == -1 && line.length() == 0) {
+                    // Eof has been reached
+                    reachedEof = true;
+                    return;
+                }
+
+                switch ((char) ch) {
+                case '\r':
+                    // Check for \r, \r\n and \r\r\n
+                    // Regard \r\r not followed by \n as two lines
+                    ++eolcount;
+                    eolStr.append('\r');
+                    reader.mark(2);
+                    switch ((ch = reader.read())) {
+                    case '\r':
+                        if ((char) (ch = reader.read()) == '\n') {
+                            eolcount += 2;
+                            eolStr.append("\r\n");
+                        } else {
+                            reader.reset();
+                        }
+                        break;
+                    case '\n':
+                        ++eolcount;
+                        eolStr.append('\n');
+                        break;
+                    case -1:
+                        // don't reposition when we've reached the end
+                        // of the stream
+                        break;
+                    default:
+                        reader.reset();
+                        break;
+                    } // end of switch ((char)(ch = reader.read()))
+                    break;
+
+                case '\n':
+                    ++eolcount;
+                    eolStr.append('\n');
+                    break;
+
+                } // end of switch ((char) ch)
+
+                // if at eolcount == 0 and trailing characters of string
+                // are CTRL-Zs, set eofStr
+                if (eolcount == 0) {
+                    int i = line.length();
+                    while (--i >= 0 && line.charAt(i) == CTRLZ) {
+                        // keep searching for the first ^Z
+                    }
+                    if (i < line.length() - 1) {
+                        // Trailing characters are ^Zs
+                        // Construct new line and eofStr
+                        eofStr.append(line.toString().substring(i + 1));
+                        if (i < 0) {
+                            line.setLength(0);
+                            reachedEof = true;
+                        } else {
+                            line.setLength(i + 1);
+                        }
+                    }
+
+                } // end of if (eolcount == 0)
+
+            } catch (IOException e) {
+                throw new BuildException(srcFile + ": " + e.getMessage(),
+                                         e, getLocation());
+            }
+        }
+
+        public String getEofStr() {
+            return eofStr.substring(0);
+        }
+
+        public int getState() {
+            return state;
+        }
+
+        public void setState(int state) {
+            this.state = state;
+        }
+
+        public boolean hasMoreElements() {
+            return !reachedEof;
+        }
+
+        public Object nextElement()
+            throws NoSuchElementException {
+            if (!hasMoreElements()) {
+                throw new NoSuchElementException("OneLiner");
+            }
+            BufferLine tmpLine =
+                    new BufferLine(line.toString(), eolStr.substring(0));
+            nextLine();
+            return tmpLine;
+        }
+
+        public void close() throws IOException {
+            if (reader != null) {
+                reader.close();
+            }
+        }
+
+        class BufferLine {
+            private int next = 0;
+            private int column = 0;
+            private int lookahead = UNDEF;
+            private String line;
+            private String eolStr;
+
+            public BufferLine(String line, String eolStr)
+                throws BuildException {
+                next = 0;
+                column = 0;
+                this.line = line;
+                this.eolStr = eolStr;
+            }
+
+            public int getNext() {
+                return next;
+            }
+
+            public void setNext(int next) {
+                this.next = next;
+            }
+
+            public int getLookahead() {
+                return lookahead;
+            }
+
+            public void setLookahead(int lookahead) {
+                this.lookahead = lookahead;
+            }
+
+            public char getChar(int i) {
+                return line.charAt(i);
+            }
+
+            public char getNextChar() {
+                return getChar(next);
+            }
+
+            public char getNextCharInc() {
+                return getChar(next++);
+            }
+
+            public int getColumn() {
+                return column;
+            }
+
+            public void setColumn(int col) {
+                column = col;
+            }
+
+            public int incColumn() {
+                return column++;
+            }
+
+            public int length() {
+                return line.length();
+            }
+
+            public int getEolLength() {
+                return eolStr.length();
+            }
+
+            public String getLineString() {
+                return line;
+            }
+
+            public String getEol() {
+                return eolStr;
+            }
+
+            public String substring(int begin) {
+                return line.substring(begin);
+            }
+
+            public String substring(int begin, int end) {
+                return line.substring(begin, end);
+            }
+
+            public void setState(int state) {
+                OneLiner.this.setState(state);
+            }
+
+            public int getState() {
+                return OneLiner.this.getState();
+            }
+        }
+    }
+
+    /**
+     * Enumerated attribute with the values "asis", "add" and "remove".
+     */
+    public static class AddAsisRemove extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[] {"add", "asis", "remove"};
+        }
+    }
+
+    /**
+     * Enumerated attribute with the values "asis", "cr", "lf" and "crlf".
+     */
+    public static class CrLf extends EnumeratedAttribute {
+        /**
+         * @see EnumeratedAttribute#getValues
+         */
+        public String[] getValues() {
+            return new String[] {"asis", "cr", "lf", "crlf",
+                                 "mac", "unix", "dos"};
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/GUnzip.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/GUnzip.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/GUnzip.java	(revision 16139)
@@ -0,0 +1,90 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.zip.GZIPInputStream;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Expands a file that has been compressed with the GZIP
+ * algorithm. Normally used to compress non-compressed archives such
+ * as TAR files.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="packaging"
+ */
+
+public class GUnzip extends Unpack {
+
+    private static final String DEFAULT_EXTENSION = ".gz";
+
+    protected String getDefaultExtension() {
+        return DEFAULT_EXTENSION;
+    }
+
+    protected void extract() {
+        if (source.lastModified() > dest.lastModified()) {
+            log("Expanding " + source.getAbsolutePath() + " to "
+                        + dest.getAbsolutePath());
+
+            FileOutputStream out = null;
+            GZIPInputStream zIn = null;
+            FileInputStream fis = null;
+            try {
+                out = new FileOutputStream(dest);
+                fis = new FileInputStream(source);
+                zIn = new GZIPInputStream(fis);
+                byte[] buffer = new byte[8 * 1024];
+                int count = 0;
+                do {
+                    out.write(buffer, 0, count);
+                    count = zIn.read(buffer, 0, buffer.length);
+                } while (count != -1);
+            } catch (IOException ioe) {
+                String msg = "Problem expanding gzip " + ioe.getMessage();
+                throw new BuildException(msg, ioe, getLocation());
+            } finally {
+                if (fis != null) {
+                    try {
+                        fis.close();
+                    } catch (IOException ioex) {
+                        //ignore
+                    }
+                }
+                if (out != null) {
+                    try {
+                        out.close();
+                    } catch (IOException ioex) {
+                        //ignore
+                    }
+                }
+                if (zIn != null) {
+                    try {
+                        zIn.close();
+                    } catch (IOException ioex) {
+                        //ignore
+                    }
+                }
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/GZip.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/GZip.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/GZip.java	(revision 16139)
@@ -0,0 +1,57 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.zip.GZIPOutputStream;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Compresses a file with the GZIP algorithm. Normally used to compress
+ * non-compressed archives such as TAR files.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="packaging"
+ */
+
+public class GZip extends Pack {
+    /**
+     * perform the GZip compression operation.
+     */
+    protected void pack() {
+        GZIPOutputStream zOut = null;
+        try {
+            zOut = new GZIPOutputStream(new FileOutputStream(zipFile));
+            zipFile(source, zOut);
+        } catch (IOException ioe) {
+            String msg = "Problem creating gzip " + ioe.getMessage();
+            throw new BuildException(msg, ioe, getLocation());
+        } finally {
+            if (zOut != null) {
+                try {
+                    // close up
+                    zOut.close();
+                } catch (IOException e) {
+                    // do nothing
+                }
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/GenerateKey.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/GenerateKey.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/GenerateKey.java	(revision 16139)
@@ -0,0 +1,371 @@
+/*
+ * Copyright  2000,2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Generates a key in a keystore.
+ *
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task name="genkey" category="java"
+ */
+public class GenerateKey extends Task {
+
+    public static class DnameParam {
+        private String name;
+        private String value;
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getName() {
+            return name;
+        }
+
+        public void setValue(String value) {
+            this.value = value;
+        }
+
+        public String getValue() {
+            return value;
+        }
+    }
+
+    public static class DistinguishedName {
+        private Vector params = new Vector();
+
+        public Object createParam() {
+            DnameParam param = new DnameParam();
+            params.addElement(param);
+
+            return param;
+        }
+
+        public Enumeration getParams() {
+            return params.elements();
+        }
+
+        public String toString() {
+            final int size = params.size();
+            final StringBuffer sb = new StringBuffer();
+            boolean firstPass = true;
+
+            for (int i = 0; i < size; i++) {
+                if (!firstPass) {
+                    sb.append(" ,");
+                }
+                firstPass = false;
+
+                final DnameParam param = (DnameParam) params.elementAt(i);
+                sb.append(encode(param.getName()));
+                sb.append('=');
+                sb.append(encode(param.getValue()));
+            }
+
+            return sb.toString();
+        }
+
+        public String encode(final String string) {
+            int end = string.indexOf(',');
+
+            if (-1 == end) {
+              return string;
+            }
+
+            final StringBuffer sb = new StringBuffer();
+
+            int start = 0;
+
+            while (-1 != end) {
+                sb.append(string.substring(start, end));
+                sb.append("\\,");
+                start = end + 1;
+                end = string.indexOf(',', start);
+            }
+
+            sb.append(string.substring(start));
+
+            return sb.toString();
+        }
+    }
+
+    /**
+     * The alias of signer.
+     */
+    protected String alias;
+
+    /**
+     * The name of keystore file.
+     */
+    protected String keystore;
+    protected String storepass;
+    protected String storetype;
+    protected String keypass;
+
+    protected String sigalg;
+    protected String keyalg;
+    protected String dname;
+    protected DistinguishedName expandedDname;
+    protected int keysize;
+    protected int validity;
+    protected boolean verbose;
+
+    /**
+     * Distinguished name list.
+     *
+     * @return Distinguished name container.
+     * @throws BuildException If specified more than once or dname
+     *                        attribute is used.
+     */
+    public DistinguishedName createDname() throws BuildException {
+        if (null != expandedDname) {
+            throw new BuildException("DName sub-element can only be "
+                                     + "specified once.");
+        }
+        if (null != dname) {
+            throw new BuildException("It is not possible to specify dname "
+                                    + " both as attribute and element.");
+        }
+        expandedDname = new DistinguishedName();
+        return expandedDname;
+    }
+
+    /**
+     * The distinguished name for entity.
+     *
+     * @param dname distinguished name
+     */
+    public void setDname(final String dname) {
+        if (null != expandedDname) {
+            throw new BuildException("It is not possible to specify dname "
+                                    + " both as attribute and element.");
+        }
+        this.dname = dname;
+    }
+
+    /**
+     * The alias to add under.
+     *
+     * @param alias alias to add under
+     */
+    public void setAlias(final String alias) {
+        this.alias = alias;
+    }
+
+    /**
+     * Keystore location.
+     *
+     * @param keystore location
+     */
+    public void setKeystore(final String keystore) {
+        this.keystore = keystore;
+    }
+
+    /**
+     * Password for keystore integrity.
+     * Must be at least 6 characters long.
+     * @param storepass password
+     */
+    public void setStorepass(final String storepass) {
+        this.storepass = storepass;
+    }
+
+    /**
+     * Keystore type.
+     *
+     * @param storetype type
+     */
+    public void setStoretype(final String storetype) {
+        this.storetype = storetype;
+    }
+
+    /**
+     * Password for private key (if different).
+     *
+     * @param keypass password
+     */
+    public void setKeypass(final String keypass) {
+        this.keypass = keypass;
+    }
+
+    /**
+     * The algorithm to use in signing.
+     *
+     * @param sigalg algorithm
+     */
+    public void setSigalg(final String sigalg) {
+        this.sigalg = sigalg;
+    }
+
+    /**
+     * The method to use when generating name-value pair.
+     * @param keyalg algorithm
+     */
+    public void setKeyalg(final String keyalg) {
+        this.keyalg = keyalg;
+    }
+
+    /**
+     * Indicates the size of key generated.
+     *
+     * @param keysize size of key
+     * @throws BuildException If not an Integer
+     * @todo Could convert this to a plain Integer setter.
+     */
+    public void setKeysize(final String keysize) throws BuildException {
+        try {
+            this.keysize = Integer.parseInt(keysize);
+        } catch (final NumberFormatException nfe) {
+            throw new BuildException("KeySize attribute should be a integer");
+        }
+    }
+
+    /**
+     * Indicates how many days certificate is valid.
+     *
+     * @param validity days valid
+     * @throws BuildException If not an Integer
+     */
+    public void setValidity(final String validity) throws BuildException {
+        try {
+            this.validity = Integer.parseInt(validity);
+        } catch (final NumberFormatException nfe) {
+            throw new BuildException("Validity attribute should be a integer");
+        }
+    }
+
+    /**
+     * If true, verbose output when signing.
+     * @param verbose verbose or not
+     */
+    public void setVerbose(final boolean verbose) {
+        this.verbose = verbose;
+    }
+
+    public void execute() throws BuildException {
+        if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
+            throw new BuildException("The genkey task is only available on JDK"
+                                     + " versions 1.2 or greater");
+        }
+
+        if (null == alias) {
+            throw new BuildException("alias attribute must be set");
+        }
+
+        if (null == storepass) {
+            throw new BuildException("storepass attribute must be set");
+        }
+
+        if (null == dname && null == expandedDname) {
+            throw new BuildException("dname must be set");
+        }
+
+        final StringBuffer sb = new StringBuffer();
+
+        sb.append("-genkey ");
+
+        if (verbose) {
+            sb.append("-v ");
+        }
+
+        sb.append("-alias \"");
+        sb.append(alias);
+        sb.append("\" ");
+
+        if (null != dname) {
+            sb.append("-dname \"");
+            sb.append(dname);
+            sb.append("\" ");
+        }
+
+        if (null != expandedDname) {
+            sb.append("-dname \"");
+            sb.append(expandedDname);
+            sb.append("\" ");
+        }
+
+        if (null != keystore) {
+            sb.append("-keystore \"");
+            sb.append(keystore);
+            sb.append("\" ");
+        }
+
+        if (null != storepass) {
+            sb.append("-storepass \"");
+            sb.append(storepass);
+            sb.append("\" ");
+        }
+
+        if (null != storetype) {
+            sb.append("-storetype \"");
+            sb.append(storetype);
+            sb.append("\" ");
+        }
+
+        sb.append("-keypass \"");
+        if (null != keypass) {
+            sb.append(keypass);
+        } else {
+            sb.append(storepass);
+        }
+        sb.append("\" ");
+
+        if (null != sigalg) {
+            sb.append("-sigalg \"");
+            sb.append(sigalg);
+            sb.append("\" ");
+        }
+
+        if (null != keyalg) {
+            sb.append("-keyalg \"");
+            sb.append(keyalg);
+            sb.append("\" ");
+        }
+
+
+        if (0 < keysize) {
+            sb.append("-keysize \"");
+            sb.append(keysize);
+            sb.append("\" ");
+        }
+
+        if (0 < validity) {
+            sb.append("-validity \"");
+            sb.append(validity);
+            sb.append("\" ");
+        }
+
+        log("Generating Key for " + alias);
+        final ExecTask cmd = (ExecTask) getProject().createTask("exec");
+        cmd.setExecutable(JavaEnvUtils.getJdkExecutable("keytool"));
+        Commandline.Argument arg = cmd.createArg();
+        arg.setLine(sb.toString());
+        cmd.setFailonerror(true);
+        cmd.setTaskName(getTaskName());
+        cmd.execute();
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Get.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Get.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Get.java	(revision 16139)
@@ -0,0 +1,498 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Date;
+
+/**
+ * Gets a particular file from a URL source.
+ * Options include verbose reporting, timestamp based fetches and controlling
+ * actions on failures. NB: access through a firewall only works if the whole
+ * Java runtime is correctly configured.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="network"
+ */
+public class Get extends Task {
+
+    private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+    private URL source; // required
+    private File dest; // required
+    private boolean verbose = false;
+    private boolean useTimestamp = false; //off by default
+    private boolean ignoreErrors = false;
+    private String uname = null;
+    private String pword = null;
+
+
+
+    /**
+     * Does the work.
+     *
+     * @exception BuildException Thrown in unrecoverable error.
+     */
+    public void execute() throws BuildException {
+
+        //set up logging
+        int logLevel = Project.MSG_INFO;
+        DownloadProgress progress = null;
+        if (verbose) {
+            progress = new VerboseProgress(System.out);
+        }
+
+        //execute the get
+        try {
+            doGet(logLevel, progress);
+        } catch (IOException ioe) {
+            log("Error getting " + source + " to " + dest);
+            if (!ignoreErrors) {
+                throw new BuildException(ioe, getLocation());
+            }
+        }
+    }
+
+    /**
+     * make a get request, with the supplied progress and logging info.
+     * All the other config parameters are set at the task level,
+     * source, dest, ignoreErrors, etc.
+     * @param logLevel level to log at, see {@link Project#log(String, int)}
+     * @param progress progress callback; null for no-callbacks
+     * @return true for a successful download, false otherwise.
+     * The return value is only relevant when {@link #ignoreErrors} is true, as
+     * when false all failures raise BuildExceptions.
+     * @throws IOException for network trouble
+     * @throws BuildException for argument errors, or other trouble when ignoreErrors
+     * is false.
+     */
+    public boolean doGet(int logLevel, DownloadProgress progress)
+            throws IOException {
+        if (source == null) {
+            throw new BuildException("src attribute is required", getLocation());
+        }
+
+        if (dest == null) {
+            throw new BuildException("dest attribute is required", getLocation());
+        }
+
+        if (dest.exists() && dest.isDirectory()) {
+            throw new BuildException("The specified destination is a directory",
+                    getLocation());
+        }
+
+        if (dest.exists() && !dest.canWrite()) {
+            throw new BuildException("Can't write to " + dest.getAbsolutePath(),
+                    getLocation());
+        }
+        //dont do any progress, unless asked
+        if (progress == null) {
+            progress = new NullProgress();
+        }
+        log("Getting: " + source, logLevel);
+        log("To: " + dest.getAbsolutePath(), logLevel);
+
+        //set the timestamp to the file date.
+        long timestamp = 0;
+
+        boolean hasTimestamp = false;
+        if (useTimestamp && dest.exists()) {
+            timestamp = dest.lastModified();
+            if (verbose) {
+                Date t = new Date(timestamp);
+                log("local file date : " + t.toString(), logLevel);
+            }
+            hasTimestamp = true;
+        }
+
+        //set up the URL connection
+        URLConnection connection = source.openConnection();
+        //modify the headers
+        //NB: things like user authentication could go in here too.
+        if (hasTimestamp) {
+            connection.setIfModifiedSince(timestamp);
+        }
+        // prepare Java 1.1 style credentials
+        if (uname != null || pword != null) {
+            String up = uname + ":" + pword;
+            String encoding;
+            //we do not use the sun impl for portability,
+            //and always use our own implementation for consistent
+            //testing
+            Base64Converter encoder = new Base64Converter();
+            encoding = encoder.encode(up.getBytes());
+            connection.setRequestProperty ("Authorization",
+                    "Basic " + encoding);
+        }
+
+        //connect to the remote site (may take some time)
+        connection.connect();
+        //next test for a 304 result (HTTP only)
+        if (connection instanceof HttpURLConnection) {
+            HttpURLConnection httpConnection
+                    = (HttpURLConnection) connection;
+            long lastModified = httpConnection.getLastModified();
+            if (httpConnection.getResponseCode()
+                    == HttpURLConnection.HTTP_NOT_MODIFIED
+                || (lastModified != 0 && hasTimestamp
+                && timestamp > lastModified)) {
+                //not modified so no file download. just return
+                //instead and trace out something so the user
+                //doesn't think that the download happened when it
+                //didn't
+                log("Not modified - so not downloaded", logLevel);
+                return false;
+            }
+            // test for 401 result (HTTP only)
+            if (httpConnection.getResponseCode()
+                    == HttpURLConnection.HTTP_UNAUTHORIZED)  {
+                String message = "HTTP Authorization failure";
+                if (ignoreErrors) {
+                    log(message, logLevel);
+                    return false;
+                } else {
+                    throw new BuildException(message);
+                }
+            }
+
+        }
+
+        //REVISIT: at this point even non HTTP connections may
+        //support the if-modified-since behaviour -we just check
+        //the date of the content and skip the write if it is not
+        //newer. Some protocols (FTP) don't include dates, of
+        //course.
+
+        InputStream is = null;
+        for (int i = 0; i < 3; i++) {
+            //this three attempt trick is to get round quirks in different
+            //Java implementations. Some of them take a few goes to bind
+            //property; we ignore the first couple of such failures.
+            try {
+                is = connection.getInputStream();
+                break;
+            } catch (IOException ex) {
+                log("Error opening connection " + ex, logLevel);
+            }
+        }
+        if (is == null) {
+            log("Can't get " + source + " to " + dest, logLevel);
+            if (ignoreErrors) {
+                return false;
+            }
+            throw new BuildException("Can't get " + source + " to " + dest,
+                    getLocation());
+        }
+
+        FileOutputStream fos = new FileOutputStream(dest);
+        progress.beginDownload();
+        boolean finished = false;
+        try {
+            byte[] buffer = new byte[100 * 1024];
+            int length;
+            while ((length = is.read(buffer)) >= 0) {
+                fos.write(buffer, 0, length);
+                progress.onTick();
+            }
+            finished = true;
+        } finally {
+            FileUtils.close(fos);
+            FileUtils.close(is);
+
+            // we have started to (over)write dest, but failed.
+            // Try to delete the garbage we'd otherwise leave
+            // behind.
+            if (!finished) {
+                dest.delete();
+            }
+        }
+        progress.endDownload();
+
+        //if (and only if) the use file time option is set, then
+        //the saved file now has its timestamp set to that of the
+        //downloaded file
+        if (useTimestamp)  {
+            long remoteTimestamp = connection.getLastModified();
+            if (verbose)  {
+                Date t = new Date(remoteTimestamp);
+                log("last modified = " + t.toString()
+                        + ((remoteTimestamp == 0)
+                        ? " - using current time instead"
+                        : ""), logLevel);
+            }
+            if (remoteTimestamp != 0) {
+                FILE_UTILS.setFileLastModified(dest, remoteTimestamp);
+            }
+        }
+
+        //successful download
+        return true;
+    }
+
+
+    /**
+     * Set the URL to get.
+     *
+     * @param u URL for the file.
+     */
+    public void setSrc(URL u) {
+        this.source = u;
+    }
+
+    /**
+     * Where to copy the source file.
+     *
+     * @param dest Path to file.
+     */
+    public void setDest(File dest) {
+        this.dest = dest;
+    }
+
+    /**
+     * If true, show verbose progress information.
+     *
+     * @param v if "true" then be verbose
+     */
+    public void setVerbose(boolean v) {
+        verbose = v;
+    }
+
+    /**
+     * If true, log errors but do not treat as fatal.
+     *
+     * @param v if "true" then don't report download errors up to ant
+     */
+    public void setIgnoreErrors(boolean v) {
+        ignoreErrors = v;
+    }
+
+    /**
+     * If true, conditionally download a file based on the timestamp
+     * of the local copy.
+     *
+     * <p>In this situation, the if-modified-since header is set so
+     * that the file is only fetched if it is newer than the local
+     * file (or there is no local file) This flag is only valid on
+     * HTTP connections, it is ignored in other cases.  When the flag
+     * is set, the local copy of the downloaded file will also have
+     * its timestamp set to the remote file time.</p>
+     *
+     * <p>Note that remote files of date 1/1/1970 (GMT) are treated as
+     * 'no timestamp', and web servers often serve files with a
+     * timestamp in the future by replacing their timestamp with that
+     * of the current time. Also, inter-computer clock differences can
+     * cause no end of grief.</p>
+     * @param v "true" to enable file time fetching
+     */
+    public void setUseTimestamp(boolean v) {
+        useTimestamp = v;
+    }
+
+
+    /**
+     * Username for basic auth.
+     *
+     * @param u username for authentication
+     */
+    public void setUsername(String u) {
+        this.uname = u;
+    }
+
+    /**
+     * password for the basic authentication.
+     *
+     * @param p password for authentication
+     */
+    public void setPassword(String p) {
+        this.pword = p;
+    }
+
+    /*********************************************************************
+    * BASE 64 encoding of a String or an array of bytes.
+    *
+    * Based on RFC 1421.
+    *
+    *********************************************************************/
+
+    protected static class Base64Converter {
+
+        public final char[] alphabet = {
+            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',  //  0 to  7
+            'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',  //  8 to 15
+            'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',  // 16 to 23
+            'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',  // 24 to 31
+            'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',  // 32 to 39
+            'o', 'p', 'q', 'r', 's', 't', 'u', 'v',  // 40 to 47
+            'w', 'x', 'y', 'z', '0', '1', '2', '3',  // 48 to 55
+            '4', '5', '6', '7', '8', '9', '+', '/'}; // 56 to 63
+
+        public String encode(String s) {
+            return encode(s.getBytes());
+        }
+
+        public String encode(byte[] octetString) {
+            int bits24;
+            int bits6;
+
+            char[] out = new char[((octetString.length - 1) / 3 + 1) * 4];
+            int outIndex = 0;
+            int i = 0;
+
+            while ((i + 3) <= octetString.length) {
+                // store the octets
+                bits24 = (octetString[i++] & 0xFF) << 16;
+                bits24 |= (octetString[i++] & 0xFF) << 8;
+                bits24 |= octetString[i++];
+
+                bits6 = (bits24 & 0x00FC0000) >> 18;
+                out[outIndex++] = alphabet[bits6];
+                bits6 = (bits24 & 0x0003F000) >> 12;
+                out[outIndex++] = alphabet[bits6];
+                bits6  = (bits24 & 0x00000FC0) >> 6;
+                out[outIndex++] = alphabet[bits6];
+                bits6 = (bits24 & 0x0000003F);
+                out[outIndex++] = alphabet[bits6];
+            }
+            if (octetString.length - i == 2) {
+                // store the octets
+                bits24 = (octetString[i] & 0xFF) << 16;
+                bits24 |= (octetString[i + 1] & 0xFF) << 8;
+                bits6 = (bits24 & 0x00FC0000) >> 18;
+                out[outIndex++] = alphabet[bits6];
+                bits6 = (bits24 & 0x0003F000) >> 12;
+                out[outIndex++] = alphabet[bits6];
+                bits6 = (bits24 & 0x00000FC0) >> 6;
+                out[outIndex++] = alphabet[bits6];
+
+                // padding
+                out[outIndex++] = '=';
+            } else if (octetString.length - i == 1) {
+                // store the octets
+                bits24 = (octetString[i] & 0xFF) << 16;
+                bits6 = (bits24 & 0x00FC0000) >> 18;
+                out[outIndex++] = alphabet[bits6];
+                bits6 = (bits24 & 0x0003F000) >> 12;
+                out[outIndex++] = alphabet[bits6];
+
+                // padding
+                out[outIndex++] = '=';
+                out[outIndex++] = '=';
+            }
+            return new String(out);
+        }
+    }
+
+    public interface DownloadProgress {
+        /**
+         * begin a download
+         */
+        public void beginDownload();
+
+        /**
+         * tick handler
+         *
+         */
+        public void onTick();
+
+        /**
+         * end a download
+         */
+        public void endDownload();
+    }
+
+    /**
+     * do nothing with progress info
+     */
+    public static class NullProgress implements DownloadProgress {
+
+        /**
+         * begin a download
+         */
+        public void beginDownload() {
+
+        }
+
+        /**
+         * tick handler
+         *
+         */
+        public void onTick() {
+        }
+
+        /**
+         * end a download
+         */
+        public void endDownload() {
+
+        }
+    }
+
+    /**
+     * verbose progress system prints to some output stream
+     */
+    public static class VerboseProgress implements DownloadProgress  {
+        private int dots = 0;
+        PrintStream out;
+
+        public VerboseProgress(PrintStream out) {
+            this.out = out;
+        }
+
+        /**
+         * begin a download
+         */
+        public void beginDownload() {
+            dots = 0;
+        }
+
+        /**
+         * tick handler
+         *
+         */
+        public void onTick() {
+            out.print(".");
+            if (dots++ > 50) {
+                out.flush();
+                dots = 0;
+            }
+        }
+
+        /**
+         * end a download
+         */
+        public void endDownload() {
+            out.println();
+            out.flush();
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ImportTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ImportTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ImportTask.java	(revision 16139)
@@ -0,0 +1,147 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.FileUtils;
+
+import java.io.File;
+import java.util.Vector;
+
+/**
+ * <p>
+ * Task to import another build file into the current project.
+ * <p>
+ * It must be 'top level'. On execution it will read another Ant file
+ * into the same Project.
+ * <p>
+ * <b>Important</b>: we have not finalized how relative file references
+ * will be resolved in deep/complex build hierarchies -such as what happens
+ * when an imported file imports another file. Use absolute references for
+ * enhanced build file stability, especially in the imported files.
+ *
+ * Examples
+ * <pre>
+ * &lt;import file="../common-targets.xml" /&gt;
+ * </pre>
+ * Import targets from a file in a parent directory.
+ *<p>
+ * <pre>
+ * &lt;import file="${deploy-platform}.xml" /&gt;
+ * </pre>
+ * Import the project defined by the property deploy-platform
+ *
+ * @since Ant1.6
+ * @ant.task category="control"
+ */
+public class ImportTask extends Task {
+    private String file;
+    private boolean optional;
+    private static final FileUtils FILE_UTILS = FileUtils.newFileUtils();
+
+    /**
+     * sets the optional attribute
+     *
+     * @param optional if true ignore files that are not present,
+     *                 default is false
+     */
+    public void setOptional(boolean optional) {
+        this.optional = optional;
+    }
+
+    /**
+     * the name of the file to import. How relative paths are resolved is still
+     * in flux: use absolute paths for safety.
+     * @param file the name of the file
+     */
+    public void setFile(String file) {
+        // I don't think we can use File - different rules
+        // for relative paths.
+        this.file = file;
+    }
+
+    /**
+     *  This relies on the task order model.
+     *
+     */
+    public void execute() {
+        if (file == null) {
+            throw new BuildException("import requires file attribute");
+        }
+        if (getOwningTarget() == null
+            || !"".equals(getOwningTarget().getName())) {
+            throw new BuildException("import only allowed as a top-level task");
+        }
+
+        ProjectHelper helper =
+                (ProjectHelper) getProject().getReference("ant.projectHelper");
+        Vector importStack = helper.getImportStack();
+
+        if (importStack.size() == 0) {
+            // this happens if ant is used with a project
+            // helper that doesn't set the import.
+            throw new BuildException("import requires support in ProjectHelper");
+        }
+
+        if (getLocation() == null || getLocation().getFileName() == null) {
+            throw new BuildException("Unable to get location of import task");
+        }
+
+        File buildFile = new File(getLocation().getFileName());
+        buildFile = new File(buildFile.getAbsolutePath());
+
+        getProject().log("Importing file " + file + " from "
+                         + buildFile.getAbsolutePath(), Project.MSG_VERBOSE);
+
+        // Paths are relative to the build file they're imported from,
+        // *not* the current directory (same as entity includes).
+
+        File buildFileParent = new File(buildFile.getParent());
+        File importedFile = FILE_UTILS.resolveFile(buildFileParent,  file);
+
+        if (!importedFile.exists()) {
+            String message =
+                "Cannot find " + file + " imported from "
+                + buildFile.getAbsolutePath();
+            if (optional) {
+                getProject().log(message, Project.MSG_VERBOSE);
+                return;
+            } else {
+                throw new BuildException(message);
+            }
+        }
+
+        if (importStack.contains(importedFile)) {
+            getProject().log(
+                "Skipped already imported file:\n   "
+                + importedFile + "\n", Project.MSG_VERBOSE);
+            return;
+        }
+
+        try {
+            helper.parse(getProject(), importedFile);
+        } catch (BuildException ex) {
+            throw ProjectHelper.addLocationToBuildException(
+                ex, getLocation());
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Input.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Input.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Input.java	(revision 16139)
@@ -0,0 +1,129 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.input.InputRequest;
+import org.apache.tools.ant.input.MultipleChoiceInputRequest;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Reads an input line from the console.
+ *
+ *
+ * @since Ant 1.5
+ *
+ * @ant.task category="control"
+ */
+public class Input extends Task {
+    private String validargs = null;
+    private String message = "";
+    private String addproperty = null;
+    private String defaultvalue = null;
+
+    /**
+     * Defines valid input parameters as comma separated strings. If set, input
+     * task will reject any input not defined as accepted and requires the user
+     * to reenter it. Validargs are case sensitive. If you want 'a' and 'A' to
+     * be accepted you need to define both values as accepted arguments.
+     *
+     * @param validargs A comma separated String defining valid input args.
+     */
+    public void setValidargs (String validargs) {
+        this.validargs = validargs;
+    }
+
+    /**
+     * Defines the name of a property to be created from input. Behaviour is
+     * according to property task which means that existing properties
+     * cannot be overridden.
+     *
+     * @param addproperty Name for the property to be created from input
+     */
+    public void setAddproperty (String addproperty) {
+        this.addproperty = addproperty;
+    }
+
+    /**
+     * Sets the Message which gets displayed to the user during the build run.
+     * @param message The message to be displayed.
+     */
+    public void setMessage (String message) {
+        this.message = message;
+    }
+
+    /**
+     * Defines the default value of the property to be created from input.
+     * Property value will be set to default if not input is received.
+     *
+     * @param defaultvalue Default value for the property if no input
+     * is received
+     */
+    public void setDefaultvalue (String defaultvalue) {
+        this.defaultvalue = defaultvalue;
+    }
+
+    /**
+     * Set a multiline message.
+     * @param msg The message to be displayed.
+     */
+    public void addText(String msg) {
+        message += getProject().replaceProperties(msg);
+    }
+
+    /**
+     * No arg constructor.
+     */
+    public Input () {
+    }
+
+    /**
+     * Actual method executed by ant.
+     * @throws BuildException on error
+     */
+    public void execute () throws BuildException {
+        if (addproperty != null
+            && getProject().getProperty(addproperty) != null) {
+            log("skipping " + getTaskName() + " as property " + addproperty
+                + " has already been set.");
+            return;
+        }
+
+        InputRequest request = null;
+        if (validargs != null) {
+            Vector accept = StringUtils.split(validargs, ',');
+            request = new MultipleChoiceInputRequest(message, accept);
+        } else {
+            request = new InputRequest(message);
+        }
+
+        getProject().getInputHandler().handleInput(request);
+
+        String value = request.getInput();
+        if ((value == null || value.trim().length() == 0)
+            && defaultvalue != null) {
+            value = defaultvalue;
+        }
+        if (addproperty != null && value != null) {
+            getProject().setNewProperty(addproperty, value);
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/JDBCTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/JDBCTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/JDBCTask.java	(revision 16139)
@@ -0,0 +1,450 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.Driver;
+import java.sql.SQLException;
+import java.util.Hashtable;
+import java.util.Properties;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Handles JDBC configuration needed by SQL type tasks.
+ * <p>
+ * The following example class prints the contents of the first column of each row in TableName.
+ *</p>
+ *<code><pre>
+package examples;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.JDBCTask;
+
+public class SQLExampleTask extends JDBCTask {
+
+    private String tableName;
+
+    public void execute() throws BuildException {
+        Connection conn = getConnection();
+        Statement stmt=null;
+        try {
+            if (tableName == null) {
+                throw new BuildException("TableName must be specified",location);
+            }
+            String sql = "SELECT * FROM "+tableName;
+            stmt= conn.createStatement();
+            ResultSet rs = stmt.executeQuery(sql);
+            while (rs.next()) {
+                log(rs.getObject(1).toString());
+            }
+        } catch (SQLException e) {
+
+        } finally {
+            if (stmt != null) {
+                try {stmt.close();}catch (SQLException ingore) {}
+            }
+            if (conn != null) {
+                try {conn.close();}catch (SQLException ingore) {}
+            }
+        }
+    }
+    public void setTableName(String tableName) {
+        this.tableName = tableName;
+    }
+
+}
+
+
+</pre></code>
+
+
+ * @since Ant 1.5
+ *
+ */
+
+public abstract class JDBCTask extends Task {
+
+
+    /**
+     * Used for caching loaders / driver. This is to avoid
+     * getting an OutOfMemoryError when calling this task
+     * multiple times in a row.
+     */
+    private static Hashtable loaderMap = new Hashtable(3);
+
+    private boolean caching = true;
+
+    private Path classpath;
+
+    private AntClassLoader loader;
+
+    /**
+     * Autocommit flag. Default value is false
+     */
+    private boolean autocommit = false;
+
+    /**
+     * DB driver.
+     */
+    private String driver = null;
+
+    /**
+     * DB url.
+     */
+    private String url = null;
+
+    /**
+     * User name.
+     */
+    private String userId = null;
+
+    /**
+     * Password
+     */
+    private String password = null;
+
+    /**
+     * RDBMS Product needed for this SQL.
+     **/
+    private String rdbms = null;
+
+    /**
+     * RDBMS Version needed for this SQL.
+     **/
+    private String version = null;
+
+    /**
+     * Sets the classpath for loading the driver.
+     * @param classpath The classpath to set
+     */
+    public void setClasspath(Path classpath) {
+        this.classpath = classpath;
+    }
+
+    /**
+     * Caching loaders / driver. This is to avoid
+     * getting an OutOfMemoryError when calling this task
+     * multiple times in a row; default: true
+     * @param enable
+     */
+    public void setCaching(boolean enable) {
+        caching = enable;
+    }
+
+    /**
+     * Add a path to the classpath for loading the driver.
+     */
+    public Path createClasspath() {
+        if (this.classpath == null) {
+            this.classpath = new Path(getProject());
+        }
+        return this.classpath.createPath();
+    }
+
+    /**
+     * Set the classpath for loading the driver
+     * using the classpath reference.
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+
+    /**
+     * Class name of the JDBC driver; required.
+     * @param driver The driver to set
+     */
+    public void setDriver(String driver) {
+        this.driver = driver;
+    }
+
+    /**
+     * Sets the database connection URL; required.
+     * @param url The url to set
+     */
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    /**
+     * Sets the password; required.
+     * @param password The password to set
+     */
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    /**
+     * Auto commit flag for database connection;
+     * optional, default false.
+     * @param autocommit The autocommit to set
+     */
+    public void setAutocommit(boolean autocommit) {
+        this.autocommit = autocommit;
+    }
+
+    /**
+     * Execute task only if the lower case product name
+     * of the DB matches this
+     * @param rdbms The rdbms to set
+     */
+    public void setRdbms(String rdbms) {
+        this.rdbms = rdbms;
+    }
+
+    /**
+     * Sets the version string, execute task only if
+     * rdbms version match; optional.
+     * @param version The version to set
+     */
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    /**
+     * Verify we are connected to the correct RDBMS
+     */
+    protected boolean isValidRdbms(Connection conn) {
+        if (rdbms == null && version == null) {
+            return true;
+        }
+
+        try {
+            DatabaseMetaData dmd = conn.getMetaData();
+
+            if (rdbms != null) {
+                String theVendor = dmd.getDatabaseProductName().toLowerCase();
+
+                log("RDBMS = " + theVendor, Project.MSG_VERBOSE);
+                if (theVendor == null || theVendor.indexOf(rdbms) < 0) {
+                    log("Not the required RDBMS: " + rdbms, Project.MSG_VERBOSE);
+                    return false;
+                }
+            }
+
+            if (version != null) {
+                // XXX maybe better toLowerCase(Locale.US)
+                String theVersion = dmd.getDatabaseProductVersion().toLowerCase();
+
+                log("Version = " + theVersion, Project.MSG_VERBOSE);
+                if (theVersion == null
+                        || !(theVersion.startsWith(version)
+                        || theVersion.indexOf(" " + version) >= 0)) {
+                    log("Not the required version: \"" + version + "\"", Project.MSG_VERBOSE);
+                    return false;
+                }
+            }
+        } catch (SQLException e) {
+            // Could not get the required information
+            log("Failed to obtain required RDBMS information", Project.MSG_ERR);
+            return false;
+        }
+
+        return true;
+    }
+
+    protected static Hashtable getLoaderMap() {
+        return loaderMap;
+    }
+
+    protected AntClassLoader getLoader() {
+        return loader;
+    }
+
+    /**
+     * Creates a new Connection as using the driver, url, userid and password
+     * specified.
+     *
+     * The calling method is responsible for closing the connection.
+     *
+     * @return Connection the newly created connection.
+     * @throws BuildException if the UserId/Password/Url is not set or there
+     * is no suitable driver or the driver fails to load.
+     */
+    protected Connection getConnection() throws BuildException {
+        if (userId == null) {
+            throw new BuildException("User Id attribute must be set!", getLocation());
+        }
+        if (password == null) {
+            throw new BuildException("Password attribute must be set!", getLocation());
+        }
+        if (url == null) {
+            throw new BuildException("Url attribute must be set!", getLocation());
+        }
+        try {
+
+            log("connecting to " + getUrl(), Project.MSG_VERBOSE);
+            Properties info = new Properties();
+            info.put("user", getUserId());
+            info.put("password", getPassword());
+            Connection conn = getDriver().connect(getUrl(), info);
+
+            if (conn == null) {
+                // Driver doesn't understand the URL
+                throw new SQLException("No suitable Driver for " + url);
+            }
+
+            conn.setAutoCommit(autocommit);
+            return conn;
+        } catch (SQLException e) {
+            throw new BuildException(e, getLocation());
+        }
+
+    }
+
+    /**
+     * Gets an instance of the required driver.
+     * Uses the ant class loader and the optionally the provided classpath.
+     * @return Driver
+     * @throws BuildException
+     */
+    private Driver getDriver() throws BuildException {
+        if (driver == null) {
+            throw new BuildException("Driver attribute must be set!", getLocation());
+        }
+
+        Driver driverInstance = null;
+        try {
+            Class dc;
+            if (classpath != null) {
+                // check first that it is not already loaded otherwise
+                // consecutive runs seems to end into an OutOfMemoryError
+                // or it fails when there is a native library to load
+                // several times.
+                // this is far from being perfect but should work
+                // in most cases.
+                synchronized (loaderMap) {
+                    if (caching) {
+                        loader = (AntClassLoader) loaderMap.get(driver);
+                    }
+                    if (loader == null) {
+                        log("Loading " + driver
+                            + " using AntClassLoader with classpath "
+                            + classpath, Project.MSG_VERBOSE);
+                        loader = getProject().createClassLoader(classpath);
+                        if (caching) {
+                            loaderMap.put(driver, loader);
+                        }
+                    } else {
+                        log("Loading " + driver
+                            + " using a cached AntClassLoader.",
+                                Project.MSG_VERBOSE);
+                    }
+                }
+                dc = loader.loadClass(driver);
+            } else {
+                log("Loading " + driver + " using system loader.",
+                    Project.MSG_VERBOSE);
+                dc = Class.forName(driver);
+            }
+            driverInstance = (Driver) dc.newInstance();
+        } catch (ClassNotFoundException e) {
+            throw new BuildException(
+                    "Class Not Found: JDBC driver " + driver + " could not be loaded",
+                    getLocation());
+        } catch (IllegalAccessException e) {
+            throw new BuildException(
+                    "Illegal Access: JDBC driver " + driver + " could not be loaded",
+                    getLocation());
+        } catch (InstantiationException e) {
+            throw new BuildException(
+                    "Instantiation Exception: JDBC driver " + driver + " could not be loaded",
+                    getLocation());
+        }
+        return driverInstance;
+    }
+
+
+    public void isCaching(boolean value) {
+        caching = value;
+    }
+
+    /**
+     * Gets the classpath.
+     * @return Returns a Path
+     */
+    public Path getClasspath() {
+        return classpath;
+    }
+
+    /**
+     * Gets the autocommit.
+     * @return Returns a boolean
+     */
+    public boolean isAutocommit() {
+        return autocommit;
+    }
+
+    /**
+     * Gets the url.
+     * @return Returns a String
+     */
+    public String getUrl() {
+        return url;
+    }
+
+    /**
+     * Gets the userId.
+     * @return Returns a String
+     */
+    public String getUserId() {
+        return userId;
+    }
+
+    /**
+     * Set the user name for the connection; required.
+     * @param userId The userId to set
+     */
+    public void setUserid(String userId) {
+        this.userId = userId;
+    }
+
+    /**
+     * Gets the password.
+     * @return Returns a String
+     */
+    public String getPassword() {
+        return password;
+    }
+
+    /**
+     * Gets the rdbms.
+     * @return Returns a String
+     */
+    public String getRdbms() {
+        return rdbms;
+    }
+
+    /**
+     * Gets the version.
+     * @return Returns a String
+     */
+    public String getVersion() {
+        return version;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Jar.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Jar.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Jar.java	(revision 16139)
@@ -0,0 +1,861 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+import java.util.Vector;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.ZipFileSet;
+import org.apache.tools.zip.JarMarker;
+import org.apache.tools.zip.ZipExtraField;
+import org.apache.tools.zip.ZipOutputStream;
+
+/**
+ * Creates a JAR archive.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="packaging"
+ */
+public class Jar extends Zip {
+    /** The index file name. */
+    private static final String INDEX_NAME = "META-INF/INDEX.LIST";
+
+    /** The manifest file name. */
+    private static final String MANIFEST_NAME = "META-INF/MANIFEST.MF";
+
+    /** merged manifests added through addConfiguredManifest */
+    private Manifest configuredManifest;
+    /** shadow of the above if upToDate check alters the value */
+    private Manifest savedConfiguredManifest;
+
+    /**  merged manifests added through filesets */
+    private Manifest filesetManifest;
+
+    /**
+     * Manifest of original archive, will be set to null if not in
+     * update mode.
+     */
+    private Manifest originalManifest;
+
+    /**
+     *  whether to merge fileset manifests;
+     *  value is true if filesetmanifest is 'merge' or 'mergewithoutmain'
+     */
+    private FilesetManifestConfig filesetManifestConfig;
+
+    /**
+     * whether to merge the main section of fileset manifests;
+     * value is true if filesetmanifest is 'merge'
+     */
+    private boolean mergeManifestsMain = true;
+
+    /** the manifest specified by the 'manifest' attribute **/
+    private Manifest manifest;
+
+    /** The encoding to use when reading in a manifest file */
+    private String manifestEncoding;
+
+    /**
+     * The file found from the 'manifest' attribute.  This can be
+     * either the location of a manifest, or the name of a jar added
+     * through a fileset.  If its the name of an added jar, the
+     * manifest is looked for in META-INF/MANIFEST.MF
+     */
+    private File manifestFile;
+
+    /** jar index is JDK 1.3+ only */
+    private boolean index = false;
+
+    /**
+     * whether to really create the archive in createEmptyZip, will
+     * get set in getResourcesToAdd.
+     */
+    private boolean createEmpty = false;
+
+    /**
+     * Stores all files that are in the root of the archive (i.e. that
+     * have a name that doesn't contain a slash) so they can get
+     * listed in the index.
+     *
+     * Will not be filled unless the user has asked for an index.
+     *
+     * @since Ant 1.6
+     */
+    private Vector rootEntries;
+
+    /**
+     * Path containing jars that shall be indexed in addition to this archive.
+     *
+     * @since Ant 1.6.2
+     */
+    private Path indexJars;
+
+    /**
+     * Extra fields needed to make Solaris recognize the archive as a jar file.
+     *
+     * @since Ant 1.6.3
+     */
+    private ZipExtraField[] JAR_MARKER = new ZipExtraField[] {
+        JarMarker.getInstance()
+    };
+
+    /** constructor */
+    public Jar() {
+        super();
+        archiveType = "jar";
+        emptyBehavior = "create";
+        setEncoding("UTF8");
+        rootEntries = new Vector();
+    }
+
+    /**
+     * @ant.attribute ignore="true"
+     */
+    public void setWhenempty(WhenEmpty we) {
+        log("JARs are never empty, they contain at least a manifest file",
+            Project.MSG_WARN);
+    }
+
+    /**
+     * @deprecated Use setDestFile(File) instead
+     */
+    public void setJarfile(File jarFile) {
+        setDestFile(jarFile);
+    }
+
+    /**
+     * Set whether or not to create an index list for classes.
+     * This may speed up classloading in some cases.
+     */
+    public void setIndex(boolean flag) {
+        index = flag;
+    }
+
+    /**
+     * Set whether or not to create an index list for classes.
+     * This may speed up classloading in some cases.
+     */
+    public void setManifestEncoding(String manifestEncoding) {
+        this.manifestEncoding = manifestEncoding;
+    }
+
+    /**
+     * Allows the manifest for the archive file to be provided inline
+     * in the build file rather than in an external file.
+     *
+     * @param newManifest
+     * @throws ManifestException
+     */
+    public void addConfiguredManifest(Manifest newManifest)
+        throws ManifestException {
+        if (configuredManifest == null) {
+            configuredManifest = newManifest;
+        } else {
+            configuredManifest.merge(newManifest);
+        }
+        savedConfiguredManifest = configuredManifest;
+    }
+
+    /**
+     * The manifest file to use. This can be either the location of a manifest,
+     * or the name of a jar added through a fileset. If its the name of an added
+     * jar, the task expects the manifest to be in the jar at META-INF/MANIFEST.MF.
+     *
+     * @param manifestFile the manifest file to use.
+     */
+    public void setManifest(File manifestFile) {
+        if (!manifestFile.exists()) {
+            throw new BuildException("Manifest file: " + manifestFile
+                                     + " does not exist.", getLocation());
+        }
+
+        this.manifestFile = manifestFile;
+    }
+
+    private Manifest getManifest(File manifestFile) {
+
+        Manifest newManifest = null;
+        FileInputStream fis = null;
+        InputStreamReader isr = null;
+        try {
+            fis = new FileInputStream(manifestFile);
+            if (manifestEncoding == null) {
+                isr = new InputStreamReader(fis);
+            } else {
+                isr = new InputStreamReader(fis, manifestEncoding);
+            }
+            newManifest = getManifest(isr);
+        } catch (UnsupportedEncodingException e) {
+            throw new BuildException("Unsupported encoding while reading manifest: "
+                                     + e.getMessage(), e);
+        } catch (IOException e) {
+            throw new BuildException("Unable to read manifest file: "
+                                     + manifestFile
+                                     + " (" + e.getMessage() + ")", e);
+        } finally {
+            if (isr != null) {
+                try {
+                    isr.close();
+                } catch (IOException e) {
+                    // do nothing
+                }
+            }
+        }
+        return newManifest;
+    }
+
+    /**
+     * @return null if jarFile doesn't contain a manifest, the
+     * manifest otherwise.
+     * @since Ant 1.5.2
+     */
+    private Manifest getManifestFromJar(File jarFile) throws IOException {
+        ZipFile zf = null;
+        try {
+            zf = new ZipFile(jarFile);
+
+            // must not use getEntry as "well behaving" applications
+            // must accept the manifest in any capitalization
+            Enumeration e = zf.entries();
+            while (e.hasMoreElements()) {
+                ZipEntry ze = (ZipEntry) e.nextElement();
+                if (ze.getName().equalsIgnoreCase(MANIFEST_NAME)) {
+                    InputStreamReader isr =
+                        new InputStreamReader(zf.getInputStream(ze), "UTF-8");
+                    return getManifest(isr);
+                }
+            }
+            return null;
+        } finally {
+            if (zf != null) {
+                try {
+                    zf.close();
+                } catch (IOException e) {
+                    // XXX - log an error?  throw an exception?
+                }
+            }
+        }
+    }
+
+    private Manifest getManifest(Reader r) {
+
+        Manifest newManifest = null;
+        try {
+            newManifest = new Manifest(r);
+        } catch (ManifestException e) {
+            log("Manifest is invalid: " + e.getMessage(), Project.MSG_ERR);
+            throw new BuildException("Invalid Manifest: " + manifestFile,
+                                     e, getLocation());
+        } catch (IOException e) {
+            throw new BuildException("Unable to read manifest file"
+                                     + " (" + e.getMessage() + ")", e);
+        }
+        return newManifest;
+    }
+
+    /**
+     * Behavior when a Manifest is found in a zipfileset or zipgroupfileset file.
+     * Valid values are "skip", "merge", and "mergewithoutmain".
+     * "merge" will merge all of manifests together, and merge this into any
+     * other specified manifests.
+     * "mergewithoutmain" merges everything but the Main section of the manifests.
+     * Default value is "skip".
+     *
+     * Note: if this attribute's value is not "skip", the created jar will not
+     * be readable by using java.util.jar.JarInputStream
+     *
+     * @param config setting for found manifest behavior.
+     */
+    public void setFilesetmanifest(FilesetManifestConfig config) {
+        filesetManifestConfig = config;
+        mergeManifestsMain = "merge".equals(config.getValue());
+
+        if (filesetManifestConfig != null
+            && !filesetManifestConfig.getValue().equals("skip")) {
+
+            doubleFilePass = true;
+        }
+    }
+
+    /**
+     * Adds a zipfileset to include in the META-INF directory.
+     *
+     * @param fs zipfileset to add
+     */
+    public void addMetainf(ZipFileSet fs) {
+        // We just set the prefix for this fileset, and pass it up.
+        fs.setPrefix("META-INF/");
+        super.addFileset(fs);
+    }
+
+    /**
+     * @since Ant 1.6.2
+     */
+    public void addConfiguredIndexJars(Path p) {
+        if (indexJars == null) {
+            indexJars = new Path(getProject());
+        }
+        indexJars.append(p);
+    }
+
+    protected void initZipOutputStream(ZipOutputStream zOut)
+        throws IOException, BuildException {
+
+        if (!skipWriting) {
+            Manifest jarManifest = createManifest();
+            writeManifest(zOut, jarManifest);
+        }
+    }
+
+    private Manifest createManifest()
+        throws BuildException {
+        try {
+            Manifest finalManifest = Manifest.getDefaultManifest();
+
+            if (manifest == null) {
+                if (manifestFile != null) {
+                    // if we haven't got the manifest yet, attempt to
+                    // get it now and have manifest be the final merge
+                    manifest = getManifest(manifestFile);
+                }
+            }
+
+            /*
+             * Precedence: manifestFile wins over inline manifest,
+             * over manifests read from the filesets over the original
+             * manifest.
+             *
+             * merge with null argument is a no-op
+             */
+
+            if (isInUpdateMode()) {
+                finalManifest.merge(originalManifest);
+            }
+            finalManifest.merge(filesetManifest);
+            finalManifest.merge(configuredManifest);
+            finalManifest.merge(manifest, !mergeManifestsMain);
+
+            return finalManifest;
+
+        } catch (ManifestException e) {
+            log("Manifest is invalid: " + e.getMessage(), Project.MSG_ERR);
+            throw new BuildException("Invalid Manifest", e, getLocation());
+        }
+    }
+
+    private void writeManifest(ZipOutputStream zOut, Manifest manifest)
+        throws IOException {
+        for (Enumeration e = manifest.getWarnings();
+             e.hasMoreElements();) {
+            log("Manifest warning: " + (String) e.nextElement(),
+                Project.MSG_WARN);
+        }
+
+        zipDir(null, zOut, "META-INF/", ZipFileSet.DEFAULT_DIR_MODE,
+               JAR_MARKER);
+        // time to write the manifest
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        OutputStreamWriter osw = new OutputStreamWriter(baos, "UTF-8");
+        PrintWriter writer = new PrintWriter(osw);
+        manifest.write(writer);
+        writer.flush();
+
+        ByteArrayInputStream bais =
+            new ByteArrayInputStream(baos.toByteArray());
+        super.zipFile(bais, zOut, MANIFEST_NAME,
+                      System.currentTimeMillis(), null,
+                      ZipFileSet.DEFAULT_FILE_MODE);
+        super.initZipOutputStream(zOut);
+    }
+
+    protected void finalizeZipOutputStream(ZipOutputStream zOut)
+        throws IOException, BuildException {
+
+        if (index) {
+            createIndexList(zOut);
+        }
+    }
+
+    /**
+     * Create the index list to speed up classloading.
+     * This is a JDK 1.3+ specific feature and is enabled by default. See
+     * <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#JAR%20Index">
+     * the JAR index specification</a> for more details.
+     *
+     * @param zOut the zip stream representing the jar being built.
+     * @throws IOException thrown if there is an error while creating the
+     * index and adding it to the zip stream.
+     */
+    private void createIndexList(ZipOutputStream zOut) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        // encoding must be UTF8 as specified in the specs.
+        PrintWriter writer = new PrintWriter(new OutputStreamWriter(baos,
+                                                                    "UTF8"));
+
+        // version-info blankline
+        writer.println("JarIndex-Version: 1.0");
+        writer.println();
+
+        // header newline
+        writer.println(zipFile.getName());
+
+        writeIndexLikeList(new ArrayList(addedDirs.keySet()), 
+                           rootEntries, writer);
+        writer.println();
+
+        if (indexJars != null) {
+            Manifest mf = createManifest();
+            Manifest.Attribute classpath =
+                mf.getMainSection().getAttribute(Manifest.ATTRIBUTE_CLASSPATH);
+            String[] cpEntries = null;
+            if (classpath != null) {
+                StringTokenizer tok = new StringTokenizer(classpath.getValue(),
+                                                          " ");
+                cpEntries = new String[tok.countTokens()];
+                int c = 0;
+                while (tok.hasMoreTokens()) {
+                    cpEntries[c++] = tok.nextToken();
+                }
+            }
+            String[] indexJarEntries = indexJars.list();
+            for (int i = 0; i < indexJarEntries.length; i++) {
+                String name = findJarName(indexJarEntries[i], cpEntries);
+                if (name != null) {
+                    ArrayList dirs = new ArrayList();
+                    ArrayList files = new ArrayList();
+                    grabFilesAndDirs(indexJarEntries[i], dirs, files);
+                    if (dirs.size() + files.size() > 0) {
+                        writer.println(name);
+                        writeIndexLikeList(dirs, files, writer);
+                        writer.println();
+                    }
+                }
+            }
+        }
+
+        writer.flush();
+        ByteArrayInputStream bais =
+            new ByteArrayInputStream(baos.toByteArray());
+        super.zipFile(bais, zOut, INDEX_NAME, System.currentTimeMillis(), null,
+                      ZipFileSet.DEFAULT_FILE_MODE);
+    }
+
+    /**
+     * Overridden from Zip class to deal with manifests and index lists.
+     */
+    protected void zipFile(InputStream is, ZipOutputStream zOut, String vPath,
+                           long lastModified, File fromArchive, int mode)
+        throws IOException {
+        if (MANIFEST_NAME.equalsIgnoreCase(vPath))  {
+            if (!doubleFilePass || (doubleFilePass && skipWriting)) {
+                filesetManifest(fromArchive, is);
+            }
+        } else if (INDEX_NAME.equalsIgnoreCase(vPath) && index) {
+            log("Warning: selected " + archiveType
+                + " files include a META-INF/INDEX.LIST which will"
+                + " be replaced by a newly generated one.", Project.MSG_WARN);
+        } else {
+            if (index && vPath.indexOf("/") == -1) {
+                rootEntries.addElement(vPath);
+            }
+            super.zipFile(is, zOut, vPath, lastModified, fromArchive, mode);
+        }
+    }
+
+    private void filesetManifest(File file, InputStream is) throws IOException {
+        if (manifestFile != null && manifestFile.equals(file)) {
+            // If this is the same name specified in 'manifest', this
+            // is the manifest to use
+            log("Found manifest " + file, Project.MSG_VERBOSE);
+            try {
+                if (is != null) {
+                    InputStreamReader isr;
+                    if (manifestEncoding == null) {
+                        isr = new InputStreamReader(is);
+                    } else {
+                        isr = new InputStreamReader(is, manifestEncoding);
+                    }
+                    manifest = getManifest(isr);
+                } else {
+                    manifest = getManifest(file);
+                }
+            } catch (UnsupportedEncodingException e) {
+                throw new BuildException("Unsupported encoding while reading "
+                    + "manifest: " + e.getMessage(), e);
+            }
+        } else if (filesetManifestConfig != null
+                    && !filesetManifestConfig.getValue().equals("skip")) {
+            // we add this to our group of fileset manifests
+            log("Found manifest to merge in file " + file,
+                Project.MSG_VERBOSE);
+
+            try {
+                Manifest newManifest = null;
+                if (is != null) {
+                    InputStreamReader isr;
+                    if (manifestEncoding == null) {
+                        isr = new InputStreamReader(is);
+                    } else {
+                        isr = new InputStreamReader(is, manifestEncoding);
+                    }
+                    newManifest = getManifest(isr);
+                } else {
+                    newManifest = getManifest(file);
+                }
+
+                if (filesetManifest == null) {
+                    filesetManifest = newManifest;
+                } else {
+                    filesetManifest.merge(newManifest);
+                }
+            } catch (UnsupportedEncodingException e) {
+                throw new BuildException("Unsupported encoding while reading "
+                    + "manifest: " + e.getMessage(), e);
+            } catch (ManifestException e) {
+                log("Manifest in file " + file + " is invalid: "
+                    + e.getMessage(), Project.MSG_ERR);
+                throw new BuildException("Invalid Manifest", e, getLocation());
+            }
+        } else {
+            // assuming 'skip' otherwise
+            // don't warn if skip has been requested explicitly, warn if user
+            // didn't set the attribute
+
+            // Hide warning also as it makes no sense since
+            // the filesetmanifest attribute itself has been
+            // hidden
+
+            //int logLevel = filesetManifestConfig == null ?
+            //    Project.MSG_WARN : Project.MSG_VERBOSE;
+            //log("File " + file
+            //    + " includes a META-INF/MANIFEST.MF which will be ignored. "
+            //    + "To include this file, set filesetManifest to a value other "
+            //    + "than 'skip'.", logLevel);
+        }
+    }
+
+    /**
+     * Collect the resources that are newer than the corresponding
+     * entries (or missing) in the original archive.
+     *
+     * <p>If we are going to recreate the archive instead of updating
+     * it, all resources should be considered as new, if a single one
+     * is.  Because of this, subclasses overriding this method must
+     * call <code>super.getResourcesToAdd</code> and indicate with the
+     * third arg if they already know that the archive is
+     * out-of-date.</p>
+     *
+     * @param filesets The filesets to grab resources from
+     * @param zipFile intended archive file (may or may not exist)
+     * @param needsUpdate whether we already know that the archive is
+     * out-of-date.  Subclasses overriding this method are supposed to
+     * set this value correctly in their call to
+     * super.getResourcesToAdd.
+     * @return an array of resources to add for each fileset passed in as well
+     *         as a flag that indicates whether the archive is uptodate.
+     *
+     * @exception BuildException if it likes
+     */
+    protected ArchiveState getResourcesToAdd(FileSet[] filesets,
+                                             File zipFile,
+                                             boolean needsUpdate)
+        throws BuildException {
+
+        // need to handle manifest as a special check
+        if (zipFile.exists()) {
+            // if it doesn't exist, it will get created anyway, don't
+            // bother with any up-to-date checks.
+
+            try {
+                originalManifest = getManifestFromJar(zipFile);
+                if (originalManifest == null) {
+                    log("Updating jar since the current jar has no manifest",
+                        Project.MSG_VERBOSE);
+                    needsUpdate = true;
+                } else {
+                    Manifest mf = createManifest();
+                    if (!mf.equals(originalManifest)) {
+                        log("Updating jar since jar manifest has changed",
+                            Project.MSG_VERBOSE);
+                        needsUpdate = true;
+                    }
+                }
+            } catch (Throwable t) {
+                log("error while reading original manifest: " + t.getMessage(),
+                    Project.MSG_WARN);
+                needsUpdate = true;
+            }
+
+        } else {
+            // no existing archive
+            needsUpdate = true;
+        }
+
+        createEmpty = needsUpdate;
+        return super.getResourcesToAdd(filesets, zipFile, needsUpdate);
+    }
+
+    protected boolean createEmptyZip(File zipFile) throws BuildException {
+        if (!createEmpty) {
+            return true;
+        }
+
+        ZipOutputStream zOut = null;
+        try {
+            log("Building MANIFEST-only jar: "
+                + getDestFile().getAbsolutePath());
+            zOut = new ZipOutputStream(new FileOutputStream(getDestFile()));
+
+            zOut.setEncoding(getEncoding());
+            if (isCompress()) {
+                zOut.setMethod(ZipOutputStream.DEFLATED);
+            } else {
+                zOut.setMethod(ZipOutputStream.STORED);
+            }
+            initZipOutputStream(zOut);
+            finalizeZipOutputStream(zOut);
+        } catch (IOException ioe) {
+            throw new BuildException("Could not create almost empty JAR archive"
+                                     + " (" + ioe.getMessage() + ")", ioe,
+                                     getLocation());
+        } finally {
+            // Close the output stream.
+            try {
+                if (zOut != null) {
+                    zOut.close();
+                }
+            } catch (IOException ex) {
+            }
+            createEmpty = false;
+        }
+        return true;
+    }
+
+    /**
+     * Make sure we don't think we already have a MANIFEST next time this task
+     * gets executed.
+     *
+     * @see Zip#cleanUp
+     */
+    protected void cleanUp() {
+        super.cleanUp();
+
+        // we want to save this info if we are going to make another pass
+        if (!doubleFilePass || (doubleFilePass && !skipWriting)) {
+            manifest = null;
+            configuredManifest = savedConfiguredManifest;
+            filesetManifest = null;
+            originalManifest = null;
+        }
+        rootEntries.removeAllElements();
+    }
+
+    /**
+     * reset to default values.
+     *
+     * @see Zip#reset
+     *
+     * @since 1.44, Ant 1.5
+     */
+    public void reset() {
+        super.reset();
+        configuredManifest = null;
+        filesetManifestConfig = null;
+        mergeManifestsMain = false;
+        manifestFile = null;
+        index = false;
+    }
+
+    public static class FilesetManifestConfig extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[] {"skip", "merge", "mergewithoutmain"};
+        }
+    }
+
+    /**
+     * Writes the directory entries from the first and the filenames
+     * from the second list to the given writer, one entry per line.
+     *
+     * @since Ant 1.6.2
+     */
+    protected final void writeIndexLikeList(List dirs, List files,
+                                            PrintWriter writer)
+        throws IOException {
+        // JarIndex is sorting the directories by ascending order.
+        // it has no value but cosmetic since it will be read into a
+        // hashtable by the classloader, but we'll do so anyway.
+        Collections.sort(dirs);
+        Collections.sort(files);
+        Iterator iter = dirs.iterator();
+        while (iter.hasNext()) {
+            String dir = (String) iter.next();
+
+            // try to be smart, not to be fooled by a weird directory name
+            dir = dir.replace('\\', '/');
+            if (dir.startsWith("./")) {
+                dir = dir.substring(2);
+            }
+            while (dir.startsWith("/")) {
+                dir = dir.substring(1);
+            }
+            int pos = dir.lastIndexOf('/');
+            if (pos != -1) {
+                dir = dir.substring(0, pos);
+            }
+
+            // looks like nothing from META-INF should be added
+            // and the check is not case insensitive.
+            // see sun.misc.JarIndex
+            if (dir.startsWith("META-INF")) {
+                continue;
+            }
+            // name newline
+            writer.println(dir);
+        }
+
+        iter = files.iterator();
+        while (iter.hasNext()) {
+            writer.println(iter.next());
+        }
+    }
+
+    /**
+     * try to guess the name of the given file.
+     *
+     * <p>If this jar has a classpath attribute in its manifest, we
+     * can assume that it will only require an index of jars listed
+     * there.  try to find which classpath entry is most likely the
+     * one the given file name points to.</p>
+     *
+     * <p>In the absence of a classpath attribute, assume the other
+     * files will be placed inside the same directory as this jar and
+     * use their basename.</p>
+     *
+     * <p>if there is a classpath and the given file doesn't match any
+     * of its entries, return null.</p>
+     *
+     * @since Ant 1.7
+     */
+    protected static final String findJarName(String fileName, 
+                                              String[] classpath) {
+        if (classpath == null) {
+            return (new File(fileName)).getName();
+        }
+        fileName = fileName.replace(File.separatorChar, '/');
+        TreeMap matches = new TreeMap(new Comparator() {
+                // longest match comes first
+                public int compare(Object o1, Object o2) {
+                    if (o1 instanceof String && o2 instanceof String) {
+                        return ((String) o2).length()
+                            - ((String) o1).length();
+                    }
+                    return 0;
+                }
+            });
+
+        for (int i = 0; i < classpath.length; i++) {
+            if (fileName.endsWith(classpath[i])) {
+                matches.put(classpath[i], classpath[i]);
+            } else {
+                int slash = classpath[i].indexOf("/");
+                String candidate = classpath[i];
+                while (slash > -1) {
+                    candidate = candidate.substring(slash + 1);
+                    if (fileName.endsWith(candidate)) {
+                        matches.put(candidate, classpath[i]);
+                        break;
+                    }
+                    slash = candidate.indexOf("/");
+                }
+            }
+        }
+                        
+        return matches.size() == 0 
+            ? null : (String) matches.get(matches.firstKey());
+    }
+
+    /**
+     * Grab lists of all root-level files and all directories
+     * contained in the given archive.
+     *
+     * @since Ant 1.7
+     */
+    protected static final void grabFilesAndDirs(String file, List dirs, 
+                                                 List files)
+        throws IOException {
+        org.apache.tools.zip.ZipFile zf = null;
+        try {
+            zf = new org.apache.tools.zip.ZipFile(file, "utf-8");
+            Enumeration entries = zf.getEntries();
+            HashSet dirSet = new HashSet();
+            while (entries.hasMoreElements()) {
+                org.apache.tools.zip.ZipEntry ze = 
+                    (org.apache.tools.zip.ZipEntry) entries.nextElement();
+                String name = ze.getName();
+                // META-INF would be skipped anyway, avoid index for
+                // manifest-only jars.
+                if (!name.startsWith("META-INF/")) {
+                    if (ze.isDirectory()) {
+                        dirSet.add(name);
+                    } else if (name.indexOf("/") == -1) {
+                        files.add(name);
+                    } else {
+                        // a file, not in the root
+                        // since the jar may be one without directory
+                        // entries, add the parent dir of this file as
+                        // well.
+                        dirSet.add(name.substring(0, 
+                                                  name.lastIndexOf("/") + 1));
+                    }
+                }
+            }
+            dirs.addAll(dirSet);
+        } finally {
+            if (zf != null) {
+                zf.close();
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Java.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Java.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Java.java	(revision 16139)
@@ -0,0 +1,868 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ExitException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.Environment;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.PropertySet;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Assertions;
+import org.apache.tools.ant.types.Permissions;
+import org.apache.tools.ant.types.RedirectorElement;
+import org.apache.tools.ant.util.KeepAliveInputStream;
+
+/**
+ * Launcher for Java applications. Allows use of
+ * the same JVM for the called application thus resulting in much
+ * faster operation.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="java"
+ */
+public class Java extends Task {
+
+    private CommandlineJava cmdl = new CommandlineJava();
+    private Environment env = new Environment();
+    private boolean fork = false;
+    private boolean newEnvironment = false;
+    private File dir = null;
+    private boolean failOnError = false;
+    private Long timeout = null;
+
+    //include locally for screening purposes
+    private String inputString;
+    private File input;
+    private File output;
+    private File error;
+    
+    protected Redirector redirector = new Redirector(this);
+    protected RedirectorElement redirectorElement;
+    
+    private String resultProperty;
+    private Permissions perm = null;
+
+    private boolean spawn = false;
+    private boolean incompatibleWithSpawn = false;
+    /**
+     * Do the execution.
+     * @throws BuildException if failOnError is set to true and the application
+     * returns a non 0 result code
+     */
+    public void execute() throws BuildException {
+        File savedDir = dir;
+        Permissions savedPermissions = perm;
+
+        int err = -1;
+        try {
+            err = executeJava();
+            if (err != 0) {
+                if (failOnError) {
+                    throw new BuildException("Java returned: " + err, getLocation());
+                } else {
+                    log("Java Result: " + err, Project.MSG_ERR);
+                }
+            }
+            maybeSetResultPropertyValue(err);
+        } finally {
+            dir = savedDir;
+            perm = savedPermissions;
+        }
+    }
+
+    /**
+     * Do the execution and return a return code.
+     *
+     * @return the return code from the execute java class if it was
+     * executed in a separate VM (fork = "yes").
+     *
+     * @throws BuildException if required parameters are missing
+     */
+    public int executeJava() throws BuildException {
+        String classname = cmdl.getClassname();
+        if (classname == null && cmdl.getJar() == null) {
+            throw new BuildException("Classname must not be null.");
+        }
+
+        if (!fork && cmdl.getJar() != null) {
+            throw new BuildException("Cannot execute a jar in non-forked mode."
+                                     + " Please set fork='true'. ");
+        }
+        if (spawn && !fork) {
+            throw new BuildException("Cannot spawn a java process in non-forked mode."
+                                     + " Please set fork='true'. ");
+        }
+        if (spawn && incompatibleWithSpawn) {
+            getProject().log("spawn does not allow attributes related to input, "
+            + "output, error, result", Project.MSG_ERR);
+            getProject().log("spawn also does not allow timeout", Project.MSG_ERR);
+            getProject().log( "finally, spawn is not compatible "
+                + "with a nested I/O <redirector>", Project.MSG_ERR);
+            throw new BuildException("You have used an attribute "
+                + "or nested element which is not compatible with spawn");
+        }
+        if (cmdl.getAssertions() != null && !fork) {
+            log("Assertion statements are currently ignored in non-forked mode");
+        }
+
+        if (fork) {
+            if (perm != null) {
+                log("Permissions can not be set this way in forked mode.", Project.MSG_WARN);
+            }
+            log(cmdl.describeCommand(), Project.MSG_VERBOSE);
+        } else {
+            if (cmdl.getVmCommand().size() > 1) {
+                log("JVM args ignored when same JVM is used.",
+                    Project.MSG_WARN);
+            }
+            if (dir != null) {
+                log("Working directory ignored when same JVM is used.",
+                    Project.MSG_WARN);
+            }
+
+            if (newEnvironment || null != env.getVariables()) {
+                log("Changes to environment variables are ignored when same "
+                    + "JVM is used.", Project.MSG_WARN);
+            }
+
+            if (cmdl.getBootclasspath() != null) {
+                log("bootclasspath ignored when same JVM is used.",
+                    Project.MSG_WARN);
+            }
+            if (perm == null && failOnError == true) {
+                perm = new Permissions(true);
+                log("running " + this.cmdl.getClassname()
+                    + " with default permissions (exit forbidden)", Project.MSG_VERBOSE);
+            }
+            log("Running in same VM " + cmdl.describeJavaCommand(),
+                Project.MSG_VERBOSE);
+        }
+
+        setupRedirector();
+        try {
+            if (fork) {
+                if (!spawn) {
+                    return fork(cmdl.getCommandline());
+                } else {
+                    spawn(cmdl.getCommandline());
+                    return 0;
+                }
+            } else {
+                try {
+                    run(cmdl);
+                    return 0;
+                } catch (ExitException ex) {
+                    return ex.getStatus();
+                }
+            }
+        } catch (BuildException e) {
+            if (e.getLocation() == null && getLocation() != null) {
+                e.setLocation(getLocation());
+            }
+            if (failOnError) {
+                throw e;
+            } else {
+                log(e);
+                return 0;
+            }
+        } catch (Throwable t) {
+            if (failOnError) {
+                throw new BuildException(t, getLocation());
+            } else {
+                log(t);
+                return 0;
+            }
+        }
+    }
+
+    /**
+     * set whether or not you want the process to be spawned
+     * default is not spawned
+     * @param spawn if true you do not want ant to wait for the end of the process
+     * @since ant 1.6
+     */
+    public void setSpawn(boolean spawn) {
+        this.spawn = spawn;
+    }
+
+    /**
+     * Set the classpath to be used when running the Java class
+     *
+     * @param s an Ant Path object containing the classpath.
+     */
+    public void setClasspath(Path s) {
+        createClasspath().append(s);
+    }
+
+    /**
+     * Adds a path to the classpath.
+     *
+     * @return created classpath
+     */
+    public Path createClasspath() {
+        return cmdl.createClasspath(getProject()).createPath();
+    }
+
+    /**
+     * Adds a path to the bootclasspath.
+     * @since Ant 1.6
+     *
+     * @return created bootclasspath
+     */
+    public Path createBootclasspath() {
+        return cmdl.createBootclasspath(getProject()).createPath();
+    }
+
+    /**
+     * Sets the permissions for the application run inside the same JVM.
+     * @since Ant 1.6
+     * @return .
+     */
+    public Permissions createPermissions() {
+        if (perm == null) {
+            perm = new Permissions();
+        }
+        return perm;
+    }
+
+    /**
+     * Classpath to use, by reference.
+     *
+     * @param r a reference to an existing classpath
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+
+    /**
+     * The location of the JAR file to execute.
+     *
+     * @param jarfile the jarfile that one wants to execute
+     *
+     * @throws BuildException if there is also a main class specified
+     */
+    public void setJar(File jarfile) throws BuildException {
+        if (cmdl.getClassname() != null) {
+            throw new BuildException("Cannot use 'jar' and 'classname' "
+                                     + "attributes in same command.");
+        }
+        cmdl.setJar(jarfile.getAbsolutePath());
+    }
+
+    /**
+     * Sets the Java class to execute.
+     *
+     * @param s the name of the main class
+     *
+     * @throws BuildException if the jar attribute has been set
+     */
+    public void setClassname(String s) throws BuildException {
+        if (cmdl.getJar() != null) {
+            throw new BuildException("Cannot use 'jar' and 'classname' "
+                                     + "attributes in same command");
+        }
+        cmdl.setClassname(s);
+    }
+
+    /**
+     * Deprecated: use nested arg instead.
+     * Set the command line arguments for the class.
+     *
+     * @param s arguments
+     *
+     * @ant.attribute ignore="true"
+     */
+    public void setArgs(String s) {
+        log("The args attribute is deprecated. "
+            + "Please use nested arg elements.", Project.MSG_WARN);
+        cmdl.createArgument().setLine(s);
+    }
+
+    /**
+     * Adds a command-line argument.
+     *
+     * @return created argument
+     */
+    public Commandline.Argument createArg() {
+        return cmdl.createArgument();
+    }
+
+    /**
+     * The name of a property in which the return code of the
+     * command should be stored. Only of interest if failonerror=false.
+     *
+     * @param resultProperty name of property
+     *
+     * @since Ant 1.6
+     */
+    public void setResultProperty(String resultProperty) {
+        this.resultProperty = resultProperty;
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * helper method to set result property to the
+     * passed in value if appropriate
+     *
+     * @param result the exit code
+     */
+    protected void maybeSetResultPropertyValue(int result) {
+        String res = Integer.toString(result);
+        if (resultProperty != null) {
+            getProject().setNewProperty(resultProperty, res);
+        }
+    }
+
+    /**
+     * If true, execute in a new VM.
+     *
+     * @param s do you want to run Java in a new VM.
+     */
+    public void setFork(boolean s) {
+        this.fork = s;
+    }
+
+    /**
+     * Set the command line arguments for the JVM.
+     *
+     * @param s jvmargs
+     */
+    public void setJvmargs(String s) {
+        log("The jvmargs attribute is deprecated. "
+            + "Please use nested jvmarg elements.", Project.MSG_WARN);
+        cmdl.createVmArgument().setLine(s);
+    }
+
+    /**
+     * Adds a JVM argument.
+     *
+     * @return JVM argument created
+     */
+    public Commandline.Argument createJvmarg() {
+        return cmdl.createVmArgument();
+    }
+
+    /**
+     * Set the command used to start the VM (only if forking).
+     *
+     * @param s command to start the VM
+     */
+    public void setJvm(String s) {
+        cmdl.setVm(s);
+    }
+
+    /**
+     * Adds a system property.
+     *
+     * @param sysp system property
+     */
+    public void addSysproperty(Environment.Variable sysp) {
+        cmdl.addSysproperty(sysp);
+    }
+
+    /**
+     * Adds a set of properties as system properties.
+     *
+     * @param sysp set of properties to add
+     *
+     * @since Ant 1.6
+     */
+    public void addSyspropertyset(PropertySet sysp) {
+        cmdl.addSyspropertyset(sysp);
+    }
+
+    /**
+     * If true, then fail if the command exits with a
+     * returncode other than 0
+     *
+     * @param fail if true fail the build when the command exits with a non
+     * zero returncode
+     */
+    public void setFailonerror(boolean fail) {
+        failOnError = fail;
+        incompatibleWithSpawn |= fail;
+    }
+
+    /**
+     * The working directory of the process
+     *
+     * @param d working directory
+     *
+     */
+    public void setDir(File d) {
+        this.dir = d;
+    }
+
+    /**
+     * File the output of the process is redirected to.
+     *
+     * @param out name of the output file
+     */
+    public void setOutput(File out) {
+        this.output = out;
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Set the input to use for the task
+     *
+     * @param input name of the input file
+     */
+    public void setInput(File input) {
+        if (inputString != null) {
+            throw new BuildException("The \"input\" and \"inputstring\" "
+                + "attributes cannot both be specified");
+        }
+        this.input = input;
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Set the string to use as input
+     *
+     * @param inputString the string which is used as the input source
+     */
+    public void setInputString(String inputString) {
+        if (input != null) {
+            throw new BuildException("The \"input\" and \"inputstring\" "
+                + "attributes cannot both be specified");
+        }
+        this.inputString = inputString;
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Controls whether error output of exec is logged. This is only useful
+     * when output is being redirected and error output is desired in the
+     * Ant log
+     *
+     * @param logError get in the ant log the messages coming from stderr
+     * in the case that fork = true
+     */
+    public void setLogError(boolean logError) {
+        redirector.setLogError(logError);
+        incompatibleWithSpawn |= logError;
+    }
+
+    /**
+     * File the error stream of the process is redirected to.
+     *
+     * @param error file getting the error stream
+     *
+     * @since ant 1.6
+     */
+    public void setError(File error) {
+        this.error = error;
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Property name whose value should be set to the output of
+     * the process.
+     *
+     * @param outputProp property name
+     *
+     */
+    public void setOutputproperty(String outputProp) {
+        redirector.setOutputProperty(outputProp);
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Property name whose value should be set to the error of
+     * the process.
+     *
+     * @param errorProperty property name
+     *
+     * @since ant 1.6
+     */
+    public void setErrorProperty(String errorProperty) {
+        redirector.setErrorProperty(errorProperty);
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Corresponds to -mx or -Xmx depending on VM version.
+     *
+     * @param max max memory parameter
+     */
+    public void setMaxmemory(String max) {
+        cmdl.setMaxmemory(max);
+    }
+
+    /**
+     * Sets the JVM version.
+     * @param value JVM version
+     */
+    public void setJVMVersion(String value) {
+        cmdl.setVmversion(value);
+    }
+
+    /**
+     * Adds an environment variable.
+     *
+     * <p>Will be ignored if we are not forking a new VM.
+     *
+     * @param var new environment variable
+     *
+     * @since Ant 1.5
+     */
+    public void addEnv(Environment.Variable var) {
+        env.addVariable(var);
+    }
+
+    /**
+     * If true, use a completely new environment.
+     *
+     * <p>Will be ignored if we are not forking a new VM.
+     *
+     * @param newenv if true, use a completely new environment.
+     *
+     * @since Ant 1.5
+     */
+    public void setNewenvironment(boolean newenv) {
+        newEnvironment = newenv;
+    }
+
+    /**
+     * If true, append output to existing file.
+     *
+     * @param append if true, append output to existing file
+     *
+     * @since Ant 1.5
+     */
+    public void setAppend(boolean append) {
+        redirector.setAppend(append);
+        incompatibleWithSpawn = true;
+    }
+
+    /**
+     * Timeout in milliseconds after which the process will be killed.
+     *
+     * @param value time out in milliseconds
+     *
+     * @since Ant 1.5
+     */
+    public void setTimeout(Long value) {
+        timeout = value;
+        incompatibleWithSpawn |= timeout != null;
+    }
+
+    /**
+     *  assertions to enable in this program (if fork=true)
+     * @since Ant 1.6
+     * @param asserts assertion set
+     */
+    public void addAssertions(Assertions asserts) {
+        if(cmdl.getAssertions() != null) {
+            throw new BuildException("Only one assertion declaration is allowed");
+        }
+        cmdl.setAssertions(asserts);
+    }
+
+    /**
+     * Add a <CODE>RedirectorElement</CODE> to this task.
+     * @param redirectorElement   <CODE>RedirectorElement</CODE>.
+     */
+    public void addConfiguredRedirector(RedirectorElement redirectorElement) {
+        if (this.redirectorElement != null) {
+            throw new BuildException("cannot have > 1 nested <redirector>s");
+        } else {
+            this.redirectorElement = redirectorElement;
+            incompatibleWithSpawn = true;
+        }
+    }
+
+    /**
+     * Pass output sent to System.out to specified output file.
+     *
+     * @param output a string of output on its way to the handlers
+     *
+     * @since Ant 1.5
+     */
+    protected void handleOutput(String output) {
+        if (redirector.getOutputStream() != null) {
+            redirector.handleOutput(output);
+        } else {
+            super.handleOutput(output);
+        }
+    }
+
+    /**
+     * Handle an input request by this task
+     *
+     * @param buffer the buffer into which data is to be read.
+     * @param offset the offset into the buffer at which data is stored.
+     * @param length the amount of data to read
+     *
+     * @return the number of bytes read
+     *
+     * @exception IOException if the data cannot be read
+     * @since Ant 1.6
+     */
+    public int handleInput(byte[] buffer, int offset, int length)
+        throws IOException {
+        // Should work whether or not redirector.inputStream == null:
+        return redirector.handleInput(buffer, offset, length);
+    }
+
+    /**
+     * Pass output sent to System.out to specified output file.
+     *
+     * @param output string of output on its way to its handlers
+     *
+     * @since Ant 1.5.2
+     */
+    protected void handleFlush(String output) {
+        if (redirector.getOutputStream() != null) {
+            redirector.handleFlush(output);
+        } else {
+            super.handleFlush(output);
+        }
+    }
+
+    /**
+     * Handle output sent to System.err
+     *
+     * @param output string of stderr
+     *
+     * @since Ant 1.5
+     */
+    protected void handleErrorOutput(String output) {
+        if (redirector.getErrorStream() != null) {
+            redirector.handleErrorOutput(output);
+        } else {
+            super.handleErrorOutput(output);
+        }
+    }
+
+    /**
+     * Handle output sent to System.err and flush the stream.
+     *
+     * @param output string of stderr
+     *
+     * @since Ant 1.5.2
+     */
+    protected void handleErrorFlush(String output) {
+        if (redirector.getErrorStream() != null) {
+            redirector.handleErrorFlush(output);
+        } else {
+            super.handleErrorOutput(output);
+        }
+    }
+
+    /**
+     * Set up properties on the redirector that we needed to store locally.
+     */
+    protected void setupRedirector() {
+        redirector.setInput(input);
+        redirector.setInputString(inputString);
+        redirector.setOutput(output);
+        redirector.setError(error);
+        if (redirectorElement != null) {
+            redirectorElement.configure(redirector);
+        }
+        if (!spawn && input == null && inputString == null) {
+            // #24918: send standard input to the process by default.
+            redirector.setInputStream(new KeepAliveInputStream(getProject().getDefaultInputStream()));
+        }
+    }
+
+    /**
+     * Executes the given classname with the given arguments as it
+     * was a command line application.
+     */
+    private void run(CommandlineJava command) throws BuildException {
+        try {
+            ExecuteJava exe = new ExecuteJava();
+            exe.setJavaCommand(command.getJavaCommand());
+            exe.setClasspath(command.getClasspath());
+            exe.setSystemProperties(command.getSystemProperties());
+            exe.setPermissions(perm);
+            exe.setTimeout(timeout);
+            redirector.createStreams();
+            exe.execute(getProject());
+            redirector.complete();
+            if (exe.killedProcess()) {
+                throw new BuildException("Timeout: killed the sub-process");
+            }
+        } catch (IOException e) {
+            throw new BuildException(e);
+        }
+    }
+
+    /**
+     * Executes the given classname with the given arguments in a separate VM.
+     */
+    private int fork(String[] command) throws BuildException {
+
+        Execute exe
+            = new Execute(redirector.createHandler(), createWatchdog());
+        exe.setAntRun(getProject());
+
+        if (dir == null) {
+            dir = getProject().getBaseDir();
+        } else if (!dir.exists() || !dir.isDirectory()) {
+            throw new BuildException(dir.getAbsolutePath()
+                                     + " is not a valid directory",
+                                     getLocation());
+        }
+
+        exe.setWorkingDirectory(dir);
+
+        String[] environment = env.getVariables();
+        if (environment != null) {
+            for (int i = 0; i < environment.length; i++) {
+                log("Setting environment variable: " + environment[i],
+                    Project.MSG_VERBOSE);
+            }
+        }
+        exe.setNewenvironment(newEnvironment);
+        exe.setEnvironment(environment);
+
+        exe.setCommandline(command);
+        try {
+            int rc = exe.execute();
+            redirector.complete();
+            if (exe.killedProcess()) {
+                throw new BuildException("Timeout: killed the sub-process");
+            }
+            return rc;
+        } catch (IOException e) {
+            throw new BuildException(e, getLocation());
+        }
+    }
+
+    /**
+     * Executes the given classname with the given arguments in a separate VM.
+     */
+    private void spawn(String[] command) throws BuildException {
+
+        Execute exe
+            = new Execute();
+        exe.setAntRun(getProject());
+
+        if (dir == null) {
+            dir = getProject().getBaseDir();
+        } else if (!dir.exists() || !dir.isDirectory()) {
+            throw new BuildException(dir.getAbsolutePath()
+                                     + " is not a valid directory",
+                                     getLocation());
+        }
+
+        exe.setWorkingDirectory(dir);
+
+        String[] environment = env.getVariables();
+        if (environment != null) {
+            for (int i = 0; i < environment.length; i++) {
+                log("Setting environment variable: " + environment[i],
+                    Project.MSG_VERBOSE);
+            }
+        }
+        exe.setNewenvironment(newEnvironment);
+        exe.setEnvironment(environment);
+
+        exe.setCommandline(command);
+        try {
+            exe.spawn();
+        } catch (IOException e) {
+            throw new BuildException(e, getLocation());
+        }
+    }
+    /**
+     * Executes the given classname with the given arguments as it
+     * was a command line application.
+     *
+     * @param classname the name of the class to run
+     * @param args  arguments for the class
+     * @throws BuildException in case of IO Exception in the execution
+     */
+    protected void run(String classname, Vector args) throws BuildException {
+        CommandlineJava cmdj = new CommandlineJava();
+        cmdj.setClassname(classname);
+        for (int i = 0; i < args.size(); i++) {
+            cmdj.createArgument().setValue((String) args.elementAt(i));
+        }
+        run(cmdj);
+    }
+
+    /**
+     * Clear out the arguments to this java task.
+     */
+    public void clearArgs() {
+        cmdl.clearJavaArgs();
+    }
+
+    /**
+     * Create the Watchdog to kill a runaway process.
+     *
+     * @return new watchdog
+     *
+     * @throws BuildException under unknown circumstances
+     *
+     * @since Ant 1.5
+     */
+    protected ExecuteWatchdog createWatchdog() throws BuildException {
+        if (timeout == null) {
+            return null;
+        }
+        return new ExecuteWatchdog(timeout.longValue());
+    }
+
+    /**
+     * @since 1.6.2
+     */
+    private void log(Throwable t) {
+        StringWriter sw = new StringWriter();
+        PrintWriter w = new PrintWriter(sw);
+        t.printStackTrace(w);
+        w.close();
+        log(sw.toString(), Project.MSG_ERR);
+    }
+
+    /**
+     * accessor to the command line
+     *
+     * @return the current command line
+     * @since 1.6.3
+     */
+    public CommandlineJava getCommandLine() {
+        return cmdl;
+    }
+
+    /**
+     * get the system properties of the command line
+     *
+     * @return the current properties of this java invocation
+     * @since 1.6.3
+     */
+    public CommandlineJava.SysProperties getSysProperties() {
+        return cmdl.getSystemProperties();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Javac.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Javac.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Javac.java	(revision 16139)
@@ -0,0 +1,957 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.compilers.CompilerAdapter;
+import org.apache.tools.ant.taskdefs.compilers.CompilerAdapterFactory;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.GlobPatternMapper;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.SourceFileScanner;
+import org.apache.tools.ant.util.facade.FacadeTaskHelper;
+
+/**
+ * Compiles Java source files. This task can take the following
+ * arguments:
+ * <ul>
+ * <li>sourcedir
+ * <li>destdir
+ * <li>deprecation
+ * <li>classpath
+ * <li>bootclasspath
+ * <li>extdirs
+ * <li>optimize
+ * <li>debug
+ * <li>encoding
+ * <li>target
+ * <li>depend
+ * <li>verbose
+ * <li>failonerror
+ * <li>includeantruntime
+ * <li>includejavaruntime
+ * <li>source
+ * <li>compiler
+ * </ul>
+ * Of these arguments, the <b>sourcedir</b> and <b>destdir</b> are required.
+ * <p>
+ * When this task executes, it will recursively scan the sourcedir and
+ * destdir looking for Java source files to compile. This task makes its
+ * compile decision based on timestamp.
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="java"
+ */
+
+public class Javac extends MatchingTask {
+
+    private static final String FAIL_MSG
+        = "Compile failed; see the compiler error output for details.";
+
+    private Path src;
+    private File destDir;
+    private Path compileClasspath;
+    private Path compileSourcepath;
+    private String encoding;
+    private boolean debug = false;
+    private boolean optimize = false;
+    private boolean deprecation = false;
+    private boolean depend = false;
+    private boolean verbose = false;
+    private String target;
+    private Path bootclasspath;
+    private Path extdirs;
+    private boolean includeAntRuntime = true;
+    private boolean includeJavaRuntime = false;
+    private boolean fork = false;
+    private String forkedExecutable = null;
+    private boolean nowarn = false;
+    private String memoryInitialSize;
+    private String memoryMaximumSize;
+    private FacadeTaskHelper facade = null;
+
+    protected boolean failOnError = true;
+    protected boolean listFiles = false;
+    protected File[] compileList = new File[0];
+
+    private String source;
+    private String debugLevel;
+    private File tmpDir;
+
+    /**
+     * Javac task for compilation of Java files.
+     */
+    public Javac() {
+        if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
+            facade = new FacadeTaskHelper("javac1.1");
+        } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_2)) {
+            facade = new FacadeTaskHelper("javac1.2");
+        } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_3)) {
+            facade = new FacadeTaskHelper("javac1.3");
+        } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_4)) {
+            facade = new FacadeTaskHelper("javac1.4");
+        } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_5)) {
+            facade = new FacadeTaskHelper("javac1.5");
+        } else {
+            facade = new FacadeTaskHelper("classic");
+        }
+    }
+
+    /**
+     * Get the value of debugLevel.
+     * @return value of debugLevel.
+     */
+    public String getDebugLevel() {
+        return debugLevel;
+    }
+
+    /**
+     * Keyword list to be appended to the -g command-line switch.
+     *
+     * This will be ignored by all implementations except modern
+     * and classic(ver >= 1.2). Legal values are none or a
+     * comma-separated list of the following keywords: lines, vars,
+     * and source. If debuglevel is not specified, by default, :none
+     * will be appended to -g. If debug is not turned on, this attribute
+     * will be ignored.
+     *
+     * @param v  Value to assign to debugLevel.
+     */
+    public void setDebugLevel(String  v) {
+        this.debugLevel = v;
+    }
+
+    /**
+     * Get the value of source.
+     * @return value of source.
+     */
+    public String getSource() {
+        return source;
+    }
+
+    /**
+     * Value of the -source command-line switch; will be ignored
+     * by all implementations except modern and jikes.
+     *
+     * If you use this attribute together with jikes, you must
+     * make sure that your version of jikes supports the -source switch.
+     * Legal values are 1.3, 1.4 and 1.5 - by default, no -source argument
+     * will be used at all.
+     *
+     * @param v  Value to assign to source.
+     */
+    public void setSource(String  v) {
+        this.source = v;
+    }
+
+    /**
+     * Adds a path for source compilation.
+     *
+     * @return a nested src element.
+     */
+    public Path createSrc() {
+        if (src == null) {
+            src = new Path(getProject());
+        }
+        return src.createPath();
+    }
+
+    /**
+     * Recreate src.
+     *
+     * @return a nested src element.
+     */
+    protected Path recreateSrc() {
+        src = null;
+        return createSrc();
+    }
+
+    /**
+     * Set the source directories to find the source Java files.
+     * @param srcDir the source directories as a path
+     */
+    public void setSrcdir(Path srcDir) {
+        if (src == null) {
+            src = srcDir;
+        } else {
+            src.append(srcDir);
+        }
+    }
+
+    /**
+     * Gets the source dirs to find the source java files.
+     * @return the source directories as a path
+     */
+    public Path getSrcdir() {
+        return src;
+    }
+
+    /**
+     * Set the destination directory into which the Java source
+     * files should be compiled.
+     * @param destDir the destination director
+     */
+    public void setDestdir(File destDir) {
+        this.destDir = destDir;
+    }
+
+    /**
+     * Gets the destination directory into which the java source files
+     * should be compiled.
+     * @return the destination directory
+     */
+    public File getDestdir() {
+        return destDir;
+    }
+
+    /**
+     * Set the sourcepath to be used for this compilation.
+     * @param sourcepath the source path
+     */
+    public void setSourcepath(Path sourcepath) {
+        if (compileSourcepath == null) {
+            compileSourcepath = sourcepath;
+        } else {
+            compileSourcepath.append(sourcepath);
+        }
+    }
+
+    /**
+     * Gets the sourcepath to be used for this compilation.
+     * @return the source path
+     */
+    public Path getSourcepath() {
+        return compileSourcepath;
+    }
+
+    /**
+     * Adds a path to sourcepath.
+     * @return a sourcepath to be configured
+     */
+    public Path createSourcepath() {
+        if (compileSourcepath == null) {
+            compileSourcepath = new Path(getProject());
+        }
+        return compileSourcepath.createPath();
+    }
+
+    /**
+     * Adds a reference to a source path defined elsewhere.
+     * @param r a reference to a source path
+     */
+    public void setSourcepathRef(Reference r) {
+        createSourcepath().setRefid(r);
+    }
+
+    /**
+     * Set the classpath to be used for this compilation.
+     *
+     * @param classpath an Ant Path object containing the compilation classpath.
+     */
+    public void setClasspath(Path classpath) {
+        if (compileClasspath == null) {
+            compileClasspath = classpath;
+        } else {
+            compileClasspath.append(classpath);
+        }
+    }
+
+    /**
+     * Gets the classpath to be used for this compilation.
+     * @return the class path
+     */
+    public Path getClasspath() {
+        return compileClasspath;
+    }
+
+    /**
+     * Adds a path to the classpath.
+     * @return a class path to be configured
+     */
+    public Path createClasspath() {
+        if (compileClasspath == null) {
+            compileClasspath = new Path(getProject());
+        }
+        return compileClasspath.createPath();
+    }
+
+    /**
+     * Adds a reference to a classpath defined elsewhere.
+     * @param r a reference to a classpath
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+
+    /**
+     * Sets the bootclasspath that will be used to compile the classes
+     * against.
+     * @param bootclasspath a path to use as a boot class path (may be more
+     *                      than one)
+     */
+    public void setBootclasspath(Path bootclasspath) {
+        if (this.bootclasspath == null) {
+            this.bootclasspath = bootclasspath;
+        } else {
+            this.bootclasspath.append(bootclasspath);
+        }
+    }
+
+    /**
+     * Gets the bootclasspath that will be used to compile the classes
+     * against.
+     * @return the boot path
+     */
+    public Path getBootclasspath() {
+        return bootclasspath;
+    }
+
+    /**
+     * Adds a path to the bootclasspath.
+     * @return a path to be configured
+     */
+    public Path createBootclasspath() {
+        if (bootclasspath == null) {
+            bootclasspath = new Path(getProject());
+        }
+        return bootclasspath.createPath();
+    }
+
+    /**
+     * Adds a reference to a classpath defined elsewhere.
+     * @param r a reference to a classpath
+     */
+    public void setBootClasspathRef(Reference r) {
+        createBootclasspath().setRefid(r);
+    }
+
+    /**
+     * Sets the extension directories that will be used during the
+     * compilation.
+     * @param extdirs a path
+     */
+    public void setExtdirs(Path extdirs) {
+        if (this.extdirs == null) {
+            this.extdirs = extdirs;
+        } else {
+            this.extdirs.append(extdirs);
+        }
+    }
+
+    /**
+     * Gets the extension directories that will be used during the
+     * compilation.
+     * @return the extension directories as a path
+     */
+    public Path getExtdirs() {
+        return extdirs;
+    }
+
+    /**
+     * Adds a path to extdirs.
+     * @return a path to be configured
+     */
+    public Path createExtdirs() {
+        if (extdirs == null) {
+            extdirs = new Path(getProject());
+        }
+        return extdirs.createPath();
+    }
+
+    /**
+     * If true, list the source files being handed off to the compiler.
+     * @param list if true list the source files
+     */
+    public void setListfiles(boolean list) {
+        listFiles = list;
+    }
+
+    /**
+     * Get the listfiles flag.
+     * @return the listfiles flag
+     */
+    public boolean getListfiles() {
+        return listFiles;
+    }
+
+    /**
+     * Indicates whether the build will continue
+     * even if there are compilation errors; defaults to true.
+     * @param fail if true halt the build on failure
+     */
+    public void setFailonerror(boolean fail) {
+        failOnError = fail;
+    }
+
+    /**
+     * @ant.attribute ignore="true"
+     * @param proceed inverse of failoferror
+     */
+    public void setProceed(boolean proceed) {
+        failOnError = !proceed;
+    }
+
+    /**
+     * Gets the failonerror flag.
+     * @return the failonerror flag
+     */
+    public boolean getFailonerror() {
+        return failOnError;
+    }
+
+    /**
+     * Indicates whether source should be
+     * compiled with deprecation information; defaults to off.
+     * @param deprecation if true turn on deprecation information
+     */
+    public void setDeprecation(boolean deprecation) {
+        this.deprecation = deprecation;
+    }
+
+    /**
+     * Gets the deprecation flag.
+     * @return the deprecation flag
+     */
+    public boolean getDeprecation() {
+        return deprecation;
+    }
+
+    /**
+     * The initial size of the memory for the underlying VM
+     * if javac is run externally; ignored otherwise.
+     * Defaults to the standard VM memory setting.
+     * (Examples: 83886080, 81920k, or 80m)
+     * @param memoryInitialSize string to pass to VM
+     */
+    public void setMemoryInitialSize(String memoryInitialSize) {
+        this.memoryInitialSize = memoryInitialSize;
+    }
+
+    /**
+     * Gets the memoryInitialSize flag.
+     * @return the memoryInitialSize flag
+     */
+    public String getMemoryInitialSize() {
+        return memoryInitialSize;
+    }
+
+    /**
+     * The maximum size of the memory for the underlying VM
+     * if javac is run externally; ignored otherwise.
+     * Defaults to the standard VM memory setting.
+     * (Examples: 83886080, 81920k, or 80m)
+     * @param memoryMaximumSize string to pass to VM
+     */
+    public void setMemoryMaximumSize(String memoryMaximumSize) {
+        this.memoryMaximumSize = memoryMaximumSize;
+    }
+
+    /**
+     * Gets the memoryMaximumSize flag.
+     * @return the memoryMaximumSize flag
+     */
+    public String getMemoryMaximumSize() {
+        return memoryMaximumSize;
+    }
+
+    /**
+     * Set the Java source file encoding name.
+     * @param encoding the source file encoding
+     */
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * Gets the java source file encoding name.
+     * @return the source file encoding name
+     */
+    public String getEncoding() {
+        return encoding;
+    }
+
+    /**
+     * Indicates whether source should be compiled
+     * with debug information; defaults to off.
+     * @param debug if true compile with debug information
+     */
+    public void setDebug(boolean debug) {
+        this.debug = debug;
+    }
+
+    /**
+     * Gets the debug flag.
+     * @return the debug flag
+     */
+    public boolean getDebug() {
+        return debug;
+    }
+
+    /**
+     * If true, compiles with optimization enabled.
+     * @param optimize if true compile with optimization enabled
+     */
+    public void setOptimize(boolean optimize) {
+        this.optimize = optimize;
+    }
+
+    /**
+     * Gets the optimize flag.
+     * @return the optimize flag
+     */
+    public boolean getOptimize() {
+        return optimize;
+    }
+
+    /**
+     * Enables dependency-tracking for compilers
+     * that support this (jikes and classic).
+     * @param depend if true enable dependency-tracking
+     */
+    public void setDepend(boolean depend) {
+        this.depend = depend;
+    }
+
+    /**
+     * Gets the depend flag.
+     * @return the depend flag
+     */
+    public boolean getDepend() {
+        return depend;
+    }
+
+    /**
+     * If true, asks the compiler for verbose output.
+     * @param verbose if true, asks the compiler for verbose output
+     */
+    public void setVerbose(boolean verbose) {
+        this.verbose = verbose;
+    }
+
+    /**
+     * Gets the verbose flag.
+     * @return the verbose flag
+     */
+    public boolean getVerbose() {
+        return verbose;
+    }
+
+    /**
+     * Sets the target VM that the classes will be compiled for. Valid
+     * values depend on the compiler, for jdk 1.4 the valid values are
+     * "1.1", "1.2", "1.3", "1.4" and "1.5".
+     * @param target the target VM
+     */
+    public void setTarget(String target) {
+        this.target = target;
+    }
+
+    /**
+     * Gets the target VM that the classes will be compiled for.
+     * @return the target VM
+     */
+    public String getTarget() {
+        return target;
+    }
+
+    /**
+     * If true, includes Ant's own classpath in the classpath.
+     * @param include if true, includes Ant's own classpath in the classpath
+     */
+    public void setIncludeantruntime(boolean include) {
+        includeAntRuntime = include;
+    }
+
+    /**
+     * Gets whether or not the ant classpath is to be included in the classpath.
+     * @return whether or not the ant classpath is to be included in the classpath
+     */
+    public boolean getIncludeantruntime() {
+        return includeAntRuntime;
+    }
+
+    /**
+     * If true, includes the Java runtime libraries in the classpath.
+     * @param include if true, includes the Java runtime libraries in the classpath
+     */
+    public void setIncludejavaruntime(boolean include) {
+        includeJavaRuntime = include;
+    }
+
+    /**
+     * Gets whether or not the java runtime should be included in this
+     * task's classpath.
+     * @return the includejavaruntime attribute
+     */
+    public boolean getIncludejavaruntime() {
+        return includeJavaRuntime;
+    }
+
+    /**
+     * If true, forks the javac compiler.
+     *
+     * @param f "true|false|on|off|yes|no"
+     */
+    public void setFork(boolean f) {
+        fork = f;
+    }
+
+    /**
+     * Sets the name of the javac executable.
+     *
+     * <p>Ignored unless fork is true or extJavac has been specified
+     * as the compiler.</p>
+     * @param forkExec the name of the executable
+     */
+    public void setExecutable(String forkExec) {
+        forkedExecutable = forkExec;
+    }
+
+    /**
+     * The value of the executable attribute, if any.
+     *
+     * @since Ant 1.6
+     * @return the name of the java executable
+     */
+    public String getExecutable() {
+        return forkedExecutable;
+    }
+
+    /**
+     * Is this a forked invocation of JDK's javac?
+     * @return true if this is a forked invocation
+     */
+    public boolean isForkedJavac() {
+        return fork || "extJavac".equals(getCompiler());
+    }
+
+    /**
+     * The name of the javac executable to use in fork-mode.
+     *
+     * <p>This is either the name specified with the executable
+     * attribute or the full path of the javac compiler of the VM Ant
+     * is currently running in - guessed by Ant.</p>
+     *
+     * <p>You should <strong>not</strong> invoke this method if you
+     * want to get the value of the executable command - use {@link
+     * #getExecutable getExecutable} for this.</p>
+     * @return the name of the javac executable
+     */
+    public String getJavacExecutable() {
+        if (forkedExecutable == null && isForkedJavac()) {
+            forkedExecutable = getSystemJavac();
+        } else if (forkedExecutable != null && !isForkedJavac()) {
+            forkedExecutable = null;
+        }
+        return forkedExecutable;
+    }
+
+    /**
+     * If true, enables the -nowarn option.
+     * @param flag if true, enable the -nowarn option
+     */
+    public void setNowarn(boolean flag) {
+        this.nowarn = flag;
+    }
+
+    /**
+     * Should the -nowarn option be used.
+     * @return true if the -nowarn option should be used
+     */
+    public boolean getNowarn() {
+        return nowarn;
+    }
+
+    /**
+     * Adds an implementation specific command-line argument.
+     * @return a ImplementationSpecificArgument to be configured
+     */
+    public ImplementationSpecificArgument createCompilerArg() {
+        ImplementationSpecificArgument arg =
+            new ImplementationSpecificArgument();
+        facade.addImplementationArgument(arg);
+        return arg;
+    }
+
+    /**
+     * Get the additional implementation specific command line arguments.
+     * @return array of command line arguments, guaranteed to be non-null.
+     */
+    public String[] getCurrentCompilerArgs() {
+        String chosen = facade.getExplicitChoice();
+        // make sure facade knows about magic properties and fork setting
+        facade.setImplementation(getCompiler());
+        try {
+            return facade.getArgs();
+        } finally {
+            facade.setImplementation(chosen);
+        }
+    }
+
+    /**
+     * Where Ant should place temporary files.
+     *
+     * @since Ant 1.6
+     * @param tmpDir the temporary directory
+     */
+    public void setTempdir(File tmpDir) {
+        this.tmpDir = tmpDir;
+    }
+
+    /**
+     * Where Ant should place temporary files.
+     *
+     * @since Ant 1.6
+     * @return the temporary directory
+     */
+    public File getTempdir() {
+        return tmpDir;
+    }
+
+    /**
+     * Executes the task.
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+        checkParameters();
+        resetFileLists();
+
+        // scan source directories and dest directory to build up
+        // compile lists
+        String[] list = src.list();
+        for (int i = 0; i < list.length; i++) {
+            File srcDir = getProject().resolveFile(list[i]);
+            if (!srcDir.exists()) {
+                throw new BuildException("srcdir \""
+                                         + srcDir.getPath()
+                                         + "\" does not exist!", getLocation());
+            }
+
+            DirectoryScanner ds = this.getDirectoryScanner(srcDir);
+            String[] files = ds.getIncludedFiles();
+
+            scanDir(srcDir, destDir != null ? destDir : srcDir, files);
+        }
+
+        compile();
+    }
+
+    /**
+     * Clear the list of files to be compiled and copied..
+     */
+    protected void resetFileLists() {
+        compileList = new File[0];
+    }
+
+    /**
+     * Scans the directory looking for source files to be compiled.
+     * The results are returned in the class variable compileList
+     *
+     * @param srcDir   The source directory
+     * @param destDir  The destination directory
+     * @param files    An array of filenames
+     */
+    protected void scanDir(File srcDir, File destDir, String[] files) {
+        GlobPatternMapper m = new GlobPatternMapper();
+        m.setFrom("*.java");
+        m.setTo("*.class");
+        SourceFileScanner sfs = new SourceFileScanner(this);
+        File[] newFiles = sfs.restrictAsFiles(files, srcDir, destDir, m);
+
+        if (newFiles.length > 0) {
+            File[] newCompileList
+                = new File[compileList.length + newFiles.length];
+            System.arraycopy(compileList, 0, newCompileList, 0,
+                    compileList.length);
+            System.arraycopy(newFiles, 0, newCompileList,
+                    compileList.length, newFiles.length);
+            compileList = newCompileList;
+        }
+    }
+
+    /**
+     * Gets the list of files to be compiled.
+     * @return the list of files as an array
+     */
+    public File[] getFileList() {
+        return compileList;
+    }
+
+    /**
+     * Is the compiler implementation a jdk compiler
+     *
+     * @param compilerImpl the name of the compiler implementation
+     * @return true if compilerImpl is "modern", "classic", "javac1.1",
+     *                 "javac1.2", "javac1.3", "javac1.4" or "javac1.5".
+     */
+    protected boolean isJdkCompiler(String compilerImpl) {
+        return "modern".equals(compilerImpl)
+            || "classic".equals(compilerImpl)
+            || "javac1.1".equals(compilerImpl)
+            || "javac1.2".equals(compilerImpl)
+            || "javac1.3".equals(compilerImpl)
+            || "javac1.4".equals(compilerImpl)
+            || "javac1.5".equals(compilerImpl);
+    }
+
+    /**
+     * @return the executable name of the java compiler
+     */
+    protected String getSystemJavac() {
+        return JavaEnvUtils.getJdkExecutable("javac");
+    }
+
+    /**
+     * Choose the implementation for this particular task.
+     * @param compiler the name of the compiler
+     * @since Ant 1.5
+     */
+    public void setCompiler(String compiler) {
+        facade.setImplementation(compiler);
+    }
+
+    /**
+     * The implementation for this particular task.
+     *
+     * <p>Defaults to the build.compiler property but can be overridden
+     * via the compiler and fork attributes.</p>
+     *
+     * <p>If fork has been set to true, the result will be extJavac
+     * and not classic or java1.2 - no matter what the compiler
+     * attribute looks like.</p>
+     *
+     * @see #getCompilerVersion
+     *
+     * @since Ant 1.5
+     */
+    public String getCompiler() {
+        String compilerImpl = getCompilerVersion();
+        if (fork) {
+            if (isJdkCompiler(compilerImpl)) {
+                compilerImpl = "extJavac";
+            } else {
+                log("Since compiler setting isn't classic or modern,"
+                    + "ignoring fork setting.", Project.MSG_WARN);
+            }
+        }
+        return compilerImpl;
+    }
+
+    /**
+     * The implementation for this particular task.
+     *
+     * <p>Defaults to the build.compiler property but can be overridden
+     * via the compiler attribute.</p>
+     *
+     * <p>This method does not take the fork attribute into
+     * account.</p>
+     *
+     * @see #getCompiler
+     *
+     * @since Ant 1.5
+     */
+    public String getCompilerVersion() {
+        facade.setMagicValue(getProject().getProperty("build.compiler"));
+        return facade.getImplementation();
+    }
+
+    /**
+     * Check that all required attributes have been set and nothing
+     * silly has been entered.
+     *
+     * @since Ant 1.5
+     * @exception BuildException if an error occurs
+     */
+    protected void checkParameters() throws BuildException {
+        if (src == null) {
+            throw new BuildException("srcdir attribute must be set!",
+                                     getLocation());
+        }
+        if (src.size() == 0) {
+            throw new BuildException("srcdir attribute must be set!",
+                                     getLocation());
+        }
+
+        if (destDir != null && !destDir.isDirectory()) {
+            throw new BuildException("destination directory \""
+                                     + destDir
+                                     + "\" does not exist "
+                                     + "or is not a directory", getLocation());
+        }
+    }
+
+    /**
+     * Perform the compilation.
+     *
+     * @since Ant 1.5
+     */
+    protected void compile() {
+        String compilerImpl = getCompiler();
+
+        if (compileList.length > 0) {
+            log("Compiling " + compileList.length + " source file"
+                + (compileList.length == 1 ? "" : "s")
+                + (destDir != null ? " to " + destDir : ""));
+
+            if (listFiles) {
+                for (int i = 0; i < compileList.length; i++) {
+                  String filename = compileList[i].getAbsolutePath();
+                  log(filename);
+                }
+            }
+
+            CompilerAdapter adapter =
+                CompilerAdapterFactory.getCompiler(compilerImpl, this);
+
+            // now we need to populate the compiler adapter
+            adapter.setJavac(this);
+
+            // finally, lets execute the compiler!!
+            if (!adapter.execute()) {
+                if (failOnError) {
+                    throw new BuildException(FAIL_MSG, getLocation());
+                } else {
+                    log(FAIL_MSG, Project.MSG_ERR);
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds an "compiler" attribute to Commandline$Attribute used to
+     * filter command line attributes based on the current
+     * implementation.
+     */
+    public class ImplementationSpecificArgument extends
+        org.apache.tools.ant.util.facade.ImplementationSpecificArgument {
+
+        /**
+         * @param impl the name of the compiler
+         */
+        public void setCompiler(String impl) {
+            super.setImplementation(impl);
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Javadoc.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Javadoc.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Javadoc.java	(revision 16139)
@@ -0,0 +1,2137 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.DirSet;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Generates Javadoc documentation for a collection
+ * of source code.
+ *
+ * <P>Current known limitations are:
+ *
+ * <P><UL>
+ *    <LI>patterns must be of the form "xxx.*", every other pattern doesn't
+ *        work.
+ *    <LI>there is no control on arguments sanity since they are left
+ *        to the javadoc implementation.
+ * </UL>
+ *
+ * <P>If no <CODE>doclet</CODE> is set, then the <CODE>version</CODE> and
+ * <CODE>author</CODE> are by default <CODE>"yes"</CODE>.
+ *
+ * <P>Note: This task is run on another VM because the Javadoc code calls
+ * <CODE>System.exit()</CODE> which would break Ant functionality.
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="java"
+ */
+public class Javadoc extends Task {
+    /**
+     * Inner class used to manage doclet parameters.
+     */
+    public class DocletParam {
+        /** The parameter name */
+        private String name;
+
+        /** The parameter value */
+        private String value;
+
+        /**
+         * Set the name of the parameter.
+         *
+         * @param name the name of the doclet parameter
+         */
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        /**
+         * Get the parameter name.
+         *
+         * @return the parameter's name.
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * Set the parameter value.
+         *
+         * Note that only string values are supported. No resolution of file
+         * paths is performed.
+         *
+         * @param value the parameter value.
+         */
+        public void setValue(String value) {
+            this.value = value;
+        }
+
+        /**
+         * Get the parameter value.
+         *
+         * @return the parameter value.
+         */
+        public String getValue() {
+            return value;
+        }
+    }
+
+    /**
+     * A project aware class used for Javadoc extensions which take a name
+     * and a path such as doclet and taglet arguments.
+     *
+     */
+    public static class ExtensionInfo extends ProjectComponent {
+        /** The name of the extension */
+        private String name;
+
+        /** The optional path to use to load the extension */
+        private Path path;
+
+        /**
+         * Set the name of the extension
+         *
+         * @param name the extension's name.
+         */
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        /**
+         * Get the name of the extension.
+         *
+         * @return the extension's name.
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * Set the path to use when loading the component.
+         *
+         * @param path a Path instance containing the classpath to use.
+         */
+        public void setPath(Path path) {
+            if (this.path == null) {
+                this.path = path;
+            } else {
+                this.path.append(path);
+            }
+        }
+
+        /**
+         * Get the extension's path.
+         *
+         * @return the path to be used to load the extension.
+         * May be <code>null</code>
+         */
+        public Path getPath() {
+            return path;
+        }
+
+        /**
+         * Create an empty nested path to be configured by Ant with the
+         * classpath for the extension.
+         *
+         * @return a new Path instance to be configured.
+         */
+        public Path createPath() {
+            if (path == null) {
+                path = new Path(getProject());
+            }
+            return path.createPath();
+        }
+
+        /**
+         * Adds a reference to a CLASSPATH defined elsewhere.
+         *
+         * @param r the reference containing the path.
+         */
+        public void setPathRef(Reference r) {
+            createPath().setRefid(r);
+        }
+    }
+
+    /**
+     * This class stores info about doclets.
+     *
+     */
+    public class DocletInfo extends ExtensionInfo {
+
+        /** Collection of doclet parameters. */
+        private Vector params = new Vector();
+
+        /**
+         * Create a doclet parameter to be configured by Ant.
+         *
+         * @return a new DocletParam instance to be configured.
+         */
+        public DocletParam createParam() {
+            DocletParam param = new DocletParam();
+            params.addElement(param);
+
+            return param;
+        }
+
+        /**
+         * Get the doclet's parameters.
+         *
+         * @return an Enumeration of DocletParam instances.
+         */
+        public Enumeration getParams() {
+            return params.elements();
+        }
+    }
+
+    /**
+     * Used to track info about the packages to be javadoc'd
+     */
+    public static class PackageName {
+        /** The package name */
+        private String name;
+
+        /**
+         * Set the name of the package
+         *
+         * @param name the package name.
+         */
+        public void setName(String name) {
+            this.name = name.trim();
+        }
+
+        /**
+         * Get the package name.
+         *
+         * @return the package's name.
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * @see java.lang.Object#toString
+         */
+        public String toString() {
+            return getName();
+        }
+    }
+
+    /**
+     * This class is used to manage the source files to be processed.
+     */
+    public static class SourceFile {
+        /** The source file */
+        private File file;
+
+        /**
+         * Default constructor
+         */
+        public SourceFile() {
+        }
+
+        /**
+         * Constructor specifying the source file directly
+         *
+         * @param file the source file
+         */
+        public SourceFile(File file) {
+            this.file = file;
+        }
+
+        /**
+         * Set the source file.
+         *
+         * @param file the source file.
+         */
+        public void setFile(File file) {
+            this.file = file;
+        }
+
+        /**
+         * Get the source file.
+         *
+         * @return the source file.
+         */
+        public File getFile() {
+            return file;
+        }
+    }
+
+    /**
+     * An HTML element in the javadoc.
+     *
+     * This class is used for those javadoc elements which contain HTML such as
+     * footers, headers, etc.
+     */
+    public static class Html {
+        /** The text for the element */
+        private StringBuffer text = new StringBuffer();
+
+        /**
+         * Add text to the element.
+         *
+         * @param t the text to be added.
+         */
+        public void addText(String t) {
+            text.append(t);
+        }
+
+        /**
+         * Get the current text for the element.
+         *
+         * @return the current text.
+         */
+        public String getText() {
+            return text.substring(0);
+        }
+    }
+
+    /**
+     * EnumeratedAttribute implementation supporting the javadoc scoping
+     * values.
+     */
+    public static class AccessType extends EnumeratedAttribute {
+        /**
+         * @see EnumeratedAttribute#getValues().
+         */
+        public String[] getValues() {
+            // Protected first so if any GUI tool offers a default
+            // based on enum #0, it will be right.
+            return new String[] {"protected", "public", "package", "private"};
+        }
+    }
+
+    /** The command line built to execute Javadoc. */
+    private Commandline cmd = new Commandline();
+
+    /**
+     * Utility method to add an argument to the command line conditionally
+     * based on the given flag.
+     *
+     * @param b the flag which controls if the argument is added.
+     * @param arg the argument value.
+     */
+    private void addArgIf(boolean b, String arg) {
+        if (b) {
+            cmd.createArgument().setValue(arg);
+        }
+    }
+
+    /**
+     * Utility method to add a javadoc argument.
+     *
+     * @param key the argument name.
+     * @param value the argument value.
+     */
+    private void addArgIfNotEmpty(String key, String value) {
+        if (value != null && value.length() != 0) {
+            cmd.createArgument().setValue(key);
+            cmd.createArgument().setValue(value);
+        } else {
+            log("Warning: Leaving out empty argument '" + key + "'",
+                Project.MSG_WARN);
+        }
+    }
+
+    /**
+     * Flag which indicates if the task should fail if there is a
+     * javadoc error.
+     */
+    private boolean failOnError = false;
+    private Path sourcePath = null;
+    private File destDir = null;
+    private Vector sourceFiles = new Vector();
+    private Vector packageNames = new Vector();
+    private Vector excludePackageNames = new Vector(1);
+    private boolean author = true;
+    private boolean version = true;
+    private DocletInfo doclet = null;
+    private Path classpath = null;
+    private Path bootclasspath = null;
+    private String group = null;
+    private String packageList = null;
+    private Vector links = new Vector();
+    private Vector groups = new Vector();
+    private Vector tags = new Vector();
+    private boolean useDefaultExcludes = true;
+    private Html doctitle = null;
+    private Html header = null;
+    private Html footer = null;
+    private Html bottom = null;
+    private boolean useExternalFile = false;
+    private FileUtils fileUtils = FileUtils.newFileUtils();
+    private String source = null;
+    private boolean linksource = false;
+    private boolean breakiterator = false;
+    private String noqualifier;
+    private boolean includeNoSourcePackages = false;
+    private boolean old = false;
+    private String executable = null;
+
+    private Vector fileSets = new Vector();
+    private Vector packageSets = new Vector();
+
+    /**
+     * Work around command line length limit by using an external file
+     * for the sourcefiles.
+     *
+     * @param b true if an external file is to be used.
+     */
+    public void setUseExternalFile(boolean b) {
+        useExternalFile = b;
+    }
+
+    /**
+     * Sets whether default exclusions should be used or not.
+     *
+     * @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
+     *                           should be used, "false"|"off"|"no" when they
+     *                           shouldn't be used.
+     */
+    public void setDefaultexcludes(boolean useDefaultExcludes) {
+        this.useDefaultExcludes = useDefaultExcludes;
+    }
+
+    /**
+     * Set the maximum memory to be used by the javadoc process
+     *
+     * @param max a string indicating the maximum memory according to the
+     *        JVM conventions (e.g. 128m is 128 Megabytes)
+     */
+    public void setMaxmemory(String max) {
+        cmd.createArgument().setValue("-J-Xmx" + max);
+    }
+
+    /**
+     * Set an additional parameter on the command line
+     *
+     * @param add the additional command line parameter for the javadoc task.
+     */
+    public void setAdditionalparam(String add) {
+        cmd.createArgument().setLine(add);
+    }
+
+    /**
+     * Adds a command-line argument.
+     * @since Ant 1.6
+     */
+    public Commandline.Argument createArg() {
+        return cmd.createArgument();
+    }
+
+    /**
+     * Specify where to find source file
+     *
+     * @param src a Path instance containing the various source directories.
+     */
+    public void setSourcepath(Path src) {
+        if (sourcePath == null) {
+            sourcePath = src;
+        } else {
+            sourcePath.append(src);
+        }
+    }
+
+    /**
+     * Create a path to be configured with the locations of the source
+     * files.
+     *
+     * @return a new Path instance to be configured by the Ant core.
+     */
+    public Path createSourcepath() {
+        if (sourcePath == null) {
+            sourcePath = new Path(getProject());
+        }
+        return sourcePath.createPath();
+    }
+
+    /**
+     * Adds a reference to a CLASSPATH defined elsewhere.
+     *
+     * @param r the reference containing the source path definition.
+     */
+    public void setSourcepathRef(Reference r) {
+        createSourcepath().setRefid(r);
+    }
+
+    /**
+     * Set the directory where the Javadoc output will be generated.
+     *
+     * @param dir the destination directory.
+     */
+    public void setDestdir(File dir) {
+        destDir = dir;
+        cmd.createArgument().setValue("-d");
+        cmd.createArgument().setFile(destDir);
+    }
+
+    /**
+     * Set the list of source files to process.
+     *
+     * @param src a comma separated list of source files.
+     */
+    public void setSourcefiles(String src) {
+        StringTokenizer tok = new StringTokenizer(src, ",");
+        while (tok.hasMoreTokens()) {
+            String f = tok.nextToken();
+            SourceFile sf = new SourceFile();
+            sf.setFile(getProject().resolveFile(f.trim()));
+            addSource(sf);
+        }
+    }
+
+    /**
+     * Add a single source file.
+     *
+     * @param sf the source file to be processed.
+     */
+    public void addSource(SourceFile sf) {
+        sourceFiles.addElement(sf);
+    }
+
+    /**
+     * Set the package names to be processed.
+     *
+     * @param packages a comma separated list of packages specs
+     *        (may be wildcarded).
+     *
+     * @see #addPackage for wildcard information.
+     */
+    public void setPackagenames(String packages) {
+        StringTokenizer tok = new StringTokenizer(packages, ",");
+        while (tok.hasMoreTokens()) {
+            String p = tok.nextToken();
+            PackageName pn = new PackageName();
+            pn.setName(p);
+            addPackage(pn);
+        }
+    }
+
+    /**
+     * Add a single package to be processed.
+     *
+     * If the package name ends with &quot;.*&quot; the Javadoc task
+     * will find and process all subpackages.
+     *
+     * @param pn the package name, possibly wildcarded.
+     */
+    public void addPackage(PackageName pn) {
+        packageNames.addElement(pn);
+    }
+
+    /**
+     * Set the list of packages to be excluded.
+     *
+     * @param packages a comma separated list of packages to be excluded.
+     *        This may not include wildcards.
+     */
+    public void setExcludePackageNames(String packages) {
+        StringTokenizer tok = new StringTokenizer(packages, ",");
+        while (tok.hasMoreTokens()) {
+            String p = tok.nextToken();
+            PackageName pn = new PackageName();
+            pn.setName(p);
+            addExcludePackage(pn);
+        }
+    }
+
+    /**
+     * Add a package to be excluded from the javadoc run.
+     *
+     * @param pn the name of the package (wildcards are not permitted).
+     */
+    public void addExcludePackage(PackageName pn) {
+        excludePackageNames.addElement(pn);
+    }
+
+    /**
+     * Specify the file containing the overview to be included in the generated
+     * documentation.
+     *
+     * @param f the file containing the overview.
+     */
+    public void setOverview(File f) {
+        cmd.createArgument().setValue("-overview");
+        cmd.createArgument().setFile(f);
+    }
+
+    /**
+     * Indicate whether only public classes and members are to be included in
+     * the scope processed
+     *
+     * @param b true if scope is to be public.
+     */
+    public void setPublic(boolean b) {
+        addArgIf(b, "-public");
+    }
+
+    /**
+     * Indicate whether only protected and public classes and members are to
+     * be included in the scope processed
+     *
+     * @param b true if scope is to be protected.
+     */
+    public void setProtected(boolean b) {
+        addArgIf(b, "-protected");
+    }
+
+    /**
+     * Indicate whether only package, protected and public classes and
+     * members are to be included in the scope processed
+     *
+     * @param b true if scope is to be package level.
+     */
+    public void setPackage(boolean b) {
+        addArgIf(b, "-package");
+    }
+
+    /**
+     * Indicate whether all classes and
+     * members are to be included in the scope processed
+     *
+     * @param b true if scope is to be private level.
+     */
+    public void setPrivate(boolean b) {
+        addArgIf(b, "-private");
+    }
+
+    /**
+     * Set the scope to be processed. This is an alternative to the
+     * use of the setPublic, setPrivate, etc methods. It gives better build
+     * file control over what scope is processed.
+     *
+     * @param at the scope to be processed.
+     */
+    public void setAccess(AccessType at) {
+        cmd.createArgument().setValue("-" + at.getValue());
+    }
+
+    /**
+     * Set the class that starts the doclet used in generating the
+     * documentation.
+     *
+     * @param docletName the name of the doclet class.
+     */
+    public void setDoclet(String docletName) {
+        if (doclet == null) {
+            doclet = new DocletInfo();
+            doclet.setProject(getProject());
+        }
+        doclet.setName(docletName);
+    }
+
+    /**
+     * Set the classpath used to find the doclet class.
+     *
+     * @param docletPath the doclet classpath.
+     */
+    public void setDocletPath(Path docletPath) {
+        if (doclet == null) {
+            doclet = new DocletInfo();
+            doclet.setProject(getProject());
+        }
+        doclet.setPath(docletPath);
+    }
+
+    /**
+     * Set the classpath used to find the doclet class by reference.
+     *
+     * @param r the reference to the Path instance to use as the doclet
+     *        classpath.
+     */
+    public void setDocletPathRef(Reference r) {
+        if (doclet == null) {
+            doclet = new DocletInfo();
+            doclet.setProject(getProject());
+        }
+        doclet.createPath().setRefid(r);
+    }
+
+    /**
+     * Create a doclet to be used in the documentation generation.
+     *
+     * @return a new DocletInfo instance to be configured.
+     */
+    public DocletInfo createDoclet() {
+        doclet = new DocletInfo();
+        return doclet;
+    }
+
+    /**
+     * Add a taglet
+     *
+     * @param tagletInfo information about the taglet.
+     */
+    public void addTaglet(ExtensionInfo tagletInfo) {
+        tags.addElement(tagletInfo);
+    }
+
+    /**
+     * Indicate whether Javadoc should produce old style (JDK 1.1)
+     * documentation.
+     *
+     * This is not supported by JDK 1.1 and has been phased out in JDK 1.4
+     *
+     * @param b if true attempt to generate old style documentation.
+     */
+    public void setOld(boolean b) {
+        old = b;
+    }
+
+    /**
+     * Set the classpath to be used for this javadoc run.
+     *
+     * @param path an Ant Path object containing the compilation
+     *        classpath.
+     */
+    public void setClasspath(Path path) {
+        if (classpath == null) {
+            classpath = path;
+        } else {
+            classpath.append(path);
+        }
+    }
+
+    /**
+     * Create a Path to be configured with the classpath to use
+     *
+     * @return a new Path instance to be configured with the classpath.
+     */
+    public Path createClasspath() {
+        if (classpath == null) {
+            classpath = new Path(getProject());
+        }
+        return classpath.createPath();
+    }
+
+    /**
+     * Adds a reference to a CLASSPATH defined elsewhere.
+     *
+     * @param r the reference to an instance defining the classpath.
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+
+    /**
+     * Set the boot classpath to use.
+     *
+     * @param path the boot classpath.
+     */
+    public void setBootclasspath(Path path) {
+        if (bootclasspath == null) {
+            bootclasspath = path;
+        } else {
+            bootclasspath.append(path);
+        }
+    }
+
+    /**
+     * Create a Path to be configured with the boot classpath
+     *
+     * @return a new Path instance to be configured with the boot classpath.
+     */
+    public Path createBootclasspath() {
+        if (bootclasspath == null) {
+            bootclasspath = new Path(getProject());
+        }
+        return bootclasspath.createPath();
+    }
+
+    /**
+     * Adds a reference to a CLASSPATH defined elsewhere.
+     *
+     * @param r the reference to an instance defining the bootclasspath.
+     */
+    public void setBootClasspathRef(Reference r) {
+        createBootclasspath().setRefid(r);
+    }
+
+    /**
+     * Set the location of the extensions directories.
+     *
+     * @param path the string version of the path.
+     * @deprecated Use the {@link #setExtdirs(Path)} version.
+     */
+    public void setExtdirs(String path) {
+        cmd.createArgument().setValue("-extdirs");
+        cmd.createArgument().setValue(path);
+    }
+
+    /**
+     * Set the location of the extensions directories.
+     *
+     * @param path a path containing the extension directories.
+     */
+    public void setExtdirs(Path path) {
+        cmd.createArgument().setValue("-extdirs");
+        cmd.createArgument().setPath(path);
+    }
+
+    /**
+     * Run javadoc in verbose mode
+     *
+     * @param b true if operation is to be verbose.
+     */
+    public void setVerbose(boolean b) {
+        addArgIf(b, "-verbose");
+    }
+
+    /**
+     * Set the local to use in documentation generation.
+     *
+     * @param locale the locale to use.
+     */
+    public void setLocale(String locale) {
+        // createArgument(true) is necessary to make sure, -locale
+        // is the first argument (required in 1.3+).
+        cmd.createArgument(true).setValue(locale);
+        cmd.createArgument(true).setValue("-locale");
+    }
+
+    /**
+     * Set the encoding name of the source files,
+     *
+     * @param enc the name of the encoding for the source files.
+     */
+    public void setEncoding(String enc) {
+        cmd.createArgument().setValue("-encoding");
+        cmd.createArgument().setValue(enc);
+    }
+
+    /**
+     * Include the version tag in the generated documentation.
+     *
+     * @param b true if the version tag should be included.
+     */
+    public void setVersion(boolean b) {
+        this.version = b;
+    }
+
+    /**
+     * Generate the &quot;use&quot page for each package.
+     *
+     * @param b true if the use page should be generated.
+     */
+    public void setUse(boolean b) {
+        addArgIf(b, "-use");
+    }
+
+
+    /**
+     * Include the author tag in the generated documentation.
+     *
+     * @param b true if the author tag should be included.
+     */
+    public void setAuthor(boolean b) {
+        author = b;
+    }
+
+    /**
+     * Generate a split index
+     *
+     * @param b true if the index should be split into a file per letter.
+     */
+    public void setSplitindex(boolean b) {
+        addArgIf(b, "-splitindex");
+    }
+
+    /**
+     * Set the title to be placed in the HTML &lt;title&gt; tag of the
+     * generated documentation.
+     *
+     * @param title the window title to use.
+     */
+    public void setWindowtitle(String title) {
+        addArgIfNotEmpty("-windowtitle", title);
+    }
+
+    /**
+     * Set the title of the generated overview page.
+     *
+     * @param doctitle the Document title.
+     */
+    public void setDoctitle(String doctitle) {
+        Html h = new Html();
+        h.addText(doctitle);
+        addDoctitle(h);
+    }
+
+    /**
+     * Add a document title to use for the overview page.
+     *
+     * @param text the HTML element containing the document title.
+     */
+    public void addDoctitle(Html text) {
+        doctitle = text;
+    }
+
+    /**
+     * Set the header text to be placed at the top of each output file.
+     *
+     * @param header the header text
+     */
+    public void setHeader(String header) {
+        Html h = new Html();
+        h.addText(header);
+        addHeader(h);
+    }
+
+    /**
+     * Set the header text to be placed at the top of each output file.
+     *
+     * @param text the header text
+     */
+    public void addHeader(Html text) {
+        header = text;
+    }
+
+    /**
+     * Set the footer text to be placed at the bottom of each output file.
+     *
+     * @param footer the footer text.
+     */
+    public void setFooter(String footer) {
+        Html h = new Html();
+        h.addText(footer);
+        addFooter(h);
+    }
+
+    /**
+     * Set the footer text to be placed at the bottom of each output file.
+     *
+     * @param text the footer text.
+     */
+    public void addFooter(Html text) {
+        footer = text;
+    }
+
+    /**
+     * Set the text to be placed at the bottom of each output file.
+     *
+     * @param bottom the bottom text.
+     */
+    public void setBottom(String bottom) {
+        Html h = new Html();
+        h.addText(bottom);
+        addBottom(h);
+    }
+
+    /**
+     * Set the text to be placed at the bottom of each output file.
+     *
+     * @param text the bottom text.
+     */
+    public void addBottom(Html text) {
+        bottom = text;
+    }
+
+    /**
+     * Link to docs at "url" using package list at "url2"
+     * - separate the URLs by using a space character.
+     *
+     * @param src the offline link specification (url and package list)
+     */
+    public void setLinkoffline(String src) {
+        if (/*1.2+*/true) {
+            LinkArgument le = createLink();
+            le.setOffline(true);
+            String linkOfflineError = "The linkoffline attribute must include"
+                + " a URL and a package-list file location separated by a"
+                + " space";
+            if (src.trim().length() == 0) {
+                throw new BuildException(linkOfflineError);
+            }
+            StringTokenizer tok = new StringTokenizer(src, " ", false);
+            le.setHref(tok.nextToken());
+
+            if (!tok.hasMoreTokens()) {
+                throw new BuildException(linkOfflineError);
+            }
+            le.setPackagelistLoc(getProject().resolveFile(tok.nextToken()));
+        }
+    }
+
+    /**
+     * Group specified packages together in overview page.
+     *
+     * @param src the group packages - a command separated list of group specs,
+     *        each one being a group name and package specification separated
+     *        by a space.
+     */
+    public void setGroup(String src) {
+        group = src;
+    }
+
+    /**
+     * Create links to javadoc output at the given URL.
+     */
+    public void setLink(String src) {
+        createLink().setHref(src);
+    }
+
+    /**
+     * Control deprecation infromation
+     *
+     * @param b If true, do not include deprecated information.
+     */
+    public void setNodeprecated(boolean b) {
+        addArgIf(b, "-nodeprecated");
+    }
+
+    /**
+     * Control deprecated list generation
+     *
+     * @param b if true, do not generate deprecated list.
+     */
+    public void setNodeprecatedlist(boolean b) {
+        addArgIf(b, "-nodeprecatedlist");
+    }
+
+    /**
+     * Control class tree generation.
+     *
+     * @param b if true, do not generate class hierarchy.
+     */
+    public void setNotree(boolean b) {
+        addArgIf(b, "-notree");
+    }
+
+    /**
+     * Control generation of index.
+     *
+     * @param b if true, do not generate index.
+     */
+    public void setNoindex(boolean b) {
+        addArgIf(b, "-noindex");
+    }
+
+    /**
+     * Control generation of help link.
+     *
+     * @param b if true, do not generate help link
+     */
+    public void setNohelp(boolean b) {
+        addArgIf(b, "-nohelp");
+    }
+
+    /**
+     * Control generation of the navigation bar.
+     *
+     * @param b if true, do not generate navigation bar.
+     */
+    public void setNonavbar(boolean b) {
+        addArgIf(b, "-nonavbar");
+    }
+
+    /**
+     * Control warnings about serial tag.
+     *
+     * @param b if true, generate warning about the serial tag.
+     */
+    public void setSerialwarn(boolean b) {
+        addArgIf(b, "-serialwarn");
+    }
+
+    /**
+     * Specifies the CSS stylesheet file to use.
+     *
+     * @param f the file with the CSS to use.
+     */
+    public void setStylesheetfile(File f) {
+        cmd.createArgument().setValue("-stylesheetfile");
+        cmd.createArgument().setFile(f);
+    }
+
+    /**
+     * Specifies the HTML help file to use.
+     *
+     * @param f the file containing help content.
+     */
+    public void setHelpfile(File f) {
+        cmd.createArgument().setValue("-helpfile");
+        cmd.createArgument().setFile(f);
+    }
+
+    /**
+     * Output file encoding name.
+     *
+     * @param enc name of the encoding to use.
+     */
+    public void setDocencoding(String enc) {
+        cmd.createArgument().setValue("-docencoding");
+        cmd.createArgument().setValue(enc);
+    }
+
+    /**
+     * The name of a file containing the packages to process.
+     *
+     * @param src the file containing the package list.
+     */
+    public void setPackageList(String src) {
+        packageList = src;
+    }
+
+    /**
+     * Create link to javadoc output at the given URL.
+     *
+     * @return link argument to configure
+     */
+    public LinkArgument createLink() {
+        LinkArgument la = new LinkArgument();
+        links.addElement(la);
+        return la;
+    }
+
+    /**
+     * Represents a link triplet (href, whether link is offline, location of the
+     * package list if off line)
+     */
+    public class LinkArgument {
+        private String href;
+        private boolean offline = false;
+        private File packagelistLoc;
+        private boolean resolveLink = false;
+
+        public LinkArgument() {
+        }
+
+        public void setHref(String hr) {
+            href = hr;
+        }
+
+        public String getHref() {
+            return href;
+        }
+
+        public void setPackagelistLoc(File src) {
+            packagelistLoc = src;
+        }
+
+        public File getPackagelistLoc() {
+            return packagelistLoc;
+        }
+
+        public void setOffline(boolean offline) {
+            this.offline = offline;
+        }
+
+        public boolean isLinkOffline() {
+            return offline;
+        }
+
+        /**
+         * Sets whether Ant should resolve the link attribute relative
+         * to the current basedir.
+         * @param resolve a <code>boolean</code> value
+         */
+        public void setResolveLink(boolean resolve) {
+            this.resolveLink = resolve;
+        }
+
+        /**
+         * should Ant resolve the link attribute relative to the
+         * current basedir?
+         */
+        public boolean shouldResolveLink() {
+            return resolveLink;
+        }
+
+    }
+
+    /**
+     * Creates and adds a -tag argument. This is used to specify
+     * custom tags. This argument is only available for JavaDoc 1.4,
+     * and will generate a verbose message (and then be ignored)
+     * when run on Java versions below 1.4.
+     */
+    public TagArgument createTag() {
+        TagArgument ta = new TagArgument();
+        tags.addElement (ta);
+        return ta;
+    }
+
+    /**
+     * Scope element verbose names. (Defined here as fields
+     * cannot be static in inner classes.) The first letter
+     * from each element is used to build up the scope string.
+     */
+    static final String[] SCOPE_ELEMENTS = {
+        "overview", "packages", "types", "constructors",
+        "methods", "fields"
+    };
+
+    /**
+     * Class representing a -tag argument.
+     */
+    public class TagArgument extends FileSet {
+        /** Name of the tag. */
+        private String name = null;
+        /** Description of the tag to place in the JavaDocs. */
+        private String description = null;
+        /** Whether or not the tag is enabled. */
+        private boolean enabled = true;
+        /**
+         * Scope string of the tag. This will form the middle
+         * argument of the -tag parameter when the tag is enabled
+         * (with an X prepended for and is parsed from human-readable form.
+         */
+        private String scope = "a";
+
+        /** Sole constructor. */
+        public TagArgument () {
+        }
+
+        /**
+         * Sets the name of the tag.
+         *
+         * @param name The name of the tag.
+         *             Must not be <code>null</code> or empty.
+         */
+        public void setName (String name) {
+            this.name = name;
+        }
+
+        /**
+         * Sets the description of the tag. This is what appears in
+         * the JavaDoc.
+         *
+         * @param description The description of the tag.
+         *                    Must not be <code>null</code> or empty.
+         */
+        public void setDescription (String description) {
+            this.description = description;
+        }
+
+        /**
+         * Sets the scope of the tag. This is in comma-separated
+         * form, with each element being one of "all" (the default),
+         * "overview", "packages", "types", "constructors", "methods",
+         * "fields". The elements are treated in a case-insensitive
+         * manner.
+         *
+         * @param verboseScope The scope of the tag.
+         *                     Must not be <code>null</code>,
+         *                     should not be empty.
+         *
+         * @exception BuildException if all is specified along with
+         * other elements, if any elements are repeated, if no
+         * elements are specified, or if any unrecognised elements are
+         * specified.
+         */
+        public void setScope (String verboseScope) throws BuildException {
+            verboseScope = verboseScope.toLowerCase(Locale.US);
+
+            boolean[] elements = new boolean[SCOPE_ELEMENTS.length];
+
+            boolean gotAll = false;
+            boolean gotNotAll = false;
+
+            // Go through the tokens one at a time, updating the
+            // elements array and issuing warnings where appropriate.
+            StringTokenizer tok = new StringTokenizer (verboseScope, ",");
+            while (tok.hasMoreTokens()) {
+                String next = tok.nextToken().trim();
+                if (next.equals("all")) {
+                    if (gotAll) {
+                        getProject().log ("Repeated tag scope element: all",
+                                          Project.MSG_VERBOSE);
+                    }
+                    gotAll = true;
+                } else {
+                    int i;
+                    for (i = 0; i < SCOPE_ELEMENTS.length; i++) {
+                        if (next.equals (SCOPE_ELEMENTS[i])) {
+                            break;
+                        }
+                    }
+                    if (i == SCOPE_ELEMENTS.length) {
+                        throw new BuildException ("Unrecognised scope element: "
+                                                  + next);
+                    } else {
+                        if (elements[i]) {
+                            getProject().log ("Repeated tag scope element: "
+                                              + next, Project.MSG_VERBOSE);
+                        }
+                        elements[i] = true;
+                        gotNotAll = true;
+                    }
+                }
+            }
+
+            if (gotNotAll && gotAll) {
+                throw new BuildException ("Mixture of \"all\" and other scope "
+                                          + "elements in tag parameter.");
+            }
+            if (!gotNotAll && !gotAll) {
+                throw new BuildException ("No scope elements specified in tag "
+                                          + "parameter.");
+            }
+            if (gotAll) {
+                this.scope = "a";
+            } else {
+                StringBuffer buff = new StringBuffer (elements.length);
+                for (int i = 0; i < elements.length; i++) {
+                    if (elements[i]) {
+                        buff.append (SCOPE_ELEMENTS[i].charAt(0));
+                    }
+                }
+                this.scope = buff.toString();
+            }
+        }
+
+        /**
+         * Sets whether or not the tag is enabled.
+         *
+         * @param enabled Whether or not this tag is enabled.
+         */
+        public void setEnabled (boolean enabled) {
+            this.enabled = enabled;
+        }
+
+        /**
+         * Returns the -tag parameter this argument represented.
+         *
+         * @exception BuildException if either the name or description
+         *                           is <code>null</code> or empty.
+         */
+        public String getParameter () throws BuildException {
+            if (name == null || name.equals("")) {
+                throw new BuildException ("No name specified for custom tag.");
+            }
+            if (description != null) {
+                return name + ":" + (enabled ? "" : "X")
+                    + scope + ":" + description;
+            } else {
+                return name;
+            }
+        }
+    }
+
+    /**
+     * Separates packages on the overview page into whatever
+     * groups you specify, one group per table.
+     */
+    public GroupArgument createGroup() {
+        GroupArgument ga = new GroupArgument();
+        groups.addElement(ga);
+        return ga;
+    }
+
+    public class GroupArgument {
+        private Html title;
+        private Vector packages = new Vector();
+
+        public GroupArgument() {
+        }
+
+        public void setTitle(String src) {
+            Html h = new Html();
+            h.addText(src);
+            addTitle(h);
+        }
+        public void addTitle(Html text) {
+            title = text;
+        }
+
+        public String getTitle() {
+            return title != null ? title.getText() : null;
+        }
+
+        public void setPackages(String src) {
+            StringTokenizer tok = new StringTokenizer(src, ",");
+            while (tok.hasMoreTokens()) {
+                String p = tok.nextToken();
+                PackageName pn = new PackageName();
+                pn.setName(p);
+                addPackage(pn);
+            }
+        }
+        public void addPackage(PackageName pn) {
+            packages.addElement(pn);
+        }
+
+        public String getPackages() {
+            StringBuffer p = new StringBuffer();
+            for (int i = 0; i < packages.size(); i++) {
+                if (i > 0) {
+                    p.append(":");
+                }
+                p.append(packages.elementAt(i).toString());
+            }
+            return p.toString();
+        }
+    }
+
+    /**
+     * Charset for cross-platform viewing of generated documentation.
+     */
+    public void setCharset(String src) {
+        this.addArgIfNotEmpty("-charset", src);
+    }
+
+    /**
+     * Should the build process fail if javadoc fails (as indicated by
+     * a non zero return code)?
+     *
+     * <p>Default is false.</p>
+     */
+    public void setFailonerror(boolean b) {
+        failOnError = b;
+    }
+
+    /**
+     * Enables the -source switch, will be ignored if javadoc is not
+     * the 1.4 version.
+     *
+     * @since Ant 1.5
+     */
+    public void setSource(String source) {
+        this.source = source;
+    }
+    
+    /**
+     * Sets the actual executable command to invoke, instead of the binary
+     * <code>javadoc</code> found in Ant's JDK.
+     * @since Ant 1.6.3
+     */
+    public void setExecutable(String executable) {
+        this.executable = executable;
+    }
+
+    /**
+     * Adds a packageset.
+     *
+     * <p>All included directories will be translated into package
+     * names be converting the directory separator into dots.</p>
+     *
+     * @since 1.5
+     */
+    public void addPackageset(DirSet packageSet) {
+        packageSets.addElement(packageSet);
+    }
+
+    /**
+     * Adds a fileset.
+     *
+     * <p>All included files will be added as sourcefiles.  The task
+     * will automatically add
+     * <code>includes=&quot;**&#47;*.java&quot;</code> to the
+     * fileset.</p>
+     *
+     * @since 1.5
+     */
+    public void addFileset(FileSet fs) {
+        fileSets.addElement(fs);
+    }
+
+    /**
+     * Enables the -linksource switch, will be ignored if javadoc is not
+     * the 1.4 version. Default is false
+     *
+     * @since Ant 1.6
+     */
+    public void setLinksource(boolean b) {
+        this.linksource = b;
+    }
+
+    /**
+     * Enables the -linksource switch, will be ignored if javadoc is not
+     * the 1.4 version. Default is false
+     *
+     * @since Ant 1.6
+     */
+    public void setBreakiterator(boolean b) {
+        this.breakiterator = b;
+    }
+
+    /**
+     * Enables the -noqualifier switch, will be ignored if javadoc is not
+     * the 1.4 version.
+     *
+     * @since Ant 1.6
+     */
+    public void setNoqualifier(String noqualifier) {
+        this.noqualifier = noqualifier;
+    }
+
+    /**
+     * If set to true, Ant will also accept packages that only hold
+     * package.html files but no Java sources.
+     * @since Ant 1.6.3
+     */
+    public void setIncludeNoSourcePackages(boolean b) {
+        this.includeNoSourcePackages = b;
+    }
+
+    public void execute() throws BuildException {
+        if ("javadoc2".equals(getTaskType())) {
+            log("!! javadoc2 is deprecated. Use javadoc instead. !!");
+        }
+
+        // Whether *this VM* is 1.4+ (but also check executable != null).
+        boolean javadoc4 =
+            !JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_2) &&
+            !JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_3);
+
+        Vector packagesToDoc = new Vector();
+        Path sourceDirs = new Path(getProject());
+
+        if (packageList != null && sourcePath == null) {
+            String msg = "sourcePath attribute must be set when "
+                + "specifying packagelist.";
+            throw new BuildException(msg);
+        }
+
+        if (sourcePath != null) {
+            sourceDirs.addExisting(sourcePath);
+        }
+
+        parsePackages(packagesToDoc, sourceDirs);
+
+        if (packagesToDoc.size() != 0 && sourceDirs.size() == 0) {
+            String msg = "sourcePath attribute must be set when "
+                + "specifying package names.";
+            throw new BuildException(msg);
+        }
+
+        Vector sourceFilesToDoc = (Vector) sourceFiles.clone();
+        addFileSets(sourceFilesToDoc);
+
+        if (packageList == null && packagesToDoc.size() == 0
+            && sourceFilesToDoc.size() == 0) {
+            throw new BuildException("No source files and no packages have "
+                                     + "been specified.");
+        }
+
+        log("Generating Javadoc", Project.MSG_INFO);
+
+        Commandline toExecute = (Commandline) cmd.clone();
+        if (executable != null) {
+            toExecute.setExecutable(executable);
+        } else {
+            toExecute.setExecutable(JavaEnvUtils.getJdkExecutable("javadoc"));
+        }
+
+        // ------------------------------------------ general javadoc arguments
+        if (doctitle != null) {
+            toExecute.createArgument().setValue("-doctitle");
+            toExecute.createArgument().setValue(expand(doctitle.getText()));
+        }
+        if (header != null) {
+            toExecute.createArgument().setValue("-header");
+            toExecute.createArgument().setValue(expand(header.getText()));
+        }
+        if (footer != null) {
+            toExecute.createArgument().setValue("-footer");
+            toExecute.createArgument().setValue(expand(footer.getText()));
+        }
+        if (bottom != null) {
+            toExecute.createArgument().setValue("-bottom");
+            toExecute.createArgument().setValue(expand(bottom.getText()));
+        }
+
+        if (classpath == null) {
+            classpath = (new Path(getProject())).concatSystemClasspath("last");
+        } else {
+            classpath = classpath.concatSystemClasspath("ignore");
+        }
+
+        if (/*1.2+*/true) {
+            if (classpath.size() > 0) {
+                toExecute.createArgument().setValue("-classpath");
+                toExecute.createArgument().setPath(classpath);
+            }
+            if (sourceDirs.size() > 0) {
+                toExecute.createArgument().setValue("-sourcepath");
+                toExecute.createArgument().setPath(sourceDirs);
+            }
+        }
+
+        if (version && doclet == null) {
+            toExecute.createArgument().setValue("-version");
+        }
+        if (author && doclet == null) {
+            toExecute.createArgument().setValue("-author");
+        }
+
+        if (doclet == null) {
+            if (destDir == null) {
+                String msg = "destDir attribute must be set!";
+                throw new BuildException(msg);
+            }
+        }
+
+        // ---------------------------- javadoc2 arguments for default doclet
+
+        if (/*1.2+*/true) {
+            if (doclet != null) {
+                if (doclet.getName() == null) {
+                    throw new BuildException("The doclet name must be "
+                                             + "specified.", getLocation());
+                } else {
+                    toExecute.createArgument().setValue("-doclet");
+                    toExecute.createArgument().setValue(doclet.getName());
+                    if (doclet.getPath() != null) {
+                        Path docletPath
+                            = doclet.getPath().concatSystemClasspath("ignore");
+                        if (docletPath.size() != 0) {
+                            toExecute.createArgument().setValue("-docletpath");
+                            toExecute.createArgument().setPath(docletPath);
+                        }
+                    }
+                    for (Enumeration e = doclet.getParams();
+                         e.hasMoreElements();) {
+                        DocletParam param = (DocletParam) e.nextElement();
+                        if (param.getName() == null) {
+                            throw new BuildException("Doclet parameters must "
+                                                     + "have a name");
+                        }
+
+                        toExecute.createArgument().setValue(param.getName());
+                        if (param.getValue() != null) {
+                            toExecute.createArgument()
+                                .setValue(param.getValue());
+                        }
+                    }
+                }
+            }
+            if (bootclasspath != null && bootclasspath.size() > 0) {
+                toExecute.createArgument().setValue("-bootclasspath");
+                toExecute.createArgument().setPath(bootclasspath);
+            }
+
+            // add the links arguments
+            if (links.size() != 0) {
+                for (Enumeration e = links.elements(); e.hasMoreElements();) {
+                    LinkArgument la = (LinkArgument) e.nextElement();
+
+                    if (la.getHref() == null || la.getHref().length() == 0) {
+                        log("No href was given for the link - skipping",
+                            Project.MSG_VERBOSE);
+                        continue;
+                    }
+                    String link = null;
+                    if (la.shouldResolveLink()) {
+                        File hrefAsFile = 
+                            getProject().resolveFile(la.getHref());
+                        if (hrefAsFile.exists()) {
+                            try {
+                                link = fileUtils.getFileURL(hrefAsFile)
+                                    .toExternalForm();
+                            } catch (MalformedURLException ex) {
+                                // should be impossible
+                                log("Warning: link location was invalid " 
+                                    + hrefAsFile, Project.MSG_WARN);
+                            }
+                        }
+                    }
+                    if (link == null) {
+                        // is the href a valid URL
+                        try {
+                            URL base = new URL("file://.");
+                            new URL(base, la.getHref());
+                            link = la.getHref();
+                        } catch (MalformedURLException mue) {
+                            // ok - just skip
+                            log("Link href \"" + la.getHref()
+                                + "\" is not a valid url - skipping link",
+                                Project.MSG_WARN);
+                            continue;
+                        }
+                    }
+
+                    if (la.isLinkOffline()) {
+                        File packageListLocation = la.getPackagelistLoc();
+                        if (packageListLocation == null) {
+                            throw new BuildException("The package list "
+                                                     + " location for link " + la.getHref()
+                                                     + " must be provided because the link is "
+                                                     + "offline");
+                        }
+                        File packageListFile =
+                            new File(packageListLocation, "package-list");
+                        if (packageListFile.exists()) {
+                            try {
+                                String packageListURL =
+                                    fileUtils.getFileURL(packageListLocation)
+                                    .toExternalForm();
+                                toExecute.createArgument()
+                                    .setValue("-linkoffline");
+                                toExecute.createArgument()
+                                    .setValue(link);
+                                toExecute.createArgument()
+                                    .setValue(packageListURL);
+                            } catch (MalformedURLException ex) {
+                                log("Warning: Package list location was "
+                                    + "invalid " + packageListLocation,
+                                    Project.MSG_WARN);
+                            }
+                        } else {
+                            log("Warning: No package list was found at "
+                                + packageListLocation, Project.MSG_VERBOSE);
+                        }
+                    } else {
+                        toExecute.createArgument().setValue("-link");
+                        toExecute.createArgument().setValue(link);
+                    }
+                }
+            }
+
+            // add the single group arguments
+            // Javadoc 1.2 rules:
+            //   Multiple -group args allowed.
+            //   Each arg includes 3 strings: -group [name] [packagelist].
+            //   Elements in [packagelist] are colon-delimited.
+            //   An element in [packagelist] may end with the * wildcard.
+
+            // Ant javadoc task rules for group attribute:
+            //   Args are comma-delimited.
+            //   Each arg is 2 space-delimited strings.
+            //   E.g., group="XSLT_Packages org.apache.xalan.xslt*,
+            //                XPath_Packages org.apache.xalan.xpath*"
+            if (group != null) {
+                StringTokenizer tok = new StringTokenizer(group, ",", false);
+                while (tok.hasMoreTokens()) {
+                    String grp = tok.nextToken().trim();
+                    int space = grp.indexOf(" ");
+                    if (space > 0) {
+                        String name = grp.substring(0, space);
+                        String pkgList = grp.substring(space + 1);
+                        toExecute.createArgument().setValue("-group");
+                        toExecute.createArgument().setValue(name);
+                        toExecute.createArgument().setValue(pkgList);
+                    }
+                }
+            }
+
+            // add the group arguments
+            if (groups.size() != 0) {
+                for (Enumeration e = groups.elements(); e.hasMoreElements();) {
+                    GroupArgument ga = (GroupArgument) e.nextElement();
+                    String title = ga.getTitle();
+                    String packages = ga.getPackages();
+                    if (title == null || packages == null) {
+                        throw new BuildException("The title and packages must "
+                                                 + "be specified for group "
+                                                 + "elements.");
+                    }
+                    toExecute.createArgument().setValue("-group");
+                    toExecute.createArgument().setValue(expand(title));
+                    toExecute.createArgument().setValue(packages);
+                }
+            }
+
+            // JavaDoc 1.4 parameters
+            if (javadoc4 || executable != null) {
+                for (Enumeration e = tags.elements(); e.hasMoreElements();) {
+                    Object element = e.nextElement();
+                    if (element instanceof TagArgument) {
+                        TagArgument ta = (TagArgument) element;
+                        File tagDir = ta.getDir(getProject());
+                        if (tagDir == null) {
+                            // The tag element is not used as a fileset,
+                            // but specifies the tag directly.
+                            toExecute.createArgument().setValue ("-tag");
+                            toExecute.createArgument().setValue (ta.getParameter());
+                        } else {
+                            // The tag element is used as a fileset. Parse all the files and
+                            // create -tag arguments.
+                            DirectoryScanner tagDefScanner = ta.getDirectoryScanner(getProject());
+                            String[] files = tagDefScanner.getIncludedFiles();
+                            for (int i = 0; i < files.length; i++) {
+                                File tagDefFile = new File(tagDir, files[i]);
+                                try {
+                                    BufferedReader in
+                                        = new BufferedReader(new FileReader(tagDefFile));
+                                    String line = null;
+                                    while ((line = in.readLine()) != null) {
+                                        toExecute.createArgument().setValue ("-tag");
+                                        toExecute.createArgument().setValue (line);
+                                    }
+                                    in.close();
+                                } catch (IOException ioe) {
+                                    throw new BuildException("Couldn't read "
+                                        + " tag file from "
+                                        + tagDefFile.getAbsolutePath(), ioe);
+                                }
+                            }
+                        }
+                    } else {
+                        ExtensionInfo tagletInfo = (ExtensionInfo) element;
+                        toExecute.createArgument().setValue("-taglet");
+                        toExecute.createArgument().setValue(tagletInfo
+                                                            .getName());
+                        if (tagletInfo.getPath() != null) {
+                            Path tagletPath = tagletInfo.getPath()
+                                .concatSystemClasspath("ignore");
+                            if (tagletPath.size() != 0) {
+                                toExecute.createArgument()
+                                    .setValue("-tagletpath");
+                                toExecute.createArgument().setPath(tagletPath);
+                            }
+                        }
+                    }
+                }
+
+                if (source != null) {
+                    toExecute.createArgument().setValue("-source");
+                    toExecute.createArgument().setValue(source);
+                }
+
+                if (linksource && doclet == null) {
+                    toExecute.createArgument().setValue("-linksource");
+                }
+                if (breakiterator && doclet == null) {
+                    toExecute.createArgument().setValue("-breakiterator");
+                }
+                if (noqualifier != null && doclet == null) {
+                    toExecute.createArgument().setValue("-noqualifier");
+                    toExecute.createArgument().setValue(noqualifier);
+                }
+            } else {
+                // Not 1.4+.
+                if (!tags.isEmpty()) {
+                    log("-tag and -taglet options not supported on Javadoc < 1.4",
+                        Project.MSG_VERBOSE);
+                }
+                if (source != null) {
+                    log("-source option not supported on JavaDoc < 1.4",
+                        Project.MSG_VERBOSE);
+                }
+                if (linksource) {
+                    log("-linksource option not supported on JavaDoc < 1.4",
+                        Project.MSG_VERBOSE);
+                }
+                if (breakiterator) {
+                    log("-breakiterator option not supported on JavaDoc < 1.4",
+                        Project.MSG_VERBOSE);
+                }
+                if (noqualifier != null) {
+                    log("-noqualifier option not supported on JavaDoc < 1.4",
+                        Project.MSG_VERBOSE);
+                }
+            }
+            // Javadoc 1.2/1.3 parameters:
+            if (!javadoc4 || executable != null) {
+                if (old) {
+                    toExecute.createArgument().setValue("-1.1");
+                }
+            } else {
+                if (old) {
+                    log("Javadoc 1.4 doesn't support the -1.1 switch anymore",
+                        Project.MSG_WARN);
+                }
+            }
+
+        }
+
+        File tmpList = null;
+        PrintWriter srcListWriter = null;
+        try {
+
+            /**
+             * Write sourcefiles and package names to a temporary file
+             * if requested.
+             */
+            if (useExternalFile) {
+                if (tmpList == null) {
+                    tmpList = fileUtils.createTempFile("javadoc", "", null);
+                    tmpList.deleteOnExit();
+                    toExecute.createArgument()
+                        .setValue("@" + tmpList.getAbsolutePath());
+                }
+                srcListWriter = new PrintWriter(
+                                                new FileWriter(tmpList.getAbsolutePath(),
+                                                               true));
+            }
+
+            Enumeration e = packagesToDoc.elements();
+            while (e.hasMoreElements()) {
+                String packageName = (String) e.nextElement();
+                if (useExternalFile) {
+                    srcListWriter.println(packageName);
+                } else {
+                    toExecute.createArgument().setValue(packageName);
+                }
+            }
+
+            e = sourceFilesToDoc.elements();
+            while (e.hasMoreElements()) {
+                SourceFile sf = (SourceFile) e.nextElement();
+                String sourceFileName = sf.getFile().getAbsolutePath();
+                if (useExternalFile) {
+                    if (javadoc4 && sourceFileName.indexOf(" ") > -1) {
+                        String name = sourceFileName;
+                        if (File.separatorChar == '\\') {
+                            name = sourceFileName.replace(File.separatorChar, '/');
+                        }
+                        srcListWriter.println("\"" + name + "\"");
+                    } else {
+                        srcListWriter.println(sourceFileName);
+                    }
+                } else {
+                    toExecute.createArgument().setValue(sourceFileName);
+                }
+            }
+
+        } catch (IOException e) {
+            tmpList.delete();
+            throw new BuildException("Error creating temporary file",
+                                     e, getLocation());
+        } finally {
+            if (srcListWriter != null) {
+                srcListWriter.close();
+            }
+        }
+
+        if (packageList != null) {
+            toExecute.createArgument().setValue("@" + packageList);
+        }
+        log(toExecute.describeCommand(), Project.MSG_VERBOSE);
+
+        log("Javadoc execution", Project.MSG_INFO);
+
+        JavadocOutputStream out = new JavadocOutputStream(Project.MSG_INFO);
+        JavadocOutputStream err = new JavadocOutputStream(Project.MSG_WARN);
+        Execute exe = new Execute(new PumpStreamHandler(out, err));
+        exe.setAntRun(getProject());
+
+        /*
+         * No reason to change the working directory as all filenames and
+         * path components have been resolved already.
+         *
+         * Avoid problems with command line length in some environments.
+         */
+        exe.setWorkingDirectory(null);
+        try {
+            exe.setCommandline(toExecute.getCommandline());
+            int ret = exe.execute();
+            if (ret != 0 && failOnError) {
+                throw new BuildException("Javadoc returned " + ret, getLocation());
+            }
+        } catch (IOException e) {
+            throw new BuildException("Javadoc failed: " + e, e, getLocation());
+        } finally {
+            if (tmpList != null) {
+                tmpList.delete();
+                tmpList = null;
+            }
+
+            out.logFlush();
+            err.logFlush();
+            try {
+                out.close();
+                err.close();
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+    }
+
+    /**
+     * Add the files matched by the nested filesets to the Vector as
+     * SourceFile instances.
+     *
+     * @since 1.5
+     */
+    private void addFileSets(Vector sf) {
+        Enumeration e = fileSets.elements();
+        while (e.hasMoreElements()) {
+            FileSet fs = (FileSet) e.nextElement();
+            if (!fs.hasPatterns() && !fs.hasSelectors()) {
+                fs = (FileSet) fs.clone();
+                fs.createInclude().setName("**/*.java");
+                if (includeNoSourcePackages) {
+                    fs.createInclude().setName("**/package.html");
+                }
+            }
+            File baseDir = fs.getDir(getProject());
+            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+            String[] files = ds.getIncludedFiles();
+            for (int i = 0; i < files.length; i++) {
+                sf.addElement(new SourceFile(new File(baseDir, files[i])));
+            }
+        }
+    }
+
+    /**
+     * Add the directories matched by the nested dirsets to the Vector
+     * and the base directories of the dirsets to the Path.  It also
+     * handles the packages and excludepackages attributes and
+     * elements.
+     *
+     * @since 1.5
+     */
+    private void parsePackages(Vector pn, Path sp) {
+        Vector addedPackages = new Vector();
+        Vector dirSets = (Vector) packageSets.clone();
+
+        // for each sourcePath entry, add a directoryset with includes
+        // taken from packagenames attribute and nested package
+        // elements and excludes taken from excludepackages attribute
+        // and nested excludepackage elements
+        if (sourcePath != null && packageNames.size() > 0) {
+            PatternSet ps = new PatternSet();
+            Enumeration e = packageNames.elements();
+            while (e.hasMoreElements()) {
+                PackageName p = (PackageName) e.nextElement();
+                String pkg = p.getName().replace('.', '/');
+                if (pkg.endsWith("*")) {
+                    pkg += "*";
+                }
+                ps.createInclude().setName(pkg);
+            }
+
+            e = excludePackageNames.elements();
+            while (e.hasMoreElements()) {
+                PackageName p = (PackageName) e.nextElement();
+                String pkg = p.getName().replace('.', '/');
+                if (pkg.endsWith("*")) {
+                    pkg += "*";
+                }
+                ps.createExclude().setName(pkg);
+            }
+
+
+            String[] pathElements = sourcePath.list();
+            for (int i = 0; i < pathElements.length; i++) {
+                DirSet ds = new DirSet();
+                ds.setDefaultexcludes(useDefaultExcludes);
+                ds.setDir(new File(pathElements[i]));
+                ds.createPatternSet().addConfiguredPatternset(ps);
+                dirSets.addElement(ds);
+            }
+        }
+
+        Enumeration e = dirSets.elements();
+        while (e.hasMoreElements()) {
+            DirSet ds = (DirSet) e.nextElement();
+            File baseDir = ds.getDir(getProject());
+            log("scanning " + baseDir + " for packages.", Project.MSG_DEBUG);
+            DirectoryScanner dsc = ds.getDirectoryScanner(getProject());
+            String[] dirs = dsc.getIncludedDirectories();
+            boolean containsPackages = false;
+            for (int i = 0; i < dirs.length; i++) {
+                // are there any java files in this directory?
+                File pd = new File(baseDir, dirs[i]);
+                String[] files = pd.list(new FilenameFilter () {
+                        public boolean accept(File dir1, String name) {
+                            return name.endsWith(".java")
+                                || (includeNoSourcePackages 
+                                    && name.equals("package.html"));
+                        }
+                    });
+
+                if (files.length > 0) {
+                    containsPackages = true;
+                    String packageName =
+                        dirs[i].replace(File.separatorChar, '.');
+                    if (!addedPackages.contains(packageName)) {
+                        addedPackages.addElement(packageName);
+                        pn.addElement(packageName);
+                    }
+                }
+            }
+            if (containsPackages) {
+                // We don't need to care for duplicates here,
+                // Path.list does it for us.
+                sp.createPathElement().setLocation(baseDir);
+            } else {
+                log(baseDir + " doesn\'t contain any packages, dropping it.",
+                    Project.MSG_VERBOSE);
+            }
+        }
+    }
+
+    private class JavadocOutputStream extends LogOutputStream {
+        JavadocOutputStream(int level) {
+            super(Javadoc.this, level);
+        }
+
+        //
+        // Override the logging of output in order to filter out Generating
+        // messages.  Generating messages are set to a priority of VERBOSE
+        // unless they appear after what could be an informational message.
+        //
+        private String queuedLine = null;
+        protected void processLine(String line, int messageLevel) {
+            if (messageLevel == Project.MSG_INFO
+                && line.startsWith("Generating ")) {
+                if (queuedLine != null) {
+                    super.processLine(queuedLine, Project.MSG_VERBOSE);
+                }
+                queuedLine = line;
+            } else {
+                if (queuedLine != null) {
+                    if (line.startsWith("Building ")) {
+                        super.processLine(queuedLine, Project.MSG_VERBOSE);
+                    } else {
+                        super.processLine(queuedLine, Project.MSG_INFO);
+                    }
+                    queuedLine = null;
+                }
+                super.processLine(line, messageLevel);
+            }
+        }
+
+
+        protected void logFlush() {
+            if (queuedLine != null) {
+                super.processLine(queuedLine, Project.MSG_VERBOSE);
+                queuedLine = null;
+            }
+        }
+    }
+
+    /**
+     * Convenience method to expand properties.
+     */
+    protected String expand(String content) {
+        return getProject().replaceProperties(content);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Jikes.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Jikes.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Jikes.java	(revision 16139)
@@ -0,0 +1,129 @@
+/*
+ * Copyright  2000,2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Random;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Encapsulates a Jikes compiler, by directly executing an external
+ * process.
+ *
+ * <p><strong>As of Ant 1.2, this class is considered to be dead code
+ * by the Ant developers and is unmaintained.  Don't use
+ * it.</strong></p>
+ *
+ * @deprecated merged into the class Javac.
+ */
+public class Jikes {
+
+    protected JikesOutputParser jop;
+    protected String command;
+    protected Project project;
+
+    /**
+     * Constructs a new Jikes object.
+     * @param jop - Parser to send jike's output to
+     * @param command - name of jikes executable
+     */
+    protected Jikes(JikesOutputParser jop, String command, Project project) {
+        super();
+
+        System.err.println("As of Ant 1.2 released in October 2000, "
+            + "the Jikes class");
+        System.err.println("is considered to be dead code by the Ant "
+            + "developers and is unmaintained.");
+        System.err.println("Don\'t use it!");
+
+        this.jop = jop;
+        this.command = command;
+        this.project = project;
+    }
+
+    /**
+     * Do the compile with the specified arguments.
+     * @param args - arguments to pass to process on command line
+     */
+    protected void compile(String[] args) {
+        String[] commandArray = null;
+        File tmpFile = null;
+
+        try {
+            String myos = System.getProperty("os.name");
+
+            // Windows has a 32k limit on total arg size, so
+            // create a temporary file to store all the arguments
+
+            // There have been reports that 300 files could be compiled
+            // so 250 is a conservative approach
+            if (myos.toLowerCase().indexOf("windows") >= 0
+                && args.length > 250) {
+                PrintWriter out = null;
+                try {
+                    String tempFileName = "jikes"
+                        + (new Random(System.currentTimeMillis())).nextLong();
+                    tmpFile = new File(tempFileName);
+                    out = new PrintWriter(new FileWriter(tmpFile));
+                    for (int i = 0; i < args.length; i++) {
+                        out.println(args[i]);
+                    }
+                    out.flush();
+                    commandArray = new String[] {command,
+                                               "@" + tmpFile.getAbsolutePath()};
+                } catch (IOException e) {
+                    throw new BuildException("Error creating temporary file",
+                                             e);
+                } finally {
+                    if (out != null) {
+                        try {
+                            out.close();
+                        } catch (Throwable t) {
+                            // ignore
+                        }
+                    }
+                }
+            } else {
+                commandArray = new String[args.length + 1];
+                commandArray[0] = command;
+                System.arraycopy(args, 0, commandArray, 1, args.length);
+            }
+
+            // We assume, that everything jikes writes goes to
+            // standard output, not to standard error. The option
+            // -Xstdout that is given to Jikes in Javac.doJikesCompile()
+            // should guarantee this. At least I hope so. :)
+            try {
+                Execute exe = new Execute(jop);
+                exe.setAntRun(project);
+                exe.setWorkingDirectory(project.getBaseDir());
+                exe.setCommandline(commandArray);
+                exe.execute();
+            } catch (IOException e) {
+                throw new BuildException("Error running Jikes compiler", e);
+            }
+        } finally {
+            if (tmpFile != null) {
+                tmpFile.delete();
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/JikesOutputParser.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/JikesOutputParser.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/JikesOutputParser.java	(revision 16139)
@@ -0,0 +1,171 @@
+/*
+ * Copyright  2000,2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Parses output from jikes and
+ * passes errors and warnings
+ * into the right logging channels of Project.
+ *
+ * <p><strong>As of Ant 1.2, this class is considered to be dead code
+ * by the Ant developers and is unmaintained.  Don't use
+ * it.</strong></p>
+ *
+ * @deprecated use Jikes' exit value to detect compilation failure.
+ */
+public class JikesOutputParser implements ExecuteStreamHandler {
+    protected Task task;
+    protected boolean errorFlag = false; // no errors so far
+    protected int errors;
+    protected int warnings;
+    protected boolean error = false;
+    protected boolean emacsMode;
+
+    protected BufferedReader br;
+
+    /**
+     * Ignore.
+     */
+    public void setProcessInputStream(OutputStream os) {
+    }
+
+    /**
+     * Ignore.
+     */
+    public void setProcessErrorStream(InputStream is) {
+    }
+
+    /**
+     * Set the inputstream
+     */
+    public void setProcessOutputStream(InputStream is) throws IOException {
+        br = new BufferedReader(new InputStreamReader(is));
+    }
+
+    /**
+     * Invokes parseOutput.
+     */
+    public void start() throws IOException {
+        parseOutput(br);
+    }
+
+    /**
+     * Ignore.
+     */
+    public void stop() {
+    }
+
+    /**
+     * Construct a new Parser object
+     * @param task - task in which context we are called
+     */
+    protected JikesOutputParser(Task task, boolean emacsMode) {
+        super();
+
+        System.err.println("As of Ant 1.2 released in October 2000, the "
+            + "JikesOutputParser class");
+        System.err.println("is considered to be dead code by the Ant "
+            + "developers and is unmaintained.");
+        System.err.println("Don\'t use it!");
+
+        this.task = task;
+        this.emacsMode = emacsMode;
+    }
+
+    /**
+     * Parse the output of a jikes compiler
+     * @param reader - Reader used to read jikes's output
+     */
+    protected void parseOutput(BufferedReader reader) throws IOException {
+       if (emacsMode) {
+           parseEmacsOutput(reader);
+       } else {
+           parseStandardOutput(reader);
+       }
+    }
+
+    private void parseStandardOutput(BufferedReader reader) throws IOException {
+        String line;
+        String lower;
+        // We assume, that every output, jikes does, stands for an error/warning
+        // XXX
+        // Is this correct?
+
+        // TODO:
+        // A warning line, that shows code, which contains a variable
+        // error will cause some trouble. The parser should definitely
+        // be much better.
+
+        while ((line = reader.readLine()) != null) {
+            lower = line.toLowerCase();
+            if (line.trim().equals("")) {
+                continue;
+            }
+            if (lower.indexOf("error") != -1) {
+                setError(true);
+            } else if (lower.indexOf("warning") != -1) {
+                setError(false);
+                   } else {
+                // If we don't know the type of the line
+                // and we are in emacs mode, it will be
+                // an error, because in this mode, jikes won't
+                // always print "error", but sometimes other
+                // keywords like "Syntax". We should look for
+                // all those keywords.
+                if (emacsMode) {
+                    setError(true);
+                }
+            }
+            log(line);
+        }
+    }
+
+    private void parseEmacsOutput(BufferedReader reader) throws IOException {
+       // This may change, if we add advanced parsing capabilities.
+       parseStandardOutput(reader);
+    }
+
+    private void setError(boolean err) {
+        error = err;
+        if (error) {
+            errorFlag = true;
+        }
+    }
+
+    private void log(String line) {
+       if (!emacsMode) {
+           task.log("", (error ? Project.MSG_ERR : Project.MSG_WARN));
+       }
+       task.log(line, (error ? Project.MSG_ERR : Project.MSG_WARN));
+    }
+
+    /**
+     * Indicate if there were errors during the compile
+     * @return if errors occurred
+     */
+    protected boolean getErrorFlag() {
+        return errorFlag;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/KeySubst.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/KeySubst.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/KeySubst.java	(revision 16139)
@@ -0,0 +1,187 @@
+/*
+ * Copyright  2000,2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Keyword substitution. Input file is written to output file.
+ * Do not make input file same as output file.
+ * Keywords in input files look like this: @foo@. See the docs for the
+ * setKeys method to understand how to do the substitutions.
+ *
+ * @since Ant 1.1
+ * @deprecated KeySubst is deprecated since Ant 1.1. Use Filter + Copy
+ * instead.
+ */
+public class KeySubst extends Task {
+    private File source = null;
+    private File dest = null;
+    private String sep = "*";
+    private Hashtable replacements = new Hashtable();
+
+    /**
+        Do the execution.
+    */
+    public void execute() throws BuildException {
+        log("!! KeySubst is deprecated. Use Filter + Copy instead. !!");
+        log("Performing Substitutions");
+        if (source == null || dest == null) {
+            log("Source and destinations must not be null");
+            return;
+        }
+        BufferedReader br = null;
+        BufferedWriter bw = null;
+        try {
+            br = new BufferedReader(new FileReader(source));
+            dest.delete();
+            bw = new BufferedWriter(new FileWriter(dest));
+
+            String line = null;
+            String newline = null;
+            line = br.readLine();
+            while (line != null) {
+                if (line.length() == 0) {
+                    bw.newLine();
+                } else {
+                    newline = KeySubst.replace(line, replacements);
+                    bw.write(newline);
+                    bw.newLine();
+                }
+                line = br.readLine();
+            }
+            bw.flush();
+        } catch (IOException ioe) {
+            ioe.printStackTrace();
+        } finally {
+            if (bw != null) {
+                try {
+                    bw.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+            if (br != null) {
+                try {
+                    br.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    /**
+        Set the source file.
+    */
+    public void setSrc(File s) {
+        this.source = s;
+    }
+
+    /**
+        Set the destination file.
+    */
+    public void setDest(File dest) {
+        this.dest = dest;
+    }
+
+    /**
+        Sets the separator between name=value arguments
+        in setKeys(). By default it is "*".
+    */
+    public void setSep(String sep) {
+        this.sep = sep;
+    }
+    /**
+     * Sets the keys.
+     *
+        Format string is like this:
+        <p>
+        name=value*name2=value
+        <p>
+        Names are case sensitive.
+        <p>
+        Use the setSep() method to change the * to something else
+        if you need to use * as a name or value.
+    */
+    public void setKeys(String keys) {
+        if (keys != null && keys.length() > 0) {
+            StringTokenizer tok =
+            new StringTokenizer(keys, this.sep, false);
+            while (tok.hasMoreTokens()) {
+                String token = tok.nextToken().trim();
+                StringTokenizer itok =
+                new StringTokenizer(token, "=", false);
+
+                String name = itok.nextToken();
+                String value = itok.nextToken();
+                replacements.put(name, value);
+            }
+        }
+    }
+
+
+    public static void main(String[] args) {
+        try {
+            Hashtable hash = new Hashtable();
+            hash.put("VERSION", "1.0.3");
+            hash.put("b", "ffff");
+            System.out.println(KeySubst.replace("$f ${VERSION} f ${b} jj $",
+                                                hash));
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+        Does replacement on text using the hashtable of keys.
+
+        @return the string with the replacements in it.
+    */
+    public static String replace(String origString, Hashtable keys)
+        throws BuildException {
+        StringBuffer finalString = new StringBuffer();
+        int index = 0;
+        int i = 0;
+        String key = null;
+        while ((index = origString.indexOf("${", i)) > -1) {
+            key = origString.substring(index + 2, origString.indexOf("}",
+                                       index + 3));
+            finalString.append (origString.substring(i, index));
+            if (keys.containsKey(key)) {
+                finalString.append (keys.get(key));
+            } else {
+                finalString.append ("${");
+                finalString.append (key);
+                finalString.append ("}");
+            }
+            i = index + 3 + key.length();
+        }
+        finalString.append (origString.substring(i));
+        return finalString.toString();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Length.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Length.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Length.java	(revision 16139)
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.Vector;
+import java.util.Iterator;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Gets lengths:  of files/resources, byte size; of strings, length (optionally trimmed).
+ * The task is overloaded in this way for semantic reasons, much like Available.
+ * @since Ant 1.6.3
+ */
+public class Length extends Task implements Condition {
+
+    private static final String ALL = "all";
+    private static final String EACH = "each";
+    private static final String STRING = "string";
+
+    private static final String LENGTH_REQUIRED
+        = "Use of the Length condition requires that the length attribute be set.";
+
+    private String property;
+    private String string;
+    private Boolean trim;
+    private String mode = ALL;
+    private When when = When.EQUAL;
+    private Long length;
+    private Vector filesets;
+
+    /**
+     * The property in which the length will be stored.
+     * @param property the <code>String</code> property key.
+     */
+    public synchronized void setProperty(String property) {
+        this.property = property;
+    }
+
+    /**
+     * Set the single file for this task.
+     * @param file the <code>File</code> whose length to retrieve.
+     */
+    public synchronized void setFile(File file) {
+        FileSet fs = new FileSet();
+        fs.setFile(file);
+        add(fs);
+    }
+
+    /**
+     * Add a FileSet.
+     * @param fs the <code>FileSet</code> to add.
+     */
+    public synchronized void add(FileSet fs) {
+        if (fs == null) {
+            return;
+        }
+        filesets = (filesets == null) ? new Vector() : filesets;
+        filesets.add(fs);
+    }
+
+    /**
+     * Set the target count number for use as a Condition.
+     * @param ell the long length to compare with.
+     */
+    public synchronized void setLength(long ell) {
+        length = new Long(ell);
+    }
+
+    /**
+     * Set the comparison criteria for use as a Condition:
+     * "equal", "greater", "less". Default is "equal".
+     * @param w EnumeratedAttribute When.
+     */
+    public synchronized void setWhen(When w) {
+        when = w;
+    }
+
+    /**
+     * Set the execution mode for working with files.
+     * @param m the <code>FileMode</code> to use.
+     */
+    public synchronized void setMode(FileMode m) {
+        this.mode = m.getValue();
+    }
+
+    /**
+     * Set the string whose length to get.
+     * @param string <code>String</code>.
+     */
+    public synchronized void setString(String string) {
+        this.string = string;
+        this.mode = STRING;
+    }
+
+    /**
+     * Set whether to trim in string mode.
+     * @param trim <code>boolean</code>.
+     */
+    public synchronized void setTrim(boolean trim) {
+        this.trim = trim ? Boolean.TRUE : Boolean.FALSE;
+    }
+
+    /**
+     * Learn whether strings will be trimmed.
+     * @return boolean trim setting.
+     */
+    public boolean getTrim() {
+        return trim != null && trim.booleanValue();
+    }
+
+    /**
+     * Execute the length task.
+     */
+    public void execute() {
+        validate();
+        PrintStream ps = new PrintStream((property != null)
+            ? (OutputStream) new PropertyOutputStream()
+            : (OutputStream) new LogOutputStream(this, Project.MSG_INFO));
+
+        if (STRING.equals(mode)) {
+            ps.print(getLength(string, getTrim()));
+            ps.close();
+        } else if (EACH.equals(mode)) {
+            handleResources(new EachHandler(ps));
+        } else if (ALL.equals(mode)) {
+            handleResources(new AllHandler(ps));
+        }
+    }
+
+    /**
+     * Fulfill the condition contract.
+     * @return true if the condition is true.
+     * @throws BuildException if an error occurs.
+     */
+    public boolean eval() {
+        validate();
+        if (length == null) {
+            throw new BuildException(LENGTH_REQUIRED);
+        }
+        Long ell = null;
+        if (STRING.equals(mode)) {
+            ell = new Long(getLength(string, getTrim()));
+        } else {
+            ConditionHandler h = new ConditionHandler();
+            handleResources(h);
+            ell = new Long(h.getLength());
+        }
+        int w = when.getIndex();
+        int comp = ell.compareTo(length);
+        return (w == 0 && comp == 0)
+            || (w == 1 && comp > 0)
+            || (w == 2 && comp < 0);
+    }
+
+    private void validate() {
+        if (string != null) {
+            if (filesets != null && filesets.size() > 0) {
+                throw new BuildException("the string length function"
+                    + " is incompatible with the file length function");
+            }
+            if (!(STRING.equals(mode))) {
+                throw new BuildException("the mode attribute is for use"
+                    + " with the file/resource length function");
+            }
+        } else if (filesets != null) {
+            if (!(EACH.equals(mode) || ALL.equals(mode))) {
+                throw new BuildException("invalid mode setting for"
+                    + " file length function: \"" + mode + "\"");
+            } else if (trim != null) {
+                throw new BuildException("the trim attribute is"
+                    + " for use with the string length function only");
+            }
+        } else {
+            throw new BuildException("you must set either the string attribute"
+                + " or specify one or more files using the file attribute or"
+                + " nested filesets");
+        }
+    }
+
+    private void handleResources(Handler h) {
+        for (Iterator i = filesets.iterator(); i.hasNext();) {
+            FileSet fs = (FileSet) i.next();
+            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+            String[] f = ds.getIncludedFiles();
+            for (int j = 0; j < f.length; j++) {
+                Resource r = ds.getResource(f[j]);
+                if (!r.isExists()) {
+                    log(r.getName() + " does not exist", Project.MSG_ERR);
+                } else if (r.isDirectory()) {
+                    log(r.getName() + " is a directory; length unspecified",
+                        Project.MSG_ERR);
+                } else {
+                    //force a full path:
+                    File basedir = ds.getBasedir();
+                    String s = FileUtils.getFileUtils().resolveFile(
+                        basedir, r.getName()).getAbsolutePath();
+                    h.handle(new Resource(s, true,
+                        r.getLastModified(), false, r.getSize()));
+                }
+            }
+        }
+        h.complete();
+    }
+
+    private static long getLength(String s, boolean t) {
+        return (t ? s.trim() : s).length();
+    }
+
+    /** EnumeratedAttribute operation mode */
+    public static class FileMode extends EnumeratedAttribute {
+        static final String[] MODES = new String[] {EACH, ALL};
+
+        /**
+         * Return the possible values for FileMode.
+         * @return <code>String[]</code>.
+         */
+        public String[] getValues() {
+            return MODES;
+        }
+
+    }
+
+    /**
+     * EnumeratedAttribute for the when attribute.
+     */
+    public static class When extends EnumeratedAttribute {
+        private static final String[] VALUES
+            = new String[] {"equal", "greater", "less"};
+
+        private static final When EQUAL = new When("equal");
+
+        public When() {
+        }
+        public When(String value) {
+            setValue(value);
+        }
+            public String[] getValues() {
+            return VALUES;
+        }
+    }
+
+    private class PropertyOutputStream extends ByteArrayOutputStream {
+        public void close() {
+            getProject().setNewProperty(
+                property, new String(toByteArray()).trim());
+        }
+    }
+
+    private abstract class Handler {
+        PrintStream ps;
+        Handler(PrintStream ps) {
+            this.ps = ps;
+        }
+
+        protected abstract void handle(Resource r);
+
+        void complete() {
+            ps.close();
+        }
+    }
+
+    private class EachHandler extends Handler {
+        EachHandler(PrintStream ps) {
+            super(ps);
+        }
+        protected void handle(Resource r) {
+            ps.print(r.getName());
+            ps.print(" : ");
+            //when writing to the log, we'll see what's happening:
+            long size = r.getSize();
+            if (size == Resource.UNKNOWN_SIZE) {
+                ps.println("unknown");
+            } else {
+                ps.println(size);
+            }
+       }
+    }
+
+    private class AllHandler extends Handler {
+        long accum = 0L;
+        AllHandler(PrintStream ps) {
+            super(ps);
+        }
+        protected synchronized void handle(Resource r) {
+            long size = r.getSize();
+            if (size == Resource.UNKNOWN_SIZE) {
+                log("Size unknown for " + r.getName(), Project.MSG_WARN);
+            } else {
+                accum += size;
+            }
+        }
+        void complete() {
+            ps.print(accum);
+            super.complete();
+        }
+    }
+
+    private class ConditionHandler extends AllHandler {
+        ConditionHandler() {
+            super(null);
+        }
+        void complete() {
+        }
+        long getLength() {
+            return accum;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/LoadFile.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/LoadFile.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/LoadFile.java	(revision 16139)
@@ -0,0 +1,200 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
+import org.apache.tools.ant.types.FilterChain;
+
+/**
+ * Load a file into a property
+ *
+ * @since Ant 1.5
+ * @ant.task category="utility"
+ */
+public class LoadFile extends Task {
+
+    /**
+     * source file, usually null
+     */
+    private File srcFile = null;
+
+    /**
+     * what to do when it goes pear-shaped
+     */
+    private boolean failOnError = true;
+
+    /**
+     * Encoding to use for filenames, defaults to the platform's default
+     * encoding.
+     */
+    private String encoding = null;
+
+    /**
+     * name of property
+     */
+    private String property = null;
+
+    /**
+     * Holds FilterChains
+     */
+    private final Vector filterChains = new Vector();
+
+    /**
+     * Encoding to use for input, defaults to the platform's default
+     * encoding. <p>
+     *
+     * For a list of possible values see
+     * <a href="http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html">
+     * http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html
+     * </a>.</p>
+     *
+     * @param encoding The new Encoding value
+     */
+
+    public final void setEncoding(final String encoding) {
+        this.encoding = encoding;
+    }
+
+
+    /**
+     * Property name to save to.
+     *
+     * @param property The new Property value
+     */
+    public final void setProperty(final String property) {
+        this.property = property;
+    }
+
+
+    /**
+     * Sets the file to load.
+     *
+     * @param srcFile The new SrcFile value
+     */
+    public final void setSrcFile(final File srcFile) {
+        this.srcFile = srcFile;
+    }
+
+
+    /**
+     * If true, fail on load error.
+     *
+     * @param fail The new Failonerror value
+     */
+    public final void setFailonerror(final boolean fail) {
+        failOnError = fail;
+    }
+
+
+    /**
+     * read in a source file to a property
+     *
+     * @exception BuildException if something goes wrong with the build
+     */
+    public final void execute()
+        throws BuildException {
+        //validation
+        if (srcFile == null) {
+            throw new BuildException("source file not defined");
+        }
+        if (property == null) {
+            throw new BuildException("output property not defined");
+        }
+        FileInputStream fis = null;
+        BufferedInputStream bis = null;
+        Reader instream = null;
+        log("loading " + srcFile + " into property " + property,
+            Project.MSG_VERBOSE);
+        try {
+            final long len = srcFile.length();
+            log("file size = " + len, Project.MSG_DEBUG);
+            //discard most of really big files
+            final int size = (int) len;
+            //open up the file
+            fis = new FileInputStream(srcFile);
+            bis = new BufferedInputStream(fis);
+            if (encoding == null) {
+                instream = new InputStreamReader(bis);
+            } else {
+                instream = new InputStreamReader(bis, encoding);
+            }
+
+            String text = "";
+            if (size != 0) {
+                ChainReaderHelper crh = new ChainReaderHelper();
+                crh.setBufferSize(size);
+                crh.setPrimaryReader(instream);
+                crh.setFilterChains(filterChains);
+                crh.setProject(getProject());
+                instream = crh.getAssembledReader();
+
+                text = crh.readFully(instream);
+            }
+
+            if (text != null) {
+                if (text.length() > 0) {
+                    getProject().setNewProperty(property, text);
+                    log("loaded " + text.length() + " characters",
+                        Project.MSG_VERBOSE);
+                    log(property + " := " + text, Project.MSG_DEBUG);
+                }
+            }
+
+        } catch (final IOException ioe) {
+            final String message = "Unable to load file: " + ioe.toString();
+            if (failOnError) {
+                throw new BuildException(message, ioe, getLocation());
+            } else {
+                log(message, Project.MSG_ERR);
+            }
+        } catch (final BuildException be) {
+            if (failOnError) {
+                throw be;
+            } else {
+                log(be.getMessage(), Project.MSG_ERR);
+            }
+        } finally {
+            try {
+                if (fis != null) {
+                    fis.close();
+                }
+            } catch (IOException ioex) {
+                //ignore
+            }
+        }
+    }
+
+    /**
+     * Add the FilterChain element.
+     * @param filter the filter to add
+     */
+    public final void addFilterChain(FilterChain filter) {
+        filterChains.addElement(filter);
+    }
+
+//end class
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/LoadProperties.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/LoadProperties.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/LoadProperties.java	(revision 16139)
@@ -0,0 +1,257 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.util.Properties;
+import java.util.Vector;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.FilterChain;
+
+/**
+ * Load a file's contents as Ant properties.
+ *
+ * @since Ant 1.5
+ * @ant.task category="utility"
+ */
+public class LoadProperties extends Task {
+
+    /**
+     * Source file
+     */
+    private File srcFile = null;
+
+    /**
+     * Resource
+     */
+    private String resource = null;
+
+    /**
+     * Classpath
+     */
+    private Path classpath = null;
+
+    /**
+     * Holds filterchains
+     */
+    private final Vector filterChains = new Vector();
+
+    /**
+     * Encoding to use for input; defaults to the platform's default encoding.
+     */
+    private String encoding = null;
+
+    /**
+     * Set the file to load.
+     *
+     * @param srcFile The new SrcFile value
+     */
+    public final void setSrcFile(final File srcFile) {
+        this.srcFile = srcFile;
+    }
+
+    /**
+     * Set the resource name of a property file to load.
+     *
+     * @param resource resource on classpath
+     */
+    public void setResource(String resource) {
+        this.resource = resource;
+    }
+
+    /**
+     * Encoding to use for input, defaults to the platform's default
+     * encoding. <p>
+     *
+     * For a list of possible values see
+     * <a href="http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html">
+     * http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html
+     * </a>.</p>
+     *
+     * @param encoding The new Encoding value
+     */
+    public final void setEncoding(final String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * Set the classpath to use when looking up a resource.
+     * @param classpath to add to any existing classpath
+     */
+    public void setClasspath(Path classpath) {
+        if (this.classpath == null) {
+            this.classpath = classpath;
+        } else {
+            this.classpath.append(classpath);
+        }
+    }
+
+    /**
+     * Add a classpath to use when looking up a resource.
+     * @return The classpath to be configured
+     */
+    public Path createClasspath() {
+        if (this.classpath == null) {
+            this.classpath = new Path(getProject());
+        }
+        return this.classpath.createPath();
+    }
+
+    /**
+     * Set the classpath to use when looking up a resource,
+     * given as reference to a &lt;path&gt; defined elsewhere
+     * @param r The reference value
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+
+    /**
+     * get the classpath used by this <CODE>LoadProperties</CODE>.
+     * @return The classpath
+     */
+    public Path getClasspath() {
+        return classpath;
+    }
+
+    /**
+     * load Ant properties from the source file or resource
+     *
+     * @exception BuildException if something goes wrong with the build
+     */
+    public final void execute() throws BuildException {
+        //validation
+        if (srcFile == null && resource == null) {
+            throw new BuildException(
+                "One of \"srcfile\" or \"resource\" is required.");
+        }
+
+        BufferedInputStream bis = null;
+
+        if (srcFile != null ) {
+            if (!srcFile.exists()) {
+                throw new BuildException("Source file does not exist.");
+            }
+
+            if (!srcFile.isFile()) {
+                throw new BuildException("Source file is not a file.");
+            }
+
+            try {
+                bis = new BufferedInputStream(new FileInputStream(srcFile));
+            } catch (IOException eyeOhEx) {
+                throw new BuildException(eyeOhEx);
+            }
+        } else {
+            ClassLoader cL = (classpath != null)
+                ? getProject().createClassLoader(classpath)
+                : LoadProperties.class.getClassLoader();
+
+            InputStream is = (cL == null)
+                ? ClassLoader.getSystemResourceAsStream(resource)
+                : cL.getResourceAsStream(resource);
+
+            if (is != null) {
+                bis = new BufferedInputStream(is);
+            } else { // do it like Property
+                log("Unable to find resource " + resource, Project.MSG_WARN);
+                return;
+            }
+        }
+
+        Reader instream = null;
+        ByteArrayInputStream tis = null;
+
+        try {
+            if (encoding == null) {
+                instream = new InputStreamReader(bis);
+            } else {
+                instream = new InputStreamReader(bis, encoding);
+            }
+
+            ChainReaderHelper crh = new ChainReaderHelper();
+            crh.setPrimaryReader(instream);
+            crh.setFilterChains(filterChains);
+            crh.setProject(getProject());
+            instream = crh.getAssembledReader();
+
+            String text = crh.readFully(instream);
+
+            if (text != null) {
+                if (!text.endsWith("\n")) {
+                    text = text + "\n";
+                }
+
+                if (encoding == null) {
+                    tis = new ByteArrayInputStream(text.getBytes());
+                } else {
+                    tis = new ByteArrayInputStream(text.getBytes(encoding));
+                }
+                final Properties props = new Properties();
+                props.load(tis);
+
+                Property propertyTask =
+                    (Property) getProject().createTask("property");
+                propertyTask.setTaskName(getTaskName());
+                propertyTask.addProperties(props);
+            }
+
+        } catch (final IOException ioe) {
+            final String message = "Unable to load file: " + ioe.toString();
+            throw new BuildException(message, ioe, getLocation());
+        } catch (final BuildException be) {
+            throw be;
+        } finally {
+            try {
+                if (bis != null) {
+                    bis.close();
+                }
+            } catch (IOException ioex) {
+                //ignore
+            }
+            try {
+                if (tis != null) {
+                    tis.close();
+                }
+            } catch (IOException ioex) {
+                //ignore
+            }
+        }
+    }
+
+    /**
+     * Adds a FilterChain.
+     * @param filter the filter to add
+     */
+    public final void addFilterChain(FilterChain filter) {
+        filterChains.addElement(filter);
+    }
+
+//end class
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/LogOutputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/LogOutputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/LogOutputStream.java	(revision 16139)
@@ -0,0 +1,180 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+
+
+/**
+ * Logs each line written to this stream to the log system of ant.
+ *
+ * Tries to be smart about line separators.<br>
+ * TODO: This class can be split to implement other line based processing
+ * of data written to the stream.
+ *
+ * @since Ant 1.2
+ */
+public class LogOutputStream extends OutputStream {
+
+    /** Initial buffer size. */
+    private static final int INTIAL_SIZE = 132;
+
+    /** Carriage return */
+    private static final int CR = 0x0d;
+
+    /** Linefeed */
+    private static final int LF = 0x0a;
+
+    private ByteArrayOutputStream buffer
+        = new ByteArrayOutputStream(INTIAL_SIZE);
+    private boolean skip = false;
+
+    private ProjectComponent pc;
+    private int level = Project.MSG_INFO;
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param task the task for whom to log
+     * @param level loglevel used to log data written to this stream.
+     */
+    public LogOutputStream(Task task, int level) {
+        this((ProjectComponent) task, level);
+    }
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param task the task for whom to log
+     * @param level loglevel used to log data written to this stream.
+     * @since Ant 1.6.3
+     */
+    public LogOutputStream(ProjectComponent pc, int level) {
+        this.pc = pc;
+        this.level = level;
+    }
+
+
+    /**
+     * Write the data to the buffer and flush the buffer, if a line
+     * separator is detected.
+     *
+     * @param cc data to log (byte).
+     */
+    public void write(int cc) throws IOException {
+        final byte c = (byte) cc;
+        if ((c == '\n') || (c == '\r')) {
+            if (!skip) {
+              processBuffer();
+            }
+        } else {
+          buffer.write(cc);
+        }
+        skip = (c == '\r');
+    }
+
+    /**
+     * Flush this log stream
+     */
+    public void flush() {
+        if (buffer.size() > 0) {
+            processBuffer();
+        }
+    }
+
+
+    /**
+     * Converts the buffer to a string and sends it to <code>processLine</code>
+     */
+    protected void processBuffer() {
+        processLine(buffer.toString());
+        buffer.reset();
+    }
+
+    /**
+     * Logs a line to the log system of ant.
+     *
+     * @param line the line to log.
+     */
+    protected void processLine(String line) {
+        processLine(line, level);
+    }
+
+    /**
+     * Logs a line to the log system of ant.
+     *
+     * @param line the line to log.
+     */
+    protected void processLine(String line, int level) {
+        pc.log(line, level);
+    }
+
+
+    /**
+     * Writes all remaining
+     */
+    public void close() throws IOException {
+        if (buffer.size() > 0) {
+          processBuffer();
+        }
+        super.close();
+    }
+
+    public int getMessageLevel() {
+        return level;
+    }
+
+    /**
+     * Write a block of characters to the output stream
+     *
+     * @param b the array containing the data
+     * @param off the offset into the array where data starts
+     * @param len the length of block
+     *
+     * @throws IOException if the data cannot be written into the stream.
+     */
+    public void write(byte[] b, int off, int len) throws IOException {
+        // find the line breaks and pass other chars through in blocks
+        int offset = off;
+        int blockStartOffset = offset;
+        int remaining = len;
+        while (remaining > 0) {
+            while (remaining > 0 && b[offset] != LF && b[offset] != CR) {
+                offset++;
+                remaining--;
+            }
+            // either end of buffer or a line separator char
+            int blockLength = offset - blockStartOffset;
+            if (blockLength > 0) {
+                buffer.write(b, blockStartOffset, blockLength);
+            }
+            while (remaining > 0 && (b[offset] == LF || b[offset] == CR)) {
+                write(b[offset]);
+                offset++;
+                remaining--;
+            }
+            blockStartOffset = offset;
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/LogStreamHandler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/LogStreamHandler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/LogStreamHandler.java	(revision 16139)
@@ -0,0 +1,56 @@
+/*
+ * Copyright  2000,2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Logs standard output and error of a subprocess to the log system of ant.
+ *
+ * @since Ant 1.2
+ */
+public class LogStreamHandler extends PumpStreamHandler {
+
+    /**
+     * Creates log stream handler
+     *
+     * @param task the task for whom to log
+     * @param outlevel the loglevel used to log standard output
+     * @param errlevel the loglevel used to log standard error
+     */
+    public LogStreamHandler(Task task, int outlevel, int errlevel) {
+        super(new LogOutputStream(task, outlevel),
+              new LogOutputStream(task, errlevel));
+    }
+
+    /**
+     * Stop the log stream handler.
+     */
+    public void stop() {
+        super.stop();
+        try {
+            getErr().close();
+            getOut().close();
+        } catch (IOException e) {
+            // plain impossible
+            throw new BuildException(e);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/MacroDef.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/MacroDef.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/MacroDef.java	(revision 16139)
@@ -0,0 +1,781 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Locale;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.apache.tools.ant.AntTypeDefinition;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ComponentHelper;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.RuntimeConfigurable;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.UnknownElement;
+
+/**
+ * Describe class <code>MacroDef</code> here.
+ *
+ * @since Ant 1.6
+ */
+public class MacroDef extends AntlibDefinition  {
+    private NestedSequential nestedSequential;
+    private String     name;
+    private List       attributes = new ArrayList();
+    private Map        elements   = new HashMap();
+    private String     textName   = null;
+    private Text       text       = null;
+    private boolean    hasImplicitElement = false;
+
+    /**
+     * Name of the definition
+     * @param name the name of the definition
+     */
+     public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Add the text element.
+     * @param text the nested text element to add
+     * @since ant 1.6.1
+     */
+    public void addConfiguredText(Text text) {
+        if (this.text != null) {
+            throw new BuildException(
+                "Only one nested text element allowed");
+        }
+        if (text.getName() == null) {
+            throw new BuildException(
+                "the text nested element needed a \"name\" attribute");
+        }
+        // Check if used by attributes
+        for (Iterator i = attributes.iterator(); i.hasNext();) {
+            Attribute attribute = (Attribute) i.next();
+            if (text.getName().equals(attribute.getName())) {
+                throw new BuildException(
+                    "the name \"" + text.getName()
+                    + "\" is already used as an attribute");
+            }
+        }
+        this.text = text;
+        this.textName = text.getName();
+    }
+
+    /**
+     * @return the nested text element
+     * @since ant 1.6.1
+     */
+
+    public Text getText() {
+        return text;
+    }
+
+    /**
+     * This is the sequential nested element of the macrodef.
+     *
+     * @return a sequential element to be configured.
+     */
+    public NestedSequential createSequential() {
+        if (this.nestedSequential != null) {
+            throw new BuildException("Only one sequential allowed");
+        }
+        this.nestedSequential = new NestedSequential();
+        return this.nestedSequential;
+    }
+
+    /**
+     * The class corresponding to the sequential nested element.
+     * This is a simple task container.
+     */
+    public static class NestedSequential implements TaskContainer {
+        private List nested = new ArrayList();
+
+        /**
+         * Add a task or type to the container.
+         *
+         * @param task an unknown element.
+         */
+        public void addTask(Task task) {
+            nested.add(task);
+        }
+
+        /**
+         * @return the list of unknown elements
+         */
+        public List getNested() {
+            return nested;
+        }
+
+        /**
+         * A compare function to compare this with another
+         * NestedSequential.
+         * It calls similar on the nested unknown elements.
+         *
+         * @param other the nested sequential to compare with.
+         * @return true if they are similar, false otherwise
+         */
+        public boolean similar(NestedSequential other) {
+            if (nested.size() != other.nested.size()) {
+                return false;
+            }
+            for (int i = 0; i < nested.size(); ++i) {
+                UnknownElement me = (UnknownElement) nested.get(i);
+                UnknownElement o = (UnknownElement) other.nested.get(i);
+                if (!me.similar(o)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+    }
+
+    /**
+     * Convert the nested sequential to an unknown element
+     * @return the nested sequential as an unknown element.
+     */
+    public UnknownElement getNestedTask() {
+        UnknownElement ret = new UnknownElement("sequential");
+        ret.setTaskName("sequential");
+        ret.setNamespace("");
+        ret.setQName("sequential");
+        new RuntimeConfigurable(ret, "sequential");
+        for (int i = 0; i < nestedSequential.getNested().size(); ++i) {
+            UnknownElement e =
+                (UnknownElement) nestedSequential.getNested().get(i);
+            ret.addChild(e);
+            ret.getWrapper().addChild(e.getWrapper());
+        }
+        return ret;
+    }
+
+    /**
+     * @return the nested Attributes
+     */
+    public List getAttributes() {
+        return attributes;
+    }
+
+    /**
+     * @return the nested elements
+     */
+    public Map getElements() {
+        return elements;
+    }
+
+    /**
+     * Check if a character is a valid character for an element or
+     * attribute name
+     * @param c the character to check
+     * @return true if the character is a letter or digit or '.' or '-'
+     *         attribute name
+     */
+    public static boolean isValidNameCharacter(char c) {
+        // ? is there an xml api for this ?
+        return Character.isLetterOrDigit(c) || c == '.' || c == '-';
+    }
+
+    /**
+     * Check if a string is a valid name for an element or
+     * attribute
+     * @param name the string to check
+     * @return true if the name consists of valid name characters
+     */
+    private static boolean isValidName(String name) {
+        if (name.length() == 0) {
+            return false;
+        }
+        for (int i = 0; i < name.length(); ++i) {
+            if (!isValidNameCharacter(name.charAt(i))) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Add an attribute element.
+     *
+     * @param attribute an attribute nested element.
+     */
+    public void addConfiguredAttribute(Attribute attribute) {
+        if (attribute.getName() == null) {
+            throw new BuildException(
+                "the attribute nested element needed a \"name\" attribute");
+        }
+        if (attribute.getName().equals(textName)) {
+            throw new BuildException(
+                "the attribute name \"" + attribute.getName()
+                + "\" has already been used by the text element");
+        }
+        for (int i = 0; i < attributes.size(); ++i) {
+            if (((Attribute) attributes.get(i)).getName().equals(
+                    attribute.getName())) {
+                throw new BuildException(
+                    "the attribute " + attribute.getName()
+                    + " has already been specified");
+            }
+        }
+        attributes.add(attribute);
+    }
+
+    /**
+     * Add an element element.
+     *
+     * @param element an element nested element.
+     */
+    public void addConfiguredElement(TemplateElement element) {
+        if (element.getName() == null) {
+            throw new BuildException(
+                "the element nested element needed a \"name\" attribute");
+        }
+        if (elements.get(element.getName()) != null) {
+            throw new BuildException(
+                "the element " + element.getName()
+                + " has already been specified");
+        }
+        if (hasImplicitElement
+            || (element.isImplicit() && elements.size() != 0)) {
+            throw new BuildException(
+                "Only one element allowed when using implicit elements");
+        }
+        hasImplicitElement = element.isImplicit();
+        elements.put(element.getName(), element);
+    }
+
+    /**
+     * Create a new ant type based on the embedded tasks and types.
+     *
+     */
+    public void execute() {
+        if (nestedSequential == null) {
+            throw new BuildException("Missing sequential element");
+        }
+        if (name == null) {
+            throw new BuildException("Name not specified");
+        }
+
+        name = ProjectHelper.genComponentName(getURI(), name);
+
+        MyAntTypeDefinition def = new MyAntTypeDefinition(this);
+        def.setName(name);
+        def.setClass(MacroInstance.class);
+
+        ComponentHelper helper = ComponentHelper.getComponentHelper(
+            getProject());
+
+        helper.addDataTypeDefinition(def);
+    }
+
+
+    /**
+     * A nested element for the MacroDef task.
+     *
+     */
+    public static class Attribute {
+        private String name;
+        private String defaultValue;
+        private String description;
+
+        /**
+         * The name of the attribute.
+         *
+         * @param name the name of the attribute
+         */
+        public void setName(String name) {
+            if (!isValidName(name)) {
+                throw new BuildException(
+                    "Illegal name [" + name + "] for attribute");
+            }
+            this.name = name.toLowerCase(Locale.US);
+        }
+
+        /**
+         * @return the name of the attribute
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * The default value to use if the parameter is not
+         * used in the templated instance.
+         *
+         * @param defaultValue the default value
+         */
+        public void setDefault(String defaultValue) {
+            this.defaultValue = defaultValue;
+        }
+
+        /**
+         * @return the default value, null if not set
+         */
+        public String getDefault() {
+            return defaultValue;
+        }
+
+        /**
+         * @param desc Description of the element.
+         * @since ant 1.6.1
+         */
+        public void setDescription(String desc) {
+            description = desc;
+        }
+
+        /**
+         * @return the description of the element, or <code>null</code> if
+         *         no description is available.
+         * @since ant 1.6.1
+         */
+        public String getDescription() {
+            return description;
+        }
+
+        /**
+         * equality method
+         *
+         * @param obj an <code>Object</code> value
+         * @return a <code>boolean</code> value
+         */
+        public boolean equals(Object obj) {
+            if (obj == null) {
+                return false;
+            }
+            if (obj.getClass() != getClass()) {
+                return false;
+            }
+            Attribute other = (Attribute) obj;
+            if (name == null) {
+                if (other.name != null) {
+                    return false;
+                }
+            } else if (!name.equals(other.name)) {
+                return false;
+            }
+            if (defaultValue == null) {
+                if (other.defaultValue != null) {
+                    return false;
+                }
+            } else if (!defaultValue.equals(other.defaultValue)) {
+                return false;
+            }
+            return true;
+        }
+
+        /**
+         * @return a hash code value for this object.
+         */
+        public int hashCode() {
+            return objectHashCode(defaultValue) + objectHashCode(name);
+        }
+    }
+
+    /**
+     * A nested text element for the MacroDef task.
+     * @since ant 1.6.1
+     */
+    public static class Text {
+        private String  name;
+        private boolean optional;
+        private boolean trim;
+        private String  description;
+
+        /**
+         * The name of the attribute.
+         *
+         * @param name the name of the attribute
+         */
+        public void setName(String name) {
+            if (!isValidName(name)) {
+                throw new BuildException(
+                    "Illegal name [" + name + "] for attribute");
+            }
+            this.name = name.toLowerCase(Locale.US);
+        }
+
+        /**
+         * @return the name of the attribute
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * The optional attribute of the text element.
+         *
+         * @param optional if true this is optional
+         */
+        public void setOptional(boolean optional) {
+            this.optional = optional;
+        }
+
+        /**
+         * @return true if the text is optional
+         */
+        public boolean getOptional() {
+            return optional;
+        }
+
+        /**
+         * The trim attribute of the text element.
+         *
+         * @param trim if true this String.trim() is called on
+         *             the contents of the text element.
+         */
+        public void setTrim(boolean trim) {
+            this.trim = trim;
+        }
+
+        /**
+         * @return true if the text is trim
+         */
+        public boolean getTrim() {
+            return trim;
+        }
+
+        /**
+         * @param desc Description of the text.
+         */
+        public void setDescription(String desc) {
+            description = desc;
+        }
+
+        /**
+         * @return the description of the text, or <code>null</code> if
+         *         no description is available.
+         */
+        public String getDescription() {
+            return description;
+        }
+
+        /**
+         * equality method
+         *
+         * @param obj an <code>Object</code> value
+         * @return a <code>boolean</code> value
+         */
+        public boolean equals(Object obj) {
+            if (obj == null) {
+                return false;
+            }
+            if (obj.getClass() != getClass()) {
+                return false;
+            }
+            Text other = (Text) obj;
+            if (name == null) {
+                if (other.name != null) {
+                    return false;
+                }
+            } else if (!name.equals(other.name)) {
+                return false;
+            }
+            if (optional != other.optional) {
+                return false;
+            }
+            if (trim != other.trim) {
+                return false;
+            }
+            return true;
+        }
+
+        /**
+         * @return a hash code value for this object.
+         */
+        public int hashCode() {
+            return objectHashCode(name);
+        }
+    }
+
+    /**
+     * A nested element for the MacroDef task.
+     *
+     */
+    public static class TemplateElement {
+        private String name;
+        private boolean optional = false;
+        private boolean implicit = false;
+        private String description;
+
+        /**
+         * The name of the element.
+         *
+         * @param name the name of the element.
+         */
+        public void setName(String name) {
+            if (!isValidName(name)) {
+                throw new BuildException(
+                    "Illegal name [" + name + "] for attribute");
+            }
+            this.name = name.toLowerCase(Locale.US);
+        }
+
+        /**
+         * @return the name of the element.
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * is this element optional ?
+         *
+         * @param optional if true this element may be left out, default
+         *                 is false.
+         */
+        public void setOptional(boolean optional) {
+            this.optional = optional;
+        }
+
+        /**
+         * @return the optional attribute
+         */
+        public boolean isOptional() {
+            return optional;
+        }
+
+        /**
+         * is this element implicit ?
+         *
+         * @param implicit if true this element may be left out, default
+         *                 is false.
+         */
+        public void setImplicit(boolean implicit) {
+            this.implicit = implicit;
+        }
+
+        /**
+         * @return the implicit attribute
+         */
+        public boolean isImplicit() {
+            return implicit;
+        }
+
+        /**
+         * @param desc Description of the element.
+         * @since ant 1.6.1
+         */
+        public void setDescription(String desc) {
+            description = desc;
+        }
+
+        /**
+         * @return the description of the element, or <code>null</code> if
+         *         no description is available.
+         * @since ant 1.6.1
+         */
+        public String getDescription() {
+            return description;
+        }
+
+        /**
+         * equality method
+         *
+         * @param obj an <code>Object</code> value
+         * @return a <code>boolean</code> value
+         */
+        public boolean equals(Object obj) {
+            if (obj == null) {
+                return false;
+            }
+            if (obj.getClass() != getClass()) {
+                return false;
+            }
+            TemplateElement other = (TemplateElement) obj;
+            if (name == null) {
+                if (other.name != null) {
+                    return false;
+                }
+            } else if (!name.equals(other.name)) {
+                return false;
+            }
+            return optional == other.optional && implicit == other.implicit;
+        }
+
+        /**
+         * @return a hash code value for this object.
+         */
+        public int hashCode() {
+            return objectHashCode(name)
+                + (optional ? 1 : 0) + (implicit ? 1 : 0);
+        }
+    }
+
+    /**
+     * same or similar equality method for macrodef, ignores project and
+     * runtime info.
+     *
+     * @param obj an <code>Object</code> value
+     * @param same if true test for sameness, otherwise just similiar
+     * @return a <code>boolean</code> value
+     */
+    private boolean sameOrSimilar(Object obj, boolean same) {
+        if (obj == this) {
+            return true;
+        }
+
+        if (obj == null) {
+            return false;
+        }
+        if (!obj.getClass().equals(getClass())) {
+            return false;
+        }
+        MacroDef other = (MacroDef) obj;
+        if (name == null) {
+            return other.name == null;
+        }
+        if (!name.equals(other.name)) {
+            return false;
+        }
+        // Allow two macro definitions with the same location
+        // to be treated as similar - bugzilla 31215
+        if (other.getLocation() != null
+            && other.getLocation().equals(getLocation())
+            && !same) {
+            return true;
+        }
+        if (text == null) {
+            if (other.text != null) {
+                return false;
+            }
+        } else {
+            if (!text.equals(other.text)) {
+                return false;
+            }
+        }
+        if (getURI() == null || getURI().equals("")
+            || getURI().equals(ProjectHelper.ANT_CORE_URI)) {
+            if (!(other.getURI() == null || other.getURI().equals("")
+                  || other.getURI().equals(ProjectHelper.ANT_CORE_URI))) {
+                return false;
+            }
+        } else {
+            if (!getURI().equals(other.getURI())) {
+                return false;
+            }
+        }
+
+        if (!nestedSequential.similar(other.nestedSequential)) {
+            return false;
+        }
+        if (!attributes.equals(other.attributes)) {
+            return false;
+        }
+        if (!elements.equals(other.elements)) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Similar method for this definition
+     *
+     * @param obj another definition
+     * @return true if the definitions are similar
+     */
+    public boolean similar(Object obj) {
+        return sameOrSimilar(obj, false);
+    }
+
+    /**
+     * Equality method for this definition
+     *
+     * @param obj another definition
+     * @return true if the definitions are the same
+     */
+    public boolean sameDefinition(Object obj) {
+        return sameOrSimilar(obj, true);
+    }
+
+    /**
+     * extends AntTypeDefinition, on create
+     * of the object, the template macro definition
+     * is given.
+     */
+    private static class MyAntTypeDefinition extends AntTypeDefinition {
+        private MacroDef    macroDef;
+
+        /**
+         * Creates a new <code>MyAntTypeDefinition</code> instance.
+         *
+         * @param macroDef a <code>MacroDef</code> value
+         */
+        public MyAntTypeDefinition(MacroDef macroDef) {
+            this.macroDef = macroDef;
+        }
+
+        /**
+         * create an instance of the definition.
+         * The instance may be wrapped in a proxy class.
+         * @param project the current project
+         * @return the created object
+         */
+        public Object create(Project project) {
+            Object o = super.create(project);
+            if (o == null) {
+                return null;
+            }
+            ((MacroInstance) o).setMacroDef(macroDef);
+            return o;
+        }
+
+        /**
+         * Equality method for this definition
+         *
+         * @param other another definition
+         * @param project the current project
+         * @return true if the definitions are the same
+         */
+        public boolean sameDefinition(AntTypeDefinition other, Project project) {
+            if (!super.sameDefinition(other, project)) {
+                return false;
+            }
+            MyAntTypeDefinition otherDef = (MyAntTypeDefinition) other;
+            return macroDef.sameDefinition(otherDef.macroDef);
+        }
+
+        /**
+         * Similar method for this definition
+         *
+         * @param other another definition
+         * @param project the current project
+         * @return true if the definitions are the same
+         */
+        public boolean similarDefinition(
+            AntTypeDefinition other, Project project) {
+            if (!super.similarDefinition(other, project)) {
+                return false;
+            }
+            MyAntTypeDefinition otherDef = (MyAntTypeDefinition) other;
+            return macroDef.similar(otherDef.macroDef);
+        }
+    }
+
+    private static int objectHashCode(Object o) {
+        if (o == null) {
+            return 0;
+        } else {
+            return o.hashCode();
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/MacroInstance.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/MacroInstance.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/MacroInstance.java	(revision 16139)
@@ -0,0 +1,386 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Enumeration;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DynamicAttribute;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.RuntimeConfigurable;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.UnknownElement;
+
+/**
+ * The class to be placed in the ant type definition.
+ * It is given a pointer to the template definition,
+ * and makes a copy of the unknown element, substituting
+ * the parameter values in attributes and text.
+ * @since Ant 1.6
+ */
+public class MacroInstance extends Task implements DynamicAttribute, TaskContainer {
+    private MacroDef macroDef;
+    private Map      map = new HashMap();
+    private Map      nsElements = null;
+    private Map      presentElements;
+    private Hashtable localProperties;
+    private String    text = null;
+    private String    implicitTag =     null;
+    private List      unknownElements = new ArrayList();
+
+    /**
+     * Called from MacroDef.MyAntTypeDefinition#create()
+     *
+     * @param macroDef a <code>MacroDef</code> value
+     */
+    public void setMacroDef(MacroDef macroDef) {
+        this.macroDef = macroDef;
+    }
+
+    /**
+     * @return the macro definition object for this macro instance.
+     */
+    public MacroDef getMacroDef() {
+        return macroDef;
+    }
+
+    /**
+     * A parameter name value pair as a xml attribute.
+     *
+     * @param name the name of the attribute
+     * @param value the value of the attribute
+     */
+    public void setDynamicAttribute(String name, String value) {
+        map.put(name, value);
+    }
+
+    /**
+     * Method present for BC purposes.
+     * @param name not used
+     * @return nothing
+     * @deprecated
+     * @throws BuildException always
+     */
+    public Object createDynamicElement(String name) throws BuildException {
+        throw new BuildException("Not implemented any more");
+    }
+
+    private Map getNsElements() {
+        if (nsElements == null) {
+            nsElements = new HashMap();
+            for (Iterator i = macroDef.getElements().entrySet().iterator();
+                 i.hasNext();) {
+                Map.Entry entry = (Map.Entry) i.next();
+                nsElements.put((String) entry.getKey(),
+                               entry.getValue());
+                MacroDef.TemplateElement te = (MacroDef.TemplateElement)
+                    entry.getValue();
+                if (te.isImplicit()) {
+                    implicitTag = te.getName();
+                }
+            }
+        }
+        return nsElements;
+    }
+
+    /**
+     * Add a unknownElement for the macro instances nested elements.
+     *
+     * @param nestedTask a nested element.
+     */
+    public void addTask(Task nestedTask) {
+        unknownElements.add(nestedTask);
+    }
+
+    private void processTasks() {
+        if (implicitTag != null) {
+            return;
+        }
+        for (Iterator i = unknownElements.iterator(); i.hasNext();) {
+            UnknownElement ue = (UnknownElement) i.next();
+            String name = ProjectHelper.extractNameFromComponentName(
+                ue.getTag()).toLowerCase(Locale.US);
+            if (getNsElements().get(name) == null) {
+                throw new BuildException("unsupported element " + name);
+            }
+            if (presentElements.get(name) != null) {
+                throw new BuildException("Element " + name + " already present");
+            }
+            presentElements.put(name, ue.getChildren());
+        }
+    }
+
+    /**
+     * Embedded element in macro instance
+     */
+    public static class Element implements TaskContainer {
+        private List unknownElements = new ArrayList();
+
+        /**
+         * Add an unknown element (to be snipped into the macroDef instance)
+         *
+         * @param nestedTask an unknown element
+         */
+        public void addTask(Task nestedTask) {
+            unknownElements.add(nestedTask);
+        }
+
+        /**
+         * @return the list of unknown elements
+         */
+        public List getUnknownElements() {
+            return unknownElements;
+        }
+    }
+
+    private static final int STATE_NORMAL         = 0;
+    private static final int STATE_EXPECT_BRACKET = 1;
+    private static final int STATE_EXPECT_NAME    = 2;
+
+    private String macroSubs(String s, Map macroMapping) {
+        if (s == null) {
+            return null;
+        }
+        StringBuffer ret = new StringBuffer();
+        StringBuffer macroName = null;
+        boolean inMacro = false;
+        int state = STATE_NORMAL;
+        for (int i = 0; i < s.length(); ++i) {
+            char ch = s.charAt(i);
+            switch (state) {
+                case STATE_NORMAL:
+                    if (ch == '@') {
+                        state = STATE_EXPECT_BRACKET;
+                    } else {
+                        ret.append(ch);
+                    }
+                    break;
+                case STATE_EXPECT_BRACKET:
+                    if (ch == '{') {
+                        state = STATE_EXPECT_NAME;
+                        macroName = new StringBuffer();
+                    } else if (ch == '@') {
+                        state = STATE_NORMAL;
+                        ret.append('@');
+                    } else {
+                        state = STATE_NORMAL;
+                        ret.append('@');
+                        ret.append(ch);
+                    }
+                    break;
+                case STATE_EXPECT_NAME:
+                    if (ch == '}') {
+                        state = STATE_NORMAL;
+                        String name = macroName.toString().toLowerCase(Locale.US);
+                        String value = (String) macroMapping.get(name);
+                        if (value == null) {
+                            ret.append("@{" + name + "}");
+                        } else {
+                            ret.append(value);
+                        }
+                        macroName = null;
+                    } else {
+                        macroName.append(ch);
+                    }
+                    break;
+                default:
+                    break;
+            }
+        }
+        switch (state) {
+            case STATE_NORMAL:
+                break;
+            case STATE_EXPECT_BRACKET:
+                ret.append('@');
+                break;
+            case STATE_EXPECT_NAME:
+                ret.append("@{");
+                ret.append(macroName.toString());
+                break;
+            default:
+                break;
+        }
+
+        return ret.toString();
+    }
+
+    /**
+     * Set the text contents for the macro.
+     * @param text the text to be added to the macro.
+     */
+
+    public void addText(String text) {
+        this.text = text;
+    }
+
+    private UnknownElement copy(UnknownElement ue) {
+        UnknownElement ret = new UnknownElement(ue.getTag());
+        ret.setNamespace(ue.getNamespace());
+        ret.setProject(getProject());
+        ret.setQName(ue.getQName());
+        ret.setTaskType(ue.getTaskType());
+        ret.setTaskName(ue.getTaskName());
+        ret.setLocation(ue.getLocation());
+        if (getOwningTarget() == null) {
+            Target t = new Target();
+            t.setProject(getProject());
+            ret.setOwningTarget(t);
+        } else {
+            ret.setOwningTarget(getOwningTarget());
+        }
+        RuntimeConfigurable rc = new RuntimeConfigurable(
+            ret, ue.getTaskName());
+        rc.setPolyType(ue.getWrapper().getPolyType());
+        Map map = ue.getWrapper().getAttributeMap();
+        for (Iterator i = map.entrySet().iterator(); i.hasNext();) {
+            Map.Entry entry = (Map.Entry) i.next();
+            rc.setAttribute(
+                (String) entry.getKey(),
+                macroSubs((String) entry.getValue(), localProperties));
+        }
+        rc.addText(macroSubs(ue.getWrapper().getText().toString(),
+                             localProperties));
+
+        Enumeration e = ue.getWrapper().getChildren();
+        while (e.hasMoreElements()) {
+            RuntimeConfigurable r = (RuntimeConfigurable) e.nextElement();
+            UnknownElement unknownElement = (UnknownElement) r.getProxy();
+            String tag = unknownElement.getTaskType();
+            if (tag != null) {
+                tag = tag.toLowerCase(Locale.US);
+            }
+            MacroDef.TemplateElement templateElement =
+                (MacroDef.TemplateElement) getNsElements().get(tag);
+            if (templateElement == null) {
+                UnknownElement child = copy(unknownElement);
+                rc.addChild(child.getWrapper());
+                ret.addChild(child);
+            } else if (templateElement.isImplicit()) {
+                if (unknownElements.size() == 0 && !templateElement.isOptional()) {
+                    throw new BuildException(
+                        "Missing nested elements for implicit element "
+                        + templateElement.getName());
+                }
+                for (Iterator i = unknownElements.iterator();
+                     i.hasNext();) {
+                    UnknownElement child = (UnknownElement) i.next();
+                    rc.addChild(child.getWrapper());
+                    ret.addChild(child);
+                }
+            } else {
+                List list = (List) presentElements.get(tag);
+                if (list == null) {
+                    if (!templateElement.isOptional()) {
+                        throw new BuildException(
+                            "Required nested element "
+                            + templateElement.getName() + " missing");
+                    }
+                    continue;
+                }
+                for (Iterator i = list.iterator();
+                     i.hasNext();) {
+                    UnknownElement child = (UnknownElement) i.next();
+                    rc.addChild(child.getWrapper());
+                    ret.addChild(child);
+                }
+            }
+        }
+        return ret;
+    }
+
+    /**
+     * Execute the templates instance.
+     * Copies the unknown element, substitutes the attributes,
+     * and calls perform on the unknown element.
+     *
+     */
+    public void execute() {
+        presentElements = new HashMap();
+        getNsElements();
+        processTasks();
+        localProperties = new Hashtable();
+        Set copyKeys = new HashSet(map.keySet());
+        for (Iterator i = macroDef.getAttributes().iterator(); i.hasNext();) {
+            MacroDef.Attribute attribute = (MacroDef.Attribute) i.next();
+            String value = (String) map.get(attribute.getName());
+            if (value == null && "description".equals(attribute.getName())) {
+                value = getDescription();
+            }
+            if (value == null) {
+                value = attribute.getDefault();
+                value = macroSubs(value, localProperties);
+            }
+            if (value == null) {
+                throw new BuildException(
+                    "required attribute " + attribute.getName() + " not set");
+            }
+            localProperties.put(attribute.getName(), value);
+            copyKeys.remove(attribute.getName());
+        }
+        if (copyKeys.contains("id")) {
+            copyKeys.remove("id");
+        }
+        if (macroDef.getText() != null) {
+            if (text == null) {
+                if (!macroDef.getText().getOptional()) {
+                    throw new BuildException(
+                        "required text missing");
+                }
+                text = "";
+            }
+            if (macroDef.getText().getTrim()) {
+                text = text.trim();
+            }
+            localProperties.put(macroDef.getText().getName(), text);
+        } else {
+            if (text != null && !text.trim().equals("")) {
+                throw new BuildException(
+                    "The \"" + getTaskName() + "\" macro does not support"
+                    + " nested text data.");
+            }
+        }
+        if (copyKeys.size() != 0) {
+            throw new BuildException(
+                "Unknown attribute" + (copyKeys.size() > 1 ? "s " : " ")
+                + copyKeys);
+        }
+
+        // need to set the project on unknown element
+        UnknownElement c = copy(macroDef.getNestedTask());
+        c.init();
+        try {
+            c.perform();
+        } catch (BuildException ex) {
+            throw ProjectHelper.addLocationToBuildException(
+                ex, getLocation());
+        }
+        presentElements = null;
+        localProperties = null;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Manifest.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Manifest.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Manifest.java	(revision 16139)
@@ -0,0 +1,1029 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.CollectionUtils;
+
+/**
+ * Holds the data of a jar manifest.
+ *
+ * Manifests are processed according to the
+ * {@link <a href="http://java.sun.com/j2se/1.4/docs/guide/jar/jar.html">Jar
+ * file specification.</a>}.
+ * Specifically, a manifest element consists of
+ * a set of attributes and sections. These sections in turn may contain
+ * attributes. Note in particular that this may result in manifest lines
+ * greater than 72 bytes being wrapped and continued on the next
+ * line. If an application can not handle the continuation mechanism, it
+ * is a defect in the application, not this task.
+ *
+ *
+ * @since Ant 1.4
+ */
+public class Manifest {
+    /** The standard manifest version header */
+    public static final String ATTRIBUTE_MANIFEST_VERSION
+        = "Manifest-Version";
+
+    /** The standard Signature Version header */
+    public static final String ATTRIBUTE_SIGNATURE_VERSION
+        = "Signature-Version";
+
+    /** The Name Attribute is the first in a named section */
+    public static final String ATTRIBUTE_NAME = "Name";
+
+    /** The From Header is disallowed in a Manifest */
+    public static final String ATTRIBUTE_FROM = "From";
+
+    /** The Class-Path Header is special - it can be duplicated */
+    public static final String ATTRIBUTE_CLASSPATH = "Class-Path";
+
+    /** Default Manifest version if one is not specified */
+    public static final  String DEFAULT_MANIFEST_VERSION = "1.0";
+
+    /** The max length of a line in a Manifest */
+    public static final int MAX_LINE_LENGTH = 72;
+
+    /**
+     * Max length of a line section which is continued. Need to allow
+     * for the CRLF.
+     */
+    public static final int MAX_SECTION_LENGTH = MAX_LINE_LENGTH - 2;
+
+    /** The End-Of-Line marker in manifests */
+    public static final String EOL = "\r\n";
+
+    /**
+     * An attribute for the manifest.
+     * Those attributes that are not nested into a section will be added to the "Main" section.
+     */
+    public static class Attribute {
+        /** The attribute's name */
+        private String name = null;
+
+        /** The attribute's value */
+        private Vector values = new Vector();
+
+        /**
+         * For multivalued attributes, this is the index of the attribute
+         * currently being defined.
+         */
+        private int currentIndex = 0;
+
+        /**
+         * Construct an empty attribute */
+        public Attribute() {
+        }
+
+        /**
+         * Construct an attribute by parsing a line from the Manifest
+         *
+         * @param line the line containing the attribute name and value
+         *
+         * @throws ManifestException if the line is not valid
+         */
+        public Attribute(String line) throws ManifestException {
+            parse(line);
+        }
+
+        /**
+         * Construct a manifest by specifying its name and value
+         *
+         * @param name the attribute's name
+         * @param value the Attribute's value
+         */
+        public Attribute(String name, String value) {
+            this.name = name;
+            setValue(value);
+        }
+
+        /**
+         * @see java.lang.Object#hashCode
+         */
+        public int hashCode() {
+            int hashCode = 0;
+
+            if (name != null) {
+                hashCode += name.hashCode();
+            }
+
+            hashCode += values.hashCode();
+            return hashCode;
+        }
+
+        /**
+         * @see java.lang.Object#equals
+         */
+        public boolean equals(Object rhs) {
+            if (rhs == null || rhs.getClass() != getClass()) {
+                return false;
+            }
+
+            if (rhs == this) {
+                return true;
+            }
+
+            Attribute rhsAttribute = (Attribute) rhs;
+            String lhsKey = getKey();
+            String rhsKey = rhsAttribute.getKey();
+            if ((lhsKey == null && rhsKey != null)
+                 || (lhsKey != null && rhsKey == null)
+                 || !lhsKey.equals(rhsKey)) {
+                return false;
+            }
+
+            return CollectionUtils.equals(values, rhsAttribute.values);
+        }
+
+        /**
+         * Parse a line into name and value pairs
+         *
+         * @param line the line to be parsed
+         *
+         * @throws ManifestException if the line does not contain a colon
+         * separating the name and value
+         */
+        public void parse(String line) throws ManifestException {
+            int index = line.indexOf(": ");
+            if (index == -1) {
+                throw new ManifestException("Manifest line \"" + line
+                    + "\" is not valid as it does not "
+                    + "contain a name and a value separated by ': ' ");
+            }
+            name = line.substring(0, index);
+            setValue(line.substring(index + 2));
+        }
+
+        /**
+         * Set the Attribute's name; required
+         *
+         * @param name the attribute's name
+         */
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        /**
+         * Get the Attribute's name
+         *
+         * @return the attribute's name.
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * Get the attribute's Key - its name in lower case.
+         *
+         * @return the attribute's key.
+         */
+        public String getKey() {
+            if (name == null) {
+                return null;
+            }
+            return name.toLowerCase();
+        }
+
+        /**
+         * Set the Attribute's value; required
+         *
+         * @param value the attribute's value
+         */
+        public void setValue(String value) {
+            if (currentIndex >= values.size()) {
+                values.addElement(value);
+                currentIndex = values.size() - 1;
+            } else {
+                values.setElementAt(value, currentIndex);
+            }
+        }
+
+        /**
+         * Get the Attribute's value.
+         *
+         * @return the attribute's value.
+         */
+        public String getValue() {
+            if (values.size() == 0) {
+                return null;
+            }
+
+            String fullValue = "";
+            for (Enumeration e = getValues(); e.hasMoreElements();) {
+                String value = (String) e.nextElement();
+                fullValue += value + " ";
+            }
+            return fullValue.trim();
+        }
+
+        /**
+         * Add a new value to this attribute - making it multivalued.
+         *
+         * @param value the attribute's additional value
+         */
+        public void addValue(String value) {
+            currentIndex++;
+            setValue(value);
+        }
+
+        /**
+         * Get all the attribute's values.
+         *
+         * @return an enumeration of the attributes values
+         */
+        public Enumeration getValues() {
+            return values.elements();
+        }
+
+        /**
+         * Add a continuation line from the Manifest file.
+         *
+         * When lines are too long in a manifest, they are continued on the
+         * next line by starting with a space. This method adds the continuation
+         * data to the attribute value by skipping the first character.
+         *
+         * @param line the continuation line.
+         */
+        public void addContinuation(String line) {
+            String currentValue = (String) values.elementAt(currentIndex);
+            setValue(currentValue + line.substring(1));
+        }
+
+        /**
+         * Write the attribute out to a print writer.
+         *
+         * @param writer the Writer to which the attribute is written
+         *
+         * @throws IOException if the attribute value cannot be written
+         */
+        public void write(PrintWriter writer) throws IOException {
+            for (Enumeration e = getValues(); e.hasMoreElements();) {
+                writeValue(writer, (String) e.nextElement());
+            }
+        }
+
+        /**
+         * Write a single attribute value out
+         *
+         * @param writer the Writer to which the attribute is written
+         * @param value the attribute value
+         *
+         * @throws IOException if the attribute value cannot be written
+         */
+        private void writeValue(PrintWriter writer, String value)
+             throws IOException {
+            String line = name + ": " + value;
+            while (line.getBytes().length > MAX_LINE_LENGTH) {
+                // try to find a MAX_LINE_LENGTH byte section
+                int breakIndex = MAX_SECTION_LENGTH;
+                String section = line.substring(0, breakIndex);
+                while (section.getBytes().length > MAX_SECTION_LENGTH
+                     && breakIndex > 0) {
+                    breakIndex--;
+                    section = line.substring(0, breakIndex);
+                }
+                if (breakIndex == 0) {
+                    throw new IOException("Unable to write manifest line "
+                        + name + ": " + value);
+                }
+                writer.print(section + EOL);
+                line = " " + line.substring(breakIndex);
+            }
+            writer.print(line + EOL);
+        }
+    }
+
+    /**
+     * A manifest section - you can nest attribute elements into sections.
+     * A section consists of a set of attribute values,
+     * separated from other sections by a blank line.
+     */
+    public static class Section {
+        /** Warnings for this section */
+        private Vector warnings = new Vector();
+
+        /**
+         * The section's name if any. The main section in a
+         * manifest is unnamed.
+         */
+        private String name = null;
+
+        /** The section's attributes.*/
+        private Hashtable attributes = new Hashtable();
+
+        /** Index used to retain the attribute ordering */
+        private Vector attributeIndex = new Vector();
+
+        /**
+         * The name of the section; optional -default is the main section.
+         * @param name the section's name
+         */
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        /**
+         * Get the Section's name.
+         *
+         * @return the section's name.
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * Read a section through a reader.
+         *
+         * @param reader the reader from which the section is read
+         *
+         * @return the name of the next section if it has been read as
+         *         part of this section - This only happens if the
+         *         Manifest is malformed.
+         *
+         * @throws ManifestException if the section is not valid according
+         *         to the JAR spec
+         * @throws IOException if the section cannot be read from the reader.
+         */
+        public String read(BufferedReader reader)
+             throws ManifestException, IOException {
+            Attribute attribute = null;
+            while (true) {
+                String line = reader.readLine();
+                if (line == null || line.length() == 0) {
+                    return null;
+                }
+                if (line.charAt(0) == ' ') {
+                    // continuation line
+                    if (attribute == null) {
+                        if (name != null) {
+                            // a continuation on the first line is a
+                            // continuation of the name - concatenate this
+                            // line and the name
+                            name += line.substring(1);
+                        } else {
+                            throw new ManifestException("Can't start an "
+                                + "attribute with a continuation line " + line);
+                        }
+                    } else {
+                        attribute.addContinuation(line);
+                    }
+                } else {
+                    attribute = new Attribute(line);
+                    String nameReadAhead = addAttributeAndCheck(attribute);
+                    // refresh attribute in case of multivalued attributes.
+                    attribute = getAttribute(attribute.getKey());
+                    if (nameReadAhead != null) {
+                        return nameReadAhead;
+                    }
+                }
+            }
+        }
+
+        /**
+         * Merge in another section
+         *
+         * @param section the section to be merged with this one.
+         *
+         * @throws ManifestException if the sections cannot be merged.
+         */
+        public void merge(Section section) throws ManifestException {
+            if (name == null && section.getName() != null
+                || name != null
+                && !(name.equalsIgnoreCase(section.getName()))) {
+                throw new ManifestException("Unable to merge sections "
+                    + "with different names");
+            }
+
+            Enumeration e = section.getAttributeKeys();
+            Attribute classpathAttribute = null;
+            while (e.hasMoreElements()) {
+                String attributeName = (String) e.nextElement();
+                Attribute attribute = section.getAttribute(attributeName);
+                if (attributeName.equalsIgnoreCase(ATTRIBUTE_CLASSPATH)) {
+                    if (classpathAttribute == null) {
+                        classpathAttribute = new Attribute();
+                        classpathAttribute.setName(ATTRIBUTE_CLASSPATH);
+                    }
+                    Enumeration cpe = attribute.getValues();
+                    while (cpe.hasMoreElements()) {
+                        String value = (String) cpe.nextElement();
+                        classpathAttribute.addValue(value);
+                    }
+                } else {
+                    // the merge file always wins
+                    storeAttribute(attribute);
+                }
+            }
+
+            if (classpathAttribute != null) {
+                // the merge file *always* wins, even for Class-Path
+                storeAttribute(classpathAttribute);
+            }
+
+            // add in the warnings
+            Enumeration warnEnum = section.warnings.elements();
+            while (warnEnum.hasMoreElements()) {
+                warnings.addElement(warnEnum.nextElement());
+            }
+        }
+
+        /**
+         * Write the section out to a print writer.
+         *
+         * @param writer the Writer to which the section is written
+         *
+         * @throws IOException if the section cannot be written
+         */
+        public void write(PrintWriter writer) throws IOException {
+            if (name != null) {
+                Attribute nameAttr = new Attribute(ATTRIBUTE_NAME, name);
+                nameAttr.write(writer);
+            }
+            Enumeration e = getAttributeKeys();
+            while (e.hasMoreElements()) {
+                String key = (String) e.nextElement();
+                Attribute attribute = getAttribute(key);
+                attribute.write(writer);
+            }
+            writer.print(EOL);
+        }
+
+        /**
+         * Get a attribute of the section
+         *
+         * @param attributeName the name of the attribute
+         * @return a Manifest.Attribute instance if the attribute is
+         *         single-valued, otherwise a Vector of Manifest.Attribute
+         *         instances.
+         */
+        public Attribute getAttribute(String attributeName) {
+            return (Attribute) attributes.get(attributeName.toLowerCase());
+        }
+
+        /**
+         * Get the attribute keys.
+         *
+         * @return an Enumeration of Strings, each string being the lower case
+         *         key of an attribute of the section.
+         */
+        public Enumeration getAttributeKeys() {
+            return attributeIndex.elements();
+        }
+
+        /**
+         * Get the value of the attribute with the name given.
+         *
+         * @param attributeName the name of the attribute to be returned.
+         *
+         * @return the attribute's value or null if the attribute does not exist
+         *         in the section
+         */
+        public String getAttributeValue(String attributeName) {
+            Attribute attribute = getAttribute(attributeName.toLowerCase());
+            if (attribute == null) {
+                return null;
+            }
+            return attribute.getValue();
+        }
+
+        /**
+         * Remove tge given attribute from the section
+         *
+         * @param attributeName the name of the attribute to be removed.
+         */
+        public void removeAttribute(String attributeName) {
+            String key = attributeName.toLowerCase();
+            attributes.remove(key);
+            attributeIndex.removeElement(key);
+        }
+
+        /**
+         * Add an attribute to the section.
+         *
+         * @param attribute the attribute to be added to the section
+         *
+         * @exception ManifestException if the attribute is not valid.
+         */
+        public void addConfiguredAttribute(Attribute attribute)
+             throws ManifestException {
+            String check = addAttributeAndCheck(attribute);
+            if (check != null) {
+                throw new BuildException("Specify the section name using "
+                    + "the \"name\" attribute of the <section> element rather "
+                    + "than using a \"Name\" manifest attribute");
+            }
+        }
+
+        /**
+         * Add an attribute to the section
+         *
+         * @param attribute the attribute to be added.
+         *
+         * @return the value of the attribute if it is a name
+         *         attribute - null other wise
+         *
+         * @exception ManifestException if the attribute already
+         *            exists in this section.
+         */
+        public String addAttributeAndCheck(Attribute attribute)
+             throws ManifestException {
+            if (attribute.getName() == null || attribute.getValue() == null) {
+                throw new BuildException("Attributes must have name and value");
+            }
+            if (attribute.getKey().equalsIgnoreCase(ATTRIBUTE_NAME)) {
+                warnings.addElement("\"" + ATTRIBUTE_NAME + "\" attributes "
+                    + "should not occur in the main section and must be the "
+                    + "first element in all other sections: \""
+                    + attribute.getName() + ": " + attribute.getValue() + "\"");
+                return attribute.getValue();
+            }
+
+            if (attribute.getKey().startsWith(ATTRIBUTE_FROM.toLowerCase())) {
+                warnings.addElement("Manifest attributes should not start "
+                    + "with \"" + ATTRIBUTE_FROM + "\" in \""
+                    + attribute.getName() + ": " + attribute.getValue() + "\"");
+            } else {
+                // classpath attributes go into a vector
+                String attributeKey = attribute.getKey();
+                if (attributeKey.equalsIgnoreCase(ATTRIBUTE_CLASSPATH)) {
+                    Attribute classpathAttribute =
+                        (Attribute) attributes.get(attributeKey);
+
+                    if (classpathAttribute == null) {
+                        storeAttribute(attribute);
+                    } else {
+                        warnings.addElement("Multiple Class-Path attributes "
+                            + "are supported but violate the Jar "
+                            + "specification and may not be correctly "
+                            + "processed in all environments");
+                        Enumeration e = attribute.getValues();
+                        while (e.hasMoreElements()) {
+                            String value = (String) e.nextElement();
+                            classpathAttribute.addValue(value);
+                        }
+                    }
+                } else if (attributes.containsKey(attributeKey)) {
+                    throw new ManifestException("The attribute \""
+                        + attribute.getName() + "\" may not occur more "
+                        + "than once in the same section");
+                } else {
+                    storeAttribute(attribute);
+                }
+            }
+            return null;
+        }
+
+        /**
+         * Clone this section
+         *
+         * @return the cloned Section
+         * @since Ant 1.5.2
+         */
+        public Object clone() {
+            Section cloned = new Section();
+            cloned.setName(name);
+            Enumeration e = getAttributeKeys();
+            while (e.hasMoreElements()) {
+                String key = (String) e.nextElement();
+                Attribute attribute = getAttribute(key);
+                cloned.storeAttribute(new Attribute(attribute.getName(),
+                                                    attribute.getValue()));
+            }
+            return cloned;
+        }
+
+        /**
+         * Store an attribute and update the index.
+         *
+         * @param attribute the attribute to be stored
+         */
+        private void storeAttribute(Attribute attribute) {
+            if (attribute == null) {
+                return;
+            }
+            String attributeKey = attribute.getKey();
+            attributes.put(attributeKey, attribute);
+            if (!attributeIndex.contains(attributeKey)) {
+                attributeIndex.addElement(attributeKey);
+            }
+        }
+
+        /**
+         * Get the warnings for this section.
+         *
+         * @return an Enumeration of warning strings.
+         */
+        public Enumeration getWarnings() {
+            return warnings.elements();
+        }
+
+        /**
+         * @see java.lang.Object#hashCode
+         */
+        public int hashCode() {
+            int hashCode = 0;
+
+            if (name != null) {
+                hashCode += name.hashCode();
+            }
+
+            hashCode += attributes.hashCode();
+            return hashCode;
+        }
+
+        /**
+         * @see java.lang.Object#equals
+         */
+        public boolean equals(Object rhs) {
+            if (rhs == null || rhs.getClass() != getClass()) {
+                return false;
+            }
+
+            if (rhs == this) {
+                return true;
+            }
+
+            Section rhsSection = (Section) rhs;
+
+            return CollectionUtils.equals(attributes, rhsSection.attributes);
+        }
+    }
+
+
+    /** The version of this manifest */
+    private String manifestVersion = DEFAULT_MANIFEST_VERSION;
+
+    /** The main section of this manifest */
+    private Section mainSection = new Section();
+
+    /** The named sections of this manifest */
+    private Hashtable sections = new Hashtable();
+
+    /** Index of sections - used to retain order of sections in manifest */
+    private Vector sectionIndex = new Vector();
+
+    /**
+     * Construct a manifest from Ant's default manifest file.
+     *
+     * @return the default manifest.
+     * @exception BuildException if there is a problem loading the
+     *            default manifest
+     */
+    public static Manifest getDefaultManifest() throws BuildException {
+        try {
+            String defManifest = "/org/apache/tools/ant/defaultManifest.mf";
+            InputStream in = Manifest.class.getResourceAsStream(defManifest);
+            if (in == null) {
+                throw new BuildException("Could not find default manifest: "
+                    + defManifest);
+            }
+            try {
+                Manifest defaultManifest
+                    = new Manifest(new InputStreamReader(in, "UTF-8"));
+                Attribute createdBy = new Attribute("Created-By",
+                    System.getProperty("java.vm.version") + " ("
+                    + System.getProperty("java.vm.vendor") + ")");
+                defaultManifest.getMainSection().storeAttribute(createdBy);
+                return defaultManifest;
+            } catch (UnsupportedEncodingException e) {
+                return new Manifest(new InputStreamReader(in));
+            }
+        } catch (ManifestException e) {
+            throw new BuildException("Default manifest is invalid !!", e);
+        } catch (IOException e) {
+            throw new BuildException("Unable to read default manifest", e);
+        }
+    }
+
+    /** Construct an empty manifest */
+    public Manifest() {
+        manifestVersion = null;
+    }
+
+    /**
+     * Read a manifest file from the given reader
+     *
+     * @param r is the reader from which the Manifest is read
+     *
+     * @throws ManifestException if the manifest is not valid according
+     *         to the JAR spec
+     * @throws IOException if the manifest cannot be read from the reader.
+     */
+    public Manifest(Reader r) throws ManifestException, IOException {
+        BufferedReader reader = new BufferedReader(r);
+        // This should be the manifest version
+        String nextSectionName = mainSection.read(reader);
+        String readManifestVersion
+            = mainSection.getAttributeValue(ATTRIBUTE_MANIFEST_VERSION);
+        if (readManifestVersion != null) {
+            manifestVersion = readManifestVersion;
+            mainSection.removeAttribute(ATTRIBUTE_MANIFEST_VERSION);
+        }
+
+        String line = null;
+        while ((line = reader.readLine()) != null) {
+            if (line.length() == 0) {
+                continue;
+            }
+
+            Section section = new Section();
+            if (nextSectionName == null) {
+                Attribute sectionName = new Attribute(line);
+                if (!sectionName.getName().equalsIgnoreCase(ATTRIBUTE_NAME)) {
+                    throw new ManifestException("Manifest sections should "
+                        + "start with a \"" + ATTRIBUTE_NAME
+                        + "\" attribute and not \""
+                        + sectionName.getName() + "\"");
+                }
+                nextSectionName = sectionName.getValue();
+            } else {
+                // we have already started reading this section
+                // this line is the first attribute. set it and then
+                // let the normal read handle the rest
+                Attribute firstAttribute = new Attribute(line);
+                section.addAttributeAndCheck(firstAttribute);
+            }
+
+            section.setName(nextSectionName);
+            nextSectionName = section.read(reader);
+            addConfiguredSection(section);
+        }
+    }
+
+    /**
+     * Add a section to the manifest
+     *
+     * @param section the manifest section to be added
+     *
+     * @exception ManifestException if the secti0on is not valid.
+     */
+    public void addConfiguredSection(Section section)
+         throws ManifestException {
+        String sectionName = section.getName();
+        if (sectionName == null) {
+            throw new BuildException("Sections must have a name");
+        }
+        sections.put(sectionName, section);
+        if (!sectionIndex.contains(sectionName)) {
+            sectionIndex.addElement(sectionName);
+        }
+    }
+
+    /**
+     * Add an attribute to the manifest - it is added to the main section.
+     *
+     * @param attribute the attribute to be added.
+     *
+     * @exception ManifestException if the attribute is not valid.
+     */
+    public void addConfiguredAttribute(Attribute attribute)
+         throws ManifestException {
+        if (attribute.getKey() == null || attribute.getValue() == null) {
+            throw new BuildException("Attributes must have name and value");
+        }
+        if (attribute.getKey().equalsIgnoreCase(ATTRIBUTE_MANIFEST_VERSION)) {
+            manifestVersion = attribute.getValue();
+        } else {
+            mainSection.addConfiguredAttribute(attribute);
+        }
+    }
+
+    /**
+     * Merge the contents of the given manifest into this manifest
+     *
+     * @param other the Manifest to be merged with this one.
+     *
+     * @throws ManifestException if there is a problem merging the
+     *         manifest according to the Manifest spec.
+     */
+    public void merge(Manifest other) throws ManifestException {
+        merge(other, false);
+    }
+
+    /**
+     * Merge the contents of the given manifest into this manifest
+     *
+     * @param other the Manifest to be merged with this one.
+     * @param overwriteMain whether to overwrite the main section
+     *        of the current manifest
+     *
+     * @throws ManifestException if there is a problem merging the
+     *         manifest according to the Manifest spec.
+     */
+    public void merge(Manifest other, boolean overwriteMain)
+         throws ManifestException {
+        if (other != null) {
+             if (overwriteMain) {
+                 mainSection = (Section) other.mainSection.clone();
+             } else {
+                 mainSection.merge(other.mainSection);
+             }
+
+             if (other.manifestVersion != null) {
+                 manifestVersion = other.manifestVersion;
+             }
+
+             Enumeration e = other.getSectionNames();
+             while (e.hasMoreElements()) {
+                 String sectionName = (String) e.nextElement();
+                 Section ourSection = (Section) sections.get(sectionName);
+                 Section otherSection
+                    = (Section) other.sections.get(sectionName);
+                 if (ourSection == null) {
+                     if (otherSection != null) {
+                         addConfiguredSection((Section) otherSection.clone());
+                     }
+                 } else {
+                     ourSection.merge(otherSection);
+                 }
+             }
+         }
+    }
+
+    /**
+    * Write the manifest out to a print writer.
+    *
+    * @param writer the Writer to which the manifest is written
+    *
+    * @throws IOException if the manifest cannot be written
+    */
+    public void write(PrintWriter writer) throws IOException {
+        writer.print(ATTRIBUTE_MANIFEST_VERSION + ": " + manifestVersion + EOL);
+        String signatureVersion
+            = mainSection.getAttributeValue(ATTRIBUTE_SIGNATURE_VERSION);
+        if (signatureVersion != null) {
+            writer.print(ATTRIBUTE_SIGNATURE_VERSION + ": "
+                + signatureVersion + EOL);
+            mainSection.removeAttribute(ATTRIBUTE_SIGNATURE_VERSION);
+        }
+        mainSection.write(writer);
+
+        // add it back
+        if (signatureVersion != null) {
+            try {
+                Attribute svAttr = new Attribute(ATTRIBUTE_SIGNATURE_VERSION,
+                    signatureVersion);
+                mainSection.addConfiguredAttribute(svAttr);
+            } catch (ManifestException e) {
+                // shouldn't happen - ignore
+            }
+        }
+
+        Enumeration e = sectionIndex.elements();
+        while (e.hasMoreElements()) {
+            String sectionName = (String) e.nextElement();
+            Section section = getSection(sectionName);
+            section.write(writer);
+        }
+    }
+
+    /**
+     * Convert the manifest to its string representation
+     *
+     * @return a multiline string with the Manifest as it
+     *         appears in a Manifest file.
+     */
+    public String toString() {
+        StringWriter sw = new StringWriter();
+        try {
+            write(new PrintWriter(sw));
+        } catch (IOException e) {
+            return null;
+        }
+        return sw.toString();
+    }
+
+    /**
+     * Get the warnings for this manifest.
+     *
+     * @return an enumeration of warning strings
+     */
+    public Enumeration getWarnings() {
+        Vector warnings = new Vector();
+
+        Enumeration warnEnum = mainSection.getWarnings();
+        while (warnEnum.hasMoreElements()) {
+            warnings.addElement(warnEnum.nextElement());
+        }
+
+        // create a vector and add in the warnings for all the sections
+        Enumeration e = sections.elements();
+        while (e.hasMoreElements()) {
+            Section section = (Section) e.nextElement();
+            Enumeration e2 = section.getWarnings();
+            while (e2.hasMoreElements()) {
+                warnings.addElement(e2.nextElement());
+            }
+        }
+
+        return warnings.elements();
+    }
+
+    /**
+     * @see java.lang.Object#hashCode
+     */
+    public int hashCode() {
+        int hashCode = 0;
+
+        if (manifestVersion != null) {
+            hashCode += manifestVersion.hashCode();
+        }
+        hashCode += mainSection.hashCode();
+        hashCode += sections.hashCode();
+
+        return hashCode;
+    }
+
+    /**
+     * @see java.lang.Object#equals
+     */
+    public boolean equals(Object rhs) {
+        if (rhs == null || rhs.getClass() != getClass()) {
+            return false;
+        }
+
+        if (rhs == this) {
+            return true;
+        }
+
+        Manifest rhsManifest = (Manifest) rhs;
+        if (manifestVersion == null) {
+            if (rhsManifest.manifestVersion != null) {
+                return false;
+            }
+        } else if (!manifestVersion.equals(rhsManifest.manifestVersion)) {
+            return false;
+        }
+
+        if (!mainSection.equals(rhsManifest.mainSection)) {
+            return false;
+        }
+
+        return CollectionUtils.equals(sections, rhsManifest.sections);
+    }
+
+    /**
+     * Get the version of the manifest
+     *
+     * @return the manifest's version string
+     */
+    public String getManifestVersion() {
+        return manifestVersion;
+    }
+
+    /**
+     * Get the main section of the manifest
+     *
+     * @return the main section of the manifest
+     */
+    public Section getMainSection() {
+        return mainSection;
+    }
+
+    /**
+     * Get a particular section from the manifest
+     *
+     * @param name the name of the section desired.
+     * @return the specified section or null if that section
+     * does not exist in the manifest
+     */
+    public Section getSection(String name) {
+        return (Section) sections.get(name);
+    }
+
+    /**
+     * Get the section names in this manifest.
+     *
+     * @return an Enumeration of section names
+     */
+    public Enumeration getSectionNames() {
+        return sectionIndex.elements();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ManifestException.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ManifestException.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ManifestException.java	(revision 16139)
@@ -0,0 +1,33 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+/**
+ * Exception thrown indicating problems in a JAR Manifest
+ *
+ * @since Ant 1.4
+ */
+public class ManifestException extends Exception {
+
+    /**
+     * Constructs an exception with the given descriptive message.
+     * @param msg Description of or information about the exception.
+     */
+    public ManifestException(String msg) {
+        super(msg);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ManifestTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ManifestTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ManifestTask.java	(revision 16139)
@@ -0,0 +1,213 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStreamReader;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+/**
+ * Creates a manifest file for inclusion in a JAR, Ant task wrapper
+ * around {@link Manifest Manifest}.  This task can be used to write a
+ * Manifest file, optionally replacing or updating an existing file.
+ *
+ *
+ * @since Ant 1.5
+ *
+ * @ant.task category="java"
+ */
+public class ManifestTask extends Task {
+
+    /**
+     * Holds the real data.
+     */
+    private Manifest nestedManifest = new Manifest();
+
+    /**
+     * The file to which the manifest should be written when used as a task
+     */
+    private File manifestFile;
+
+    /**
+     * The mode with which the manifest file is written
+     */
+    private Mode mode;
+
+    /**
+     * The encoding of the manifest file
+     */
+    private String encoding;
+
+    /**
+     * Helper class for Manifest's mode attribute.
+     */
+    public static class Mode extends EnumeratedAttribute {
+        /**
+         * Get Allowed values for the mode attribute.
+         *
+         * @return a String array of the allowed values.
+         */
+        public String[] getValues() {
+            return new String[] {"update", "replace"};
+        }
+    }
+
+    /**
+     * Default constructor
+     */
+    public ManifestTask() {
+        mode = new Mode();
+        mode.setValue("replace");
+    }
+
+    /**
+     * Add a section to the manifest
+     *
+     * @param section the manifest section to be added
+     *
+     * @exception ManifestException if the section is not valid.
+     */
+    public void addConfiguredSection(Manifest.Section section)
+         throws ManifestException {
+        nestedManifest.addConfiguredSection(section);
+    }
+
+    /**
+     * Add an attribute to the manifest - it is added to the main section.
+     *
+     * @param attribute the attribute to be added.
+     *
+     * @exception ManifestException if the attribute is not valid.
+     */
+    public void addConfiguredAttribute(Manifest.Attribute attribute)
+         throws ManifestException {
+        nestedManifest.addConfiguredAttribute(attribute);
+    }
+
+    /**
+     * The name of the manifest file to create/update.
+     * Required if used as a task.
+     * @param f the Manifest file to be written
+     */
+    public void setFile(File f) {
+        manifestFile = f;
+    }
+
+    /**
+     * The encoding to use for reading in an existing manifest file
+     * @param encoding the manifest file encoding.
+     */
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * Update policy: either "update" or "replace"; default is "replace".
+     * @param m the mode value - update or replace.
+     */
+    public void setMode(Mode m) {
+        mode = m;
+    }
+
+    /**
+     * Create or update the Manifest when used as a task.
+     *
+     * @throws BuildException if the manifest cannot be written.
+     */
+    public void execute() throws BuildException {
+        if (manifestFile == null) {
+            throw new BuildException("the file attribute is required");
+        }
+
+        Manifest toWrite = Manifest.getDefaultManifest();
+        Manifest current = null;
+        BuildException error = null;
+
+        if (manifestFile.exists()) {
+            FileInputStream fis = null;
+            InputStreamReader isr = null;
+            try {
+                fis = new FileInputStream(manifestFile);
+                if (encoding == null) {
+                    isr = new InputStreamReader(fis, "UTF-8");
+                } else {
+                    isr = new InputStreamReader(fis, encoding);
+                }
+                current = new Manifest(isr);
+            } catch (ManifestException m) {
+                error = new BuildException("Existing manifest " + manifestFile
+                                           + " is invalid", m, getLocation());
+            } catch (IOException e) {
+                error = new BuildException("Failed to read " + manifestFile,
+                                           e, getLocation());
+            } finally {
+                if (isr != null) {
+                    try {
+                        isr.close();
+                    } catch (IOException e) {
+                        // ignore
+                    }
+                }
+            }
+        }
+
+        try {
+            if (mode.getValue().equals("update") && manifestFile.exists()) {
+                if (current != null) {
+                    toWrite.merge(current);
+                } else if (error != null) {
+                    throw error;
+                }
+            }
+
+            toWrite.merge(nestedManifest);
+        } catch (ManifestException m) {
+            throw new BuildException("Manifest is invalid", m, getLocation());
+        }
+
+        if (toWrite.equals(current)) {
+            log("Manifest has not changed, do not recreate",
+                Project.MSG_VERBOSE);
+            return;
+        }
+
+        PrintWriter w = null;
+        try {
+            FileOutputStream fos = new FileOutputStream(manifestFile);
+            OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
+            w = new PrintWriter(osw);
+            toWrite.write(w);
+        } catch (IOException e) {
+            throw new BuildException("Failed to write " + manifestFile,
+                                     e, getLocation());
+        } finally {
+            if (w != null) {
+                w.close();
+            }
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/MatchingTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/MatchingTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/MatchingTask.java	(revision 16139)
@@ -0,0 +1,439 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.selectors.AndSelector;
+import org.apache.tools.ant.types.selectors.ContainsRegexpSelector;
+import org.apache.tools.ant.types.selectors.ContainsSelector;
+import org.apache.tools.ant.types.selectors.DateSelector;
+import org.apache.tools.ant.types.selectors.DependSelector;
+import org.apache.tools.ant.types.selectors.DepthSelector;
+import org.apache.tools.ant.types.selectors.DifferentSelector;
+import org.apache.tools.ant.types.selectors.ExtendSelector;
+import org.apache.tools.ant.types.selectors.FileSelector;
+import org.apache.tools.ant.types.selectors.FilenameSelector;
+import org.apache.tools.ant.types.selectors.MajoritySelector;
+import org.apache.tools.ant.types.selectors.NoneSelector;
+import org.apache.tools.ant.types.selectors.NotSelector;
+import org.apache.tools.ant.types.selectors.OrSelector;
+import org.apache.tools.ant.types.selectors.PresentSelector;
+import org.apache.tools.ant.types.selectors.SelectSelector;
+import org.apache.tools.ant.types.selectors.SelectorContainer;
+import org.apache.tools.ant.types.selectors.SizeSelector;
+import org.apache.tools.ant.types.selectors.TypeSelector;
+import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector;
+
+/**
+ * This is an abstract task that should be used by all those tasks that
+ * require to include or exclude files based on pattern matching.
+ *
+ * @since Ant 1.1
+ */
+
+public abstract class MatchingTask extends Task implements SelectorContainer {
+
+    protected FileSet fileset = new FileSet();
+
+    /**
+     * @see org.apache.tools.ant.ProjectComponent#setProject
+     */
+    public void setProject(Project project) {
+        super.setProject(project);
+        fileset.setProject(project);
+    }
+
+    /**
+     * add a name entry on the include list
+     * @return a NameEntry object to be configured
+     */
+    public PatternSet.NameEntry createInclude() {
+        return fileset.createInclude();
+    }
+
+    /**
+     * add a name entry on the include files list
+     * @return an NameEntry object to be configured
+     */
+    public PatternSet.NameEntry createIncludesFile() {
+        return fileset.createIncludesFile();
+    }
+
+    /**
+     * add a name entry on the exclude list
+     * @return an NameEntry object to be configured
+     */
+    public PatternSet.NameEntry createExclude() {
+        return fileset.createExclude();
+    }
+
+    /**
+     * add a name entry on the include files list
+     * @return an NameEntry object to be configured
+     */
+    public PatternSet.NameEntry createExcludesFile() {
+        return fileset.createExcludesFile();
+    }
+
+    /**
+     * add a set of patterns
+     * @return PatternSet object to be configured
+     */
+    public PatternSet createPatternSet() {
+        return fileset.createPatternSet();
+    }
+
+    /**
+     * Sets the set of include patterns. Patterns may be separated by a comma
+     * or a space.
+     *
+     * @param includes the string containing the include patterns
+     */
+    public void setIncludes(String includes) {
+        fileset.setIncludes(includes);
+    }
+
+    /**
+     * Set this to be the items in the base directory that you want to be
+     * included. You can also specify "*" for the items (ie: items="*")
+     * and it will include all the items in the base directory.
+     *
+     * @param itemString the string containing the files to include.
+     */
+    public void XsetItems(String itemString) {
+        log("The items attribute is deprecated. "
+            + "Please use the includes attribute.", Project.MSG_WARN);
+        if (itemString == null || itemString.equals("*")
+            || itemString.equals(".")) {
+            createInclude().setName("**");
+        } else {
+            StringTokenizer tok = new StringTokenizer(itemString, ", ");
+            while (tok.hasMoreTokens()) {
+                String pattern = tok.nextToken().trim();
+                if (pattern.length() > 0) {
+                    createInclude().setName(pattern + "/**");
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets the set of exclude patterns. Patterns may be separated by a comma
+     * or a space.
+     *
+     * @param excludes the string containing the exclude patterns
+     */
+    public void setExcludes(String excludes) {
+        fileset.setExcludes(excludes);
+    }
+
+    /**
+     * List of filenames and directory names to not include. They should be
+     * either , or " " (space) separated. The ignored files will be logged.
+     *
+     * @param ignoreString the string containing the files to ignore.
+     */
+    public void XsetIgnore(String ignoreString) {
+        log("The ignore attribute is deprecated."
+            + "Please use the excludes attribute.", Project.MSG_WARN);
+        if (ignoreString != null && ignoreString.length() > 0) {
+            StringTokenizer tok = new StringTokenizer(ignoreString, ", ",
+                                                      false);
+            while (tok.hasMoreTokens()) {
+                createExclude().setName("**/" + tok.nextToken().trim() + "/**");
+            }
+        }
+    }
+
+    /**
+     * Sets whether default exclusions should be used or not.
+     *
+     * @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
+     *                           should be used, "false"|"off"|"no" when they
+     *                           shouldn't be used.
+     */
+    public void setDefaultexcludes(boolean useDefaultExcludes) {
+        fileset.setDefaultexcludes(useDefaultExcludes);
+    }
+
+    /**
+     * Returns the directory scanner needed to access the files to process.
+     */
+    protected DirectoryScanner getDirectoryScanner(File baseDir) {
+        fileset.setDir(baseDir);
+        return fileset.getDirectoryScanner(getProject());
+    }
+
+    /**
+     * Sets the name of the file containing the includes patterns.
+     *
+     * @param includesfile A string containing the filename to fetch
+     * the include patterns from.
+     */
+    public void setIncludesfile(File includesfile) {
+        fileset.setIncludesfile(includesfile);
+    }
+
+    /**
+     * Sets the name of the file containing the includes patterns.
+     *
+     * @param excludesfile A string containing the filename to fetch
+     * the include patterns from.
+     */
+    public void setExcludesfile(File excludesfile) {
+        fileset.setExcludesfile(excludesfile);
+    }
+
+    /**
+     * Sets case sensitivity of the file system
+     *
+     * @param isCaseSensitive "true"|"on"|"yes" if file system is case
+     *                           sensitive, "false"|"off"|"no" when not.
+     */
+    public void setCaseSensitive(boolean isCaseSensitive) {
+        fileset.setCaseSensitive(isCaseSensitive);
+    }
+
+    /**
+     * Sets whether or not symbolic links should be followed.
+     *
+     * @param followSymlinks whether or not symbolic links should be followed
+     */
+    public void setFollowSymlinks(boolean followSymlinks) {
+        fileset.setFollowSymlinks(followSymlinks);
+    }
+
+    /**
+     * Indicates whether there are any selectors here.
+     *
+     * @return whether any selectors are in this container
+     */
+    public boolean hasSelectors() {
+        return fileset.hasSelectors();
+    }
+
+    /**
+     * Gives the count of the number of selectors in this container
+     *
+     * @return the number of selectors in this container
+     */
+    public int selectorCount() {
+        return fileset.selectorCount();
+    }
+
+    /**
+     * Returns the set of selectors as an array.
+     * @param p the current project
+     * @return an array of selectors in this container
+     */
+    public FileSelector[] getSelectors(Project p) {
+        return fileset.getSelectors(p);
+    }
+
+    /**
+     * Returns an enumerator for accessing the set of selectors.
+     *
+     * @return an enumerator that goes through each of the selectors
+     */
+    public Enumeration selectorElements() {
+        return fileset.selectorElements();
+    }
+
+    /**
+     * Add a new selector into this container.
+     *
+     * @param selector the new selector to add
+     */
+    public void appendSelector(FileSelector selector) {
+        fileset.appendSelector(selector);
+    }
+
+    /* Methods below all add specific selectors */
+
+    /**
+     * add a "Select" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addSelector(SelectSelector selector) {
+        fileset.addSelector(selector);
+    }
+
+    /**
+     * add an "And" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addAnd(AndSelector selector) {
+        fileset.addAnd(selector);
+    }
+
+    /**
+     * add an "Or" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addOr(OrSelector selector) {
+        fileset.addOr(selector);
+    }
+
+    /**
+     * add a "Not" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addNot(NotSelector selector) {
+        fileset.addNot(selector);
+    }
+
+    /**
+     * add a "None" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addNone(NoneSelector selector) {
+        fileset.addNone(selector);
+    }
+
+    /**
+     * add a majority selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addMajority(MajoritySelector selector) {
+        fileset.addMajority(selector);
+    }
+
+    /**
+     * add a selector date entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addDate(DateSelector selector) {
+        fileset.addDate(selector);
+    }
+
+    /**
+     * add a selector size entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addSize(SizeSelector selector) {
+        fileset.addSize(selector);
+    }
+
+    /**
+     * add a selector filename entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addFilename(FilenameSelector selector) {
+        fileset.addFilename(selector);
+    }
+
+    /**
+     * add an extended selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addCustom(ExtendSelector selector) {
+        fileset.addCustom(selector);
+    }
+
+    /**
+     * add a contains selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addContains(ContainsSelector selector) {
+        fileset.addContains(selector);
+    }
+
+    /**
+     * add a present selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addPresent(PresentSelector selector) {
+        fileset.addPresent(selector);
+    }
+
+    /**
+     * add a depth selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addDepth(DepthSelector selector) {
+        fileset.addDepth(selector);
+    }
+
+    /**
+     * add a depends selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addDepend(DependSelector selector) {
+        fileset.addDepend(selector);
+    }
+
+    /**
+     * add a regular expression selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addContainsRegexp(ContainsRegexpSelector selector) {
+        fileset.addContainsRegexp(selector);
+    }
+
+    /**
+     * add a type selector entry on the type list
+     * @param selector the selector to add
+     * @since ant 1.6
+     */
+    public void addDifferent(DifferentSelector selector) {
+        fileset.addDifferent(selector);
+    }
+
+    /**
+     * add a type selector entry on the type list
+     * @param selector the selector to add
+     * @since ant 1.6
+     */
+    public void addType(TypeSelector selector) {
+        fileset.addType(selector);
+    }
+
+    /**
+     * add the modified selector
+     * @param selector the selector to add
+     * @since ant 1.6
+     */
+    public void addModified(ModifiedSelector selector) {
+        fileset.addModified(selector);
+    }
+
+    /**
+     * add an arbitary selector
+     * @param selector the selector to add
+     * @since Ant 1.6
+     */
+    public void add(FileSelector selector) {
+        fileset.add(selector);
+    }
+
+    /**
+     * Accessor for the implicit fileset.
+     *
+     * @since Ant 1.5.2
+     */
+    protected final FileSet getImplicitFileSet() {
+        return fileset;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Mkdir.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Mkdir.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Mkdir.java	(revision 16139)
@@ -0,0 +1,93 @@
+/*
+ * Copyright  2000,2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Creates a given directory.
+ * Creates a directory and any non-existent parent directories, when
+ * necessary
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="filesystem"
+ */
+
+public class Mkdir extends Task {
+
+    private static final int MKDIR_RETRY_SLEEP_MILLIS = 10;
+    /**
+     * our little directory
+     */
+    private File dir;
+
+    /**
+     * create the directory and all parents
+     * @throws BuildException if dir is somehow invalid, or creation failed.
+     */
+    public void execute() throws BuildException {
+        if (dir == null) {
+            throw new BuildException("dir attribute is required", getLocation());
+        }
+
+        if (dir.isFile()) {
+            throw new BuildException("Unable to create directory as a file "
+                                     + "already exists with that name: "
+                                     + dir.getAbsolutePath());
+        }
+
+        if (!dir.exists()) {
+            boolean result = mkdirs(dir);
+            if (!result) {
+                String msg = "Directory " + dir.getAbsolutePath()
+                    + " creation was not successful for an unknown reason";
+                throw new BuildException(msg, getLocation());
+            }
+            log("Created dir: " + dir.getAbsolutePath());
+        }
+    }
+
+    /**
+     * the directory to create; required.
+     *
+     * @param dir the directory to be made.
+     */
+    public void setDir(File dir) {
+        this.dir = dir;
+    }
+    /**
+     * Attempt to fix possible race condition when creating
+     * directories on WinXP. If the mkdirs does not work,
+     * wait a little and try again.
+     */
+    private boolean mkdirs(File f) {
+        if (!f.mkdirs()) {
+            try {
+                Thread.sleep(MKDIR_RETRY_SLEEP_MILLIS);
+                return f.mkdirs();
+            } catch (InterruptedException ex) {
+                return f.mkdirs();
+            }
+        }
+        return true;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Move.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Move.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Move.java	(revision 16139)
@@ -0,0 +1,384 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.FilterSet;
+import org.apache.tools.ant.types.FilterSetCollection;
+
+/**
+ * Moves a file or directory to a new file or directory.
+ * By default, the
+ * destination file is overwritten if it already exists.
+ * When <i>overwrite</i> is
+ * turned off, then files are only moved if the source file is
+ * newer than the destination file, or when the destination file does
+ * not exist.
+ *
+ * <p>Source files and directories are only deleted when the file or
+ * directory has been copied to the destination successfully.  Filtering
+ * also works.</p>
+ *
+ * <p>This implementation is based on Arnout Kuiper's initial design
+ * document, the following mailing list discussions, and the
+ * copyfile/copydir tasks.</p>
+ *
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task category="filesystem"
+ */
+public class Move extends Copy {
+
+    /**
+     * Constructor of object.
+     * This sets the forceOverwrite attribute of the Copy parent class
+     * to true.
+     *
+     */
+    public Move() {
+        super();
+        setOverwrite(true);
+    }
+
+    // inherit doc
+    protected void validateAttributes() throws BuildException {
+        if (file != null && file.isDirectory()) {
+            if ((destFile != null && destDir != null)
+                || (destFile == null && destDir == null)) {
+                throw new BuildException("One and only one of tofile and todir "
+                                         + "must be set.");
+            }
+            destFile = (destFile == null)
+                ? new File(destDir, file.getName()) : destFile;
+            destDir = (destDir == null)
+                ? destFile.getParentFile() : destDir;
+
+            completeDirMap.put(file, destFile);
+            file = null;
+        } else {
+            super.validateAttributes();
+        }
+    }
+
+//************************************************************************
+//  protected and private methods
+//************************************************************************
+
+    /**
+     * Override copy's doFileOperations to move the
+     * files instead of copying them.
+     */
+    protected void doFileOperations() {
+        //Attempt complete directory renames, if any, first.
+        if (completeDirMap.size() > 0) {
+            Enumeration e = completeDirMap.keys();
+            while (e.hasMoreElements()) {
+                File fromDir = (File) e.nextElement();
+                File toDir = (File) completeDirMap.get(fromDir);
+                boolean renamed = false;
+                try {
+                    log("Attempting to rename dir: " + fromDir
+                        + " to " + toDir, verbosity);
+                    renamed =
+                        renameFile(fromDir, toDir, filtering, forceOverwrite);
+                } catch (IOException ioe) {
+                    String msg = "Failed to rename dir " + fromDir
+                        + " to " + toDir
+                        + " due to " + ioe.getMessage();
+                    throw new BuildException(msg, ioe, getLocation());
+                }
+                if (!renamed) {
+                    FileSet fs = new FileSet();
+                    fs.setProject(getProject());
+                    fs.setDir(fromDir);
+                    addFileset(fs);
+                    DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+                    String[] files = ds.getIncludedFiles();
+                    String[] dirs = ds.getIncludedDirectories();
+                    scan(fromDir, toDir, files, dirs);
+                }
+            }
+        }
+        int moveCount = fileCopyMap.size();
+        if (moveCount > 0) {   // files to move
+            log("Moving " + moveCount + " file"
+                + ((moveCount == 1) ? "" : "s")
+                + " to " + destDir.getAbsolutePath());
+
+            Enumeration e = fileCopyMap.keys();
+            while (e.hasMoreElements()) {
+                String fromFile = (String) e.nextElement();
+
+                File f = new File(fromFile);
+                boolean selfMove = false;
+                if (f.exists()) { //Is this file still available to be moved?
+                    String[] toFiles = (String[]) fileCopyMap.get(fromFile);
+                    for (int i = 0; i < toFiles.length; i++) {
+                        String toFile = (String) toFiles[i];
+
+                        if (fromFile.equals(toFile)) {
+                            log("Skipping self-move of " + fromFile, verbosity);
+                            selfMove = true;
+
+                            // if this is the last time through the loop then
+                            // move will not occur, but that's what we want
+                            continue;
+                        }
+                        File d = new File(toFile);
+                        if ((i + 1) == toFiles.length && !selfMove) {
+                            // Only try to move if this is the last mapped file
+                            // and one of the mappings isn't to itself
+                            moveFile(f, d, filtering, forceOverwrite);
+                        } else {
+                            copyFile(f, d, filtering, forceOverwrite);
+                        }
+                    }
+                }
+            }
+        }
+
+        if (includeEmpty) {
+            Enumeration e = dirCopyMap.keys();
+            int createCount = 0;
+            while (e.hasMoreElements()) {
+                String fromDirName = (String) e.nextElement();
+                String[] toDirNames = (String[]) dirCopyMap.get(fromDirName);
+                boolean selfMove = false;
+                for (int i = 0; i < toDirNames.length; i++) {
+
+                    if (fromDirName.equals(toDirNames[i])) {
+                        log("Skipping self-move of " + fromDirName, verbosity);
+                        selfMove = true;
+                        continue;
+                    }
+
+                    File d = new File(toDirNames[i]);
+                    if (!d.exists()) {
+                        if (!d.mkdirs()) {
+                            log("Unable to create directory "
+                                + d.getAbsolutePath(), Project.MSG_ERR);
+                        } else {
+                            createCount++;
+                        }
+                    }
+                }
+
+                File fromDir = new File(fromDirName);
+                if (!selfMove && okToDelete(fromDir)) {
+                    deleteDir(fromDir);
+                }
+
+            }
+
+            if (createCount > 0) {
+                log("Moved " + dirCopyMap.size()
+                    + " empty director"
+                    + (dirCopyMap.size() == 1 ? "y" : "ies")
+                    + " to " + createCount
+                    + " empty director"
+                    + (createCount == 1 ? "y" : "ies") + " under "
+                    + destDir.getAbsolutePath());
+            }
+        }
+    }
+
+    /**
+     * Try to move the file via a rename, but if this fails or filtering
+     * is enabled, copy the file then delete the sourceFile.
+     */
+    private void moveFile(File fromFile, File toFile,
+                          boolean filtering, boolean overwrite) {
+        boolean moved = false;
+        try {
+            log("Attempting to rename: " + fromFile
+                + " to " + toFile, verbosity);
+            moved = renameFile(fromFile, toFile, filtering, forceOverwrite);
+        } catch (IOException ioe) {
+            String msg = "Failed to rename " + fromFile
+                + " to " + toFile
+                + " due to " + ioe.getMessage();
+            throw new BuildException(msg, ioe, getLocation());
+        }
+
+        if (!moved) {
+            copyFile(fromFile, toFile, filtering, overwrite);
+            if (!fromFile.delete()) {
+                throw new BuildException("Unable to delete "
+                                        + "file "
+                                        + fromFile.getAbsolutePath());
+            }
+        }
+    }
+
+    /**
+     * Copy fromFile to toFile.
+     * @param fromFile
+     * @param toFile
+     * @param filtering
+     * @param overwrite
+     */
+    private void copyFile(File fromFile, File toFile,
+                          boolean filtering, boolean overwrite) {
+        try {
+            log("Copying " + fromFile + " to " + toFile,
+                verbosity);
+
+            FilterSetCollection executionFilters =
+                new FilterSetCollection();
+            if (filtering) {
+                executionFilters
+                    .addFilterSet(getProject().getGlobalFilterSet());
+            }
+            for (Enumeration filterEnum =
+                    getFilterSets().elements();
+                filterEnum.hasMoreElements();) {
+                executionFilters
+                    .addFilterSet((FilterSet) filterEnum
+                                .nextElement());
+            }
+
+            getFileUtils().copyFile(fromFile, toFile, executionFilters,
+                                    getFilterChains(),
+                                    forceOverwrite,
+                                    getPreserveLastModified(),
+                                    getEncoding(),
+                                    getOutputEncoding(),
+                                    getProject());
+
+        } catch (IOException ioe) {
+            String msg = "Failed to copy " + fromFile
+                + " to " + toFile
+                + " due to " + ioe.getMessage();
+            throw new BuildException(msg, ioe, getLocation());
+        }
+    }
+
+
+    /**
+     * Its only ok to delete a directory tree if there are
+     * no files in it.
+     * @param d the directory to check
+     * @return true if a deletion can go ahead
+     */
+    protected boolean okToDelete(File d) {
+        String[] list = d.list();
+        if (list == null) {
+            return false;
+        }     // maybe io error?
+
+        for (int i = 0; i < list.length; i++) {
+            String s = list[i];
+            File f = new File(d, s);
+            if (f.isDirectory()) {
+                if (!okToDelete(f)) {
+                    return false;
+                }
+            } else {
+                return false;   // found a file
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Go and delete the directory tree.
+     * @param d the directory to delete
+     */
+    protected void deleteDir(File d) {
+        deleteDir(d, false);
+    }
+
+    /**
+     * Go and delete the directory tree.
+     * @param d the directory to delete
+     * @param deleteFiles whether to delete files
+     */
+    protected void deleteDir(File d, boolean deleteFiles) {
+        String[] list = d.list();
+        if (list == null) {
+            return;
+        }      // on an io error list() can return null
+
+        for (int i = 0; i < list.length; i++) {
+            String s = list[i];
+            File f = new File(d, s);
+            if (f.isDirectory()) {
+                deleteDir(f);
+            } else if (deleteFiles && !(f.delete())) {
+                throw new BuildException("Unable to delete file "
+                                         + f.getAbsolutePath());
+            } else {
+                throw new BuildException("UNEXPECTED ERROR - The file "
+                                         + f.getAbsolutePath()
+                                         + " should not exist!");
+            }
+        }
+        log("Deleting directory " + d.getAbsolutePath(), verbosity);
+        if (!d.delete()) {
+            throw new BuildException("Unable to delete directory "
+                                     + d.getAbsolutePath());
+        }
+    }
+
+    /**
+     * Attempts to rename a file from a source to a destination.
+     * If overwrite is set to true, this method overwrites existing file
+     * even if the destination file is newer.  Otherwise, the source file is
+     * renamed only if the destination file is older than it.
+     * Method then checks if token filtering is used.  If it is, this method
+     * returns false assuming it is the responsibility to the copyFile method.
+     *
+     * @param sourceFile the file to rename
+     * @param destFile   the destination file
+     * @param filtering  if true, filtering is in operation, file will
+     *                   be copied/deleted instead of renamed
+     * @param overwrite  if true force overwrite even if destination file
+     *                   is newer than source file
+     * @return true if the file was renamed
+     * @exception IOException if an error occurs
+     * @exception BuildException if an error occurs
+     */
+    protected boolean renameFile(File sourceFile, File destFile,
+                                 boolean filtering, boolean overwrite)
+        throws IOException, BuildException {
+
+        boolean renamed = false;
+        if ((getFilterSets().size() + getFilterChains().size() == 0)
+            && !(filtering || destFile.isDirectory())) {
+            // ensure that parent dir of dest file exists!
+            File parent = destFile.getParentFile();
+            if (parent != null && !parent.exists()) {
+                parent.mkdirs();
+            }
+            if (destFile.isFile() && !destFile.delete()) {
+                throw new BuildException("Unable to remove existing "
+                                         + "file " + destFile);
+            }
+            renamed = sourceFile.renameTo(destFile);
+        }
+        return renamed;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Nice.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Nice.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Nice.java	(revision 16139)
@@ -0,0 +1,98 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * A task to provide "nice-ness" to the current thread, and/or to
+ * query the current value.
+ * Examples:
+ * <pre> &lt;nice currentPriority="current.value" &gt;</pre><p>
+ * Set <code>currentPriority</code> to the current priority
+ * <pre> &lt;nice newPriority="10" &gt;</pre><p>
+ * Raise the priority of the build process (But not forked programs)
+ * <pre> &lt;nice currentPriority="old" newPriority="3" &gt;</pre><p>
+ * Lower the priority of the build process (But not forked programs), and save
+ * the old value to the property <code>old</code>.
+ *
+ * @ant.task name="nice" category="control"
+ */
+public class Nice extends Task {
+
+    /**
+     * the new priority
+     */
+    private Integer newPriority;
+
+    /**
+     * the current priority
+     */
+    private String currentPriority;
+
+
+
+    /**
+     * Execute the task
+     * @exception BuildException if something goes wrong with the build
+     */
+    public void execute() throws BuildException {
+
+        Thread self = Thread.currentThread();
+        int priority = self.getPriority();
+        if (currentPriority != null) {
+            String current = Integer.toString(priority);
+            getProject().setNewProperty(currentPriority, current);
+        }
+        //if there is a new priority, and it is different, change it
+        if (newPriority != null && priority != newPriority.intValue()) {
+            try {
+                self.setPriority(newPriority.intValue());
+            } catch (SecurityException e) {
+                //catch permissions denial and keep going
+                log("Unable to set new priority -a security manager is in the way",
+                        Project.MSG_WARN);
+            } catch (IllegalArgumentException iae) {
+                throw new BuildException("Priority out of range", iae);
+            }
+        }
+    }
+
+    /**
+     * The name of a property to set to the value of the current
+     * thread priority. Optional
+     * @param currentPriority the property name.
+     */
+    public void setCurrentPriority(String currentPriority) {
+        this.currentPriority = currentPriority;
+    }
+
+    /**
+     * the new priority, in the range 1-10.
+     * @param newPriority the new priority value.
+     */
+    public void setNewPriority(int newPriority) {
+        if (newPriority < Thread.MIN_PRIORITY || newPriority > Thread.MAX_PRIORITY) {
+            throw new BuildException("The thread priority is out of the range 1-10");
+        }
+        this.newPriority = new Integer(newPriority);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Pack.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Pack.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Pack.java	(revision 16139)
@@ -0,0 +1,145 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Abstract Base class for pack tasks.
+ *
+ *
+ * @since Ant 1.5
+ */
+
+public abstract class Pack extends Task {
+
+    protected File zipFile;
+    protected File source;
+
+    /**
+     * the required destination file.
+     * @param zipFile
+     */
+    public void setZipfile(File zipFile) {
+        this.zipFile = zipFile;
+    }
+
+    /**
+     * the required destination file.
+     * @param zipFile
+     */
+    public void setDestfile(File zipFile) {
+        setZipfile(zipFile);
+    }
+
+    /**
+     * the file to compress; required.
+     * @param src
+     */
+    public void setSrc(File src) {
+        source = src;
+    }
+
+
+    /**
+     * validation routine
+     * @throws BuildException if anything is invalid
+     */
+    private void validate() throws BuildException {
+        if (zipFile == null) {
+            throw new BuildException("zipfile attribute is required", getLocation());
+        }
+
+        if (zipFile.isDirectory()) {
+            throw new BuildException("zipfile attribute must not "
+                                    + "represent a directory!", getLocation());
+        }
+
+        if (source == null) {
+            throw new BuildException("src attribute is required", getLocation());
+        }
+
+        if (source.isDirectory()) {
+            throw new BuildException("Src attribute must not "
+                                    + "represent a directory!", getLocation());
+        }
+    }
+
+    /**
+     * validate, then hand off to the subclass
+     * @throws BuildException
+     */
+    public void execute() throws BuildException {
+        validate();
+
+        if (!source.exists()) {
+            log("Nothing to do: " + source.getAbsolutePath()
+                + " doesn't exist.");
+        } else if (zipFile.lastModified() < source.lastModified()) {
+            log("Building: " + zipFile.getAbsolutePath());
+            pack();
+        } else {
+            log("Nothing to do: " + zipFile.getAbsolutePath()
+                + " is up to date.");
+        }
+    }
+
+    /**
+     * zip a stream to an output stream
+     * @param in
+     * @param zOut
+     * @throws IOException
+     */
+    private void zipFile(InputStream in, OutputStream zOut)
+        throws IOException {
+        byte[] buffer = new byte[8 * 1024];
+        int count = 0;
+        do {
+            zOut.write(buffer, 0, count);
+            count = in.read(buffer, 0, buffer.length);
+        } while (count != -1);
+    }
+
+    /**
+     * zip a file to an output stream
+     * @param file
+     * @param zOut
+     * @throws IOException
+     */
+    protected void zipFile(File file, OutputStream zOut)
+        throws IOException {
+        FileInputStream fIn = new FileInputStream(file);
+        try {
+            zipFile(fIn, zOut);
+        } finally {
+            fIn.close();
+        }
+    }
+
+    /**
+     * subclasses must implement this method to do their compression
+     */
+    protected abstract void pack();
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Parallel.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Parallel.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Parallel.java	(revision 16139)
@@ -0,0 +1,458 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.List;
+import java.util.ArrayList;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Executes the contained tasks in separate threads, continuing
+ * once all are completed.
+ * <p>
+ * New behavior allows for the ant script to specify a maximum number of
+ * threads that will be executed in parallel.  One should be very careful about
+ * using the <code>waitFor</code> task when specifying <code>threadCount</code>
+ * as it can cause deadlocks if the number of threads is too small or if one of
+ * the nested tasks fails to execute completely.  The task selection algorithm
+ * will insure that the tasks listed before a task have started before that
+ * task is started, but it will not insure a successful completion of those
+ * tasks or that those tasks will finish first (i.e. it's a classic race
+ * condition).
+ * </p>
+ * @since Ant 1.4
+ *
+ * @ant.task category="control"
+ */
+public class Parallel extends Task
+                      implements TaskContainer {
+
+    /** Class which holds a list of tasks to execute */
+    public static class TaskList implements TaskContainer {
+        /** Collection holding the nested tasks */
+        private List tasks = new ArrayList();
+
+        /**
+         * Add a nested task to execute parallel (asynchron).
+         * <p>
+         * @param nestedTask  Nested task to be executed in parallel.
+         *                    must not be null.
+         */
+        public void addTask(Task nestedTask) {
+            tasks.add(nestedTask);
+        }
+    }
+
+    /** Collection holding the nested tasks */
+    private Vector nestedTasks = new Vector();
+
+    /** Semaphore to notify of completed threads */
+    private final Object semaphore = new Object();
+
+    /** Total number of threads to run */
+    private int numThreads = 0;
+
+    /** Total number of threads per processor to run.  */
+    private int numThreadsPerProcessor = 0;
+
+    /** The timeout period in milliseconds */
+    private long timeout;
+
+    /** Indicates threads are still running and new threads can be issued */
+    private volatile boolean stillRunning;
+
+    /** Indicates that the execution timedout */
+    private boolean timedOut;
+
+    /**
+     * Indicates whether failure of any of the nested tasks should end
+     * execution
+     */
+    private boolean failOnAny;
+
+    /** The dameon task list if any */
+    private TaskList daemonTasks;
+
+    /** Accumulation of exceptions messages from all nested tasks */
+    private StringBuffer exceptionMessage;
+
+    /** Number of exceptions from nested tasks */
+    private int numExceptions = 0;
+
+    /** The first exception encountered */
+    private Throwable firstException;
+
+    /** The location of the first exception */
+    private Location firstLocation;
+
+    /**
+     * Add a group of daemon threads
+     * @param daemonTasks The tasks to be executed as daemon.
+     */
+    public void addDaemons(TaskList daemonTasks) {
+        if (this.daemonTasks != null) {
+            throw new BuildException("Only one daemon group is supported");
+        }
+        this.daemonTasks = daemonTasks;
+    }
+
+    /**
+     * Interval to poll for completed threads when threadCount or
+     * threadsPerProcessor is specified.  Integer in milliseconds.; optional
+     *
+     * @param pollInterval New value of property pollInterval.
+     */
+    public void setPollInterval(int pollInterval) {
+    }
+
+    /**
+     * Control whether a failure in a nested task halts execution. Note that
+     * the task will complete but existing threads will continue to run - they
+     * are not stopped
+     *
+     * @param failOnAny if true any nested task failure causes parallel to
+     *        complete.
+     */
+    public void setFailOnAny(boolean failOnAny) {
+        this.failOnAny = failOnAny;
+    }
+
+    /**
+     * Add a nested task to execute in parallel.
+     * @param nestedTask  Nested task to be executed in parallel
+     */
+    public void addTask(Task nestedTask) {
+        nestedTasks.addElement(nestedTask);
+    }
+
+    /**
+     * Dynamically generates the number of threads to execute based on the
+     * number of available processors (via
+     * <code>java.lang.Runtime.availableProcessors()</code>). Requires a J2SE
+     * 1.4 VM, and it will overwrite the value set in threadCount.
+     * If used in a 1.1, 1.2, or 1.3 VM then the task will defer to
+     * <code>threadCount</code>.; optional
+     * @param numThreadsPerProcessor Number of threads to create per available
+     *        processor.
+     *
+     */
+    public void setThreadsPerProcessor(int numThreadsPerProcessor) {
+        this.numThreadsPerProcessor = numThreadsPerProcessor;
+    }
+
+    /**
+     * Statically determine the maximum number of tasks to execute
+     * simultaneously.  If there are less tasks than threads then all will be
+     * executed at once, if there are more then only <code>threadCount</code>
+     * tasks will be executed at one time.  If <code>threadsPerProcessor</code>
+     * is set and the JVM is at least a 1.4 VM then this value is
+     * ignored.; optional
+     *
+     * @param numThreads total number of threads.
+     *
+     */
+    public void setThreadCount(int numThreads) {
+        this.numThreads = numThreads;
+    }
+
+    /**
+     * Sets the timeout on this set of tasks. If the timeout is reached
+     * before the other threads complete, the execution of this
+     * task completes with an exception.
+     *
+     * Note that existing threads continue to run.
+     *
+     * @param timeout timeout in milliseconds.
+     */
+    public void setTimeout(long timeout) {
+        this.timeout = timeout;
+    }
+
+
+
+    /**
+     * Execute the parallel tasks
+     *
+     * @exception BuildException if any of the threads failed.
+     */
+    public void execute() throws BuildException {
+        updateThreadCounts();
+        if (numThreads == 0) {
+            numThreads = nestedTasks.size();
+        }
+        spinThreads();
+    }
+
+    /**
+     * Determine the number of threads based on the number of processors
+     */
+    private void updateThreadCounts() {
+        if (numThreadsPerProcessor != 0) {
+            int numProcessors = getNumProcessors();
+            if (numProcessors != 0) {
+                numThreads = numProcessors * numThreadsPerProcessor;
+            }
+        }
+    }
+
+    private void processExceptions(TaskRunnable[] runnables) {
+        if (runnables == null) {
+            return;
+        }
+        for (int i = 0; i < runnables.length; ++i) {
+            Throwable t = runnables[i].getException();
+            if (t != null) {
+                numExceptions++;
+                if (firstException == null) {
+                    firstException = t;
+                }
+                if (t instanceof BuildException
+                    && firstLocation == Location.UNKNOWN_LOCATION) {
+                    firstLocation = ((BuildException) t).getLocation();
+                }
+                exceptionMessage.append(StringUtils.LINE_SEP);
+                exceptionMessage.append(t.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Spin up required threads with a maximum number active at any given time.
+     *
+     * @exception BuildException if any of the threads failed.
+     */
+    private void spinThreads() throws BuildException {
+        final int numTasks = nestedTasks.size();
+        TaskRunnable[] runnables = new TaskRunnable[numTasks];
+        stillRunning = true;
+        timedOut = false;
+
+        int threadNumber = 0;
+        for (Enumeration e = nestedTasks.elements(); e.hasMoreElements();
+             threadNumber++) {
+            Task nestedTask = (Task) e.nextElement();
+            runnables[threadNumber]
+                = new TaskRunnable(nestedTask);
+        }
+
+        final int maxRunning = numTasks < numThreads ? numTasks : numThreads;
+        TaskRunnable[] running = new TaskRunnable[maxRunning];
+
+        threadNumber = 0;
+        ThreadGroup group = new ThreadGroup("parallel");
+
+        TaskRunnable[] daemons = null;
+        if (daemonTasks != null && daemonTasks.tasks.size() != 0) {
+            daemons = new TaskRunnable[daemonTasks.tasks.size()];
+        }
+
+        synchronized (semaphore) {
+            // When we leave this block we can be sure all data is really
+            // stored in main memory before the new threads start, the new
+            // threads will for sure load the data from main memory.
+            //
+            // This probably is slightly paranoid.
+        }
+
+        synchronized (semaphore) {
+            // start any daemon threads
+            if (daemons != null) {
+                for (int i = 0; i < daemons.length; ++i) {
+                    daemons[i] = new TaskRunnable((Task) daemonTasks.tasks.get(i));
+                    Thread daemonThread =  new Thread(group, daemons[i]);
+                    daemonThread.setDaemon(true);
+                    daemonThread.start();
+                }
+            }
+
+            // now run main threads in limited numbers...
+            // start initial batch of threads
+            for (int i = 0; i < maxRunning; ++i) {
+                running[i] = runnables[threadNumber++];
+                Thread thread =  new Thread(group, running[i]);
+                thread.start();
+            }
+
+            if (timeout != 0) {
+                // start the timeout thread
+                Thread timeoutThread = new Thread() {
+                    public synchronized void run() {
+                        try {
+                            wait(timeout);
+                            synchronized (semaphore) {
+                                stillRunning = false;
+                                timedOut = true;
+                                semaphore.notifyAll();
+                            }
+                        } catch (InterruptedException e) {
+                            // ignore
+                        }
+                    }
+                };
+                timeoutThread.start();
+            }
+
+            // now find available running slots for the remaining threads
+            outer:
+            while (threadNumber < numTasks && stillRunning) {
+                for (int i = 0; i < maxRunning; i++) {
+                    if (running[i] == null || running[i].isFinished()) {
+                        running[i] = runnables[threadNumber++];
+                        Thread thread =  new Thread(group, running[i]);
+                        thread.start();
+                        // continue on outer while loop to get another
+                        // available slot
+                        continue outer;
+                    }
+                }
+
+                // if we got here all slots in use, so sleep until
+                // something happens
+                try {
+                    semaphore.wait();
+                } catch (InterruptedException ie) {
+                    // doesn't java know interruptions are rude?
+                    // just pretend it didn't happen and go about out business.
+                    // sheesh!
+                }
+            }
+
+            // are all threads finished
+            outer2:
+            while (stillRunning) {
+                for (int i = 0; i < maxRunning; ++i) {
+                    if (running[i] != null && !running[i].isFinished()) {
+                        //System.out.println("Thread " + i + " is still alive ");
+                        // still running - wait for it
+                        try {
+                            semaphore.wait();
+                        } catch (InterruptedException ie) {
+                            // who would interrupt me at a time like this?
+                        }
+                        continue outer2;
+                    }
+                }
+                stillRunning = false;
+            }
+        }
+
+        if (timedOut) {
+            throw new BuildException("Parallel execution timed out");
+        }
+
+        // now did any of the threads throw an exception
+        exceptionMessage = new StringBuffer();
+        numExceptions = 0;
+        firstException = null;
+        firstLocation = Location.UNKNOWN_LOCATION;
+        processExceptions(daemons);
+        processExceptions(runnables);
+
+        if (numExceptions == 1) {
+            if (firstException instanceof BuildException) {
+                throw (BuildException) firstException;
+            } else {
+                throw new BuildException(firstException);
+            }
+        } else if (numExceptions > 1) {
+            throw new BuildException(exceptionMessage.toString(),
+                                     firstLocation);
+        }
+    }
+
+    /**
+     * Determine the number of processors. Only effective on later VMs
+     *
+     * @return the number of processors available or 0 if not determinable.
+     */
+    private int getNumProcessors() {
+        try {
+            Class[] paramTypes = {};
+            Method availableProcessors =
+                Runtime.class.getMethod("availableProcessors", paramTypes);
+
+            Object[] args = {};
+            Integer ret = (Integer) availableProcessors.invoke(Runtime.getRuntime(), args);
+            return ret.intValue();
+        } catch (Exception e) {
+            // return a bogus number
+            return 0;
+        }
+    }
+
+    /**
+     * thread that execs a task
+     */
+    private class TaskRunnable implements Runnable {
+        private Throwable exception;
+        private Task task;
+        private boolean finished;
+
+        /**
+         * Construct a new TaskRunnable.<p>
+         *
+         * @param task the Task to be executed in a separate thread
+         */
+        TaskRunnable(Task task) {
+            this.task = task;
+        }
+
+        /**
+         * Executes the task within a thread and takes care about
+         * Exceptions raised within the task.
+         */
+        public void run() {
+            try {
+                task.perform();
+            } catch (Throwable t) {
+                exception = t;
+                if (failOnAny) {
+                    stillRunning = false;
+                }
+            } finally {
+                synchronized (semaphore) {
+                    finished = true;
+                    semaphore.notifyAll();
+                }
+            }
+        }
+
+        /**
+         * get any exception that got thrown during execution;
+         * @return an exception or null for no exception/not yet finished
+         */
+        public Throwable getException() {
+            return exception;
+        }
+
+        /**
+         * Provides the indicator that the task has been finished.
+         * @return Returns true when the task is finished.
+         */
+        boolean isFinished() {
+            return finished;
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Patch.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Patch.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Patch.java	(revision 16139)
@@ -0,0 +1,186 @@
+/*
+ * Copyright  2000,2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Patches a file by applying a 'diff' file to it; requires "patch" to be
+ * on the execution path.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="utility"
+ */
+public class Patch extends Task {
+
+    private File originalFile;
+    private File directory;
+    private boolean havePatchfile = false;
+    private Commandline cmd = new Commandline();
+
+    /**
+     * The file to patch; optional if it can be inferred from
+     * the diff file
+     * @param file the file to patch
+     */
+    public void setOriginalfile(File file) {
+        originalFile = file;
+    }
+
+    /**
+     * The name of a file to send the output to, instead of patching
+     * the file(s) in place; optional.
+     * @param file the file to send the output to
+     * @since Ant 1.6
+     */
+    public void setDestfile(File file) {
+        if (file != null) {
+            cmd.createArgument().setValue("-o");
+            cmd.createArgument().setFile(file);
+        }
+    }
+
+    /**
+     * The file containing the diff output; required.
+     * @param file the file containing the diff output
+     */
+    public void setPatchfile(File file) {
+        if (!file.exists()) {
+            throw new BuildException("patchfile " + file + " doesn\'t exist",
+                                     getLocation());
+        }
+        cmd.createArgument().setValue("-i");
+        cmd.createArgument().setFile(file);
+        havePatchfile = true;
+    }
+
+    /**
+     * flag to create backups; optional, default=false
+     * @param backups if true create backups
+     */
+    public void setBackups(boolean backups) {
+        if (backups) {
+            cmd.createArgument().setValue("-b");
+        }
+    }
+
+    /**
+     * flag to ignore whitespace differences; default=false
+     * @param ignore if true ignore whitespace differences
+     */
+    public void setIgnorewhitespace(boolean ignore) {
+        if (ignore) {
+            cmd.createArgument().setValue("-l");
+        }
+    }
+
+    /**
+     * Strip the smallest prefix containing <i>num</i> leading slashes
+     * from filenames.
+     *
+     * <p>patch's <i>-p</i> option.
+     * @param num number of lines to strip
+     * @exception BuildException if num is < 0, or other errors
+     */
+    public void setStrip(int num) throws BuildException {
+        if (num < 0) {
+            throw new BuildException("strip has to be >= 0", getLocation());
+        }
+        cmd.createArgument().setValue("-p" + num);
+    }
+
+    /**
+     * Work silently unless an error occurs; optional, default=false
+     * @param q if true suppress set the -s option on the patch command
+     */
+    public void setQuiet(boolean q) {
+        if (q) {
+            cmd.createArgument().setValue("-s");
+        }
+    }
+
+    /**
+     * Assume patch was created with old and new files swapped; optional,
+     * default=false
+     * @param r if true set the -R option on the patch command
+     */
+    public void setReverse(boolean r) {
+        if (r) {
+            cmd.createArgument().setValue("-R");
+        }
+    }
+
+    /**
+     * The directory to run the patch command in, defaults to the
+     * project's base directory.
+     * @param directory the directory to run the patch command in
+     * @since Ant 1.5
+     */
+    public void setDir(File directory) {
+        this.directory = directory;
+    }
+
+    /**
+     * execute patch
+     * @throws BuildException when it all goes a bit pear shaped
+     */
+    public void execute() throws BuildException {
+        if (!havePatchfile) {
+            throw new BuildException("patchfile argument is required",
+                                     getLocation());
+        }
+        Commandline toExecute = (Commandline) cmd.clone();
+        toExecute.setExecutable("patch");
+
+        if (originalFile != null) {
+            toExecute.createArgument().setFile(originalFile);
+        }
+
+        Execute exe = new Execute(new LogStreamHandler(this, Project.MSG_INFO,
+                                                       Project.MSG_WARN),
+                                  null);
+        exe.setCommandline(toExecute.getCommandline());
+
+        if (directory != null) {
+            if (directory.exists() && directory.isDirectory()) {
+                exe.setWorkingDirectory(directory);
+            } else if (!directory.isDirectory()) {
+                throw new BuildException(directory + " is not a directory.",
+                                         getLocation());
+            } else {
+                throw new BuildException("directory " + directory
+                                         + " doesn\'t exist", getLocation());
+            }
+        } else {
+            exe.setWorkingDirectory(getProject().getBaseDir());
+        }
+
+        log(toExecute.describeCommand(), Project.MSG_VERBOSE);
+        try {
+            exe.execute();
+        } catch (IOException e) {
+            throw new BuildException(e, getLocation());
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/PathConvert.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/PathConvert.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/PathConvert.java	(revision 16139)
@@ -0,0 +1,472 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.List;
+import java.util.ArrayList;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.DirSet;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileList;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.util.FileNameMapper;
+
+/**
+ * Converts path and classpath information to a specific target OS
+ * format. The resulting formatted path is placed into the specified property.
+ *
+ * @since Ant 1.4
+ * @ant.task category="utility"
+ */
+public class PathConvert extends Task {
+
+    // Members
+    /**
+     * Path to be converted
+     */
+    private Path path = null;
+    /**
+     * Reference to path/fileset to convert
+     */
+    private Reference refid = null;
+    /**
+     * The target OS type
+     */
+    private String targetOS = null;
+    /**
+     * Set when targetOS is set to windows
+     */
+    private boolean targetWindows = false;
+    /**
+     * Set if we're running on windows
+     */
+    private boolean onWindows = false;
+    /**
+     * Set if we should create a new property even if the result is empty
+     */
+    private boolean setonempty = true;
+    /**
+     * The property to receive the conversion
+     */
+    private String property = null;
+    /**
+     * Path prefix map
+     */
+    private Vector prefixMap = new Vector();
+    /**
+     * User override on path sep char
+     */
+    private String pathSep = null;
+    /**
+     * User override on directory sep char
+     */
+    private String dirSep = null;
+
+    /** Filename mapper */
+    private Mapper mapper = null;
+
+    /**
+     * Construct a new instance of the PathConvert task.
+     */
+    public PathConvert() {
+        onWindows = Os.isFamily("dos");
+    }
+
+    /**
+     * Helper class, holds the nested &lt;map&gt; values. Elements will look like
+     * this: &lt;map from=&quot;d:&quot; to=&quot;/foo&quot;/&gt;
+     *
+     * When running on windows, the prefix comparison will be case
+     * insensitive.
+     */
+    public class MapEntry {
+
+        // Members
+        private String from = null;
+        private String to = null;
+
+        /**
+         * Set the &quot;from&quot; attribute of the map entry.
+         * @param from the prefix string to search for; required.
+         * Note that this value is case-insensitive when the build is
+         * running on a Windows platform and case-sensitive when running on
+         * a Unix platform.
+         */
+        public void setFrom(String from) {
+            this.from = from;
+        }
+
+        /**
+         * Set the replacement text to use when from is matched; required.
+         * @param to new prefix.
+         */
+        public void setTo(String to) {
+            this.to = to;
+        }
+
+        /**
+         * Apply this map entry to a given path element.
+         *
+         * @param elem Path element to process.
+         * @return String Updated path element after mapping.
+         */
+        public String apply(String elem) {
+            if (from == null || to == null) {
+                throw new BuildException("Both 'from' and 'to' must be set "
+                     + "in a map entry");
+            }
+            // If we're on windows, then do the comparison ignoring case
+            String cmpElem = onWindows ? elem.toLowerCase() : elem;
+            String cmpFrom = onWindows ? from.toLowerCase() : from;
+
+            // If the element starts with the configured prefix, then
+            // convert the prefix to the configured 'to' value.
+
+            return cmpElem.startsWith(cmpFrom)
+                ? to + elem.substring(from.length()) : elem;
+        }
+    }
+
+    /**
+     * An enumeration of supported targets:
+     * "windows", "unix", "netware", and "os/2".
+     */
+    public static class TargetOs extends EnumeratedAttribute {
+        /**
+         * @return the list of values for this enumerated attribute.
+         */
+        public String[] getValues() {
+            return new String[]{"windows", "unix", "netware", "os/2", "tandem"};
+        }
+    }
+
+    /**
+     * Create a nested PATH element.
+     * @return a Path to be used by Ant reflection.
+     */
+    public Path createPath() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        if (path == null) {
+            path = new Path(getProject());
+        }
+        return path.createPath();
+    }
+
+    /**
+     * Create a nested MAP element.
+     * @return a Map to configure.
+     */
+    public MapEntry createMap() {
+        MapEntry entry = new MapEntry();
+        prefixMap.addElement(entry);
+        return entry;
+    }
+
+    /**
+     * Set targetos to a platform to one of
+     * "windows", "unix", "netware", or "os/2";
+     * current platform settings are used by default.
+     * @deprecated use the method taking a TargetOs argument instead.
+     * @see #setTargetos(PathConvert.TargetOs)
+     */
+    public void setTargetos(String target) {
+        TargetOs to = new TargetOs();
+        to.setValue(target);
+        setTargetos(to);
+    }
+
+    /**
+     * Set targetos to a platform to one of
+     * "windows", "unix", "netware", or "os/2";
+     * current platform settings are used by default.
+     * @param target the target os
+     *
+     * @since Ant 1.5
+     */
+    public void setTargetos(TargetOs target) {
+        targetOS = target.getValue();
+
+        // Currently, we deal with only two path formats: Unix and Windows
+        // And Unix is everything that is not Windows
+
+        // for NetWare and OS/2, piggy-back on Windows, since in the
+        // validateSetup code, the same assumptions can be made as
+        // with windows - that ; is the path separator
+
+        targetWindows = !targetOS.equals("unix") && !targetOS.equals("tandem");
+    }
+
+    /**
+     * Set whether the specified property will be set if the result
+     * is the empty string.
+     * @param setonempty true or false.
+     *
+     * @since Ant 1.5
+     */
+     public void setSetonempty(boolean setonempty) {
+         this.setonempty = setonempty;
+     }
+
+    /**
+     * Set the name of the property into which the converted path will be placed.
+     * @param p the property name.
+     */
+    public void setProperty(String p) {
+        property = p;
+    }
+
+    /**
+     * Add a reference to a Path, FileSet, DirSet, or FileList defined elsewhere.
+     * @param r the reference to a path, fileset, dirset or filelist.
+     */
+    public void setRefid(Reference r) {
+        if (path != null) {
+            throw noChildrenAllowed();
+        }
+        refid = r;
+    }
+
+    /**
+     * Set the default path separator string; defaults to current JVM
+     * {@link java.io.File#pathSeparator File.pathSeparator}.
+     * @param sep path separator string.
+     */
+    public void setPathSep(String sep) {
+        pathSep = sep;
+    }
+
+
+    /**
+     * Set the default directory separator string;
+     * defaults to current JVM {@link java.io.File#separator File.separator}.
+     * @param sep directory separator string.
+     */
+    public void setDirSep(String sep) {
+        dirSep = sep;
+    }
+
+    /**
+     * Learn whether the refid attribute of this element been set.
+     * @return true if refid is valid.
+     */
+    public boolean isReference() {
+        return refid != null;
+    }
+
+    /**
+     * Do the execution.
+     * @throws BuildException if something is invalid.
+     */
+    public void execute() throws BuildException {
+        Path savedPath = path;
+        String savedPathSep = pathSep; // may be altered in validateSetup
+        String savedDirSep = dirSep; // may be altered in validateSetup
+
+        try {
+            // If we are a reference, create a Path from the reference
+            if (isReference()) {
+                path = new Path(getProject()).createPath();
+                Object obj = refid.getReferencedObject(getProject());
+
+                if (obj instanceof Path) {
+                    path.setRefid(refid);
+                } else if (obj instanceof FileSet) {
+                    path.addFileset((FileSet) obj);
+                } else if (obj instanceof DirSet) {
+                    path.addDirset((DirSet) obj);
+                } else if (obj instanceof FileList) {
+                    path.addFilelist((FileList) obj);
+                } else {
+                    throw new BuildException("'refid' does not refer to a "
+                         + "path, fileset, dirset, or filelist.");
+                }
+            }
+            validateSetup(); // validate our setup
+
+            // Currently, we deal with only two path formats: Unix and Windows
+            // And Unix is everything that is not Windows
+            // (with the exception for NetWare and OS/2 below)
+
+            // for NetWare and OS/2, piggy-back on Windows, since here and
+            // in the apply code, the same assumptions can be made as with
+            // windows - that \\ is an OK separator, and do comparisons
+            // case-insensitive.
+            String fromDirSep = onWindows ? "\\" : "/";
+
+            StringBuffer rslt = new StringBuffer();
+
+            // Get the list of path components in canonical form
+            String[] elems = path.list();
+
+            if (mapper != null) {
+                FileNameMapper impl = mapper.getImplementation();
+                List ret = new ArrayList();
+                for (int i = 0; i < elems.length; ++i) {
+                    String[] mapped = impl.mapFileName(elems[i]);
+                    for (int m = 0; mapped != null && m < mapped.length; ++m) {
+                        ret.add(mapped[m]);
+                    }
+                }
+                elems = (String[]) ret.toArray(new String[] {});
+            }
+            for (int i = 0; i < elems.length; i++) {
+                String elem = mapElement(elems[i]); // Apply the path prefix map
+
+                // Now convert the path and file separator characters from the
+                // current os to the target os.
+
+                if (i != 0) {
+                    rslt.append(pathSep);
+                }
+                StringTokenizer stDirectory =
+                    new StringTokenizer(elem, fromDirSep, true);
+
+                while (stDirectory.hasMoreTokens()) {
+                    String token = stDirectory.nextToken();
+                    rslt.append(fromDirSep.equals(token) ? dirSep : token);
+                }
+            }
+            // Place the result into the specified property,
+            // unless setonempty == false
+            if (setonempty || rslt.length() > 0) {
+                String value = rslt.toString();
+                log("Set property " + property + " = " + value,
+                    Project.MSG_VERBOSE);
+                getProject().setNewProperty(property, value);
+            }
+        } finally {
+            path = savedPath;
+            dirSep = savedDirSep;
+            pathSep = savedPathSep;
+        }
+    }
+
+    /**
+     * Apply the configured map to a path element. The map is used to convert
+     * between Windows drive letters and Unix paths. If no map is configured,
+     * then the input string is returned unchanged.
+     *
+     * @param elem The path element to apply the map to.
+     * @return String Updated element.
+     */
+    private String mapElement(String elem) {
+
+        int size = prefixMap.size();
+
+        if (size != 0) {
+
+            // Iterate over the map entries and apply each one.
+            // Stop when one of the entries actually changes the element.
+
+            for (int i = 0; i < size; i++) {
+                MapEntry entry = (MapEntry) prefixMap.elementAt(i);
+                String newElem = entry.apply(elem);
+
+                // Note I'm using "!=" to see if we got a new object back from
+                // the apply method.
+
+                if (newElem != elem) {
+                    elem = newElem;
+                    break; // We applied one, so we're done
+                }
+            }
+        }
+        return elem;
+    }
+
+    /**
+     * Add a mapper to convert the file names.
+     *
+     * @param mapper a <code>Mapper</code> value.
+     */
+    public void addMapper(Mapper mapper) {
+        if (this.mapper != null) {
+            throw new BuildException(
+                "Cannot define more than one mapper");
+        }
+        this.mapper = mapper;
+    }
+
+    /**
+     * Add a nested filenamemapper.
+     * @param fileNameMapper the mapper to add.
+     * @since Ant 1.6.3
+     */
+    public void add(FileNameMapper fileNameMapper) {
+        Mapper m = new Mapper(getProject());
+        m.add(fileNameMapper);
+        addMapper(m);
+    }
+
+
+    /**
+     * Validate that all our parameters have been properly initialized.
+     *
+     * @throws BuildException if something is not set up properly.
+     */
+    private void validateSetup() throws BuildException {
+
+        if (path == null) {
+            throw new BuildException("You must specify a path to convert");
+        }
+        if (property == null) {
+            throw new BuildException("You must specify a property");
+        }
+        // Determine the separator strings.  The dirsep and pathsep attributes
+        // override the targetOS settings.
+        String dsep = File.separator;
+        String psep = File.pathSeparator;
+
+        if (targetOS != null) {
+            psep = targetWindows ? ";" : ":";
+            dsep = targetWindows ? "\\" : "/";
+        }
+        if (pathSep != null) {
+            // override with pathsep=
+            psep = pathSep;
+        }
+        if (dirSep != null) {
+            // override with dirsep=
+            dsep = dirSep;
+        }
+        pathSep = psep;
+        dirSep = dsep;
+    }
+
+    /**
+     * Creates an exception that indicates that this XML element must not have
+     * child elements if the refid attribute is set.
+     * @return BuildException.
+     */
+    private BuildException noChildrenAllowed() {
+        return new BuildException("You must not specify nested <path> "
+             + "elements when using the refid attribute.");
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/PreSetDef.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/PreSetDef.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/PreSetDef.java	(revision 16139)
@@ -0,0 +1,299 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.AntTypeDefinition;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ComponentHelper;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.UnknownElement;
+
+/**
+ * The preset definition task generates a new definition
+ * based on a current definition with some attributes or
+ * elements preset.
+ * <pre>
+ * &lt;presetdef name="my.javac"&gt;
+ *   &lt;javac deprecation="${deprecation}" debug="${debug}"/&gt;
+ * &lt;/presetdef&gt;
+ * &lt;my.javac srcdir="src" destdir="classes"/&gt;
+ * </pre>
+ *
+ * @since Ant 1.6
+ */
+public class PreSetDef extends AntlibDefinition implements TaskContainer {
+    private UnknownElement nestedTask;
+    private String         name;
+
+    /**
+     * Name of the definition
+     * @param name the name of the definition
+     */
+     public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Add a nested task to predefine attributes and elements on
+     * @param nestedTask  Nested task/type to extend
+     */
+    public void addTask(Task nestedTask) {
+        if (this.nestedTask != null) {
+            throw new BuildException("Only one nested element allowed");
+        }
+        if (!(nestedTask instanceof UnknownElement)) {
+            throw new BuildException(
+                "addTask called with a task that is not an unknown element");
+        }
+        this.nestedTask = (UnknownElement) nestedTask;
+    }
+
+
+    /**
+     * make a new definition
+     */
+    public void execute() {
+        if (nestedTask == null) {
+            throw new BuildException("Missing nested element");
+        }
+        if (name == null) {
+            throw new BuildException("Name not specified");
+        }
+
+        name = ProjectHelper.genComponentName(getURI(), name);
+
+        ComponentHelper helper = ComponentHelper.getComponentHelper(
+            getProject());
+
+        String componentName = ProjectHelper.genComponentName(
+            nestedTask.getNamespace(), nestedTask.getTag());
+
+        AntTypeDefinition def = helper.getDefinition(componentName);
+        if (def == null) {
+            throw new BuildException(
+                "Unable to find typedef " + componentName);
+        }
+
+        PreSetDefinition newDef = new PreSetDefinition(def, nestedTask);
+
+        newDef.setName(name);
+
+        helper.addDataTypeDefinition(newDef);
+    }
+
+    /**
+     * This class contains the unknown element and the object
+     * that is predefined.
+     * @see AntTypeDefinition
+     */
+    public static class PreSetDefinition extends AntTypeDefinition {
+        private AntTypeDefinition parent;
+        private UnknownElement    element;
+
+        /**
+         * Creates a new <code>PresetDefinition</code> instance.
+         *
+         * @param parent The parent of this predefintion.
+         * @param el     The predefined attributes, nested elements and text.
+         */
+        public PreSetDefinition(AntTypeDefinition parent, UnknownElement el) {
+            if (parent instanceof PreSetDefinition) {
+                PreSetDefinition p = (PreSetDefinition) parent;
+                el.applyPreSet(p.element);
+                parent = p.parent;
+            }
+            this.parent = parent;
+            this.element = el;
+        }
+
+        /**
+         * Override so that it is not allowed
+         *
+         * @param clazz a <code>Class</code> value
+         */
+        public void setClass(Class clazz) {
+            throw new BuildException("Not supported");
+        }
+
+        /**
+         * Override so that it is not allowed
+         *
+         * @param className a <code>String</code> value
+         */
+        public void setClassName(String className) {
+            throw new BuildException("Not supported");
+        }
+
+        /**
+         * get the classname of the definition
+         * @return the name of the class of this definition
+         */
+        public String getClassName() {
+            return parent.getClassName();
+        }
+
+        /**
+         * set the adapter class for this definition.
+         * NOTE Supported
+         * @param adapterClass the adapterClass
+         */
+        public void setAdapterClass(Class adapterClass) {
+            throw new BuildException("Not supported");
+        }
+
+        /**
+         * set the assignable class for this definition.
+         * NOT SUPPORTED
+         * @param adaptToClass the assignable class
+         */
+
+        public void setAdaptToClass(Class adaptToClass) {
+            throw new BuildException("Not supported");
+        }
+
+        /**
+         * set the classloader to use to create an instance
+         * of the definition
+         * @param classLoader the classLoader
+         */
+        public void setClassLoader(ClassLoader classLoader) {
+            throw new BuildException("Not supported");
+        }
+
+        /**
+         * get the classloader for this definition
+         * @return the classloader for this definition
+         */
+        public ClassLoader getClassLoader() {
+            return parent.getClassLoader();
+        }
+
+        /**
+         * get the exposed class for this definition.
+         * @param project the current project
+         * @return the exposed class
+         */
+        public Class getExposedClass(Project project) {
+            return parent.getExposedClass(project);
+        }
+
+        /**
+         * get the definition class
+         * @param project the current project
+         * @return the type of the definition
+         */
+        public Class getTypeClass(Project project) {
+            return parent.getTypeClass(project);
+        }
+
+
+        /**
+         * check if the attributes are correct
+         * @param project the current project
+         */
+        public void checkClass(Project project) {
+            parent.checkClass(project);
+        }
+
+        /**
+         * create an instance of the definition.
+         * The instance may be wrapped in a proxy class.
+         * This is a special version of create for IH and UE.
+         * @param project the current project
+         * @return the created object
+         */
+        public Object createObject(Project project) {
+            Object o = parent.create(project);
+            if (o == null) {
+                return null;
+            }
+            return o;
+        }
+
+        /**
+         * @return the predefined attributes, elements and text as
+         *         a UnknownElement
+         */
+        public UnknownElement getPreSets() {
+            return element;
+        }
+
+        /**
+         * Fake create an object, used by IH and UE to see that
+         * this is a predefined object.
+         *
+         * @param project the current project
+         * @return this object
+         */
+        public Object create(Project project) {
+            return this;
+        }
+
+        /**
+         * Equality method for this definition
+         *
+         * @param other another definition
+         * @param project the current project
+         * @return true if the definitions are the same
+         */
+        public boolean sameDefinition(AntTypeDefinition other, Project project) {
+            if (other == null) {
+                return false;
+            }
+            if (other.getClass() != getClass()) {
+                return false;
+            }
+            PreSetDefinition otherDef = (PreSetDefinition) other;
+            if (!parent.sameDefinition(otherDef.parent, project)) {
+                return false;
+            }
+            if (!element.similar(otherDef.element)) {
+                return false;
+            }
+            return true;
+        }
+
+        /**
+         * Similar method for this definition
+         *
+         * @param other another definition
+         * @param project the current project
+         * @return true if the definitions are the same
+         */
+        public boolean similarDefinition(
+            AntTypeDefinition other, Project project) {
+            if (other == null) {
+                return false;
+            }
+            if (!other.getClass().getName().equals(getClass().getName())) {
+                return false;
+            }
+            PreSetDefinition otherDef = (PreSetDefinition) other;
+            if (!parent.similarDefinition(otherDef.parent, project)) {
+                return false;
+            }
+            if (!element.similar(otherDef.element)) {
+                return false;
+            }
+            return true;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ProcessDestroyer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ProcessDestroyer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/ProcessDestroyer.java	(revision 16139)
@@ -0,0 +1,216 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * Destroys all registered <code>Process</code>es when the VM exits.
+ *
+ * @since Ant 1.5
+ */
+class ProcessDestroyer implements Runnable {
+
+    private Vector processes = new Vector();
+    // methods to register and unregister shutdown hooks
+    private Method addShutdownHookMethod;
+    private Method removeShutdownHookMethod;
+    private ProcessDestroyerImpl destroyProcessThread = null;
+
+    // whether or not this ProcessDestroyer has been registered as a
+    // shutdown hook
+    private boolean added = false;
+    // whether or not this ProcessDestroyer is currently running as
+    // shutdown hook
+    private boolean running = false;
+
+    private class ProcessDestroyerImpl extends Thread {
+        private boolean shouldDestroy = true;
+
+        public ProcessDestroyerImpl() {
+            super("ProcessDestroyer Shutdown Hook");
+        }
+        public void run() {
+            if (shouldDestroy) {
+                ProcessDestroyer.this.run();
+            }
+        }
+
+        public void setShouldDestroy(boolean shouldDestroy) {
+            this.shouldDestroy = shouldDestroy;
+        }
+    }
+
+    /**
+     * Constructs a <code>ProcessDestroyer</code> and obtains
+     * <code>Runtime.addShutdownHook()</code> and
+     * <code>Runtime.removeShutdownHook()</code> through reflection. The
+     * ProcessDestroyer manages a list of processes to be destroyed when the
+     * VM exits. If a process is added when the list is empty,
+     * this <code>ProcessDestroyer</code> is registered as a shutdown hook. If
+     * removing a process results in an empty list, the
+     * <code>ProcessDestroyer</code> is removed as a shutdown hook.
+     */
+    public ProcessDestroyer() {
+        try {
+            // check to see if the shutdown hook methods exists
+            // (support pre-JDK 1.3 VMs)
+            Class[] paramTypes = {Thread.class};
+            addShutdownHookMethod =
+                Runtime.class.getMethod("addShutdownHook", paramTypes);
+
+            removeShutdownHookMethod =
+                Runtime.class.getMethod("removeShutdownHook", paramTypes);
+            // wait to add shutdown hook as needed
+        } catch (NoSuchMethodException e) {
+            // it just won't be added as a shutdown hook... :(
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * Registers this <code>ProcessDestroyer</code> as a shutdown hook,
+     * uses reflection to ensure pre-JDK 1.3 compatibility.
+     */
+    private void addShutdownHook() {
+        if (addShutdownHookMethod != null && !running) {
+            destroyProcessThread = new ProcessDestroyerImpl();
+            Object[] args = {destroyProcessThread};
+            try {
+                addShutdownHookMethod.invoke(Runtime.getRuntime(), args);
+                added = true;
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            } catch (InvocationTargetException e) {
+                Throwable t = e.getTargetException();
+                if (t != null && t.getClass() == IllegalStateException.class) {
+                    // shutdown already is in progress
+                    running = true;
+                } else {
+                    e.printStackTrace();
+                }
+            }
+        }
+    }
+
+    /**
+     * Removes this <code>ProcessDestroyer</code> as a shutdown hook,
+     * uses reflection to ensure pre-JDK 1.3 compatibility
+     */
+    private void removeShutdownHook() {
+        if (removeShutdownHookMethod != null && added && !running) {
+            Object[] args = {destroyProcessThread};
+            try {
+                Boolean removed =
+                    (Boolean) removeShutdownHookMethod.invoke(
+                        Runtime.getRuntime(),
+                        args);
+                if (!removed.booleanValue()) {
+                    System.err.println("Could not remove shutdown hook");
+                }
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            } catch (InvocationTargetException e) {
+                Throwable t = e.getTargetException();
+                if (t != null && t.getClass() == IllegalStateException.class) {
+                    // shutdown already is in progress
+                    running = true;
+                } else {
+                    e.printStackTrace();
+                }
+            }
+            // start the hook thread, a unstarted thread may not be
+            // eligible for garbage collection
+            // Cf.: http://developer.java.sun.com/developer/bugParade/bugs/4533087.html
+            destroyProcessThread.setShouldDestroy(false);
+            destroyProcessThread.start();
+            // this should return quickly, since it basically is a NO-OP.
+            try {
+                destroyProcessThread.join(20000);
+            } catch (InterruptedException ie) {
+                // the thread didn't die in time
+                // it should not kill any processes unexpectedly
+            }
+            destroyProcessThread = null;
+            added = false;
+        }
+    }
+
+    /**
+     * Returns whether or not the ProcessDestroyer is registered as
+     * as shutdown hook
+     * @return true if this is currently added as shutdown hook
+     */
+    public boolean isAddedAsShutdownHook() {
+        return added;
+    }
+
+    /**
+     * Returns <code>true</code> if the specified <code>Process</code> was
+     * successfully added to the list of processes to destroy upon VM exit.
+     *
+     * @param   process the process to add
+     * @return  <code>true</code> if the specified <code>Process</code> was
+     *          successfully added
+     */
+    public boolean add(Process process) {
+        synchronized (processes) {
+            // if this list is empty, register the shutdown hook
+            if (processes.size() == 0) {
+                addShutdownHook();
+            }
+            processes.addElement(process);
+            return processes.contains(process);
+        }
+    }
+
+    /**
+     * Returns <code>true</code> if the specified <code>Process</code> was
+     * successfully removed from the list of processes to destroy upon VM exit.
+     *
+     * @param   process the process to remove
+     * @return  <code>true</code> if the specified <code>Process</code> was
+     *          successfully removed
+     */
+    public boolean remove(Process process) {
+        synchronized (processes) {
+            boolean processRemoved = processes.removeElement(process);
+            if (processRemoved && processes.size() == 0) {
+                removeShutdownHook();
+            }
+            return processRemoved;
+        }
+    }
+
+    /**
+     * Invoked by the VM when it is exiting.
+     */
+    public void run() {
+        synchronized (processes) {
+            running = true;
+            Enumeration e = processes.elements();
+            while (e.hasMoreElements()) {
+                ((Process) e.nextElement()).destroy();
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Property.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Property.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Property.java	(revision 16139)
@@ -0,0 +1,587 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Stack;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Sets a property by name, or set of properties (from file or
+ * resource) in the project.  </p>
+ * Properties are immutable: whoever sets a property first freezes it for the
+ * rest of the build; they are most definitely not variable.
+ * <p>There are five ways to set properties:</p>
+ * <ul>
+ *   <li>By supplying both the <i>name</i> and <i>value</i> attribute.</li>
+ *   <li>By supplying both the <i>name</i> and <i>refid</i> attribute.</li>
+ *   <li>By setting the <i>file</i> attribute with the filename of the property
+ *     file to load. This property file has the format as defined by the file used
+ *     in the class java.util.Properties.</li>
+ *   <li>By setting the <i>resource</i> attribute with the resource name of the
+ *     property file to load. This property file has the format as defined by the
+ *     file used in the class java.util.Properties.</li>
+ *   <li>By setting the <i>environment</i> attribute with a prefix to use.
+ *     Properties will be defined for every environment variable by
+ *     prefixing the supplied name and a period to the name of the variable.</li>
+ * </ul>
+ * <p>Although combinations of these ways are possible, only one should be used
+ * at a time. Problems might occur with the order in which properties are set, for
+ * instance.</p>
+ * <p>The value part of the properties being set, might contain references to other
+ * properties. These references are resolved at the time these properties are set.
+ * This also holds for properties loaded from a property file.</p>
+ * Properties are case sensitive.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.attribute.group name="name" description="One of these, when using the name attribute"
+ * @ant.attribute.group name="noname" description="One of these, when not using the name attribute"
+ */
+public class Property extends Task {
+
+    protected String name;
+    protected String value;
+    protected File file;
+    protected URL url;
+    protected String resource;
+    protected Path classpath;
+    protected String env;
+    protected Reference ref;
+    protected String prefix;
+    private Project fallback;
+
+    protected boolean userProperty; // set read-only properties
+
+    public Property() {
+        this(false);
+    }
+
+    /**
+     * @since Ant 1.5
+     */
+    protected Property(boolean userProperty) {
+        this(userProperty, null);
+    }
+
+    /**
+     * @since Ant 1.5
+     */
+    protected Property(boolean userProperty, Project fallback) {
+        this.userProperty = userProperty;
+        this.fallback = fallback;
+    }
+
+    /**
+     * The name of the property to set.
+     * @param name property name
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Sets the property to the absolute filename of the
+     * given file. If the value of this attribute is an absolute path, it
+     * is left unchanged (with / and \ characters converted to the
+     * current platforms conventions). Otherwise it is taken as a path
+     * relative to the project's basedir and expanded.
+     * @param location path to set
+     *
+     * @ant.attribute group="name"
+     */
+    public void setLocation(File location) {
+        setValue(location.getAbsolutePath());
+    }
+
+    /**
+     * The value of the property.
+     * @param value value to assign
+     *
+     * @ant.attribute group="name"
+     */
+    public void setValue(String value) {
+        this.value = value;
+    }
+
+    public String getValue() {
+        return value;
+    }
+
+    /**
+     * Filename of a property file to load.
+     * @param file filename
+     *
+     * @ant.attribute group="noname"
+     */
+    public void setFile(File file) {
+        this.file = file;
+    }
+
+    public File getFile() {
+        return file;
+    }
+
+    /**
+     * The url from which to load properties.
+     * @param url url string
+     *
+     * @ant.attribute group="noname"
+     */
+    public void setUrl(URL url) {
+        this.url = url;
+    }
+
+    public URL getUrl() {
+        return url;
+    }
+
+    /**
+     * Prefix to apply to properties loaded using <code>file</code>
+     * or <code>resource</code>.
+     * A "." is appended to the prefix if not specified.
+     * @param prefix prefix string
+     * @since Ant 1.5
+     */
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+        if (!prefix.endsWith(".")) {
+            this.prefix += ".";
+        }
+    }
+
+    /**
+     * @since Ant 1.5
+     */
+    public String getPrefix() {
+        return prefix;
+    }
+
+    /**
+     * Sets a reference to an Ant datatype
+     * declared elsewhere.
+     * Only yields reasonable results for references
+     * PATH like structures or properties.
+     * @param ref reference
+     *
+     * @ant.attribute group="name"
+     */
+    public void setRefid(Reference ref) {
+        this.ref = ref;
+    }
+
+    public Reference getRefid() {
+        return ref;
+    }
+
+    /**
+     * The resource name of a property file to load
+     * @param resource resource on classpath
+     *
+     * @ant.attribute group="noname"
+     */
+    public void setResource(String resource) {
+        this.resource = resource;
+    }
+
+    public String getResource() {
+        return resource;
+    }
+
+    /**
+     * Prefix to use when retrieving environment variables.
+     * Thus if you specify environment=&quot;myenv&quot;
+     * you will be able to access OS-specific
+     * environment variables via property names &quot;myenv.PATH&quot; or
+     * &quot;myenv.TERM&quot;.
+     * <p>
+     * Note that if you supply a property name with a final
+     * &quot;.&quot; it will not be doubled. ie environment=&quot;myenv.&quot; will still
+     * allow access of environment variables through &quot;myenv.PATH&quot; and
+     * &quot;myenv.TERM&quot;. This functionality is currently only implemented
+     * on select platforms. Feel free to send patches to increase the number of platforms
+     * this functionality is supported on ;).<br>
+     * Note also that properties are case sensitive, even if the
+     * environment variables on your operating system are not, e.g. it
+     * will be ${env.Path} not ${env.PATH} on Windows 2000.
+     * @param env prefix
+     *
+     * @ant.attribute group="noname"
+     */
+    public void setEnvironment(String env) {
+        this.env = env;
+    }
+
+    /**
+     * @since Ant 1.5
+     */
+    public String getEnvironment() {
+        return env;
+    }
+
+    /**
+     * The classpath to use when looking up a resource.
+     * @param classpath to add to any existing classpath
+     */
+    public void setClasspath(Path classpath) {
+        if (this.classpath == null) {
+            this.classpath = classpath;
+        } else {
+            this.classpath.append(classpath);
+        }
+    }
+
+    /**
+     * The classpath to use when looking up a resource.
+     */
+    public Path createClasspath() {
+        if (this.classpath == null) {
+            this.classpath = new Path(getProject());
+        }
+        return this.classpath.createPath();
+    }
+
+    /**
+     * the classpath to use when looking up a resource,
+     * given as reference to a &lt;path&gt; defined elsewhere
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+
+    /**
+     * @since Ant 1.5
+     */
+    public Path getClasspath() {
+        return classpath;
+    }
+
+    /**
+     * @deprecated This was never a supported feature and has been
+     * deprecated without replacement
+     * @ant.attribute ignore="true"
+     */
+    public void setUserProperty(boolean userProperty) {
+        log("DEPRECATED: Ignoring request to set user property in Property"
+            + " task.", Project.MSG_WARN);
+    }
+
+    /**
+     * get the value of this property
+     * @return the current value or the empty string
+     */
+    public String toString() {
+        return value == null ? "" : value;
+    }
+
+    /**
+     * set the property in the project to the value.
+     * if the task was give a file, resource or env attribute
+     * here is where it is loaded
+     */
+    public void execute() throws BuildException {
+        if (getProject() == null) {
+            throw new IllegalStateException("project has not been set");
+        }
+
+        if (name != null) {
+            if (value == null && ref == null) {
+                throw new BuildException("You must specify value, location or "
+                                         + "refid with the name attribute",
+                                         getLocation());
+            }
+        } else {
+            if (url == null && file == null && resource == null && env == null) {
+                throw new BuildException("You must specify url, file, resource or "
+                                         + "environment when not using the "
+                                         + "name attribute", getLocation());
+            }
+        }
+
+        if (url == null && file == null && resource == null && prefix != null) {
+            throw new BuildException("Prefix is only valid when loading from "
+                                     + "a url, file or resource", getLocation());
+        }
+
+        if ((name != null) && (value != null)) {
+            addProperty(name, value);
+        }
+
+        if (file != null) {
+            loadFile(file);
+        }
+
+        if (url != null) {
+            loadUrl(url);
+        }
+
+        if (resource != null) {
+            loadResource(resource);
+        }
+
+        if (env != null) {
+            loadEnvironment(env);
+        }
+
+        if ((name != null) && (ref != null)) {
+            try {
+                addProperty(name,
+                            ref.getReferencedObject(getProject()).toString());
+            } catch (BuildException be) {
+                if (fallback != null) {
+                    addProperty(name,
+                                ref.getReferencedObject(fallback).toString());
+                } else {
+                    throw be;
+                }
+            }
+        }
+    }
+
+    /**
+     * load properties from a url
+     * @param url url to load from
+     */
+    protected void loadUrl(URL url) throws BuildException {
+        Properties props = new Properties();
+        log("Loading " + url, Project.MSG_VERBOSE);
+        try {
+            InputStream is = url.openStream();
+            try {
+                props.load(is);
+            } finally {
+                if (is != null) {
+                    is.close();
+                }
+            }
+            addProperties(props);
+        } catch (IOException ex) {
+            throw new BuildException(ex, getLocation());
+        }
+    }
+
+
+    /**
+     * load properties from a file
+     * @param file file to load
+     */
+    protected void loadFile(File file) throws BuildException {
+        Properties props = new Properties();
+        log("Loading " + file.getAbsolutePath(), Project.MSG_VERBOSE);
+        try {
+            if (file.exists()) {
+                FileInputStream fis = new FileInputStream(file);
+                try {
+                    props.load(fis);
+                } finally {
+                    if (fis != null) {
+                        fis.close();
+                    }
+                }
+                addProperties(props);
+            } else {
+                log("Unable to find property file: " + file.getAbsolutePath(),
+                    Project.MSG_VERBOSE);
+            }
+        } catch (IOException ex) {
+            throw new BuildException(ex, getLocation());
+        }
+    }
+
+    /**
+     * load properties from a resource in the current classpath
+     * @param name name of resource to load
+     */
+    protected void loadResource(String name) {
+        Properties props = new Properties();
+        log("Resource Loading " + name, Project.MSG_VERBOSE);
+        InputStream is = null;
+        try {
+            ClassLoader cL = null;
+
+            if (classpath != null) {
+                cL = getProject().createClassLoader(classpath);
+            } else {
+                cL = this.getClass().getClassLoader();
+            }
+
+            if (cL == null) {
+                is = ClassLoader.getSystemResourceAsStream(name);
+            } else {
+                is = cL.getResourceAsStream(name);
+            }
+
+            if (is != null) {
+                props.load(is);
+                addProperties(props);
+            } else {
+                log("Unable to find resource " + name, Project.MSG_WARN);
+            }
+        } catch (IOException ex) {
+            throw new BuildException(ex, getLocation());
+        } finally {
+            if (is != null) {
+                try {
+                    is.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+
+    }
+
+    /**
+     * load the environment values
+     * @param prefix prefix to place before them
+     */
+    protected void loadEnvironment(String prefix) {
+        Properties props = new Properties();
+        if (!prefix.endsWith(".")) {
+            prefix += ".";
+        }
+        log("Loading Environment " + prefix, Project.MSG_VERBOSE);
+        Vector osEnv = Execute.getProcEnvironment();
+        for (Enumeration e = osEnv.elements(); e.hasMoreElements();) {
+            String entry = (String) e.nextElement();
+            int pos = entry.indexOf('=');
+            if (pos == -1) {
+                log("Ignoring: " + entry, Project.MSG_WARN);
+            } else {
+                props.put(prefix + entry.substring(0, pos),
+                entry.substring(pos + 1));
+            }
+        }
+        addProperties(props);
+    }
+
+    /**
+     * iterate through a set of properties,
+     * resolve them then assign them
+     */
+    protected void addProperties(Properties props) {
+        resolveAllProperties(props);
+        Enumeration e = props.keys();
+        while (e.hasMoreElements()) {
+            String name = (String) e.nextElement();
+            String value = props.getProperty(name);
+
+            String v = getProject().replaceProperties(value);
+
+            if (prefix != null) {
+                name = prefix + name;
+            }
+
+            addProperty(name, v);
+        }
+    }
+
+    /**
+     * add a name value pair to the project property set
+     * @param n name of property
+     * @param v value to set
+     */
+    protected void addProperty(String n, String v) {
+        if (userProperty) {
+            if (getProject().getUserProperty(n) == null) {
+                getProject().setInheritedProperty(n, v);
+            } else {
+                log("Override ignored for " + n, Project.MSG_VERBOSE);
+            }
+        } else {
+            getProject().setNewProperty(n, v);
+        }
+    }
+
+    /**
+     * resolve properties inside a properties hashtable
+     * @param props properties object to resolve
+     */
+    private void resolveAllProperties(Properties props) throws BuildException {
+        for (Enumeration e = props.keys(); e.hasMoreElements();) {
+            String name = (String) e.nextElement();
+            Stack referencesSeen = new Stack();
+            resolve(props, name, referencesSeen);
+        }
+    }
+
+    /**
+     * Recursively expand the named property using the project's
+     * reference table and the given set of properties - fail if a
+     * circular definition is detected.
+     *
+     * @param props properties object to resolve
+     * @param name of the property to resolve
+     * @param referencesSeen stack of all property names that have
+     * been tried to expand before coming here.
+     */
+    private void resolve(Properties props, String name, Stack referencesSeen)
+        throws BuildException {
+        if (referencesSeen.contains(name)) {
+            throw new BuildException("Property " + name + " was circularly "
+                                     + "defined.");
+        }
+
+        String value = props.getProperty(name);
+        Vector fragments = new Vector();
+        Vector propertyRefs = new Vector();
+        ProjectHelper.parsePropertyString(value, fragments, propertyRefs);
+
+        if (propertyRefs.size() != 0) {
+            referencesSeen.push(name);
+            StringBuffer sb = new StringBuffer();
+            Enumeration i = fragments.elements();
+            Enumeration j = propertyRefs.elements();
+            while (i.hasMoreElements()) {
+                String fragment = (String) i.nextElement();
+                if (fragment == null) {
+                    String propertyName = (String) j.nextElement();
+                    fragment = getProject().getProperty(propertyName);
+                    if (fragment == null) {
+                        if (props.containsKey(propertyName)) {
+                            resolve(props, propertyName, referencesSeen);
+                            fragment = props.getProperty(propertyName);
+                        } else {
+                            fragment = "${" + propertyName + "}";
+                        }
+                    }
+                }
+                sb.append(fragment);
+            }
+            value = sb.toString();
+            props.put(name, value);
+            referencesSeen.pop();
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/PumpStreamHandler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/PumpStreamHandler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/PumpStreamHandler.java	(revision 16139)
@@ -0,0 +1,224 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Copies standard output and error of subprocesses to standard output and
+ * error of the parent process.
+ *
+ * @since Ant 1.2
+ */
+public class PumpStreamHandler implements ExecuteStreamHandler {
+
+    private Thread outputThread;
+    private Thread errorThread;
+    private StreamPumper inputPump;
+
+    private OutputStream out;
+    private OutputStream err;
+    private InputStream input;
+
+    /**
+     * Construct a new <CODE>PumpStreamHandler</CODE>.
+     * @param out the output <CODE>OutputStream</CODE>.
+     * @param err the error <CODE>OutputStream</CODE>.
+     * @param input the input <CODE>InputStream</CODE>.
+     */
+    public PumpStreamHandler(OutputStream out, OutputStream err,
+                             InputStream input) {
+        this.out = out;
+        this.err = err;
+        this.input = input;
+    }
+
+    /**
+     * Construct a new <CODE>PumpStreamHandler</CODE>.
+     * @param out the output <CODE>OutputStream</CODE>.
+     * @param err the error <CODE>OutputStream</CODE>.
+     */
+    public PumpStreamHandler(OutputStream out, OutputStream err) {
+        this(out, err, null);
+    }
+
+    /**
+     * Construct a new <CODE>PumpStreamHandler</CODE>.
+     * @param outAndErr the output/error <CODE>OutputStream</CODE>.
+     */
+    public PumpStreamHandler(OutputStream outAndErr) {
+        this(outAndErr, outAndErr);
+    }
+
+    /**
+     * Construct a new <CODE>PumpStreamHandler</CODE>.
+     */
+    public PumpStreamHandler() {
+        this(System.out, System.err);
+    }
+
+    /**
+     * Set the <CODE>InputStream</CODE> from which to read the
+     * standard output of the process.
+     * @param is the <CODE>InputStream</CODE>.
+     */
+    public void setProcessOutputStream(InputStream is) {
+        createProcessOutputPump(is, out);
+    }
+
+    /**
+     * Set the <CODE>InputStream</CODE> from which to read the
+     * standard error of the process.
+     * @param is the <CODE>InputStream</CODE>.
+     */
+    public void setProcessErrorStream(InputStream is) {
+        if (err != null) {
+            createProcessErrorPump(is, err);
+        }
+    }
+
+    /**
+     * Set the <CODE>OutputStream</CODE> by means of which
+     * input can be sent to the process.
+     * @param os the <CODE>OutputStream</CODE>.
+     */
+    public void setProcessInputStream(OutputStream os) {
+        if (input != null) {
+            inputPump = createInputPump(input, os, true);
+        } else {
+            try {
+                os.close();
+            } catch (IOException e) {
+                //ignore
+            }
+        }
+    }
+
+    /**
+     * Start the <CODE>Thread</CODE>s.
+     */
+    public void start() {
+        outputThread.start();
+        errorThread.start();
+        if (inputPump != null) {
+            Thread inputThread = new Thread(inputPump);
+            inputThread.setDaemon(true);
+            inputThread.start();
+        }
+    }
+
+    /**
+     * Stop pumping the streams.
+     */
+    public void stop() {
+        try {
+            outputThread.join();
+        } catch (InterruptedException e) {
+            // ignore
+        }
+        try {
+            errorThread.join();
+        } catch (InterruptedException e) {
+            // ignore
+        }
+
+        if (inputPump != null) {
+            inputPump.stop();
+        }
+
+        try {
+            err.flush();
+        } catch (IOException e) {
+            // ignore
+        }
+        try {
+            out.flush();
+        } catch (IOException e) {
+            // ignore
+        }
+    }
+
+    /**
+     * Get the error stream.
+     * @return <CODE>OutputStream</CODE>.
+     */
+    protected OutputStream getErr() {
+        return err;
+    }
+
+    /**
+     * Get the output stream.
+     * @return <CODE>OutputStream</CODE>.
+     */
+    protected OutputStream getOut() {
+        return out;
+    }
+
+    /**
+     * Create the pump to handle process output.
+     * @param is the <CODE>InputStream</CODE>.
+     * @param os the <CODE>OutputStream</CODE>.
+     */
+    protected void createProcessOutputPump(InputStream is, OutputStream os) {
+        outputThread = createPump(is, os);
+    }
+
+    /**
+     * Create the pump to handle error output.
+     * @param is the <CODE>InputStream</CODE>.
+     * @param os the <CODE>OutputStream</CODE>.
+     */
+    protected void createProcessErrorPump(InputStream is, OutputStream os) {
+        errorThread = createPump(is, os);
+    }
+
+    /**
+     * Creates a stream pumper to copy the given input stream to the
+     * given output stream.
+     */
+    protected Thread createPump(InputStream is, OutputStream os) {
+        return createPump(is, os, false);
+    }
+
+    /**
+     * Creates a stream pumper to copy the given input stream to the
+     * given output stream.
+     */
+    protected Thread createPump(InputStream is, OutputStream os,
+                                boolean closeWhenExhausted) {
+        final Thread result
+            = new Thread(new StreamPumper(is, os, closeWhenExhausted));
+        result.setDaemon(true);
+        return result;
+    }
+    
+    /**
+     * Creates a stream pumper to copy the given input stream to the
+     * given output stream. Used for standard input.
+     * @since Ant 1.6.3
+     */
+    /*protected*/ StreamPumper createInputPump(InputStream is, OutputStream os,
+                                boolean closeWhenExhausted) {
+        StreamPumper pumper = new StreamPumper(is, os, closeWhenExhausted);
+        pumper.setAutoflush(true);
+        return pumper;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Recorder.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Recorder.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Recorder.java	(revision 16139)
@@ -0,0 +1,235 @@
+/*
+ * Copyright  2001-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.util.Hashtable;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+/**
+ * Adds a listener to the current build process that records the
+ * output to a file.
+ * <p>Several recorders can exist at the same time.  Each recorder is
+ * associated with a file.  The filename is used as a unique identifier for
+ * the recorders.  The first call to the recorder task with an unused filename
+ * will create a recorder (using the parameters provided) and add it to the
+ * listeners of the build.  All subsequent calls to the recorder task using
+ * this filename will modify that recorders state (recording or not) or other
+ * properties (like logging level).</p>
+ * <p>Some technical issues: the file's print stream is flushed for &quot;finished&quot;
+ * events (buildFinished, targetFinished and taskFinished), and is closed on
+ * a buildFinished event.</p>
+ * @see RecorderEntry
+ * @version 0.5
+ * @since Ant 1.4
+ * @ant.task name="record" category="utility"
+ */
+public class Recorder extends Task {
+
+    //////////////////////////////////////////////////////////////////////
+    // ATTRIBUTES
+
+    /** The name of the file to record to. */
+    private String filename = null;
+    /**
+     * Whether or not to append. Need Boolean to record an unset state (null).
+     */
+    private Boolean append = null;
+    /**
+     * Whether to start or stop recording. Need Boolean to record an unset
+     * state (null).
+     */
+    private Boolean start = null;
+    /** The level to log at. A level of -1 means not initialized yet. */
+    private int loglevel = -1;
+    /** Strip task banners if true.  */
+    private boolean emacsMode = false;
+    /** The list of recorder entries. */
+    private static Hashtable recorderEntries = new Hashtable();
+
+    //////////////////////////////////////////////////////////////////////
+    // CONSTRUCTORS / INITIALIZERS
+
+    //////////////////////////////////////////////////////////////////////
+    // ACCESSOR METHODS
+
+    /**
+     * Sets the name of the file to log to, and the name of the recorder
+     * entry.
+     *
+     * @param fname File name of logfile.
+     */
+    public void setName(String fname) {
+        filename = fname;
+    }
+
+
+    /**
+     * Sets the action for the associated recorder entry.
+     *
+     * @param action The action for the entry to take: start or stop.
+     */
+    public void setAction(ActionChoices action) {
+        if (action.getValue().equalsIgnoreCase("start")) {
+            start = Boolean.TRUE;
+        } else {
+            start = Boolean.FALSE;
+        }
+    }
+
+
+    /**
+     * Whether or not the logger should append to a previous file.
+     * @param append if true, append to a previous file.
+     */
+    public void setAppend(boolean append) {
+        this.append = (append ? Boolean.TRUE : Boolean.FALSE);
+    }
+
+
+    /**
+     * Set emacs mode.
+     * @param emacsMode if true use emacs mode
+     */
+    public void setEmacsMode(boolean emacsMode) {
+        this.emacsMode = emacsMode;
+    }
+
+
+    /**
+     * Sets the level to which this recorder entry should log to.
+     *
+     * @see VerbosityLevelChoices
+     */
+    public void setLoglevel(VerbosityLevelChoices level) {
+        //I hate cascading if/elseif clauses !!!
+        String lev = level.getValue();
+
+        if (lev.equalsIgnoreCase("error")) {
+            loglevel = Project.MSG_ERR;
+        } else if (lev.equalsIgnoreCase("warn")) {
+            loglevel = Project.MSG_WARN;
+        } else if (lev.equalsIgnoreCase("info")) {
+            loglevel = Project.MSG_INFO;
+        } else if (lev.equalsIgnoreCase("verbose")) {
+            loglevel = Project.MSG_VERBOSE;
+        } else if (lev.equalsIgnoreCase("debug")) {
+            loglevel = Project.MSG_DEBUG;
+        }
+    }
+
+    //////////////////////////////////////////////////////////////////////
+    // CORE / MAIN BODY
+
+    /**
+     * The main execution.
+     * @throws BuildException on error
+     */
+    public void execute() throws BuildException {
+        if (filename == null) {
+            throw new BuildException("No filename specified");
+        }
+
+        getProject().log("setting a recorder for name " + filename,
+            Project.MSG_DEBUG);
+
+        // get the recorder entry
+        RecorderEntry recorder = getRecorder(filename, getProject());
+        // set the values on the recorder
+        recorder.setMessageOutputLevel(loglevel);
+        if (start != null) {
+            if (start.booleanValue()) {
+                recorder.reopenFile();
+                recorder.setRecordState(start);
+            } else {
+                recorder.setRecordState(start);
+                recorder.closeFile();
+            }
+        }
+        recorder.setEmacsMode(emacsMode);
+    }
+
+    //////////////////////////////////////////////////////////////////////
+    // INNER CLASSES
+
+    /**
+     * A list of possible values for the <code>setAction()</code> method.
+     * Possible values include: start and stop.
+     */
+    public static class ActionChoices extends EnumeratedAttribute {
+        private static final String[] VALUES = {"start", "stop"};
+
+        /**
+         * @see EnumeratedAttribute#getValues()
+         */
+        public String[] getValues() {
+            return VALUES;
+        }
+    }
+
+
+    /**
+     * A list of possible values for the <code>setLoglevel()</code> method.
+     * Possible values include: error, warn, info, verbose, debug.
+     */
+    public static class VerbosityLevelChoices extends EnumeratedAttribute {
+        private static final String[] VALUES = {"error", "warn", "info",
+            "verbose", "debug"};
+
+        /**
+         * @see EnumeratedAttribute#getValues()
+         */
+        public String[] getValues() {
+            return VALUES;
+        }
+    }
+
+
+    /**
+     * Gets the recorder that's associated with the passed in name. If the
+     * recorder doesn't exist, then a new one is created.
+     * @param name the name of the recoder
+     * @param proj the current project
+     * @return a recorder
+     * @throws BuildException on error
+     */
+    protected RecorderEntry getRecorder(String name, Project proj)
+         throws BuildException {
+        Object o = recorderEntries.get(name);
+        RecorderEntry entry;
+
+        if (o == null) {
+            // create a recorder entry
+            entry = new RecorderEntry(name);
+
+            if (append == null) {
+                entry.openFile(false);
+            } else {
+                entry.openFile(append.booleanValue());
+            }
+            entry.setProject(proj);
+            recorderEntries.put(name, entry);
+        } else {
+            entry = (RecorderEntry) o;
+        }
+        return entry;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/RecorderEntry.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/RecorderEntry.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/RecorderEntry.java	(revision 16139)
@@ -0,0 +1,322 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildLogger;
+import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.SubBuildListener;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * This is a class that represents a recorder. This is the listener to the
+ * build process.
+ *
+ * @since Ant 1.4
+ */
+public class RecorderEntry implements BuildLogger, SubBuildListener {
+
+    //////////////////////////////////////////////////////////////////////
+    // ATTRIBUTES
+
+    /** The name of the file associated with this recorder entry.  */
+    private String filename = null;
+    /** The state of the recorder (recorder on or off).  */
+    private boolean record = true;
+    /** The current verbosity level to record at.  */
+    private int loglevel = Project.MSG_INFO;
+    /** The output PrintStream to record to.  */
+    private PrintStream out = null;
+    /** The start time of the last know target.  */
+    private long targetStartTime = 0L;
+    /** Strip task banners if true.  */
+    private boolean emacsMode = false;
+    /** project instance the recorder is associated with */
+    private Project project;
+
+    //////////////////////////////////////////////////////////////////////
+    // CONSTRUCTORS / INITIALIZERS
+
+    /**
+     * @param name The name of this recorder (used as the filename).
+     */
+    protected RecorderEntry(String name) {
+        targetStartTime = System.currentTimeMillis();
+        filename = name;
+    }
+
+    //////////////////////////////////////////////////////////////////////
+    // ACCESSOR METHODS
+
+    /**
+     * @return the name of the file the output is sent to.
+     */
+    public String getFilename() {
+        return filename;
+    }
+
+
+    /**
+     * Turns off or on this recorder.
+     *
+     * @param state true for on, false for off, null for no change.
+     */
+    public void setRecordState(Boolean state) {
+        if (state != null) {
+            flush();
+            record = state.booleanValue();
+        }
+    }
+
+
+    public void buildStarted(BuildEvent event) {
+        log("> BUILD STARTED", Project.MSG_DEBUG);
+    }
+
+
+    public void buildFinished(BuildEvent event) {
+        log("< BUILD FINISHED", Project.MSG_DEBUG);
+
+        if (record && out != null) {
+            Throwable error = event.getException();
+
+            if (error == null) {
+                out.println(StringUtils.LINE_SEP + "BUILD SUCCESSFUL");
+            } else {
+                out.println(StringUtils.LINE_SEP + "BUILD FAILED"
+                            + StringUtils.LINE_SEP);
+                error.printStackTrace(out);
+            }
+        }
+        cleanup();
+    }
+
+    /**
+     * Cleans up any resources held by this recorder entry at the end
+     * of a subbuild if it has been created for the subbuild's project
+     * instance.
+     *
+     * @param event the buildFinished event
+     *
+     * @since Ant 1.6.2
+     */
+    public void subBuildFinished(BuildEvent event) {
+        if (event.getProject() == project) {
+            cleanup();
+        }
+    }
+
+    /**
+     * Empty implementation to satisfy the BuildListener interface.
+     *
+     * @param event the buildStarted event
+     *
+     * @since Ant 1.6.2
+     */
+    public void subBuildStarted(BuildEvent event) {
+    }
+
+
+    public void targetStarted(BuildEvent event) {
+        log(">> TARGET STARTED -- " + event.getTarget(), Project.MSG_DEBUG);
+        log(StringUtils.LINE_SEP + event.getTarget().getName() + ":",
+            Project.MSG_INFO);
+        targetStartTime = System.currentTimeMillis();
+    }
+
+
+    public void targetFinished(BuildEvent event) {
+        log("<< TARGET FINISHED -- " + event.getTarget(), Project.MSG_DEBUG);
+
+        String time = formatTime(System.currentTimeMillis() - targetStartTime);
+
+        log(event.getTarget() + ":  duration " + time, Project.MSG_VERBOSE);
+        flush();
+    }
+
+
+    public void taskStarted(BuildEvent event) {
+        log(">>> TASK STARTED -- " + event.getTask(), Project.MSG_DEBUG);
+    }
+
+
+    public void taskFinished(BuildEvent event) {
+        log("<<< TASK FINISHED -- " + event.getTask(), Project.MSG_DEBUG);
+        flush();
+    }
+
+
+    public void messageLogged(BuildEvent event) {
+        log("--- MESSAGE LOGGED", Project.MSG_DEBUG);
+
+        StringBuffer buf = new StringBuffer();
+
+        if (event.getTask() != null) {
+            String name = event.getTask().getTaskName();
+
+            if (!emacsMode) {
+                String label = "[" + name + "] ";
+                int size = DefaultLogger.LEFT_COLUMN_SIZE - label.length();
+
+                for (int i = 0; i < size; i++) {
+                    buf.append(" ");
+                }
+                buf.append(label);
+            }
+        }
+        buf.append(event.getMessage());
+
+        log(buf.toString(), event.getPriority());
+    }
+
+
+    /**
+     * The thing that actually sends the information to the output.
+     *
+     * @param mesg The message to log.
+     * @param level The verbosity level of the message.
+     */
+    private void log(String mesg, int level) {
+        if (record && (level <= loglevel) && out != null) {
+            out.println(mesg);
+        }
+    }
+    
+    private void flush() {
+        if (record && out != null) {
+            out.flush();
+        }
+    }
+
+
+    public void setMessageOutputLevel(int level) {
+        if (level >= Project.MSG_ERR && level <= Project.MSG_DEBUG) {
+            loglevel = level;
+        }
+    }
+
+
+    public void setOutputPrintStream(PrintStream output) {
+        closeFile();
+        out = output;
+    }
+
+
+    public void setEmacsMode(boolean emacsMode) {
+        this.emacsMode = emacsMode;
+    }
+
+
+    public void setErrorPrintStream(PrintStream err) {
+        setOutputPrintStream(err);
+    }
+
+
+    private static String formatTime(long millis) {
+        long seconds = millis / 1000;
+        long minutes = seconds / 60;
+
+
+        if (minutes > 0) {
+            return Long.toString(minutes) + " minute"
+                 + (minutes == 1 ? " " : "s ")
+                 + Long.toString(seconds % 60) + " second"
+                 + (seconds % 60 == 1 ? "" : "s");
+        } else {
+            return Long.toString(seconds) + " second"
+                 + (seconds % 60 == 1 ? "" : "s");
+        }
+
+    }
+
+    /**
+     * Set the project associated with this recorder entry.
+     *
+     * @param project the project instance
+     *
+     * @since 1.6.2
+     */
+    public void setProject(Project project) {
+        this.project = project;
+        if (project != null) {
+            project.addBuildListener(this);
+        }
+    }
+
+    /**
+     * @since 1.6.2
+     */
+    public void cleanup() {
+        closeFile();
+        if (project != null) {
+            project.removeBuildListener(this);
+        }
+        project = null;
+    }
+    
+    /**
+     * Initially opens the file associated with this recorder.
+     * Used by Recorder.
+     * @param append Indicates if output must be appended to the logfile or that
+     * the logfile should be overwritten.
+     * @throws BuildException
+     * @since 1.6.3
+     */
+    void openFile(boolean append) throws BuildException {
+        openFileImpl(append);
+    }
+
+    /**
+     * Closes the file associated with this recorder.
+     * Used by Recorder.
+     * @since 1.6.3
+     */
+    void closeFile() {
+        if (out != null) {
+            out.close();
+            out = null;
+        }
+    }
+    
+    /**
+     * Re-opens the file associated with this recorder.
+     * Used by Recorder.
+     * @throws BuildException
+     * @since 1.6.3
+     */
+    void reopenFile() throws BuildException {
+        openFileImpl(true);
+    }
+
+    private void openFileImpl(boolean append) throws BuildException {
+        if (out == null) {
+            try {
+                out = new PrintStream(new FileOutputStream(filename, append));
+            } catch (IOException ioe) {
+                throw new BuildException("Problems opening file using a " +
+                        "recorder entry", ioe);
+            }
+        }        
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Redirector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Redirector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Redirector.java	(revision 16139)
@@ -0,0 +1,804 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.Reader;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.OutputStream;
+import java.io.StringReader;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.PipedOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.Arrays;
+import java.util.Vector;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.ant.util.TeeOutputStream;
+import org.apache.tools.ant.util.ReaderInputStream;
+import org.apache.tools.ant.util.LeadPipeInputStream;
+import org.apache.tools.ant.util.LazyFileOutputStream;
+import org.apache.tools.ant.util.OutputStreamFunneler;
+import org.apache.tools.ant.util.ConcatFileInputStream;
+import org.apache.tools.ant.util.KeepAliveOutputStream;
+
+/**
+ * The Redirector class manages the setup and connection of
+ * input and output redirection for an Ant project component.
+ *
+ * @since Ant 1.6
+ */
+public class Redirector {
+
+    private static final String DEFAULT_ENCODING
+        = System.getProperty("file.encoding");
+
+    private class PropertyOutputStream extends ByteArrayOutputStream {
+        String property;
+        boolean closed = false;
+
+        PropertyOutputStream(String property) {
+            super();
+            this.property = property;
+        }
+
+        public void close() throws IOException {
+            if (!closed && !(append && appendProperties)) {
+                setPropertyFromBAOS(this, property);
+                closed = true;
+            }
+        }
+    }
+
+    /**
+     * The file(s) from which standard input is being taken.
+     * If &gt; 1, files' content will be concatenated in the order received.
+     */
+    private File[] input;
+
+    /**
+     * The file(s) receiving standard output. Will also receive standard error
+     * unless standard error is redirected or logError is true.
+     */
+    private File[] out;
+
+    /**
+     * The file(s) to which standard error is being redirected
+     */
+    private File[] error;
+
+    /**
+      * Indicates if standard error should be logged to Ant's log system
+      * rather than the output. This has no effect if standard error is
+      * redirected to a file or property.
+      */
+    private boolean logError = false;
+
+    /**
+     * Buffer used to capture output for storage into a property
+     */
+    private PropertyOutputStream baos = null;
+
+    /**
+     * Buffer used to capture error output for storage into a property
+     */
+    private PropertyOutputStream errorBaos = null;
+
+    /** The name of the property into which output is to be stored */
+    private String outputProperty;
+
+    /** The name of the property into which error output is to be stored */
+    private String errorProperty;
+
+    /** String from which input is taken */
+    private String inputString;
+
+    /** Flag which indicates if error and output files are to be appended. */
+    private boolean append = false;
+
+    /** Flag which indicates that output should be always sent to the log */
+    private boolean alwaysLog = false;
+
+    /** Flag which indicates whether files should be created even when empty. */
+    private boolean createEmptyFiles = true;
+
+    /** The task for which this redirector is working */
+    private ProjectComponent managingTask;
+
+    /** The stream for output data */
+    private OutputStream outputStream = null;
+
+    /** The stream for error output */
+    private OutputStream errorStream = null;
+
+    /** The stream for input */
+    private InputStream inputStream = null;
+
+    /** Stream which is used for line oriented output */
+    private PrintStream outPrintStream = null;
+
+    /** Stream which is used for line oriented error output */
+    private PrintStream errorPrintStream = null;
+
+    /** The output filter chains */
+    private Vector outputFilterChains;
+
+    /** The error filter chains */
+    private Vector errorFilterChains;
+
+    /** The input filter chains */
+    private Vector inputFilterChains;
+
+    /** The output encoding */
+    private String outputEncoding = DEFAULT_ENCODING;
+
+    /** The error encoding */
+    private String errorEncoding = DEFAULT_ENCODING;
+
+    /** The input encoding */
+    private String inputEncoding = DEFAULT_ENCODING;
+
+    /** Whether to complete properties settings **/
+    private boolean appendProperties = true;
+
+    /** The thread group used for starting <code>StreamPumper</code> threads */
+    private ThreadGroup threadGroup = new ThreadGroup("redirector");
+
+    /**
+     * Create a redirector instance for the given task
+     *
+     * @param managingTask the task for which the redirector is to work
+     */
+    public Redirector(Task managingTask) {
+        this((ProjectComponent) managingTask);
+    }
+
+    /**
+     * Create a redirector instance for the given task
+     *
+     * @param managingTask the project component for which the
+     * redirector is to work
+     * @since Ant 1.6.3
+     */
+    public Redirector(ProjectComponent managingTask) {
+        this.managingTask = managingTask;
+    }
+
+    /**
+     * Set the input to use for the task
+     *
+     * @param input the file from which input is read.
+     */
+    public void setInput(File input) {
+        setInput((input == null) ? null : new File[] {input});
+    }
+
+    /**
+     * Set the input to use for the task
+     *
+     * @param input the files from which input is read.
+     */
+    public synchronized void setInput(File[] input) {
+        this.input = input;
+    }
+
+    /**
+     * Set the string to use as input
+     *
+     * @param inputString the string which is used as the input source
+     */
+    public synchronized void setInputString(String inputString) {
+        this.inputString = inputString;
+    }
+
+    /**
+     * Set a stream to use as input.
+     *
+     * @param inputStream the stream from which input will be read
+     * @since Ant 1.6.3
+     */
+    /*public*/ void setInputStream(InputStream inputStream) {
+        this.inputStream = inputStream;
+    }
+
+    /**
+     * File the output of the process is redirected to. If error is not
+     * redirected, it too will appear in the output
+     *
+     * @param out the file to which output stream is written
+     */
+    public void setOutput(File out) {
+        setOutput((out == null) ? null : new File[] {out});
+    }
+
+    /**
+     * Files the output of the process is redirected to. If error is not
+     * redirected, it too will appear in the output
+     *
+     * @param out the files to which output stream is written
+     */
+    public synchronized void setOutput(File[] out) {
+        this.out = out;
+    }
+
+    /**
+     * Set the output encoding.
+     *
+     * @param outputEncoding   <CODE>String</CODE>.
+     */
+    public synchronized void setOutputEncoding(String outputEncoding) {
+        if (outputEncoding == null) {
+            throw new IllegalArgumentException(
+                "outputEncoding must not be null");
+        } else {
+            this.outputEncoding = outputEncoding;
+        }
+    }
+
+    /**
+     * Set the error encoding.
+     *
+     * @param errorEncoding   <CODE>String</CODE>.
+     */
+    public synchronized void setErrorEncoding(String errorEncoding) {
+        if (errorEncoding == null) {
+            throw new IllegalArgumentException(
+                "errorEncoding must not be null");
+        } else {
+            this.errorEncoding = errorEncoding;
+        }
+    }
+
+    /**
+     * Set the input encoding.
+     *
+     * @param inputEncoding   <CODE>String</CODE>.
+     */
+    public synchronized void setInputEncoding(String inputEncoding) {
+        if (inputEncoding == null) {
+            throw new IllegalArgumentException(
+                "inputEncoding must not be null");
+        } else {
+            this.inputEncoding = inputEncoding;
+        }
+    }
+
+    /**
+     * Controls whether error output of exec is logged. This is only useful
+     * when output is being redirected and error output is desired in the
+     * Ant log
+     *
+     * @param logError if true the standard error is sent to the Ant log system
+     *        and not sent to output.
+     */
+    public synchronized void setLogError(boolean logError) {
+        this.logError = logError;
+    }
+
+    /**
+     * This <CODE>Redirector</CODE>'s subordinate
+     * <CODE>PropertyOutputStream</CODE>s will not set their respective
+     * properties <CODE>while (appendProperties && append)</CODE>.
+     *
+     * @param appendProperties whether to append properties.
+     */
+    public synchronized void setAppendProperties(boolean appendProperties) {
+        this.appendProperties = appendProperties;
+    }
+
+    /**
+     * Set the file to which standard error is to be redirected.
+     *
+     * @param error the file to which error is to be written
+     */
+    public void setError(File error) {
+        setError((error == null) ? null : new File[] {error});
+    }
+
+    /**
+     * Set the files to which standard error is to be redirected.
+     *
+     * @param error the file to which error is to be written
+     */
+    public synchronized void setError(File[] error) {
+        this.error = error;
+    }
+
+    /**
+     * Property name whose value should be set to the output of
+     * the process.
+     *
+     * @param outputProperty the name of the property to be set with the
+     *        task's output.
+     */
+    public synchronized void setOutputProperty(String outputProperty) {
+        if (outputProperty == null
+         || !(outputProperty.equals(this.outputProperty))) {
+            this.outputProperty = outputProperty;
+            baos = null;
+        }
+    }
+
+    /**
+     * Whether output should be appended to or overwrite an existing file.
+     * Defaults to false.
+     *
+     * @param append if true output and error streams are appended to their
+     *        respective files, if specified.
+     */
+    public synchronized void setAppend(boolean append) {
+        this.append = append;
+    }
+
+    /**
+     * If true, (error and non-error) output will be "teed", redirected
+     * as specified while being sent to Ant's logging mechanism as if no
+     * redirection had taken place.  Defaults to false.
+     * @param alwaysLog <code>boolean</code>
+     * @since Ant 1.6.3
+     */
+    public synchronized void setAlwaysLog(boolean alwaysLog) {
+        this.alwaysLog = alwaysLog;
+    }
+
+    /**
+     * Whether output and error files should be created even when empty.
+     * Defaults to true.
+     * @param createEmptyFiles <CODE>boolean</CODE>.
+     */
+    public synchronized void setCreateEmptyFiles(boolean createEmptyFiles) {
+        this.createEmptyFiles = createEmptyFiles;
+    }
+
+    /**
+     * Property name whose value should be set to the error of
+     * the process.
+     *
+     * @param errorProperty the name of the property to be set
+     *        with the error output.
+     */
+    public synchronized void setErrorProperty(String errorProperty) {
+        if (errorProperty == null
+         || !(errorProperty.equals(this.errorProperty))) {
+            this.errorProperty = errorProperty;
+            errorBaos = null;
+        }
+    }
+
+    /**
+     * Set the input <CODE>FilterChain</CODE>s.
+     *
+     * @param inputFilterChains <CODE>Vector</CODE> containing <CODE>FilterChain</CODE>.
+     */
+    public synchronized void setInputFilterChains(Vector inputFilterChains) {
+        this.inputFilterChains = inputFilterChains;
+    }
+
+    /**
+     * Set the output <CODE>FilterChain</CODE>s.
+     *
+     * @param outputFilterChains <CODE>Vector</CODE> containing <CODE>FilterChain</CODE>.
+     */
+    public synchronized void setOutputFilterChains(Vector outputFilterChains) {
+        this.outputFilterChains = outputFilterChains;
+    }
+
+    /**
+     * Set the error <CODE>FilterChain</CODE>s.
+     *
+     * @param errorFilterChains <CODE>Vector</CODE> containing <CODE>FilterChain</CODE>.
+     */
+    public synchronized void setErrorFilterChains(Vector errorFilterChains) {
+        this.errorFilterChains = errorFilterChains;
+    }
+
+    /**
+     * Set a property from a ByteArrayOutputStream
+     *
+     * @param baos contains the property value.
+     * @param propertyName the property name.
+     *
+     * @exception IOException if the value cannot be read form the stream.
+     */
+    private void setPropertyFromBAOS(ByteArrayOutputStream baos,
+                                     String propertyName) throws IOException {
+
+        BufferedReader in
+            = new BufferedReader(new StringReader(Execute.toString(baos)));
+        String line = null;
+        StringBuffer val = new StringBuffer();
+        while ((line = in.readLine()) != null) {
+            if (val.length() != 0) {
+                val.append(StringUtils.LINE_SEP);
+            }
+            val.append(line);
+        }
+        managingTask.getProject().setNewProperty(propertyName, val.toString());
+    }
+
+    /**
+     * Create the input, error and output streams based on the
+     * configuration options.
+     */
+    public synchronized void createStreams() {
+        if (out != null && out.length > 0) {
+            String logHead = new StringBuffer("Output ").append(
+                ((append) ? "appended" : "redirected")).append(
+                " to ").toString();
+            outputStream = foldFiles(out, logHead, Project.MSG_VERBOSE);
+        }
+        if (outputProperty != null) {
+            if (baos == null) {
+                baos = new PropertyOutputStream(outputProperty);
+                managingTask.log("Output redirected to property: "
+                    + outputProperty, Project.MSG_VERBOSE);
+            }
+            //shield it from being closed by a filtering StreamPumper
+            OutputStream keepAliveOutput = new KeepAliveOutputStream(baos);
+            outputStream = (outputStream == null) ? keepAliveOutput
+                : new TeeOutputStream(outputStream, keepAliveOutput);
+        } else {
+            baos = null;
+        }
+
+        if (error != null && error.length > 0) {
+            String logHead = new StringBuffer("Error ").append(
+                ((append) ? "appended" : "redirected")).append(
+                " to ").toString();
+            errorStream = foldFiles(error, logHead, Project.MSG_VERBOSE);
+        } else if (!(logError || outputStream == null)) {
+            long funnelTimeout = 0L;
+            OutputStreamFunneler funneler
+                = new OutputStreamFunneler(outputStream, funnelTimeout);
+            try {
+                outputStream = funneler.getFunnelInstance();
+                errorStream = funneler.getFunnelInstance();
+            } catch (IOException eyeOhEx) {
+                throw new BuildException(
+                    "error splitting output/error streams", eyeOhEx);
+            }
+        }
+        if (errorProperty != null) {
+            if (errorBaos == null) {
+                errorBaos = new PropertyOutputStream(errorProperty);
+                managingTask.log("Error redirected to property: " + errorProperty,
+                    Project.MSG_VERBOSE);
+            }
+            //shield it from being closed by a filtering StreamPumper
+            OutputStream keepAliveError = new KeepAliveOutputStream(errorBaos);
+            errorStream = (error == null || error.length == 0) ? keepAliveError
+                : new TeeOutputStream(errorStream, keepAliveError);
+        } else {
+            errorBaos = null;
+        }
+        if (alwaysLog || outputStream == null) {
+            OutputStream outputLog
+                = new LogOutputStream(managingTask, Project.MSG_INFO);
+            outputStream = (outputStream == null)
+                ? outputLog : new TeeOutputStream(outputLog, outputStream);
+        }
+        if (alwaysLog || errorStream == null) {
+            OutputStream errorLog
+                = new LogOutputStream(managingTask, Project.MSG_WARN);
+            errorStream = (errorStream == null)
+                ? errorLog : new TeeOutputStream(errorLog, errorStream);
+        }
+        if ((outputFilterChains != null && outputFilterChains.size() > 0)
+            || !(outputEncoding.equalsIgnoreCase(inputEncoding))) {
+            try {
+                LeadPipeInputStream snk = new LeadPipeInputStream();
+                snk.setManagingComponent(managingTask);
+
+                InputStream outPumpIn = snk;
+
+                Reader reader = new InputStreamReader(outPumpIn, inputEncoding);
+
+                if (outputFilterChains != null && outputFilterChains.size() > 0) {
+                    ChainReaderHelper helper = new ChainReaderHelper();
+                    helper.setPrimaryReader(reader);
+                    helper.setFilterChains(outputFilterChains);
+                    reader = helper.getAssembledReader();
+                }
+                outPumpIn = new ReaderInputStream(reader, outputEncoding);
+
+                Thread t = new Thread(threadGroup, new StreamPumper(
+                    outPumpIn, outputStream, true), "output pumper");
+                t.setPriority(Thread.MAX_PRIORITY);
+                outputStream = new PipedOutputStream(snk);
+                t.start();
+            } catch (IOException eyeOhEx) {
+                throw new BuildException(
+                    "error setting up output stream", eyeOhEx);
+            }
+        }
+
+        if ((errorFilterChains != null && errorFilterChains.size() > 0)
+            || !(errorEncoding.equalsIgnoreCase(inputEncoding))) {
+            try {
+                LeadPipeInputStream snk = new LeadPipeInputStream();
+                snk.setManagingComponent(managingTask);
+
+                InputStream errPumpIn = snk;
+
+                Reader reader = new InputStreamReader(errPumpIn, inputEncoding);
+
+                if (errorFilterChains != null && errorFilterChains.size() > 0) {
+                    ChainReaderHelper helper = new ChainReaderHelper();
+                    helper.setPrimaryReader(reader);
+                    helper.setFilterChains(errorFilterChains);
+                    reader = helper.getAssembledReader();
+                }
+                errPumpIn = new ReaderInputStream(reader, errorEncoding);
+
+                Thread t = new Thread(threadGroup, new StreamPumper(
+                    errPumpIn, errorStream, true), "error pumper");
+                t.setPriority(Thread.MAX_PRIORITY);
+                errorStream = new PipedOutputStream(snk);
+                t.start();
+            } catch (IOException eyeOhEx) {
+                throw new BuildException(
+                    "error setting up error stream", eyeOhEx);
+            }
+        }
+
+        // if input files are specified, inputString and inputStream are ignored;
+        // classes that work with redirector attributes can enforce
+        // whatever warnings are needed
+        if (input != null && input.length > 0) {
+            managingTask.log("Redirecting input from file"
+                + ((input.length == 1) ? "" : "s"), Project.MSG_VERBOSE);
+            try {
+                inputStream = new ConcatFileInputStream(input);
+            } catch (IOException eyeOhEx) {
+                throw new BuildException(eyeOhEx);
+            }
+            ((ConcatFileInputStream) inputStream).setManagingComponent(managingTask);
+        } else if (inputString != null) {
+            managingTask.log("Using input \"" + inputString + "\"",
+                Project.MSG_VERBOSE);
+            inputStream = new ByteArrayInputStream(inputString.getBytes());
+        }
+
+        if (inputStream != null
+            && inputFilterChains != null && inputFilterChains.size() > 0) {
+            ChainReaderHelper helper = new ChainReaderHelper();
+            try {
+                helper.setPrimaryReader(
+                    new InputStreamReader(inputStream, inputEncoding));
+            } catch (IOException eyeOhEx) {
+                throw new BuildException(
+                    "error setting up input stream", eyeOhEx);
+            }
+            helper.setFilterChains(inputFilterChains);
+            inputStream = new ReaderInputStream(
+                helper.getAssembledReader(), inputEncoding);
+        }
+    }
+
+    /**
+     * Create the StreamHandler to use with our Execute instance.
+     *
+     * @return the execute stream handler to manage the input, output and
+     * error streams.
+     *
+     * @throws BuildException if the execute stream handler cannot be created.
+     */
+    public synchronized ExecuteStreamHandler createHandler()
+        throws BuildException {
+        createStreams();
+        return new PumpStreamHandler(outputStream, errorStream, inputStream);
+    }
+
+    /**
+     * Pass output sent to System.out to specified output.
+     *
+     * @param output the data to be output
+     */
+    protected synchronized void handleOutput(String output) {
+        if (outPrintStream == null) {
+            outPrintStream = new PrintStream(outputStream);
+        }
+        outPrintStream.print(output);
+    }
+
+    /**
+     * Handle an input request
+     *
+     * @param buffer the buffer into which data is to be read.
+     * @param offset the offset into the buffer at which data is stored.
+     * @param length the amount of data to read
+     *
+     * @return the number of bytes read
+     *
+     * @exception IOException if the data cannot be read
+     */
+    protected synchronized int handleInput(byte[] buffer, int offset,
+                                           int length) throws IOException {
+        if (inputStream == null) {
+            return managingTask.getProject().defaultInput(buffer, offset,
+                                                          length);
+        } else {
+            return inputStream.read(buffer, offset, length);
+        }
+    }
+
+    /**
+     * Process data due to a flush operation.
+     *
+     * @param output the data being flushed.
+     */
+    protected synchronized void handleFlush(String output) {
+        if (outPrintStream == null) {
+            outPrintStream = new PrintStream(outputStream);
+        }
+        outPrintStream.print(output);
+        outPrintStream.flush();
+    }
+
+    /**
+     * Process error output
+     *
+     * @param output the error output data.
+     */
+    protected synchronized void handleErrorOutput(String output) {
+        if (errorPrintStream == null) {
+            errorPrintStream = new PrintStream(errorStream);
+        }
+        errorPrintStream.print(output);
+    }
+
+    /**
+     * Handle a flush operation on the error stream
+     *
+     * @param output the error information being flushed.
+     */
+    protected synchronized void handleErrorFlush(String output) {
+        if (errorPrintStream == null) {
+            errorPrintStream = new PrintStream(errorStream);
+        }
+        errorPrintStream.print(output);
+    }
+
+    /**
+     * Get the output stream for the redirector
+     *
+     * @return the redirector's output stream or null if no output
+     *         has been configured
+     */
+    public synchronized OutputStream getOutputStream() {
+        return outputStream;
+    }
+
+    /**
+     * Get the error stream for the redirector
+     *
+     * @return the redirector's error stream or null if no output
+     *         has been configured
+     */
+    public synchronized OutputStream getErrorStream() {
+        return errorStream;
+    }
+
+    /**
+     * Get the input stream for the redirector
+     *
+     * @return the redirector's input stream or null if no output
+     *         has been configured
+     */
+    public synchronized InputStream getInputStream() {
+        return inputStream;
+    }
+
+    /**
+     * Complete redirection.
+     *
+     * This operation will close any streams and create any specified
+     * property values.
+     *
+     * @throws IOException if the output properties cannot be read from their
+     * output streams.
+     */
+    public synchronized void complete() throws IOException {
+        System.out.flush();
+        System.err.flush();
+
+        if (inputStream != null) {
+            inputStream.close();
+        }
+
+        outputStream.flush();
+        outputStream.close();
+
+        errorStream.flush();
+        errorStream.close();
+
+        //wait for the StreamPumpers to finish
+        while (threadGroup.activeCount() > 0) {
+            try {
+                managingTask.log("waiting for " + threadGroup.activeCount()
+                    + " Threads:", Project.MSG_DEBUG);
+                Thread[] thread = new Thread[threadGroup.activeCount()];
+                threadGroup.enumerate(thread);
+                for (int i = 0; i < thread.length && thread[i] != null; i++) {
+                    try {
+                        managingTask.log(thread[i].toString(), Project.MSG_DEBUG);
+                    } catch (NullPointerException enPeaEx) {
+                        // Ignore exception
+                    }
+                }
+                Thread.sleep(1000);
+            } catch (InterruptedException eyeEx) {
+                // Ignore exception
+            }
+        }
+
+        setProperties();
+
+        inputStream = null;
+        outputStream = null;
+        errorStream = null;
+        outPrintStream = null;
+        errorPrintStream = null;
+   }
+
+    /**
+     * Notify the <CODE>Redirector</CODE> that it is now okay
+     * to set any output and/or error properties.
+     */
+    public synchronized void setProperties() {
+        if (baos != null) {
+            try {
+                baos.close();
+            } catch (IOException eyeOhEx) {
+                // Ignore exception
+            }
+        }
+        if (errorBaos != null) {
+            try {
+                errorBaos.close();
+            } catch (IOException eyeOhEx) {
+                // Ignore exception
+            }
+        }
+    }
+
+    private OutputStream foldFiles(File[] file, String logHead, int loglevel) {
+        OutputStream result
+            = new LazyFileOutputStream(file[0], append, createEmptyFiles);
+
+        managingTask.log(logHead + file[0], loglevel);
+        char[] c = new char[logHead.length()];
+        Arrays.fill(c, ' ');
+        String indent = new String(c);
+
+        for (int i = 1; i < file.length; i++) {
+            outputStream = new TeeOutputStream(outputStream,
+                new LazyFileOutputStream(file[i], append, createEmptyFiles));
+            managingTask.log(indent + file[i], loglevel);
+        }
+        return result;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Rename.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Rename.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Rename.java	(revision 16139)
@@ -0,0 +1,93 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Renames a file.
+ *
+ *
+ * @deprecated The rename task is deprecated since Ant 1.2.  Use move instead.
+ * @since Ant 1.1
+ */
+public class Rename extends Task {
+
+    private File src;
+    private File dest;
+    private boolean replace = true;
+
+
+    /**
+     * Sets the file to be renamed.
+     * @param src the file to rename
+     */
+    public void setSrc(File src) {
+        this.src = src;
+    }
+
+    /**
+     * Sets the new name of the file.
+     * @param dest the new name of the file.
+     */
+    public void setDest(File dest) {
+        this.dest = dest;
+    }
+
+    /**
+     * Sets whether an existing file should be replaced.
+     * @param replace <code>on</code>, if an existing file should be replaced.
+     */
+    public void setReplace(String replace) {
+        this.replace = Project.toBoolean(replace);
+    }
+
+
+    /**
+     * Renames the file <code>src</code> to <code>dest</code>
+     * @exception org.apache.tools.ant.BuildException The exception is
+     * thrown, if the rename operation fails.
+     */
+    public void execute() throws BuildException {
+        log("DEPRECATED - The rename task is deprecated.  Use move instead.");
+
+        if (dest == null) {
+            throw new BuildException("dest attribute is required", getLocation());
+        }
+
+        if (src == null) {
+            throw new BuildException("src attribute is required", getLocation());
+        }
+
+        if (!replace && dest.exists()) {
+            throw new BuildException(dest + " already exists.");
+        }
+
+        try {
+            FileUtils.newFileUtils().rename(src, dest);
+        } catch (IOException e) {
+            throw new BuildException("Unable to rename " + src + " to "
+                + dest, e, getLocation());
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Replace.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Replace.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Replace.java	(revision 16139)
@@ -0,0 +1,630 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Replaces all occurrences of one or more string tokens with given
+ * values in the indicated files. Each value can be either a string
+ * or the value of a property available in a designated property file.
+ * If you want to replace a text that crosses line boundaries, you
+ * must use a nested <code>&lt;replacetoken&gt;</code> element.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="filesystem"
+ */
+public class Replace extends MatchingTask {
+
+    private File src = null;
+    private NestedString token = null;
+    private NestedString value = new NestedString();
+
+    private File propertyFile = null;
+    private File replaceFilterFile = null;
+    private Properties properties = null;
+    private Vector replacefilters = new Vector();
+
+    private File dir = null;
+
+    private int fileCount;
+    private int replaceCount;
+    private boolean summary = false;
+
+    /** The encoding used to read and write files - if null, uses default */
+    private String encoding = null;
+
+    private FileUtils fileUtils = FileUtils.newFileUtils();
+
+    /**
+     * an inline string to use as the replacement text
+     */
+    public class NestedString {
+
+        private StringBuffer buf = new StringBuffer();
+
+        /**
+         * the text of the element
+         *
+         * @param val the string to add
+         */
+        public void addText(String val) {
+            buf.append(val);
+        }
+
+        /**
+         * @return the text
+         */
+        public String getText() {
+            return buf.substring(0);
+        }
+    }
+
+    /**
+     * A filter to apply.
+     */
+    public class Replacefilter {
+        private String token;
+        private String value;
+        private String property;
+
+        /**
+         * validate the filter's configuration
+         * @throws BuildException if any part is invalid
+         */
+        public void validate() throws BuildException {
+            //Validate mandatory attributes
+            if (token == null) {
+                String message = "token is a mandatory attribute "
+                    + "of replacefilter.";
+                throw new BuildException(message);
+            }
+
+            if ("".equals(token)) {
+                String message = "The token attribute must not be an empty "
+                    + "string.";
+                throw new BuildException(message);
+            }
+
+            //value and property are mutually exclusive attributes
+            if ((value != null) && (property != null)) {
+                String message = "Either value or property "
+                    + "can be specified, but a replacefilter "
+                    + "element cannot have both.";
+                throw new BuildException(message);
+            }
+
+            if ((property != null)) {
+                //the property attribute must have access to a property file
+                if (propertyFile == null) {
+                    String message = "The replacefilter's property attribute "
+                        + "can only be used with the replacetask's "
+                        + "propertyFile attribute.";
+                    throw new BuildException(message);
+                }
+
+                //Make sure property exists in property file
+                if (properties == null
+                    || properties.getProperty(property) == null) {
+                    String message = "property \"" + property
+                        + "\" was not found in " + propertyFile.getPath();
+                    throw new BuildException(message);
+                }
+            }
+        }
+
+        /**
+         * Get the replacement value for this filter token.
+         * @return the replacement value
+         */
+        public String getReplaceValue() {
+            if (property != null) {
+                return properties.getProperty(property);
+            } else if (value != null) {
+                return value;
+            } else if (Replace.this.value != null) {
+                return Replace.this.value.getText();
+            } else {
+                //Default is empty string
+                return new String("");
+            }
+        }
+
+        /**
+         * Set the token to replace
+         * @param token token
+         */
+        public void setToken(String token) {
+            this.token = token;
+        }
+
+        /**
+         * Get the string to search for
+         * @return current token
+         */
+        public String getToken() {
+            return token;
+        }
+
+        /**
+         * The replacement string; required if <code>property<code>
+         * is not set
+         * @param value value to replace
+         */
+        public void setValue(String value) {
+            this.value = value;
+        }
+
+        /**
+         * Get replacements string
+         * @return replacement or null
+         */
+        public String getValue() {
+            return value;
+        }
+
+        /**
+         * Set the name of the property whose value is to serve as
+         * the replacement value; required if <code>value</code> is not set.
+         * @param property propname
+         */
+        public void setProperty(String property) {
+            this.property = property;
+        }
+
+        /**
+         * Get the name of the property whose value is to serve as
+         * the replacement value;
+         * @return property or null
+         */
+        public String getProperty() {
+            return property;
+        }
+    }
+
+    /**
+     * Do the execution.
+     * @throws BuildException if we cant build
+     */
+    public void execute() throws BuildException {
+
+        Vector savedFilters = (Vector) replacefilters.clone();
+        Properties savedProperties =
+            properties == null ? null : (Properties) properties.clone();
+
+        try {
+            if (replaceFilterFile != null) {
+                Properties props = getProperties(replaceFilterFile);
+                Enumeration e = props.keys();
+                while (e.hasMoreElements()) {
+                    String token =  e.nextElement().toString();
+                    Replacefilter replaceFilter = createReplacefilter();
+                    replaceFilter.setToken(token);
+                    replaceFilter.setValue(props.getProperty(token));
+                }
+            }
+
+            validateAttributes();
+
+            if (propertyFile != null) {
+                properties = getProperties(propertyFile);
+            }
+
+            validateReplacefilters();
+            fileCount = 0;
+            replaceCount = 0;
+
+            if (src != null) {
+                processFile(src);
+            }
+
+            if (dir != null) {
+                DirectoryScanner ds = super.getDirectoryScanner(dir);
+                String[] srcs = ds.getIncludedFiles();
+
+                for (int i = 0; i < srcs.length; i++) {
+                    File file = new File(dir, srcs[i]);
+                    processFile(file);
+                }
+            }
+
+            if (summary) {
+                log("Replaced " + replaceCount + " occurrences in "
+                    + fileCount + " files.", Project.MSG_INFO);
+            }
+        } finally {
+            replacefilters = savedFilters;
+            properties = savedProperties;
+        } // end of finally
+
+    }
+
+    /**
+     * Validate attributes provided for this task in .xml build file.
+     *
+     * @exception BuildException if any supplied attribute is invalid or any
+     * mandatory attribute is missing
+     */
+    public void validateAttributes() throws BuildException {
+        if (src == null && dir == null) {
+            String message = "Either the file or the dir attribute "
+                + "must be specified";
+            throw new BuildException(message, getLocation());
+        }
+        if (propertyFile != null && !propertyFile.exists()) {
+            String message = "Property file " + propertyFile.getPath()
+                + " does not exist.";
+            throw new BuildException(message, getLocation());
+        }
+        if (token == null && replacefilters.size() == 0) {
+            String message = "Either token or a nested replacefilter "
+                + "must be specified";
+            throw new BuildException(message, getLocation());
+        }
+        if (token != null && "".equals(token.getText())) {
+            String message = "The token attribute must not be an empty string.";
+            throw new BuildException(message, getLocation());
+        }
+    }
+
+    /**
+     * Validate nested elements.
+     *
+     * @exception BuildException if any supplied attribute is invalid or any
+     * mandatory attribute is missing
+     */
+    public void validateReplacefilters()
+            throws BuildException {
+        for (int i = 0; i < replacefilters.size(); i++) {
+            Replacefilter element =
+                (Replacefilter) replacefilters.elementAt(i);
+            element.validate();
+        }
+    }
+
+    /**
+     * helper method to load a properties file and throw a build exception
+     * if it cannot be loaded
+     * @param propertyFile the file to load the properties from
+     * @return loaded properties collection
+     * @throws BuildException if the file could not be found or read
+     */
+    public Properties getProperties(File propertyFile) throws BuildException {
+        Properties properties = new Properties();
+
+        FileInputStream in = null;
+        try {
+            in = new FileInputStream(propertyFile);
+            properties.load(in);
+        } catch (FileNotFoundException e) {
+            String message = "Property file (" + propertyFile.getPath()
+                + ") not found.";
+            throw new BuildException(message);
+        } catch (IOException e) {
+            String message = "Property file (" + propertyFile.getPath()
+                + ") cannot be loaded.";
+            throw new BuildException(message);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+
+        return properties;
+    }
+
+    /**
+     * Perform the replacement on the given file.
+     *
+     * The replacement is performed on a temporary file which then
+     * replaces the original file.
+     *
+     * @param src the source file
+     */
+    private void processFile(File src) throws BuildException {
+        if (!src.exists()) {
+            throw new BuildException("Replace: source file " + src.getPath()
+                                     + " doesn't exist", getLocation());
+        }
+
+        File temp = fileUtils.createTempFile("rep", ".tmp",
+                                             fileUtils.getParentFile(src));
+        temp.deleteOnExit();
+
+        Reader reader = null;
+        Writer writer = null;
+        try {
+            reader = encoding == null ? new FileReader(src)
+                : new InputStreamReader(new FileInputStream(src), encoding);
+            writer = encoding == null ? new FileWriter(temp)
+                : new OutputStreamWriter(new FileOutputStream(temp), encoding);
+
+            BufferedReader br = new BufferedReader(reader);
+            BufferedWriter bw = new BufferedWriter(writer);
+
+            String buf = fileUtils.readFully(br);
+            if (buf == null) {
+                buf = "";
+            }
+
+            //Preserve original string (buf) so we can compare the result
+            String newString = new String(buf);
+
+            if (token != null) {
+                // line separators in values and tokens are "\n"
+                // in order to compare with the file contents, replace them
+                // as needed
+                String val = stringReplace(value.getText(), "\r\n",
+                                           "\n", false);
+                val = stringReplace(val, "\n",
+                                           StringUtils.LINE_SEP, false);
+                String tok = stringReplace(token.getText(), "\r\n",
+                                            "\n", false);
+                tok = stringReplace(tok, "\n",
+                                           StringUtils.LINE_SEP, false);
+
+                // for each found token, replace with value
+                log("Replacing in " + src.getPath() + ": " + token.getText()
+                    + " --> " + value.getText(), Project.MSG_VERBOSE);
+                newString = stringReplace(newString, tok, val, true);
+            }
+
+            if (replacefilters.size() > 0) {
+                newString = processReplacefilters(newString, src.getPath());
+            }
+
+            boolean changes = !newString.equals(buf);
+            if (changes) {
+                bw.write(newString, 0, newString.length());
+                bw.flush();
+            }
+
+            // cleanup
+            bw.close();
+            writer = null;
+            br.close();
+            reader = null;
+
+            // If there were changes, move the new one to the old one;
+            // otherwise, delete the new one
+            if (changes) {
+                ++fileCount;
+                fileUtils.rename(temp, src);
+                temp = null;
+            }
+        } catch (IOException ioe) {
+            throw new BuildException("IOException in " + src + " - "
+                                    + ioe.getClass().getName() + ":"
+                                    + ioe.getMessage(), ioe, getLocation());
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+            if (writer != null) {
+                try {
+                    writer.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+            if (temp != null) {
+                temp.delete();
+            }
+        }
+
+    }
+
+    /**
+     * apply all replace filters to a buffer
+     * @param buffer string to filter
+     * @param filename filename for logging purposes
+     * @return filtered string
+     */
+    private String processReplacefilters(String buffer, String filename) {
+        String newString = new String(buffer);
+
+        for (int i = 0; i < replacefilters.size(); i++) {
+            Replacefilter filter = (Replacefilter) replacefilters.elementAt(i);
+
+            //for each found token, replace with value
+            log("Replacing in " + filename + ": " + filter.getToken()
+                + " --> " + filter.getReplaceValue(), Project.MSG_VERBOSE);
+            newString = stringReplace(newString, filter.getToken(),
+                                      filter.getReplaceValue(), true);
+        }
+
+        return newString;
+    }
+
+
+    /**
+     * Set the source file; required unless <code>dir</code> is set.
+     * @param file source file
+     */
+    public void setFile(File file) {
+        this.src = file;
+    }
+
+    /**
+     * Indicates whether a summary of the replace operation should be
+     * produced, detailing how many token occurrences and files were
+     * processed; optional, default=false
+     *
+     * @param summary true if you would like a summary logged of the
+     * replace operation
+     */
+    public void setSummary(boolean summary) {
+        this.summary = summary;
+    }
+
+
+    /**
+     * Sets the name of a property file containing filters; optional.
+     * Each property will be treated as a
+     * replacefilter where token is the name of the property and value
+     * is the value of the property.
+     * @param filename file to load
+     */
+    public void setReplaceFilterFile(File filename) {
+        replaceFilterFile = filename;
+    }
+
+    /**
+     * The base directory to use when replacing a token in multiple files;
+     * required if <code>file</code> is not defined.
+     * @param dir base dir
+     */
+    public void setDir(File dir) {
+        this.dir = dir;
+    }
+
+    /**
+     * Set the string token to replace;
+     * required unless a nested
+     * <code>replacetoken</code> element or the <code>replacefilterfile</code>
+     * attribute is used.
+     * @param token token string
+     */
+    public void setToken(String token) {
+        createReplaceToken().addText(token);
+    }
+
+    /**
+     * Set the string value to use as token replacement;
+     * optional, default is the empty string ""
+     * @param value replacement value
+     */
+    public void setValue(String value) {
+        createReplaceValue().addText(value);
+    }
+
+    /**
+     * Set the file encoding to use on the files read and written by the task;
+     * optional, defaults to default JVM encoding
+     *
+     * @param encoding the encoding to use on the files
+     */
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * the token to filter as the text of a nested element
+     * @return nested token to configure
+     */
+    public NestedString createReplaceToken() {
+        if (token == null) {
+            token = new NestedString();
+        }
+        return token;
+    }
+
+    /**
+     * the string to replace the token as the text of a nested element
+     * @return replacement value to configure
+     */
+    public NestedString createReplaceValue() {
+        return value;
+    }
+
+    /**
+     * The name of a property file from which properties specified using
+     * nested <code>&lt;replacefilter&gt;</code> elements are drawn;
+     * Required only if <i>property</i> attribute of
+     * <code>&lt;replacefilter&gt;</code> is used.
+     * @param filename file to load
+     */
+    public void setPropertyFile(File filename) {
+        propertyFile = filename;
+    }
+
+    /**
+     * Add a nested &lt;replacefilter&gt; element.
+     * @return a nested ReplaceFilter object to be configured
+     */
+    public Replacefilter createReplacefilter() {
+        Replacefilter filter = new Replacefilter();
+        replacefilters.addElement(filter);
+        return filter;
+    }
+
+    /**
+     * Replace occurrences of str1 in string str with str2
+     */
+    private String stringReplace(String str, String str1, String str2,
+                                 boolean countReplaces) {
+        StringBuffer ret = new StringBuffer();
+        int start = 0;
+        int found = str.indexOf(str1);
+        while (found >= 0) {
+            // write everything up to the found str1
+            if (found > start) {
+                ret.append(str.substring(start, found));
+            }
+
+            // write the replacement str2
+            if (str2 != null) {
+                ret.append(str2);
+            }
+
+            // search again
+            start = found + str1.length();
+            found = str.indexOf(str1, start);
+            if (countReplaces) {
+                ++replaceCount;
+            }
+        }
+
+        // write the remaining characters
+        if (str.length() > start) {
+            ret.append(str.substring(start, str.length()));
+        }
+
+        return ret.toString();
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Rmic.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Rmic.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Rmic.java	(revision 16139)
@@ -0,0 +1,728 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.rmi.Remote;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.rmic.RmicAdapter;
+import org.apache.tools.ant.taskdefs.rmic.RmicAdapterFactory;
+import org.apache.tools.ant.types.FilterSetCollection;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.SourceFileScanner;
+import org.apache.tools.ant.util.facade.FacadeTaskHelper;
+
+/**
+ * Runs the rmic compiler against classes.</p>
+ * <p>Rmic can be run on a single class (as specified with the classname
+ * attribute) or a number of classes at once (all classes below base that
+ * are neither _Stub nor _Skel classes).  If you want to rmic a single
+ * class and this class is a class nested into another class, you have to
+ * specify the classname in the form <code>Outer$$Inner</code> instead of
+ * <code>Outer.Inner</code>.</p>
+ * <p>It is possible to refine the set of files that are being rmiced. This can
+ * be done with the <i>includes</i>, <i>includesfile</i>, <i>excludes</i>,
+ * <i>excludesfile</i> and <i>defaultexcludes</i>
+ * attributes. With the <i>includes</i> or <i>includesfile</i> attribute you
+ * specify the files you want to have included by using patterns. The
+ * <i>exclude</i> or <i>excludesfile</i> attribute is used to specify
+ * the files you want to have excluded. This is also done with patterns. And
+ * finally with the <i>defaultexcludes</i> attribute, you can specify whether
+ * you want to use default exclusions or not. See the section on
+ * directory based tasks</a>, on how the
+ * inclusion/exclusion of files works, and how to write patterns.</p>
+ * <p>This task forms an implicit FileSet and
+ * supports all attributes of <code>&lt;fileset&gt;</code>
+ * (<code>dir</code> becomes <code>base</code>) as well as the nested
+ * <code>&lt;include&gt;</code>, <code>&lt;exclude&gt;</code> and
+ * <code>&lt;patternset&gt;</code> elements.</p>
+ * <p>It is possible to use different compilers. This can be selected
+ * with the &quot;build.rmic&quot; property or the <code>compiler</code>
+ * attribute. <a name="compilervalues">There are three choices</a>:</p>
+ * <ul>
+ *   <li>sun (the standard compiler of the JDK)</li>
+ *   <li>kaffe (the standard compiler of
+ *       {@link <a href="http://www.kaffe.org">Kaffe</a>})</li>
+ *   <li>weblogic</li>
+ * </ul>
+ *
+ * <p> The <a href="http://dione.zcu.cz/~toman40/miniRMI/">miniRMI</a>
+ * project contains a compiler implementation for this task as well,
+ * please consult miniRMI's documentation to learn how to use it.</p>
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="java"
+ */
+
+public class Rmic extends MatchingTask {
+
+    private static final String FAIL_MSG
+        = "Rmic failed; see the compiler error output for details.";
+
+    private File baseDir;
+    private String classname;
+    private File sourceBase;
+    private String stubVersion;
+    private Path compileClasspath;
+    private Path extDirs;
+    private boolean verify = false;
+    private boolean filtering = false;
+
+    private boolean iiop = false;
+    private String  iiopOpts;
+    private boolean idl  = false;
+    private String  idlOpts;
+    private boolean debug  = false;
+    private boolean includeAntRuntime = true;
+    private boolean includeJavaRuntime = false;
+
+    private Vector compileList = new Vector();
+
+    private ClassLoader loader = null;
+
+    private FileUtils fileUtils = FileUtils.newFileUtils();
+
+    private FacadeTaskHelper facade;
+
+    /**
+     * Constructor for Rmic.
+     */
+    public Rmic() {
+        try {
+            Class.forName("kaffe.rmi.rmic.RMIC");
+            facade = new FacadeTaskHelper("kaffe");
+        } catch (ClassNotFoundException cnfe) {
+            facade = new FacadeTaskHelper("sun");
+        }
+    }
+
+    /**
+     * Sets the location to store the compiled files; required
+     * @param base the location to store the compiled files
+     */
+    public void setBase(File base) {
+        this.baseDir = base;
+    }
+
+    /**
+     * Gets the base directory to output generated class.
+     * @return the location of the compiled files
+     */
+
+    public File getBase() {
+        return this.baseDir;
+    }
+
+    /**
+     * Sets the class to run <code>rmic</code> against;
+     * optional
+     * @param classname the name of the class for rmic to create code for
+     */
+    public void setClassname(String classname) {
+        this.classname = classname;
+    }
+
+    /**
+     * Gets the class name to compile.
+     * @return the name of the class to compile
+     */
+    public String getClassname() {
+        return classname;
+    }
+
+    /**
+     * optional directory to save generated source files to.
+     * @param sourceBase the directory to save source files to.
+     */
+    public void setSourceBase(File sourceBase) {
+        this.sourceBase = sourceBase;
+    }
+
+    /**
+     * Gets the source dirs to find the source java files.
+     * @return sourceBase the directory containing the source files.
+     */
+    public File getSourceBase() {
+        return sourceBase;
+    }
+
+    /**
+     * Specify the JDK version for the generated stub code.
+     * Specify &quot;1.1&quot; to pass the &quot;-v1.1&quot; option to rmic.</td>
+     * @param stubVersion the JDK version
+     */
+    public void setStubVersion(String stubVersion) {
+        this.stubVersion = stubVersion;
+    }
+
+    /**
+     * Gets the JDK version for the generated stub code.
+     * @return stubVersion
+     */
+    public String getStubVersion() {
+        return stubVersion;
+    }
+
+    /**
+     * Sets token filtering [optional], default=false
+     * @param filter turn on token filtering
+     */
+    public void setFiltering(boolean filter) {
+        this.filtering = filter;
+    }
+
+    /**
+     * Gets whether token filtering is set
+     * @return filtering
+     */
+    public boolean getFiltering() {
+        return filtering;
+    }
+
+    /**
+     * Generate debug info (passes -g to rmic);
+     * optional, defaults to false
+     * @param debug turn on debug info
+     */
+    public void setDebug(boolean debug) {
+        this.debug = debug;
+    }
+
+    /**
+     * Gets the debug flag.
+     * @return debug
+     */
+    public boolean getDebug() {
+        return debug;
+    }
+
+    /**
+     * Set the classpath to be used for this compilation.
+     * @param classpath the classpath used for this compilation
+     */
+    public void setClasspath(Path classpath) {
+        if (compileClasspath == null) {
+            compileClasspath = classpath;
+        } else {
+            compileClasspath.append(classpath);
+        }
+    }
+
+    /**
+     * Creates a nested classpath element.
+     * @return classpath
+     */
+    public Path createClasspath() {
+        if (compileClasspath == null) {
+            compileClasspath = new Path(getProject());
+        }
+        return compileClasspath.createPath();
+    }
+
+    /**
+     * Adds to the classpath a reference to
+     * a &lt;path&gt; defined elsewhere.
+     * @param pathRef the reference to add to the classpath
+     */
+    public void setClasspathRef(Reference pathRef) {
+        createClasspath().setRefid(pathRef);
+    }
+
+    /**
+     * Gets the classpath.
+     * @return the classpath
+     */
+    public Path getClasspath() {
+        return compileClasspath;
+    }
+
+    /**
+     * Flag to enable verification so that the classes
+     * found by the directory match are
+     * checked to see if they implement java.rmi.Remote.
+     * optional; This defaults to false if not set.
+     * @param verify turn on verification for classes
+     */
+
+    public void setVerify(boolean verify) {
+        this.verify = verify;
+    }
+
+    /**
+     * Get verify flag.
+     * @return verify
+     */
+    public boolean getVerify() {
+        return verify;
+    }
+
+    /**
+     * Indicates that IIOP compatible stubs should
+     * be generated; optional, defaults to false
+     * if not set.
+     * @param iiop generate IIOP compatible stubs
+     */
+    public void setIiop(boolean iiop) {
+        this.iiop = iiop;
+    }
+
+    /**
+     * Gets iiop flags.
+     * @return iiop
+     */
+    public boolean getIiop() {
+        return iiop;
+    }
+
+    /**
+     * Set additional arguments for iiop
+     * @param iiopOpts additional arguments for iiop
+     */
+    public void setIiopopts(String iiopOpts) {
+        this.iiopOpts = iiopOpts;
+    }
+
+    /**
+     * Gets additional arguments for iiop.
+     * @return iiopOpts
+     */
+    public String getIiopopts() {
+        return iiopOpts;
+    }
+
+    /**
+     * Indicates that IDL output should be
+     * generated.  This defaults to false
+     * if not set.
+     * @param idl generate IDL output
+     */
+    public void setIdl(boolean idl) {
+        this.idl = idl;
+    }
+
+    /**
+     * Gets IDL flags.
+     * @return the idl flag
+     */
+    public boolean getIdl() {
+        return idl;
+    }
+
+    /**
+     * pass additional arguments for IDL compile
+     * @param idlOpts additional IDL arguments
+     */
+    public void setIdlopts(String idlOpts) {
+        this.idlOpts = idlOpts;
+    }
+
+    /**
+     * Gets additional arguments for idl compile.
+     * @return the idl options
+     */
+    public String getIdlopts() {
+        return idlOpts;
+    }
+
+    /**
+     * Gets file list to compile.
+     * @return the list of files to compile.
+     */
+    public Vector getFileList() {
+        return compileList;
+    }
+
+    /**
+     * Sets whether or not to include ant's own classpath in this task's
+     * classpath.
+     * Optional; default is <code>true</code>.
+     * @param include if true include ant's classpath
+     */
+    public void setIncludeantruntime(boolean include) {
+        includeAntRuntime = include;
+    }
+
+    /**
+     * Gets whether or not the ant classpath is to be included in the
+     * task's classpath.
+     * @return true if ant's classpath is to be included
+     */
+    public boolean getIncludeantruntime() {
+        return includeAntRuntime;
+    }
+
+    /**
+     * task's classpath.
+     * Enables or disables including the default run-time
+     * libraries from the executing VM; optional,
+     * defaults to false
+     * @param include if true include default run-time libraries
+     */
+    public void setIncludejavaruntime(boolean include) {
+        includeJavaRuntime = include;
+    }
+
+    /**
+     * Gets whether or not the java runtime should be included in this
+     * task's classpath.
+     * @return true if default run-time libraries are included
+     */
+    public boolean getIncludejavaruntime() {
+        return includeJavaRuntime;
+    }
+
+    /**
+     * Sets the extension directories that will be used during the
+     * compilation; optional.
+     * @param extDirs the extension directories to be used
+     */
+    public void setExtdirs(Path extDirs) {
+        if (this.extDirs == null) {
+            this.extDirs = extDirs;
+        } else {
+            this.extDirs.append(extDirs);
+        }
+    }
+
+    /**
+     * Maybe creates a nested extdirs element.
+     * @return path object to be configured with the extension directories
+     */
+    public Path createExtdirs() {
+        if (extDirs == null) {
+            extDirs = new Path(getProject());
+        }
+        return extDirs.createPath();
+    }
+
+    /**
+     * Gets the extension directories that will be used during the
+     * compilation.
+     * @return the extension directories to be used
+     */
+    public Path getExtdirs() {
+        return extDirs;
+    }
+
+    /**
+     * @return the compile list.
+     */
+    public Vector getCompileList() {
+        return compileList;
+    }
+
+    /**
+     * Sets the compiler implementation to use; optional,
+     * defaults to the value of the <code>build.rmic</code> property,
+     * or failing that, default compiler for the current VM
+     * @param compiler the compiler implemention to use
+     * @since Ant 1.5
+     */
+    public void setCompiler(String compiler) {
+        if (compiler.length() > 0) {
+            facade.setImplementation(compiler);
+        }
+    }
+
+    /**
+     * get the name of the current compiler
+     * @return the name of the compiler
+     * @since Ant 1.5
+     */
+    public String getCompiler() {
+        facade.setMagicValue(getProject().getProperty("build.rmic"));
+        return facade.getImplementation();
+    }
+
+    /**
+     * Adds an implementation specific command line argument.
+     * @return an object to be configured with a command line argument
+     * @since Ant 1.5
+     */
+    public ImplementationSpecificArgument createCompilerArg() {
+        ImplementationSpecificArgument arg =
+            new ImplementationSpecificArgument();
+        facade.addImplementationArgument(arg);
+        return arg;
+    }
+
+    /**
+     * Get the additional implementation specific command line arguments.
+     * @return array of command line arguments, guaranteed to be non-null.
+     * @since Ant 1.5
+     */
+    public String[] getCurrentCompilerArgs() {
+        getCompiler();
+        return facade.getArgs();
+    }
+
+    /**
+     * execute by creating an instance of an implementation
+     * class and getting to do the work
+     */
+    public void execute() throws BuildException {
+        if (baseDir == null) {
+            throw new BuildException("base attribute must be set!", getLocation());
+        }
+        if (!baseDir.exists()) {
+            throw new BuildException("base does not exist!", getLocation());
+        }
+
+        if (verify) {
+            log("Verify has been turned on.", Project.MSG_VERBOSE);
+        }
+
+        RmicAdapter adapter = RmicAdapterFactory.getRmic(getCompiler(), this);
+
+        // now we need to populate the compiler adapter
+        adapter.setRmic(this);
+
+        Path classpath = adapter.getClasspath();
+        loader = getProject().createClassLoader(classpath);
+
+        try {
+            // scan base dirs to build up compile lists only if a
+            // specific classname is not given
+            if (classname == null) {
+                DirectoryScanner ds = this.getDirectoryScanner(baseDir);
+                String[] files = ds.getIncludedFiles();
+                scanDir(baseDir, files, adapter.getMapper());
+            } else {
+                // otherwise perform a timestamp comparison - at least
+                scanDir(baseDir,
+                        new String[] {classname.replace('.',
+                                                        File.separatorChar)
+                                          + ".class"},
+                        adapter.getMapper());
+            }
+
+            int fileCount = compileList.size();
+            if (fileCount > 0) {
+                log("RMI Compiling " + fileCount
+                    + " class" + (fileCount > 1 ? "es" : "") + " to " + baseDir,
+                    Project.MSG_INFO);
+
+                // finally, lets execute the compiler!!
+                if (!adapter.execute()) {
+                    throw new BuildException(FAIL_MSG, getLocation());
+                }
+            }
+
+            /*
+             * Move the generated source file to the base directory.  If
+             * base directory and sourcebase are the same, the generated
+             * sources are already in place.
+             */
+            if (null != sourceBase && !baseDir.equals(sourceBase)
+                && fileCount > 0) {
+                if (idl) {
+                    log("Cannot determine sourcefiles in idl mode, ",
+                        Project.MSG_WARN);
+                    log("sourcebase attribute will be ignored.",
+                        Project.MSG_WARN);
+                } else {
+                    for (int j = 0; j < fileCount; j++) {
+                        moveGeneratedFile(baseDir, sourceBase,
+                                          (String) compileList.elementAt(j),
+                                          adapter);
+                    }
+                }
+            }
+        } finally {
+            compileList.removeAllElements();
+        }
+    }
+
+    /**
+     * Move the generated source file(s) to the base directory
+     *
+     * @throws org.apache.tools.ant.BuildException When error
+     * copying/removing files.
+     */
+    private void moveGeneratedFile (File baseDir, File sourceBaseFile,
+                                    String classname,
+                                    RmicAdapter adapter)
+        throws BuildException {
+
+        String classFileName =
+            classname.replace('.', File.separatorChar) + ".class";
+        String[] generatedFiles =
+            adapter.getMapper().mapFileName(classFileName);
+
+        for (int i = 0; i < generatedFiles.length; i++) {
+            final String generatedFile = generatedFiles[i];
+            if (!generatedFile.endsWith(".class")) {
+                // don't know how to handle that - a IDL file doesn't
+                // have a corresponding Java source for example.
+                continue;
+            }
+
+            final int pos = generatedFile.length() - ".class".length();
+            String sourceFileName =
+                generatedFile.substring(0, pos) + ".java";
+
+            File oldFile = new File(baseDir, sourceFileName);
+            if (!oldFile.exists()) {
+                // no source file generated, nothing to move
+                continue;
+            }
+
+            File newFile = new File(sourceBaseFile, sourceFileName);
+            try {
+                if (filtering) {
+                    fileUtils.copyFile(oldFile, newFile,
+                        new FilterSetCollection(getProject()
+                                                .getGlobalFilterSet()));
+                } else {
+                    fileUtils.copyFile(oldFile, newFile);
+                }
+                oldFile.delete();
+            } catch (IOException ioe) {
+                String msg = "Failed to copy " + oldFile + " to "
+                    + newFile + " due to " + ioe.getMessage();
+                throw new BuildException(msg, ioe, getLocation());
+            }
+        }
+    }
+
+    /**
+     * Scans the directory looking for class files to be compiled.
+     * The result is returned in the class variable compileList.
+     * @param baseDir the base direction
+     * @param files   the list of files to scan
+     * @param mapper  the mapper of files to target files
+     */
+    protected void scanDir(File baseDir, String[] files,
+                           FileNameMapper mapper) {
+
+        String[] newFiles = files;
+        if (idl) {
+            log("will leave uptodate test to rmic implementation in idl mode.",
+                Project.MSG_VERBOSE);
+        } else if (iiop
+                   && iiopOpts != null && iiopOpts.indexOf("-always") > -1) {
+            log("no uptodate test as -always option has been specified",
+                Project.MSG_VERBOSE);
+        } else {
+            SourceFileScanner sfs = new SourceFileScanner(this);
+            newFiles = sfs.restrict(files, baseDir, baseDir, mapper);
+        }
+
+        for (int i = 0; i < newFiles.length; i++) {
+            String classname = newFiles[i].replace(File.separatorChar, '.');
+            classname = classname.substring(0, classname.lastIndexOf(".class"));
+            compileList.addElement(classname);
+        }
+    }
+
+    /**
+     * Load named class and test whether it can be rmic'ed
+     * @param classname the name of the class to be tested
+     * @return true if the class can be rmic'ed
+     */
+    public boolean isValidRmiRemote(String classname) {
+        try {
+            Class testClass = loader.loadClass(classname);
+            // One cannot RMIC an interface for "classic" RMI (JRMP)
+            if (testClass.isInterface() && !iiop && !idl) {
+                return false;
+            }
+            return isValidRmiRemote(testClass);
+        } catch (ClassNotFoundException e) {
+            log("Unable to verify class " + classname
+                + ". It could not be found.", Project.MSG_WARN);
+        } catch (NoClassDefFoundError e) {
+            log("Unable to verify class " + classname
+                + ". It is not defined.", Project.MSG_WARN);
+        } catch (Throwable t) {
+            log("Unable to verify class " + classname
+                + ". Loading caused Exception: "
+                + t.getMessage(), Project.MSG_WARN);
+        }
+        // we only get here if an exception has been thrown
+        return false;
+    }
+
+    /**
+     * Returns the topmost interface that extends Remote for a given
+     * class - if one exists.
+     * @param testClass the class to be tested
+     * @return the topmost interface that extends Remote, or null if there
+     *         is none.
+     */
+    public Class getRemoteInterface(Class testClass) {
+        if (Remote.class.isAssignableFrom(testClass)) {
+            Class [] interfaces = testClass.getInterfaces();
+            if (interfaces != null) {
+                for (int i = 0; i < interfaces.length; i++) {
+                    if (Remote.class.isAssignableFrom(interfaces[i])) {
+                        return interfaces[i];
+                    }
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Check to see if the class or (super)interfaces implement
+     * java.rmi.Remote.
+     */
+    private boolean isValidRmiRemote (Class testClass) {
+        return getRemoteInterface(testClass) != null;
+    }
+
+    /**
+     * Classloader for the user-specified classpath.
+     * @return the classloader
+     */
+    public ClassLoader getLoader() {
+        return loader;
+    }
+
+    /**
+     * Adds an "compiler" attribute to Commandline$Attribute used to
+     * filter command line attributes based on the current
+     * implementation.
+     */
+    public class ImplementationSpecificArgument extends
+        org.apache.tools.ant.util.facade.ImplementationSpecificArgument {
+
+        /**
+         * Only pass the specified argument if the
+         * chosen compiler implementation matches the
+         * value of this attribute. Legal values are
+         * the same as those in the above list of
+         * valid compilers.)
+         * @param impl the compiler to be used.
+         */
+        public void setCompiler(String impl) {
+            super.setImplementation(impl);
+        }
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/SQLExec.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/SQLExec.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/SQLExec.java	(revision 16139)
@@ -0,0 +1,672 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+
+import java.io.File;
+import java.io.PrintStream;
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.BufferedReader;
+import java.io.StringReader;
+import java.io.FileReader;
+import java.io.InputStreamReader;
+import java.io.FileInputStream;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import java.sql.Connection;
+import java.sql.Statement;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+
+/**
+ * Executes a series of SQL statements on a database using JDBC.
+ *
+ * <p>Statements can
+ * either be read in from a text file using the <i>src</i> attribute or from
+ * between the enclosing SQL tags.</p>
+ *
+ * <p>Multiple statements can be provided, separated by semicolons (or the
+ * defined <i>delimiter</i>). Individual lines within the statements can be
+ * commented using either --, // or REM at the start of the line.</p>
+ *
+ * <p>The <i>autocommit</i> attribute specifies whether auto-commit should be
+ * turned on or off whilst executing the statements. If auto-commit is turned
+ * on each statement will be executed and committed. If it is turned off the
+ * statements will all be executed as one transaction.</p>
+ *
+ * <p>The <i>onerror</i> attribute specifies how to proceed when an error occurs
+ * during the execution of one of the statements.
+ * The possible values are: <b>continue</b> execution, only show the error;
+ * <b>stop</b> execution and commit transaction;
+ * and <b>abort</b> execution and transaction and fail task.</p>
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task name="sql" category="database"
+ */
+public class SQLExec extends JDBCTask {
+
+    /**
+     * delimiters we support, "normal" and "row"
+     */
+    public static class DelimiterType extends EnumeratedAttribute {
+        public static final String NORMAL = "normal";
+        public static final String ROW = "row";
+        public String[] getValues() {
+            return new String[] {NORMAL, ROW};
+        }
+    }
+
+
+
+    private int goodSql = 0;
+
+    private int totalSql = 0;
+
+    /**
+     * Database connection
+     */
+    private Connection conn = null;
+
+    /**
+     * files to load
+     */
+    private Vector filesets = new Vector();
+
+    /**
+     * SQL statement
+     */
+    private Statement statement = null;
+
+    /**
+     * SQL input file
+     */
+    private File srcFile = null;
+
+    /**
+     * SQL input command
+     */
+    private String sqlCommand = "";
+
+    /**
+     * SQL transactions to perform
+     */
+    private Vector transactions = new Vector();
+
+    /**
+     * SQL Statement delimiter
+     */
+    private String delimiter = ";";
+
+    /**
+     * The delimiter type indicating whether the delimiter will
+     * only be recognized on a line by itself
+     */
+    private String delimiterType = DelimiterType.NORMAL;
+
+    /**
+     * Print SQL results.
+     */
+    private boolean print = false;
+
+    /**
+     * Print header columns.
+     */
+    private boolean showheaders = true;
+
+    /**
+     * Results Output file.
+     */
+    private File output = null;
+
+
+    /**
+     * Action to perform if an error is found
+     **/
+    private String onError = "abort";
+
+    /**
+     * Encoding to use when reading SQL statements from a file
+     */
+    private String encoding = null;
+
+    /**
+     * Append to an existing file or overwrite it?
+     */
+    private boolean append = false;
+
+    /**
+     * Keep the format of a sql block?
+     */
+    private boolean keepformat = false;
+
+    /**
+     * Argument to Statement.setEscapeProcessing
+     *
+     * @since Ant 1.6
+     */
+    private boolean escapeProcessing = true;
+
+    /**
+     * Set the name of the SQL file to be run.
+     * Required unless statements are enclosed in the build file
+     */
+    public void setSrc(File srcFile) {
+        this.srcFile = srcFile;
+    }
+
+    /**
+     * Set an inline SQL command to execute.
+     * NB: Properties are not expanded in this text.
+     */
+    public void addText(String sql) {
+        this.sqlCommand += sql;
+    }
+
+    /**
+     * Adds a set of files (nested fileset attribute).
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+
+    /**
+     * Add a SQL transaction to execute
+     */
+    public Transaction createTransaction() {
+        Transaction t = new Transaction();
+        transactions.addElement(t);
+        return t;
+    }
+
+    /**
+     * Set the file encoding to use on the SQL files read in
+     *
+     * @param encoding the encoding to use on the files
+     */
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * Set the delimiter that separates SQL statements. Defaults to &quot;;&quot;;
+     * optional
+     *
+     * <p>For example, set this to "go" and delimitertype to "ROW" for
+     * Sybase ASE or MS SQL Server.</p>
+     */
+    public void setDelimiter(String delimiter) {
+        this.delimiter = delimiter;
+    }
+
+    /**
+     * Set the delimiter type: "normal" or "row" (default "normal").
+     *
+     * <p>The delimiter type takes two values - normal and row. Normal
+     * means that any occurrence of the delimiter terminate the SQL
+     * command whereas with row, only a line containing just the
+     * delimiter is recognized as the end of the command.</p>
+     */
+    public void setDelimiterType(DelimiterType delimiterType) {
+        this.delimiterType = delimiterType.getValue();
+    }
+
+    /**
+     * Print result sets from the statements;
+     * optional, default false
+     */
+    public void setPrint(boolean print) {
+        this.print = print;
+    }
+
+    /**
+     * Print headers for result sets from the
+     * statements; optional, default true.
+     */
+    public void setShowheaders(boolean showheaders) {
+        this.showheaders = showheaders;
+    }
+
+    /**
+     * Set the output file;
+     * optional, defaults to the Ant log.
+     */
+    public void setOutput(File output) {
+        this.output = output;
+    }
+
+    /**
+     * whether output should be appended to or overwrite
+     * an existing file.  Defaults to false.
+     *
+     * @since Ant 1.5
+     */
+    public void setAppend(boolean append) {
+        this.append = append;
+    }
+
+
+    /**
+     * Action to perform when statement fails: continue, stop, or abort
+     * optional; default &quot;abort&quot;
+     */
+    public void setOnerror(OnError action) {
+        this.onError = action.getValue();
+    }
+
+    /**
+     * whether or not format should be preserved.
+     * Defaults to false.
+     *
+     * @param keepformat The keepformat to set
+     */
+    public void setKeepformat(boolean keepformat) {
+        this.keepformat = keepformat;
+    }
+
+    /**
+     * Set escape processing for statements.
+     *
+     * @since Ant 1.6
+     */
+    public void setEscapeProcessing(boolean enable) {
+        escapeProcessing = enable;
+    }
+
+    /**
+     * Load the sql file and then execute it
+     */
+    public void execute() throws BuildException {
+        Vector savedTransaction = (Vector) transactions.clone();
+        String savedSqlCommand = sqlCommand;
+
+        sqlCommand = sqlCommand.trim();
+
+        try {
+            if (srcFile == null && sqlCommand.length() == 0
+                && filesets.isEmpty()) {
+                if (transactions.size() == 0) {
+                    throw new BuildException("Source file or fileset, "
+                                             + "transactions or sql statement "
+                                             + "must be set!", getLocation());
+                }
+            }
+
+            if (srcFile != null && !srcFile.exists()) {
+                throw new BuildException("Source file does not exist!", getLocation());
+            }
+
+            // deal with the filesets
+            for (int i = 0; i < filesets.size(); i++) {
+                FileSet fs = (FileSet) filesets.elementAt(i);
+                DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+                File srcDir = fs.getDir(getProject());
+
+                String[] srcFiles = ds.getIncludedFiles();
+
+                // Make a transaction for each file
+                for (int j = 0; j < srcFiles.length; j++) {
+                    Transaction t = createTransaction();
+                    t.setSrc(new File(srcDir, srcFiles[j]));
+                }
+            }
+
+            // Make a transaction group for the outer command
+            Transaction t = createTransaction();
+            t.setSrc(srcFile);
+            t.addText(sqlCommand);
+            conn = getConnection();
+            if (!isValidRdbms(conn)) {
+                return;
+            }
+            try {
+                statement = conn.createStatement();
+                statement.setEscapeProcessing(escapeProcessing);
+
+                PrintStream out = System.out;
+                try {
+                    if (output != null) {
+                        log("Opening PrintStream to output file " + output,
+                            Project.MSG_VERBOSE);
+                        out = new PrintStream(
+                                  new BufferedOutputStream(
+                                      new FileOutputStream(output
+                                                           .getAbsolutePath(),
+                                                           append)));
+                    }
+
+                    // Process all transactions
+                    for (Enumeration e = transactions.elements();
+                         e.hasMoreElements();) {
+
+                        ((Transaction) e.nextElement()).runTransaction(out);
+                        if (!isAutocommit()) {
+                            log("Committing transaction", Project.MSG_VERBOSE);
+                            conn.commit();
+                        }
+                    }
+                } finally {
+                    if (out != null && out != System.out) {
+                        out.close();
+                    }
+                }
+            } catch (IOException e) {
+                if (!isAutocommit() && conn != null && onError.equals("abort")) {
+                    try {
+                        conn.rollback();
+                    } catch (SQLException ex) {
+                        // ignore
+                    }
+                }
+                throw new BuildException(e, getLocation());
+            } catch (SQLException e) {
+                if (!isAutocommit() && conn != null && onError.equals("abort")) {
+                    try {
+                        conn.rollback();
+                    } catch (SQLException ex) {
+                        // ignore
+                    }
+                }
+                throw new BuildException(e, getLocation());
+            } finally {
+                try {
+                    if (statement != null) {
+                        statement.close();
+                    }
+                    if (conn != null) {
+                        conn.close();
+                    }
+                } catch (SQLException ex) {
+                    // ignore
+                }
+            }
+
+            log(goodSql + " of " + totalSql
+                + " SQL statements executed successfully");
+        } finally {
+            transactions = savedTransaction;
+            sqlCommand = savedSqlCommand;
+        }
+    }
+
+    /**
+     * read in lines and execute them
+     */
+    protected void runStatements(Reader reader, PrintStream out)
+        throws SQLException, IOException {
+        StringBuffer sql = new StringBuffer();
+        String line = "";
+
+        BufferedReader in = new BufferedReader(reader);
+
+        while ((line = in.readLine()) != null) {
+            if (!keepformat) {
+                line = line.trim();
+            }
+            line = getProject().replaceProperties(line);
+            if (!keepformat) {
+                if (line.startsWith("//")) {
+                    continue;
+                }
+                if (line.startsWith("--")) {
+                    continue;
+                }
+                StringTokenizer st = new StringTokenizer(line);
+                if (st.hasMoreTokens()) {
+                    String token = st.nextToken();
+                    if ("REM".equalsIgnoreCase(token)) {
+                        continue;
+                    }
+                }
+            }
+
+            if (!keepformat) {
+                sql.append(" " + line);
+            } else {
+                sql.append("\n" + line);
+            }
+
+            // SQL defines "--" as a comment to EOL
+            // and in Oracle it may contain a hint
+            // so we cannot just remove it, instead we must end it
+            if (!keepformat) {
+                if (line.indexOf("--") >= 0) {
+                    sql.append("\n");
+                }
+            }
+            if ((delimiterType.equals(DelimiterType.NORMAL)
+                 && sql.toString().endsWith(delimiter))
+                ||
+                (delimiterType.equals(DelimiterType.ROW)
+                 && line.equals(delimiter))) {
+                execSQL(sql.substring(0, sql.length() - delimiter.length()),
+                        out);
+                sql.replace(0, sql.length(), "");
+            }
+        }
+        // Catch any statements not followed by ;
+        if (!sql.equals("")) {
+            execSQL(sql.toString(), out);
+        }
+    }
+
+
+    /**
+     * Exec the sql statement.
+     */
+    protected void execSQL(String sql, PrintStream out) throws SQLException {
+        // Check and ignore empty statements
+        if ("".equals(sql.trim())) {
+            return;
+        }
+
+        ResultSet resultSet = null;
+        try {
+            totalSql++;
+            log("SQL: " + sql, Project.MSG_VERBOSE);
+
+            boolean ret;
+            int updateCount = 0, updateCountTotal = 0;
+
+            ret = statement.execute(sql);
+            updateCount = statement.getUpdateCount();
+            resultSet = statement.getResultSet();
+            do {
+                if (!ret) {
+                    if (updateCount != -1) {
+                        updateCountTotal += updateCount;
+                    }
+                } else {
+                    if (print) {
+                        printResults(resultSet, out);
+                    }
+                }
+                ret = statement.getMoreResults();
+                if (ret) {
+                    updateCount = statement.getUpdateCount();
+                    resultSet = statement.getResultSet();
+                }
+            } while (ret);
+
+            log(updateCountTotal + " rows affected",
+                Project.MSG_VERBOSE);
+
+            if (print) {
+                StringBuffer line = new StringBuffer();
+                line.append(updateCountTotal + " rows affected");
+                out.println(line);
+            }
+
+            SQLWarning warning = conn.getWarnings();
+            while (warning != null) {
+                log(warning + " sql warning", Project.MSG_VERBOSE);
+                warning = warning.getNextWarning();
+            }
+            conn.clearWarnings();
+            goodSql++;
+        } catch (SQLException e) {
+            log("Failed to execute: " + sql, Project.MSG_ERR);
+            if (!onError.equals("continue")) {
+                throw e;
+            }
+            log(e.toString(), Project.MSG_ERR);
+        } finally {
+            if (resultSet != null) {
+                resultSet.close();
+            }
+        }
+    }
+
+    /**
+     * print any results in the statement
+     * @deprecated use {@link #printResults(java.sql.ResultSet, java.io.PrintStream)
+     *             the two arg version} instead.
+     * @param out the place to print results
+     * @throws SQLException on SQL problems.
+     */
+    protected void printResults(PrintStream out) throws SQLException {
+        ResultSet rs = null;
+        rs = statement.getResultSet();
+        try {
+            printResults(rs, out);
+        } finally {
+            if (rs != null) {
+                rs.close();
+            }
+        }
+    }
+
+    /**
+     * print any results in the result set.
+     * @param rs the resultset to print information about
+     * @param out the place to print results
+     * @throws SQLException on SQL problems.
+     * @since Ant 1.6.3
+     */
+    protected void printResults(ResultSet rs, PrintStream out) throws SQLException {
+        if (rs != null) {
+            log("Processing new result set.", Project.MSG_VERBOSE);
+            ResultSetMetaData md = rs.getMetaData();
+            int columnCount = md.getColumnCount();
+            StringBuffer line = new StringBuffer();
+            if (showheaders) {
+                for (int col = 1; col < columnCount; col++) {
+                     line.append(md.getColumnName(col));
+                     line.append(",");
+                }
+                line.append(md.getColumnName(columnCount));
+                out.println(line);
+                line = new StringBuffer();
+            }
+            while (rs.next()) {
+                boolean first = true;
+                for (int col = 1; col <= columnCount; col++) {
+                    String columnValue = rs.getString(col);
+                    if (columnValue != null) {
+                        columnValue = columnValue.trim();
+                    }
+
+                    if (first) {
+                        first = false;
+                    } else {
+                        line.append(",");
+                    }
+                    line.append(columnValue);
+                }
+                out.println(line);
+                line = new StringBuffer();
+            }
+        }
+        out.println();
+    }
+
+    /**
+     * The action a task should perform on an error,
+     * one of "continue", "stop" and "abort"
+     */
+    public static class OnError extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[] {"continue", "stop", "abort"};
+        }
+    }
+
+    /**
+     * Contains the definition of a new transaction element.
+     * Transactions allow several files or blocks of statements
+     * to be executed using the same JDBC connection and commit
+     * operation in between.
+     */
+    public class Transaction {
+        private File tSrcFile = null;
+        private String tSqlCommand = "";
+
+        /**
+         *
+         */
+        public void setSrc(File src) {
+            this.tSrcFile = src;
+        }
+
+        /**
+         *
+         */
+        public void addText(String sql) {
+            this.tSqlCommand += sql;
+        }
+
+        /**
+         *
+         */
+        private void runTransaction(PrintStream out)
+            throws IOException, SQLException {
+            if (tSqlCommand.length() != 0) {
+                log("Executing commands", Project.MSG_INFO);
+                runStatements(new StringReader(tSqlCommand), out);
+            }
+
+            if (tSrcFile != null) {
+                log("Executing file: " + tSrcFile.getAbsolutePath(),
+                    Project.MSG_INFO);
+                Reader reader =
+                    (encoding == null) ? new FileReader(tSrcFile)
+                                       : new InputStreamReader(
+                                             new FileInputStream(tSrcFile),
+                                             encoding);
+                try {
+                    runStatements(reader, out);
+                } finally {
+                    reader.close();
+                }
+            }
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/SendEmail.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/SendEmail.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/SendEmail.java	(revision 16139)
@@ -0,0 +1,44 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.taskdefs.email.EmailTask;
+
+/**
+ * A task to send SMTP email.
+ * This task can send mail using either plain
+ * text, UU encoding or Mime format mail depending on what is available.
+ * Attachments may be sent using nested FileSet
+ * elements.
+
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task name="mail" category="network"
+ */
+public class SendEmail extends EmailTask {
+    /**
+     * Sets the mailport parameter of this build task.
+     * @param value mail port name.
+     *
+     * @deprecated Use {@link #setMailport(int)} instead.
+     */
+    public void setMailport(Integer value) {
+        setMailport(value.intValue());
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Sequential.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Sequential.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Sequential.java	(revision 16139)
@@ -0,0 +1,67 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+
+
+
+/**
+ * Sequential is a container task - it can contain other Ant tasks. The nested
+ * tasks are simply executed in sequence. Sequential's primary use is to support
+ * the sequential execution of a subset of tasks within the {@link Parallel Parallel Task}
+
+ * <p>
+ * The sequential task has no attributes and does not support any nested
+ * elements apart from Ant tasks. Any valid Ant task may be embedded within the
+ * sequential task.</p>
+
+ * @since Ant 1.4
+ * @ant.task category="control"
+ */
+public class Sequential extends Task
+                        implements TaskContainer {
+
+    /** Optional Vector holding the nested tasks */
+    private Vector nestedTasks = new Vector();
+
+    /**
+     * Add a nested task to Sequential.
+     * <p>
+     * @param nestedTask  Nested task to execute Sequential
+     * <p>
+     */
+    public void addTask(Task nestedTask) {
+        nestedTasks.addElement(nestedTask);
+    }
+
+    /**
+     * Execute all nestedTasks.
+     *
+     * @throws BuildException if one of the nested tasks fails.
+     */
+    public void execute() throws BuildException {
+        for (Enumeration e = nestedTasks.elements(); e.hasMoreElements();) {
+            Task nestedTask = (Task) e.nextElement();
+            nestedTask.perform();
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/SignJar.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/SignJar.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/SignJar.java	(revision 16139)
@@ -0,0 +1,374 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Signs JAR or ZIP files with the javasign command line tool. The
+ * tool detailed dependency checking: files are only signed if they
+ * are not signed. The <tt>signjar</tt> attribute can point to the file to
+ * generate; if this file exists then
+ * its modification date is used as a cue as to whether to resign any JAR file.
+ *
+ * @since Ant 1.1
+ * @ant.task category="java"
+ */
+public class SignJar extends Task {
+
+    /**
+     * The name of the jar file.
+     */
+    protected File jar;
+
+    /**
+     * The alias of signer.
+     */
+    protected String alias;
+
+    /**
+     * The name of keystore file.
+     */
+    private String keystore;
+
+    protected String storepass;
+    protected String storetype;
+    protected String keypass;
+    protected String sigfile;
+    protected File signedjar;
+    protected boolean verbose;
+    protected boolean internalsf;
+    protected boolean sectionsonly;
+
+    /** The maximum amount of memory to use for Jar signer */
+    private String maxMemory;
+
+    /**
+     * the filesets of the jars to sign
+     */
+    protected Vector filesets = new Vector();
+
+    /**
+     * Whether to assume a jar which has an appropriate .SF file in is already
+     * signed.
+     */
+    protected boolean lazy;
+
+
+    /**
+     * Set the maximum memory to be used by the jarsigner process
+     *
+     * @param max a string indicating the maximum memory according to the
+     *        JVM conventions (e.g. 128m is 128 Megabytes)
+     */
+    public void setMaxmemory(String max) {
+        maxMemory = max;
+    }
+
+    /**
+     * the jar file to sign; required
+     */
+    public void setJar(final File jar) {
+        this.jar = jar;
+    }
+
+    /**
+     * the alias to sign under; required
+     */
+    public void setAlias(final String alias) {
+        this.alias = alias;
+    }
+
+    /**
+     * keystore location; required
+     */
+    public void setKeystore(final String keystore) {
+        this.keystore = keystore;
+    }
+
+    /**
+     * password for keystore integrity; required
+     */
+    public void setStorepass(final String storepass) {
+        this.storepass = storepass;
+    }
+
+    /**
+     * keystore type; optional
+     */
+    public void setStoretype(final String storetype) {
+        this.storetype = storetype;
+    }
+
+    /**
+     * password for private key (if different); optional
+     */
+    public void setKeypass(final String keypass) {
+        this.keypass = keypass;
+    }
+
+    /**
+     * name of .SF/.DSA file; optional
+     */
+    public void setSigfile(final String sigfile) {
+        this.sigfile = sigfile;
+    }
+
+    /**
+     * name of signed JAR file; optional
+     */
+    public void setSignedjar(final File signedjar) {
+        this.signedjar = signedjar;
+    }
+
+    /**
+     * Enable verbose output when signing
+     * ; optional: default false
+     */
+    public void setVerbose(final boolean verbose) {
+        this.verbose = verbose;
+    }
+
+    /**
+     * Flag to include the .SF file inside the signature;
+     * optional; default false
+     */
+    public void setInternalsf(final boolean internalsf) {
+        this.internalsf = internalsf;
+    }
+
+    /**
+     * flag to compute hash of entire manifest;
+     * optional, default false
+     */
+    public void setSectionsonly(final boolean sectionsonly) {
+        this.sectionsonly = sectionsonly;
+    }
+
+    /**
+     * flag to control whether the presence of a signature
+     * file means a JAR is signed;
+     * optional, default false
+     */
+    public void setLazy(final boolean lazy) {
+        this.lazy = lazy;
+    }
+
+    /**
+     * Adds a set of files to sign
+     * @since Ant 1.4
+     */
+    public void addFileset(final FileSet set) {
+        filesets.addElement(set);
+    }
+
+
+    /**
+     * sign the jar(s)
+     */
+    public void execute() throws BuildException {
+        if (null == jar && filesets.size() == 0) {
+            throw new BuildException("jar must be set through jar attribute "
+                                     + "or nested filesets");
+        }
+        if (null != jar) {
+            if (filesets.size() != 0) {
+                log("nested filesets will be ignored if the jar attribute has"
+                    + " been specified.", Project.MSG_WARN);
+            }
+
+            doOneJar(jar, signedjar);
+            return;
+        } else {
+            // deal with the filesets
+            for (int i = 0; i < filesets.size(); i++) {
+                FileSet fs = (FileSet) filesets.elementAt(i);
+                DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+                String[] jarFiles = ds.getIncludedFiles();
+                for (int j = 0; j < jarFiles.length; j++) {
+                    doOneJar(new File(fs.getDir(getProject()), jarFiles[j]), null);
+                }
+            }
+        }
+    }
+
+    /**
+     * sign one jar
+     */
+    private void doOneJar(File jarSource, File jarTarget)
+        throws BuildException {
+
+        if (null == alias) {
+            throw new BuildException("alias attribute must be set");
+        }
+
+        if (null == storepass) {
+            throw new BuildException("storepass attribute must be set");
+        }
+
+        if (isUpToDate(jarSource, jarTarget)) {
+            return;
+        }
+
+        final ExecTask cmd = (ExecTask) getProject().createTask("exec");
+        cmd.setExecutable(JavaEnvUtils.getJdkExecutable("jarsigner"));
+
+        if (maxMemory != null) {
+            cmd.createArg().setValue("-J-Xmx" + maxMemory);
+        }
+
+        if (null != keystore) {
+            // is the keystore a file
+            File keystoreFile = getProject().resolveFile(keystore);
+            if (keystoreFile.exists()) {
+                cmd.createArg().setValue("-keystore");
+                cmd.createArg().setValue(keystoreFile.getPath());
+            } else {
+                // must be a URL - just pass as is
+                cmd.createArg().setValue("-keystore");
+                cmd.createArg().setValue(keystore);
+            }
+        }
+
+        if (null != storepass) {
+            cmd.createArg().setValue("-storepass");
+            cmd.createArg().setValue(storepass);
+        }
+
+        if (null != storetype) {
+            cmd.createArg().setValue("-storetype");
+            cmd.createArg().setValue(storetype);
+        }
+
+        if (null != keypass) {
+            cmd.createArg().setValue("-keypass");
+            cmd.createArg().setValue(keypass);
+        }
+
+        if (null != sigfile) {
+            cmd.createArg().setValue("-sigfile");
+            cmd.createArg().setValue(sigfile);
+        }
+
+        if (null != jarTarget) {
+            cmd.createArg().setValue("-signedjar");
+            cmd.createArg().setValue(jarTarget.toString());
+        }
+
+        if (verbose) {
+            cmd.createArg().setValue("-verbose");
+        }
+
+        if (internalsf) {
+            cmd.createArg().setValue("-internalsf");
+        }
+
+        if (sectionsonly) {
+            cmd.createArg().setValue("-sectionsonly");
+        }
+
+        cmd.createArg().setValue(jarSource.toString());
+
+        cmd.createArg().setValue(alias);
+
+        log("Signing JAR: " + jarSource.getAbsolutePath());
+        cmd.setFailonerror(true);
+        cmd.setTaskName(getTaskName());
+        cmd.execute();
+    }
+
+    protected boolean isUpToDate(File jarFile, File signedjarFile) {
+        if (null == jarFile) {
+            return false;
+        }
+
+        if (null != signedjarFile) {
+
+            if (!jarFile.exists()) {
+              return false;
+            }
+            if (!signedjarFile.exists()) {
+              return false;
+            }
+            if (jarFile.equals(signedjarFile)) {
+              return false;
+            }
+            if (signedjarFile.lastModified() > jarFile.lastModified()) {
+                return true;
+            }
+        } else {
+            if (lazy) {
+                return isSigned(jarFile);
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * test for a file being signed, by looking for a signature in the META-INF
+     * directory
+     * @param file
+     * @return true if the file is signed
+     */
+    protected boolean isSigned(File file) {
+        final String SIG_START = "META-INF/";
+        final String SIG_END = ".SF";
+
+        if (!file.exists()) {
+            return false;
+        }
+        ZipFile jarFile = null;
+        try {
+            jarFile = new ZipFile(file);
+            if (null == alias) {
+                Enumeration entries = jarFile.entries();
+                while (entries.hasMoreElements()) {
+                    String name = ((ZipEntry) entries.nextElement()).getName();
+                    if (name.startsWith(SIG_START) && name.endsWith(SIG_END)) {
+                        return true;
+                    }
+                }
+                return false;
+            } else {
+                return jarFile.getEntry(SIG_START + alias.toUpperCase()
+                                        + SIG_END) != null;
+            }
+        } catch (IOException e) {
+            return false;
+        } finally {
+            if (jarFile != null) {
+                try {
+                    jarFile.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Sleep.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Sleep.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Sleep.java	(revision 16139)
@@ -0,0 +1,190 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Sleep, or pause, for a period of time.
+ *
+ * A task for sleeping a short period of time, useful when a
+ * build or deployment process requires an interval between tasks.
+ *<p>
+ * A negative value can be supplied to any of attributes provided the total sleep time
+ * is positive, pending fundamental changes in physics and JVM
+ * execution times</p>
+ * Note that sleep times are always hints to be interpreted by the OS how it feels
+ * small times may either be ignored or rounded up to a minimum timeslice. Note
+ * also that the system clocks often have a fairly low granularity too, which complicates
+ * measuring how long a sleep actually took.</p>
+ *
+ * @since Ant 1.4
+ * @ant.task category="utility"
+ */
+
+public class Sleep extends Task {
+    /**
+     * failure flag
+     */
+    private boolean failOnError = true;
+
+    /**
+     * sleep seconds
+     */
+    private int seconds = 0;
+
+    /**
+     * sleep hours
+     */
+    private int hours = 0;
+    /**
+     * sleep minutes
+     */
+    private int minutes = 0;
+
+    /**
+     * sleep milliseconds
+     */
+    private int milliseconds = 0;
+
+
+
+    /**
+     * Creates new instance
+     */
+    public Sleep() {
+    }
+
+
+    /**
+     * seconds to add to the sleep time
+     *
+     * @param seconds The new Seconds value
+     */
+    public void setSeconds(int seconds) {
+        this.seconds = seconds;
+    }
+
+
+    /**
+     * hours to add to the sleep time.
+     *
+     * @param hours The new Hours value
+     */
+    public void setHours(int hours) {
+        this.hours = hours;
+    }
+
+
+    /**
+     * minutes to add to the sleep time
+     *
+     * @param minutes The new Minutes value
+     */
+    public void setMinutes(int minutes) {
+        this.minutes = minutes;
+    }
+
+
+    /**
+     * milliseconds to add to the sleep time
+     *
+     * @param milliseconds The new Milliseconds value
+     */
+    public void setMilliseconds(int milliseconds) {
+        this.milliseconds = milliseconds;
+    }
+
+
+    /**
+     * sleep for a period of time
+     *
+     * @param millis time to sleep
+     */
+    public void doSleep(long millis) {
+        try {
+            Thread.sleep(millis);
+        } catch (InterruptedException ie) {
+            // Ignore Exception
+        }
+    }
+
+
+    /**
+     * flag controlling whether to break the build on an error.
+     *
+     * @param failOnError The new FailOnError value
+     */
+    public void setFailOnError(boolean failOnError) {
+        this.failOnError = failOnError;
+    }
+
+
+    /**
+     * return time to sleep
+     *
+     * @return sleep time. if below 0 then there is an error
+     */
+
+    private long getSleepTime() {
+        return ((((long) hours * 60) + minutes) * 60 + seconds) * 1000
+            + milliseconds;
+    }
+
+
+    /**
+     * verify parameters
+     *
+     * @throws BuildException if something is invalid
+     */
+    public void validate()
+        throws BuildException {
+        if (getSleepTime() < 0) {
+            throw new BuildException("Negative sleep periods are not "
+                                     + "supported");
+        }
+    }
+
+
+    /**
+     * Executes this build task. Throws org.apache.tools.ant.BuildException
+     * if there is an error during task execution.
+     *
+     * @exception BuildException Description of Exception
+     */
+    public void execute()
+        throws BuildException {
+        try {
+            validate();
+            long sleepTime = getSleepTime();
+            log("sleeping for " + sleepTime + " milliseconds",
+                Project.MSG_VERBOSE);
+            doSleep(sleepTime);
+        } catch (Exception e) {
+            if (failOnError) {
+                throw new BuildException(e);
+            } else {
+                String text = e.toString();
+                log(text, Project.MSG_ERR);
+            }
+        }
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/StreamPumper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/StreamPumper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/StreamPumper.java	(revision 16139)
@@ -0,0 +1,144 @@
+/*
+ * Copyright  2000,2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Copies all data from an input stream to an output stream.
+ *
+ * @since Ant 1.2
+ */
+public class StreamPumper implements Runnable {
+
+    // TODO: make SIZE an instance variable.
+    // TODO: add a status flag to note if an error occurred in run.
+
+    private static final int SIZE = 128;
+    private InputStream is;
+    private OutputStream os;
+    private boolean finished;
+    private boolean closeWhenExhausted;
+    private boolean autoflush = false;
+
+    /**
+     * Create a new stream pumper.
+     *
+     * @param is input stream to read data from
+     * @param os output stream to write data to.
+     * @param closeWhenExhausted if true, the output stream will be closed when
+     *        the input is exhausted.
+     */
+    public StreamPumper(InputStream is, OutputStream os,
+                        boolean closeWhenExhausted) {
+        this.is = is;
+        this.os = os;
+        this.closeWhenExhausted = closeWhenExhausted;
+    }
+
+    /**
+     * Create a new stream pumper.
+     *
+     * @param is input stream to read data from
+     * @param os output stream to write data to.
+     */
+    public StreamPumper(InputStream is, OutputStream os) {
+        this(is, os, false);
+    }
+
+    /**
+     * Set whether data should be flushed through to the output stream.
+     * @param autoflush if true, push through data; if false, let it be buffered
+     * @since Ant 1.6.3
+     */
+    /*public*/ void setAutoflush(boolean autoflush) {
+        this.autoflush = autoflush;
+    }
+
+    /**
+     * Copies data from the input stream to the output stream.
+     *
+     * Terminates as soon as the input stream is closed or an error occurs.
+     */
+    public void run() {
+        synchronized (this) {
+            // Just in case this object is reused in the future
+            finished = false;
+        }
+
+        final byte[] buf = new byte[SIZE];
+
+        int length;
+        try {
+            while ((length = is.read(buf)) > 0 && !finished) {
+                os.write(buf, 0, length);
+                if (autoflush) {
+                    os.flush();
+                }
+            }
+        } catch (Exception e) {
+            // ignore errors
+        } finally {
+            if (closeWhenExhausted) {
+                try {
+                    os.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+            synchronized (this) {
+                finished = true;
+                notifyAll();
+            }
+        }
+    }
+
+    /**
+     * Tells whether the end of the stream has been reached.
+     * @return true is the stream has been exhausted.
+     **/
+    public synchronized boolean isFinished() {
+        return finished;
+    }
+
+    /**
+     * This method blocks until the stream pumper finishes.
+     * @see #isFinished()
+     **/
+    public synchronized void waitFor()
+        throws InterruptedException {
+        while (!isFinished()) {
+            wait();
+        }
+    }
+    
+    /**
+     * Stop the pumper as soon as possible.
+     * Note that it may continue to block on the input stream
+     * but it will really stop the thread as soon as it gets EOF
+     * or any byte, and it will be marked as finished.
+     * @since Ant 1.6.3
+     */
+    /*public*/ synchronized void stop() {
+        finished = true;
+        notifyAll();
+    }
+    
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/SubAnt.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/SubAnt.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/SubAnt.java	(revision 16139)
@@ -0,0 +1,574 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.util.Vector;
+import java.util.Enumeration;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.DirSet;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.FileList;
+import org.apache.tools.ant.types.PropertySet;
+import org.apache.tools.ant.types.Reference;
+
+
+/**
+ * Calls a given target for all defined sub-builds. This is an extension
+ * of ant for bulk project execution.
+ * <p>
+ * <h2> Use with directories </h2>
+ * <p>
+ * subant can be used with directory sets to execute a build from different directories.
+ * 2 different options are offered
+ * </p>
+ * <ul>
+ * <li>
+ * run the same build file /somepath/otherpath/mybuild.xml
+ * with different base directories use the genericantfile attribute
+ * </li>
+ * <li>if you want to run directory1/build.xml, directory2/build.xml, ....
+ * use the antfile attribute. The base directory does not get set by the subant task in this case,
+ * because you can specify it in each build file.
+ * </li>
+ * </ul>
+ * @since Ant1.6
+ * @ant.task name="subant" category="control"
+ */
+public class SubAnt
+             extends Task {
+
+    private Path buildpath;
+
+    private Ant ant = null;
+    private String target = null;
+    private String antfile = "build.xml";
+    private File genericantfile = null;
+    private boolean verbose = false;
+    private boolean inheritAll = false;
+    private boolean inheritRefs = false;
+    private boolean failOnError = true;
+    private String output  = null;
+
+    private Vector properties = new Vector();
+    private Vector references = new Vector();
+    private Vector propertySets = new Vector();
+
+    /**
+     * Pass output sent to System.out to the new project.
+     *
+     * @param output a line of output
+     * @since Ant 1.6.2
+     */
+    public void handleOutput(String output) {
+        if (ant != null) {
+            ant.handleOutput(output);
+        } else {
+            super.handleOutput(output);
+        }
+    }
+
+    /**
+     * Process input into the ant task
+     *
+     * @param buffer the buffer into which data is to be read.
+     * @param offset the offset into the buffer at which data is stored.
+     * @param length the amount of data to read
+     *
+     * @return the number of bytes read
+     *
+     * @exception IOException if the data cannot be read
+     *
+     * @see Task#handleInput(byte[], int, int)
+     *
+     * @since Ant 1.6.2
+     */
+    public int handleInput(byte[] buffer, int offset, int length)
+        throws IOException {
+        if (ant != null) {
+            return ant.handleInput(buffer, offset, length);
+        } else {
+            return super.handleInput(buffer, offset, length);
+        }
+    }
+
+    /**
+     * Pass output sent to System.out to the new project.
+     *
+     * @param output The output to log. Should not be <code>null</code>.
+     *
+     * @since Ant 1.6.2
+     */
+    public void handleFlush(String output) {
+        if (ant != null) {
+            ant.handleFlush(output);
+        } else {
+            super.handleFlush(output);
+        }
+    }
+
+    /**
+     * Pass output sent to System.err to the new project.
+     *
+     * @param output The error output to log. Should not be <code>null</code>.
+     *
+     * @since Ant 1.6.2
+     */
+    public void handleErrorOutput(String output) {
+        if (ant != null) {
+            ant.handleErrorOutput(output);
+        } else {
+            super.handleErrorOutput(output);
+        }
+    }
+
+    /**
+     * Pass output sent to System.err to the new project.
+     *
+     * @param output The error output to log. Should not be <code>null</code>.
+     *
+     * @since Ant 1.6.2
+     */
+    public void handleErrorFlush(String output) {
+        if (ant != null) {
+            ant.handleErrorFlush(output);
+        } else {
+            super.handleErrorFlush(output);
+        }
+    }
+
+    /**
+     * Runs the various sub-builds.
+     */
+    public void execute() {
+        if (buildpath == null) {
+            throw new BuildException("No buildpath specified");
+        }
+
+        final String[] filenames = buildpath.list();
+        final int count = filenames.length;
+        if (count < 1) {
+            log("No sub-builds to iterate on", Project.MSG_WARN);
+            return;
+        }
+/*
+    //REVISIT: there must be cleaner way of doing this, if it is merited at all
+        if (target == null) {
+            target = getOwningTarget().getName();
+        }
+*/
+        BuildException buildException = null;
+        for (int i = 0; i < count; ++i) {
+            File file = null;
+            String subdirPath = null;
+            Throwable thrownException = null;
+            try {
+                File directory = null;
+                file = new File(filenames[i]);
+                if (file.isDirectory()) {
+                    if (verbose) {
+                        subdirPath = file.getPath();
+                        log("Entering directory: " + subdirPath + "\n", Project.MSG_INFO);
+                    }
+                    if (genericantfile != null) {
+                        directory = file;
+                        file = genericantfile;
+                    } else {
+                        file = new File(file, antfile);
+                    }
+                }
+                execute(file, directory);
+                if (verbose && subdirPath != null) {
+                    log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO);
+                }
+            } catch (RuntimeException ex) {
+                if (!(getProject().isKeepGoingMode())) {
+                    if (verbose && subdirPath != null) {
+                        log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO);
+                    }
+                    throw ex; // throw further
+                }
+                thrownException = ex;
+            } catch (Throwable ex) {
+                if (!(getProject().isKeepGoingMode())) {
+                    if (verbose && subdirPath != null) {
+                        log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO);
+                    }
+                    throw new BuildException(ex);
+                }
+                thrownException = ex;
+            }
+            if (thrownException != null) {
+                if (thrownException instanceof BuildException) {
+                    log("File '" + file
+                        + "' failed with message '"
+                        + thrownException.getMessage() + "'.", Project.MSG_ERR);
+                    // only the first build exception is reported
+                    if (buildException == null) {
+                        buildException = (BuildException) thrownException;
+                    }
+                } else {
+                    log("Target '" + file
+                        + "' failed with message '"
+                        + thrownException.getMessage() + "'.", Project.MSG_ERR);
+                    thrownException.printStackTrace(System.err);
+                    if (buildException == null) {
+                        buildException =
+                            new BuildException(thrownException);
+                    }
+                }
+                if (verbose && subdirPath != null) {
+                    log("Leaving directory: " + subdirPath + "\n", Project.MSG_INFO);
+                }
+            }
+        }
+        // check if one of the builds failed in keep going mode
+        if (buildException != null) {
+            throw buildException;
+        }
+    }
+
+    /**
+     * Runs the given target on the provided build file.
+     *
+     * @param  file the build file to execute
+     * @param  directory the directory of the current iteration
+     * @throws BuildException is the file cannot be found, read, is
+     *         a directory, or the target called failed, but only if
+     *         <code>failOnError</code> is <code>true</code>. Otherwise,
+     *         a warning log message is simply output.
+     */
+    private void execute(File file, File directory)
+                throws BuildException {
+        if (!file.exists() || file.isDirectory() || !file.canRead()) {
+            String msg = "Invalid file: " + file;
+            if (failOnError) {
+                throw new BuildException(msg);
+            }
+            log(msg, Project.MSG_WARN);
+            return;
+        }
+
+        ant = createAntTask(directory);
+        String antfilename = file.getAbsolutePath();
+        ant.setAntfile(antfilename);
+        try {
+            ant.execute();
+        } catch (BuildException e) {
+            if (failOnError) {
+                throw e;
+            }
+            log("Failure for target '" + target
+               + "' of: " +  antfilename + "\n"
+               + e.getMessage(), Project.MSG_WARN);
+        } catch (Throwable e) {
+            if (failOnError) {
+                throw new BuildException(e);
+            }
+            log("Failure for target '" + target
+                + "' of: " + antfilename + "\n"
+                + e.toString(),
+                Project.MSG_WARN);
+        } finally {
+            ant = null;
+        }        
+    }
+
+    /**
+     * This method builds the file name to use in conjunction with directories.
+     * 
+     * <p>Defaults to "build.xml".
+     * If <code>genericantfile</code> is set, this attribute is ignored.</p>
+     *
+     * @param  antfile the short build file name. Defaults to "build.xml".
+     */
+    public void setAntfile(String antfile) {
+        this.antfile = antfile;
+    }
+
+    /**
+     * This method builds a file path to use in conjunction with directories.
+     * 
+     * <p>Use <code>genericantfile</code>, in order to run the same build file
+     * with different basedirs.</p>
+     * If this attribute is set, <code>antfile</code> is ignored.
+     *
+     * @param afile (path of the generic ant file, absolute or relative to
+     *               project base directory)
+     * */
+    public void setGenericAntfile(File afile) {
+        this.genericantfile = afile;
+    }
+
+    /**
+     * Sets whether to fail with a build exception on error, or go on.
+     *
+     * @param  failOnError the new value for this boolean flag.
+     */
+    public void setFailonerror(boolean failOnError) {
+        this.failOnError = failOnError;
+    }
+
+    /**
+     * The target to call on the different sub-builds. Set to "" to execute
+     * the default target.
+     * @param target the target
+     * <p>
+     */
+    //     REVISIT: Defaults to the target name that contains this task if not specified.
+    public void setTarget(String target) {
+        this.target = target;
+    }
+
+    /**
+     * Enable/ disable verbose log messages showing when each sub-build path is entered/ exited.
+     * The default value is "false".
+     * @param on true to enable verbose mode, false otherwise (default).
+     */
+    public void setVerbose(boolean on) {
+        this.verbose = on;
+    }
+
+    /**
+     * Corresponds to <code>&lt;ant&gt;</code>'s
+     * <code>output</code> attribute.
+     *
+     * @param  s the filename to write the output to.
+     */
+    public void setOutput(String s) {
+        this.output = s;
+    }
+
+    /**
+     * Corresponds to <code>&lt;ant&gt;</code>'s
+     * <code>inheritall</code> attribute.
+     *
+     * @param  b the new value for this boolean flag.
+     */
+    public void setInheritall(boolean b) {
+        this.inheritAll = b;
+    }
+
+    /**
+     * Corresponds to <code>&lt;ant&gt;</code>'s
+     * <code>inheritrefs</code> attribute.
+     *
+     * @param  b the new value for this boolean flag.
+     */
+    public void setInheritrefs(boolean b) {
+        this.inheritRefs = b;
+    }
+
+    /**
+     * Corresponds to <code>&lt;ant&gt;</code>'s
+     * nested <code>&lt;property&gt;</code> element.
+     *
+     * @param  p the property to pass on explicitly to the sub-build.
+     */
+    public void addProperty(Property p) {
+        properties.addElement(p);
+    }
+
+    /**
+     * Corresponds to <code>&lt;ant&gt;</code>'s
+     * nested <code>&lt;reference&gt;</code> element.
+     *
+     * @param  r the reference to pass on explicitly to the sub-build.
+     */
+    public void addReference(Ant.Reference r) {
+        references.addElement(r);
+    }
+
+    /**
+     * Corresponds to <code>&lt;ant&gt;</code>'s
+     * nested <code>&lt;propertyset&gt;</code> element.
+     * @param ps the propertset
+     */
+    public void addPropertyset(PropertySet ps) {
+        propertySets.addElement(ps);
+    }
+
+    /**
+     * Adds a directory set to the implicit build path.
+     * <p>
+     * <em>Note that the directories will be added to the build path
+     * in no particular order, so if order is significant, one should
+     * use a file list instead!</em>
+     *
+     * @param  set the directory set to add.
+     */
+    public void addDirset(DirSet set) {
+        getBuildpath().addDirset(set);
+    }
+
+    /**
+     * Adds a file set to the implicit build path.
+     * <p>
+     * <em>Note that the directories will be added to the build path
+     * in no particular order, so if order is significant, one should
+     * use a file list instead!</em>
+     *
+     * @param  set the file set to add.
+     */
+    public void addFileset(FileSet set) {
+        getBuildpath().addFileset(set);
+    }
+
+    /**
+     * Adds an ordered file list to the implicit build path.
+     * <p>
+     * <em>Note that contrary to file and directory sets, file lists
+     * can reference non-existent files or directories!</em>
+     *
+     * @param  list the file list to add.
+     */
+    public void addFilelist(FileList list) {
+        getBuildpath().addFilelist(list);
+    }
+
+    /**
+     * Set the buildpath to be used to find sub-projects.
+     *
+     * @param  s an Ant Path object containing the buildpath.
+     */
+    public void setBuildpath(Path s) {
+        getBuildpath().append(s);
+    }
+
+    /**
+     * Creates a nested build path, and add it to the implicit build path.
+     *
+     * @return the newly created nested build path.
+     */
+    public Path createBuildpath() {
+        return getBuildpath().createPath();
+    }
+
+    /**
+     * Creates a nested <code>&lt;buildpathelement&gt;</code>,
+     * and add it to the implicit build path.
+     *
+     * @return the newly created nested build path element.
+     */
+    public Path.PathElement createBuildpathElement() {
+        return getBuildpath().createPathElement();
+    }
+
+    /**
+     * Gets the implicit build path, creating it if <code>null</code>.
+     *
+     * @return the implicit build path.
+     */
+    private Path getBuildpath() {
+        if (buildpath == null) {
+            buildpath = new Path(getProject());
+        }
+        return buildpath;
+    }
+
+    /**
+     * Buildpath to use, by reference.
+     *
+     * @param  r a reference to an Ant Path object containing the buildpath.
+     */
+    public void setBuildpathRef(Reference r) {
+        createBuildpath().setRefid(r);
+    }
+
+    /**
+     * Creates the &lt;ant&gt; task configured to run a specific target.
+     *
+     * @param directory : if not null the directory where the build should run
+     *
+     * @return the ant task, configured with the explicit properties and
+     *         references necessary to run the sub-build.
+     */
+    private Ant createAntTask(File directory) {
+        Ant ant = (Ant) getProject().createTask("ant");
+        ant.setOwningTarget(getOwningTarget());
+        ant.setTaskName(getTaskName());
+        ant.init();
+        if (target != null && target.length() > 0) {
+            ant.setTarget(target);
+        }
+
+
+        if (output != null) {
+            ant.setOutput(output);
+        }
+
+        if (directory != null) {
+            ant.setDir(directory);
+        }
+
+        ant.setInheritAll(inheritAll);
+        for (Enumeration i = properties.elements(); i.hasMoreElements();) {
+            copyProperty(ant.createProperty(), (Property) i.nextElement());
+        }
+
+        for (Enumeration i = propertySets.elements(); i.hasMoreElements();) {
+            ant.addPropertyset((PropertySet) i.nextElement());
+        }
+
+        ant.setInheritRefs(inheritRefs);
+        for (Enumeration i = references.elements(); i.hasMoreElements();) {
+            ant.addReference((Ant.Reference) i.nextElement());
+        }
+
+        return ant;
+    }
+
+    /**
+     * Assigns an Ant property to another.
+     *
+     * @param  to the destination property whose content is modified.
+     * @param  from the source property whose content is copied.
+     */
+    private static void copyProperty(Property to, Property from) {
+        to.setName(from.getName());
+
+        if (from.getValue() != null) {
+            to.setValue(from.getValue());
+        }
+        if (from.getFile() != null) {
+            to.setFile(from.getFile());
+        }
+        if (from.getResource() != null) {
+            to.setResource(from.getResource());
+        }
+        if (from.getPrefix() != null) {
+            to.setPrefix(from.getPrefix());
+        }
+        if (from.getRefid() != null) {
+            to.setRefid(from.getRefid());
+        }
+        if (from.getEnvironment() != null) {
+            to.setEnvironment(from.getEnvironment());
+        }
+        if (from.getClasspath() != null) {
+            to.setClasspath(from.getClasspath());
+        }
+    }
+
+} // END class SubAnt
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Sync.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Sync.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Sync.java	(revision 16139)
@@ -0,0 +1,346 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * This code is based on code Copyright (c) 2002, Landmark Graphics
+ * Corp that has been kindly donated to the Apache Software
+ * Foundation.
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+
+import java.util.Hashtable;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.IdentityMapper;
+
+/**
+ * Synchronize a local target directory from the files defined
+ * in one or more filesets.
+ *
+ * <p>Uses a &lt;copy&gt; task internally, but forbidding the use of
+ * mappers and filter chains. Files of the destination directory not
+ * present in any of the source fileset are removed.</p>
+ *
+ * @since Ant 1.6
+ *
+ * revised by <a href="mailto:daniel.armbrust@mayo.edu">Dan Armbrust</a>
+ * to remove orphaned directories.
+ *
+ * @ant.task category="filesystem"
+ */
+public class Sync extends Task {
+
+    // Same as regular <copy> task... see at end-of-file!
+    private MyCopy _copy;
+
+    // Override Task#init
+    public void init()
+        throws BuildException {
+        // Instantiate it
+        _copy = new MyCopy();
+        configureTask(_copy);
+
+        // Default config of <mycopy> for our purposes.
+        _copy.setFiltering(false);
+        _copy.setIncludeEmptyDirs(false);
+        _copy.setPreserveLastModified(true);
+    }
+
+    private void configureTask(Task helper) {
+        helper.setProject(getProject());
+        helper.setTaskName(getTaskName());
+        helper.setOwningTarget(getOwningTarget());
+        helper.init();
+    }
+
+    // Override Task#execute
+    public void execute()
+        throws BuildException {
+        // The destination of the files to copy
+        File toDir = _copy.getToDir();
+
+        // The complete list of files to copy
+        Hashtable allFiles = _copy._dest2src;
+
+        // If the destination directory didn't already exist,
+        // or was empty, then no previous file removal is necessary!
+        boolean noRemovalNecessary = !toDir.exists() || toDir.list().length < 1;
+
+        // Copy all the necessary out-of-date files
+        log("PASS#1: Copying files to " + toDir, Project.MSG_DEBUG);
+        _copy.execute();
+
+        // Do we need to perform further processing?
+        if (noRemovalNecessary) {
+            log("NO removing necessary in " + toDir, Project.MSG_DEBUG);
+            return; // nope ;-)
+        }
+
+        // Get rid of all files not listed in the source filesets.
+        log("PASS#2: Removing orphan files from " + toDir, Project.MSG_DEBUG);
+        int[] removedFileCount = removeOrphanFiles(allFiles, toDir);
+        logRemovedCount(removedFileCount[0], "dangling director", "y", "ies");
+        logRemovedCount(removedFileCount[1], "dangling file", "", "s");
+
+        // Get rid of empty directories on the destination side
+        if (!_copy.getIncludeEmptyDirs()) {
+            log("PASS#3: Removing empty directories from " + toDir,
+                Project.MSG_DEBUG);
+            int removedDirCount = removeEmptyDirectories(toDir, false);
+            logRemovedCount(removedDirCount, "empty director", "y", "ies");
+        }
+    }
+
+    private void logRemovedCount(int count, String prefix,
+                                 String singularSuffix, String pluralSuffix) {
+        File toDir = _copy.getToDir();
+
+        String what = (prefix == null) ? "" : prefix;
+        what += (count < 2) ? singularSuffix : pluralSuffix;
+
+        if (count > 0) {
+            log("Removed " + count + " " + what + " from " + toDir,
+                Project.MSG_INFO);
+        } else {
+            log("NO " + what + " to remove from " + toDir,
+                Project.MSG_VERBOSE);
+        }
+    }
+
+    /**
+     * Removes all files and folders not found as keys of a table
+     * (used as a set!).
+     *
+     * <p>If the provided file is a directory, it is recursively
+     * scanned for orphaned files which will be removed as well.</p>
+     *
+     * <p>If the directory is an orphan, it will also be removed.</p>
+     *
+     * @param  nonOrphans the table of all non-orphan <code>File</code>s.
+     * @param  file the initial file or directory to scan or test.
+     * @return the number of orphaned files and directories actually removed.
+     * Position 0 of the array is the number of orphaned directories.
+     * Position 1 of the array is the number or orphaned files.
+     * Position 2 is meaningless.
+     */
+    private int[] removeOrphanFiles(Hashtable nonOrphans, File file) {
+        int[] removedCount = new int[] {0, 0, 0};
+        if (file.isDirectory()) {
+            File[] children = file.listFiles();
+            for (int i = 0; i < children.length; ++i) {
+                int[] temp = removeOrphanFiles(nonOrphans, children[i]);
+                removedCount[0] += temp[0];
+                removedCount[1] += temp[1];
+                removedCount[2] += temp[2];
+            }
+
+            if (nonOrphans.get(file) == null && removedCount[2] == 0) {
+                log("Removing orphan directory: " + file, Project.MSG_DEBUG);
+                file.delete();
+                ++removedCount[0];
+            } else {
+                /*
+                  Contrary to what is said above, position 2 is not
+                  meaningless inside the recursion.
+                  Position 2 is used to carry information back up the
+                  recursion about whether or not a directory contains
+                  a directory or file at any depth that is not an
+                  orphan
+                  This has to be done, because if you have the
+                  following directory structure: c:\src\a\file and
+                  your mapper src files were constructed like so:
+                  <include name="**\a\**\*"/>
+                  The folder 'a' will not be in the hashtable of
+                  nonorphans.  So, before deleting it as an orphan, we
+                  have to know whether or not any of its children at
+                  any level are orphans.
+                  If no, then this folder is also an orphan, and may
+                  be deleted.  I do this by changing position 2 to a
+                  '1'.
+                */
+                removedCount[2] = 1;
+            }
+
+        } else {
+            if (nonOrphans.get(file) == null) {
+                log("Removing orphan file: " + file, Project.MSG_DEBUG);
+                file.delete();
+                ++removedCount[1];
+            } else {
+                removedCount[2] = 1;
+            }
+        }
+        return removedCount;
+    }
+
+    /**
+     * Removes all empty directories from a directory.
+     *
+     * <p><em>Note that a directory that contains only empty
+     * directories, directly or not, will be removed!</em></p>
+     *
+     * <p>Recurses depth-first to find the leaf directories
+     * which are empty and removes them, then unwinds the
+     * recursion stack, removing directories which have
+     * become empty themselves, etc...</p>
+     *
+     * @param  dir the root directory to scan for empty directories.
+     * @param  removeIfEmpty whether to remove the root directory
+     *         itself if it becomes empty.
+     * @return the number of empty directories actually removed.
+     */
+    private int removeEmptyDirectories(File dir, boolean removeIfEmpty) {
+        int removedCount = 0;
+        if (dir.isDirectory()) {
+            File[] children = dir.listFiles();
+            for (int i = 0; i < children.length; ++i) {
+                File file = children[i];
+                // Test here again to avoid method call for non-directories!
+                if (file.isDirectory()) {
+                    removedCount += removeEmptyDirectories(file, true);
+                }
+            }
+            if (children.length > 0) {
+                // This directory may have become empty...
+                // We need to re-query its children list!
+                children = dir.listFiles();
+            }
+            if (children.length < 1 && removeIfEmpty) {
+                log("Removing empty directory: " + dir, Project.MSG_DEBUG);
+                dir.delete();
+                ++removedCount;
+            }
+        }
+        return removedCount;
+    }
+
+
+    //
+    // Various copy attributes/subelements of <copy> passed thru to <mycopy>
+    //
+
+    /**
+     * Sets the destination directory.
+     */
+    public void setTodir(File destDir) {
+        _copy.setTodir(destDir);
+    }
+
+    /**
+     * Used to force listing of all names of copied files.
+     */
+    public void setVerbose(boolean verbose) {
+        _copy.setVerbose(verbose);
+    }
+
+    /**
+     * Overwrite any existing destination file(s).
+     */
+    public void setOverwrite(boolean overwrite) {
+        _copy.setOverwrite(overwrite);
+    }
+
+    /**
+     * Used to copy empty directories.
+     */
+    public void setIncludeEmptyDirs(boolean includeEmpty) {
+        _copy.setIncludeEmptyDirs(includeEmpty);
+    }
+
+    /**
+     * If false, note errors to the output but keep going.
+     * @param failonerror true or false
+     */
+    public void setFailOnError(boolean failonerror) {
+        _copy.setFailOnError(failonerror);
+    }
+
+    /**
+     * Adds a set of files to copy.
+     */
+    public void addFileset(FileSet set) {
+        _copy.addFileset(set);
+    }
+
+    /**
+     * The number of milliseconds leeway to give before deciding a
+     * target is out of date.
+     *
+     * <p>Default is 0 milliseconds, or 2 seconds on DOS systems.</p>
+     *
+     * @since Ant 1.6.2
+     */
+    public void setGranularity(long granularity) {
+        _copy.setGranularity(granularity);
+    }
+
+    /**
+     * Subclass Copy in order to access it's file/dir maps.
+     */
+    public static class MyCopy extends Copy {
+
+        // List of files that must be copied, irrelevant from the
+        // fact that they are newer or not than the destination.
+        private Hashtable _dest2src = new Hashtable();
+
+        public MyCopy() {
+        }
+
+        protected void buildMap(File fromDir, File toDir, String[] names,
+                                FileNameMapper mapper, Hashtable map) {
+            assertTrue("No mapper", mapper instanceof IdentityMapper);
+
+            super.buildMap(fromDir, toDir, names, mapper, map);
+
+            for (int i = 0; i < names.length; ++i) {
+                String name = names[i];
+                File dest = new File(toDir, name);
+                // No need to instantiate the src file, as we use the
+                // table as a set (to remain Java 1.1 compatible!!!).
+                //File src = new File(fromDir, name);
+                //_dest2src.put(dest, src);
+                _dest2src.put(dest, fromDir);
+            }
+        }
+
+        public File getToDir() {
+            return destDir;
+        }
+
+        public boolean getIncludeEmptyDirs() {
+            return includeEmpty;
+        }
+
+    }
+
+    /**
+     * Pseudo-assert method.
+     */
+    private static void assertTrue(String message, boolean condition) {
+        if (!condition) {
+            throw new BuildException("Assertion Error: " + message);
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Tar.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Tar.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Tar.java	(revision 16139)
@@ -0,0 +1,766 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.zip.GZIPOutputStream;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.MergingMapper;
+import org.apache.tools.ant.util.SourceFileScanner;
+import org.apache.tools.bzip2.CBZip2OutputStream;
+import org.apache.tools.tar.TarConstants;
+import org.apache.tools.tar.TarEntry;
+import org.apache.tools.tar.TarOutputStream;
+import org.apache.tools.zip.UnixStat;
+
+/**
+ * Creates a tar archive.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="packaging"
+ */
+public class Tar extends MatchingTask {
+
+    /**
+     * @deprecated Tar.WARN is deprecated and is replaced with
+     *             Tar.TarLongFileMode.WARN
+     */
+    public static final String WARN = "warn";
+    /**
+     * @deprecated Tar.FAIL is deprecated and is replaced with
+     *             Tar.TarLongFileMode.FAIL
+     */
+    public static final String FAIL = "fail";
+    /**
+     * @deprecated Tar.TRUNCATE is deprecated and is replaced with
+     *             Tar.TarLongFileMode.TRUNCATE
+     */
+    public static final String TRUNCATE = "truncate";
+    /**
+     * @deprecated Tar.GNU is deprecated and is replaced with
+     *             Tar.TarLongFileMode.GNU
+     */
+    public static final String GNU = "gnu";
+    /**
+     * @deprecated Tar.OMIT is deprecated and is replaced with
+     *             Tar.TarLongFileMode.OMIT
+     */
+    public static final String OMIT = "omit";
+
+    File tarFile;
+    File baseDir;
+
+    private TarLongFileMode longFileMode = new TarLongFileMode();
+
+    Vector filesets = new Vector();
+    Vector fileSetFiles = new Vector();
+
+    /**
+     * Indicates whether the user has been warned about long files already.
+     */
+    private boolean longWarningGiven = false;
+
+    private TarCompressionMethod compression = new TarCompressionMethod();
+
+    /**
+     * Add a new fileset with the option to specify permissions
+     * @return the tar fileset to be used as the nested element.
+     */
+    public TarFileSet createTarFileSet() {
+        TarFileSet fileset = new TarFileSet();
+        filesets.addElement(fileset);
+        return fileset;
+    }
+
+
+    /**
+     * Set is the name/location of where to create the tar file.
+     * @param tarFile the location of the tar file.
+     * @deprecated for consistency with other tasks, please use setDestFile()
+     */
+    public void setTarfile(File tarFile) {
+        this.tarFile = tarFile;
+    }
+
+    /**
+     * Set is the name/location of where to create the tar file.
+     * @since Ant 1.5
+     * @param destFile The output of the tar
+     */
+    public void setDestFile(File destFile) {
+        this.tarFile = destFile;
+    }
+
+    /**
+     * This is the base directory to look in for things to tar.
+     * @param baseDir the base directory.
+     */
+    public void setBasedir(File baseDir) {
+        this.baseDir = baseDir;
+    }
+
+    /**
+     * Set how to handle long files, those with a path&gt;100 chars.
+     * Optional, default=warn.
+     * <p>
+     * Allowable values are
+     * <ul>
+     * <li>  truncate - paths are truncated to the maximum length
+     * <li>  fail - paths greater than the maximum cause a build exception
+     * <li>  warn - paths greater than the maximum cause a warning and GNU is used
+     * <li>  gnu - GNU extensions are used for any paths greater than the maximum.
+     * <li>  omit - paths greater than the maximum are omitted from the archive
+     * </ul>
+     * @param mode the mode string to handle long files.
+     * @deprecated setLongFile(String) is deprecated and is replaced with
+     *             setLongFile(Tar.TarLongFileMode) to make Ant's Introspection
+     *             mechanism do the work and also to encapsulate operations on
+     *             the mode in its own class.
+     */
+    public void setLongfile(String mode) {
+        log("DEPRECATED - The setLongfile(String) method has been deprecated."
+            + " Use setLongfile(Tar.TarLongFileMode) instead.");
+        this.longFileMode = new TarLongFileMode();
+        longFileMode.setValue(mode);
+    }
+
+    /**
+     * Set how to handle long files, those with a path&gt;100 chars.
+     * Optional, default=warn.
+     * <p>
+     * Allowable values are
+     * <ul>
+     * <li>  truncate - paths are truncated to the maximum length
+     * <li>  fail - paths greater than the maximum cause a build exception
+     * <li>  warn - paths greater than the maximum cause a warning and GNU is used
+     * <li>  gnu - GNU extensions are used for any paths greater than the maximum.
+     * <li>  omit - paths greater than the maximum are omitted from the archive
+     * </ul>
+     * @param mode the mode to handle long file names.
+     */
+    public void setLongfile(TarLongFileMode mode) {
+        this.longFileMode = mode;
+    }
+
+    /**
+     * Set compression method.
+     * Allowable values are
+     * <ul>
+     * <li>  none - no compression
+     * <li>  gzip - Gzip compression
+     * <li>  bzip2 - Bzip2 compression
+     * </ul>
+     * @param mode the compression method.
+     */
+    public void setCompression(TarCompressionMethod mode) {
+        this.compression = mode;
+    }
+
+    /**
+     * do the business
+     * @throws BuildException on error
+     */
+    public void execute() throws BuildException {
+        if (tarFile == null) {
+            throw new BuildException("tarfile attribute must be set!",
+                                     getLocation());
+        }
+
+        if (tarFile.exists() && tarFile.isDirectory()) {
+            throw new BuildException("tarfile is a directory!",
+                                     getLocation());
+        }
+
+        if (tarFile.exists() && !tarFile.canWrite()) {
+            throw new BuildException("Can not write to the specified tarfile!",
+                                     getLocation());
+        }
+
+        Vector savedFileSets = (Vector) filesets.clone();
+        try {
+            if (baseDir != null) {
+                if (!baseDir.exists()) {
+                    throw new BuildException("basedir does not exist!",
+                                             getLocation());
+                }
+
+                // add the main fileset to the list of filesets to process.
+                TarFileSet mainFileSet = new TarFileSet(fileset);
+                mainFileSet.setDir(baseDir);
+                filesets.addElement(mainFileSet);
+            }
+
+            if (filesets.size() == 0) {
+                throw new BuildException("You must supply either a basedir "
+                                         + "attribute or some nested filesets.",
+                                         getLocation());
+            }
+
+            // check if tar is out of date with respect to each
+            // fileset
+            boolean upToDate = true;
+            for (Enumeration e = filesets.elements(); e.hasMoreElements();) {
+                TarFileSet fs = (TarFileSet) e.nextElement();
+                String[] files = fs.getFiles(getProject());
+
+                if (!archiveIsUpToDate(files, fs.getDir(getProject()))) {
+                    upToDate = false;
+                }
+
+                for (int i = 0; i < files.length; ++i) {
+                    if (tarFile.equals(new File(fs.getDir(getProject()),
+                                                files[i]))) {
+                        throw new BuildException("A tar file cannot include "
+                                                 + "itself", getLocation());
+                    }
+                }
+            }
+
+            if (upToDate) {
+                log("Nothing to do: " + tarFile.getAbsolutePath()
+                    + " is up to date.", Project.MSG_INFO);
+                return;
+            }
+
+            log("Building tar: " + tarFile.getAbsolutePath(), Project.MSG_INFO);
+
+            TarOutputStream tOut = null;
+            try {
+                tOut = new TarOutputStream(
+                    compression.compress(
+                        new BufferedOutputStream(
+                            new FileOutputStream(tarFile))));
+                tOut.setDebug(true);
+                if (longFileMode.isTruncateMode()) {
+                    tOut.setLongFileMode(TarOutputStream.LONGFILE_TRUNCATE);
+                } else if (longFileMode.isFailMode()
+                            || longFileMode.isOmitMode()) {
+                    tOut.setLongFileMode(TarOutputStream.LONGFILE_ERROR);
+                } else {
+                    // warn or GNU
+                    tOut.setLongFileMode(TarOutputStream.LONGFILE_GNU);
+                }
+
+                longWarningGiven = false;
+                for (Enumeration e = filesets.elements();
+                     e.hasMoreElements();) {
+                    TarFileSet fs = (TarFileSet) e.nextElement();
+                    String[] files = fs.getFiles(getProject());
+                    if (files.length > 1 && fs.getFullpath().length() > 0) {
+                        throw new BuildException("fullpath attribute may only "
+                                                 + "be specified for "
+                                                 + "filesets that specify a "
+                                                 + "single file.");
+                    }
+                    for (int i = 0; i < files.length; i++) {
+                        File f = new File(fs.getDir(getProject()), files[i]);
+                        String name = files[i].replace(File.separatorChar, '/');
+                        tarFile(f, tOut, name, fs);
+                    }
+                }
+            } catch (IOException ioe) {
+                String msg = "Problem creating TAR: " + ioe.getMessage();
+                throw new BuildException(msg, ioe, getLocation());
+            } finally {
+                if (tOut != null) {
+                    try {
+                        // close up
+                        tOut.close();
+                    } catch (IOException e) {
+                        // ignore
+                    }
+                }
+            }
+        } finally {
+            filesets = savedFileSets;
+        }
+    }
+
+    /**
+     * tar a file
+     * @param file the file to tar
+     * @param tOut the output stream
+     * @param vPath the path name of the file to tar
+     * @param tarFileSet the fileset that the file came from.
+     * @throws IOException on error
+     */
+    protected void tarFile(File file, TarOutputStream tOut, String vPath,
+                           TarFileSet tarFileSet)
+        throws IOException {
+        FileInputStream fIn = null;
+
+        String fullpath = tarFileSet.getFullpath();
+        if (fullpath.length() > 0) {
+            vPath = fullpath;
+        } else {
+            // don't add "" to the archive
+            if (vPath.length() <= 0) {
+                return;
+            }
+
+            if (file.isDirectory() && !vPath.endsWith("/")) {
+                vPath += "/";
+            }
+
+            String prefix = tarFileSet.getPrefix();
+            // '/' is appended for compatibility with the zip task.
+            if (prefix.length() > 0 && !prefix.endsWith("/")) {
+                prefix = prefix + "/";
+            }
+            vPath = prefix + vPath;
+        }
+
+        if (vPath.startsWith("/") && !tarFileSet.getPreserveLeadingSlashes()) {
+            int l = vPath.length();
+            if (l <= 1) {
+                // we would end up adding "" to the archive
+                return;
+            }
+            vPath = vPath.substring(1, l);
+        }
+
+        try {
+            if (vPath.length() >= TarConstants.NAMELEN) {
+                if (longFileMode.isOmitMode()) {
+                    log("Omitting: " + vPath, Project.MSG_INFO);
+                    return;
+                } else if (longFileMode.isWarnMode()) {
+                    log("Entry: " + vPath + " longer than "
+                        + TarConstants.NAMELEN + " characters.",
+                        Project.MSG_WARN);
+                    if (!longWarningGiven) {
+                        log("Resulting tar file can only be processed "
+                            + "successfully by GNU compatible tar commands",
+                            Project.MSG_WARN);
+                        longWarningGiven = true;
+                    }
+                } else if (longFileMode.isFailMode()) {
+                    throw new BuildException("Entry: " + vPath
+                        + " longer than " + TarConstants.NAMELEN
+                        + "characters.", getLocation());
+                }
+            }
+
+            TarEntry te = new TarEntry(vPath);
+            te.setModTime(file.lastModified());
+            if (!file.isDirectory()) {
+                te.setSize(file.length());
+                te.setMode(tarFileSet.getMode());
+            } else {
+                te.setMode(tarFileSet.getDirMode());
+            }
+            te.setUserName(tarFileSet.getUserName());
+            te.setGroupName(tarFileSet.getGroup());
+            te.setUserId(tarFileSet.getUid());
+            te.setGroupId(tarFileSet.getGid());
+
+            tOut.putNextEntry(te);
+
+            if (!file.isDirectory()) {
+                fIn = new FileInputStream(file);
+
+                byte[] buffer = new byte[8 * 1024];
+                int count = 0;
+                do {
+                    tOut.write(buffer, 0, count);
+                    count = fIn.read(buffer, 0, buffer.length);
+                } while (count != -1);
+            }
+
+            tOut.closeEntry();
+        } finally {
+            if (fIn != null) {
+                fIn.close();
+            }
+        }
+    }
+
+    /**
+     * Is the archive up to date in relationship to a list of files.
+     * @param files the files to check
+     * @return true if the archive is up to date.
+     * @deprecated use the two-arg version instead.
+     */
+    protected boolean archiveIsUpToDate(String[] files) {
+        return archiveIsUpToDate(files, baseDir);
+    }
+
+    /**
+     * Is the archive up to date in relationship to a list of files.
+     * @param files the files to check
+     * @param dir   the base directory for the files.
+     * @return true if the archive is up to date.
+     * @since Ant 1.5.2
+     */
+    protected boolean archiveIsUpToDate(String[] files, File dir) {
+        SourceFileScanner sfs = new SourceFileScanner(this);
+        MergingMapper mm = new MergingMapper();
+        mm.setTo(tarFile.getAbsolutePath());
+        return sfs.restrict(files, dir, null, mm).length == 0;
+    }
+
+    /**
+     * This is a FileSet with the option to specify permissions
+     * and other attributes.
+     */
+    public static class TarFileSet extends FileSet {
+        private String[] files = null;
+
+        private int fileMode = UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM;
+        private int dirMode = UnixStat.DIR_FLAG | UnixStat.DEFAULT_DIR_PERM;
+
+        private String userName = "";
+        private String groupName = "";
+        private int    uid;
+        private int    gid;
+        private String prefix = "";
+        private String fullpath = "";
+        private boolean preserveLeadingSlashes = false;
+
+        /**
+         * Creates a new <code>TarFileSet</code> instance.
+         * Using a fileset as a constructor argument.
+         *
+         * @param fileset a <code>FileSet</code> value
+         */
+        public TarFileSet(FileSet fileset) {
+            super(fileset);
+        }
+
+        /**
+         * Creates a new <code>TarFileSet</code> instance.
+         *
+         */
+        public TarFileSet() {
+            super();
+        }
+
+        /**
+         *  Get a list of files and directories specified in the fileset.
+         * @param p the current project.
+         * @return a list of file and directory names, relative to
+         *    the baseDir for the project.
+         */
+        public String[] getFiles(Project p) {
+            if (files == null) {
+                DirectoryScanner ds = getDirectoryScanner(p);
+                String[] directories = ds.getIncludedDirectories();
+                String[] filesPerSe = ds.getIncludedFiles();
+                files = new String [directories.length + filesPerSe.length];
+                System.arraycopy(directories, 0, files, 0, directories.length);
+                System.arraycopy(filesPerSe, 0, files, directories.length,
+                        filesPerSe.length);
+            }
+
+            return files;
+        }
+
+        /**
+         * A 3 digit octal string, specify the user, group and
+         * other modes in the standard Unix fashion;
+         * optional, default=0644
+         * @param octalString a 3 digit octal string.
+         */
+        public void setMode(String octalString) {
+            this.fileMode =
+                UnixStat.FILE_FLAG | Integer.parseInt(octalString, 8);
+        }
+
+        /**
+         * @return the current mode.
+         */
+        public int getMode() {
+            return fileMode;
+        }
+
+        /**
+         * A 3 digit octal string, specify the user, group and
+         * other modes in the standard Unix fashion;
+         * optional, default=0755
+         *
+         * @param octalString a 3 digit octal string.
+         * @since Ant 1.6
+         */
+        public void setDirMode(String octalString) {
+            this.dirMode =
+                UnixStat.DIR_FLAG | Integer.parseInt(octalString, 8);
+        }
+
+        /**
+         * @return the current directory mode
+         * @since Ant 1.6
+         */
+        public int getDirMode() {
+            return dirMode;
+        }
+
+        /**
+         * The username for the tar entry
+         * This is not the same as the UID.
+         * @param userName the user name for the tar entry.
+         */
+        public void setUserName(String userName) {
+            this.userName = userName;
+        }
+
+        /**
+         * @return the user name for the tar entry
+         */
+        public String getUserName() {
+            return userName;
+        }
+
+        /**
+         * The uid for the tar entry
+         * This is not the same as the User name.
+         * @param uid the id of the user for the tar entry.
+         */
+        public void setUid(int uid) {
+            this.uid = uid;
+        }
+
+        /**
+         * @return the uid for the tar entry
+         */
+        public int getUid() {
+            return uid;
+        }
+
+        /**
+         * The groupname for the tar entry; optional, default=""
+         * This is not the same as the GID.
+         * @param groupName the group name string.
+         */
+        public void setGroup(String groupName) {
+            this.groupName = groupName;
+        }
+
+        /**
+         * @return the group name string.
+         */
+        public String getGroup() {
+            return groupName;
+        }
+
+        /**
+         * The GID for the tar entry; optional, default="0"
+         * This is not the same as the group name.
+         * @param gid the group id.
+         */
+        public void setGid(int gid) {
+            this.gid = gid;
+        }
+
+        /**
+         * @return the group identifier.
+         */
+        public int getGid() {
+            return gid;
+        }
+
+        /**
+         * If the prefix attribute is set, all files in the fileset
+         * are prefixed with that path in the archive.
+         * optional.
+         * @param prefix the path prefix.
+         */
+        public void setPrefix(String prefix) {
+            this.prefix = prefix;
+        }
+
+        /**
+         * @return the path prefix for the files in the fileset.
+         */
+        public String getPrefix() {
+            return prefix;
+        }
+
+        /**
+         * If the fullpath attribute is set, the file in the fileset
+         * is written with that path in the archive. The prefix attribute,
+         * if specified, is ignored. It is an error to have more than one file specified in
+         * such a fileset.
+         * @param fullpath the path to use for the file in a fileset.
+         */
+        public void setFullpath(String fullpath) {
+            this.fullpath = fullpath;
+        }
+
+        /**
+         * @return the path to use for a single file fileset.
+         */
+        public String getFullpath() {
+            return fullpath;
+        }
+
+        /**
+         * Flag to indicates whether leading `/'s should
+         * be preserved in the file names.
+         * Optional, default is <code>false</code>.
+         * @param b the leading slashes flag.
+         */
+        public void setPreserveLeadingSlashes(boolean b) {
+            this.preserveLeadingSlashes = b;
+        }
+
+        /**
+         * @return the leading slashes flag.
+         */
+        public boolean getPreserveLeadingSlashes() {
+            return preserveLeadingSlashes;
+        }
+    }
+
+    /**
+     * Set of options for long file handling in the task.
+     *
+     */
+    public static class TarLongFileMode extends EnumeratedAttribute {
+
+        /** permissible values for longfile attribute */
+        public static final String
+            WARN = "warn",
+            FAIL = "fail",
+            TRUNCATE = "truncate",
+            GNU = "gnu",
+            OMIT = "omit";
+
+        private final String[] validModes = {WARN, FAIL, TRUNCATE, GNU, OMIT};
+
+        /** Constructor, defaults to "warn" */
+        public TarLongFileMode() {
+            super();
+            setValue(WARN);
+        }
+
+        /**
+         * @return the possible values for this enumerated type.
+         */
+        public String[] getValues() {
+            return validModes;
+        }
+
+        /**
+         * @return true if value is "truncate".
+         */
+        public boolean isTruncateMode() {
+            return TRUNCATE.equalsIgnoreCase(getValue());
+        }
+
+        /**
+         * @return true if value is "warn".
+         */
+        public boolean isWarnMode() {
+            return WARN.equalsIgnoreCase(getValue());
+        }
+
+        /**
+         * @return true if value is "gnu".
+         */
+        public boolean isGnuMode() {
+            return GNU.equalsIgnoreCase(getValue());
+        }
+
+        /**
+         * @return true if value is "fail".
+         */
+        public boolean isFailMode() {
+            return FAIL.equalsIgnoreCase(getValue());
+        }
+
+        /**
+         * @return true if value is "omit".
+         */
+        public boolean isOmitMode() {
+            return OMIT.equalsIgnoreCase(getValue());
+        }
+    }
+
+    /**
+     * Valid Modes for Compression attribute to Tar Task
+     *
+     */
+    public static final class TarCompressionMethod extends EnumeratedAttribute {
+
+        // permissible values for compression attribute
+        /**
+         *    No compression
+         */
+        private static final String NONE = "none";
+        /**
+         *    GZIP compression
+         */
+        private static final String GZIP = "gzip";
+        /**
+         *    BZIP2 compression
+         */
+        private static final String BZIP2 = "bzip2";
+
+
+        /**
+         * Default constructor
+         */
+        public TarCompressionMethod() {
+            super();
+            setValue(NONE);
+        }
+
+        /**
+         *  Get valid enumeration values.
+         *  @return valid enumeration values
+         */
+        public String[] getValues() {
+            return new String[] {NONE, GZIP, BZIP2 };
+        }
+
+        /**
+         *  This method wraps the output stream with the
+         *     corresponding compression method
+         *
+         *  @param ostream output stream
+         *  @return output stream with on-the-fly compression
+         *  @exception IOException thrown if file is not writable
+         */
+        private OutputStream compress(final OutputStream ostream)
+            throws IOException {
+            final String value = getValue();
+            if (GZIP.equals(value)) {
+                return new GZIPOutputStream(ostream);
+            } else {
+                if (BZIP2.equals(value)) {
+                    ostream.write('B');
+                    ostream.write('Z');
+                    return new CBZip2OutputStream(ostream);
+                }
+            }
+            return ostream;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/TaskOutputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/TaskOutputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/TaskOutputStream.java	(revision 16139)
@@ -0,0 +1,91 @@
+/*
+ * Copyright  2000,2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.tools.ant.Task;
+
+/**
+ * Redirects text written to a stream thru the standard
+ * ant logging mechanism. This class is useful for integrating
+ * with tools that write to System.out and System.err. For example,
+ * the following will cause all text written to System.out to be
+ * logged with "info" priority:
+ * <pre>System.setOut(new PrintStream(new TaskOutputStream(project, Project.MSG_INFO)));</pre>
+ *
+ * <p><strong>As of Ant 1.2, this class is considered to be dead code
+ * by the Ant developers and is unmaintained.  Don't use
+ * it.</strong></p>
+ *
+ * @deprecated use LogOutputStream instead.
+ */
+
+public class TaskOutputStream extends OutputStream {
+
+    private Task task;
+    private StringBuffer line;
+    private int msgOutputLevel;
+
+    /**
+     * Constructs a new JavacOutputStream with the given project
+     * as the output source for messages.
+     */
+
+    TaskOutputStream(Task task, int msgOutputLevel) {
+        System.err.println("As of Ant 1.2 released in October 2000, the "
+            + "TaskOutputStream class");
+        System.err.println("is considered to be dead code by the Ant "
+            + "developers and is unmaintained.");
+        System.err.println("Don\'t use it!");
+
+        this.task = task;
+        this.msgOutputLevel = msgOutputLevel;
+
+        line = new StringBuffer();
+    }
+
+    /**
+     * Write a character to the output stream. This method looks
+     * to make sure that there isn't an error being reported and
+     * will flush each line of input out to the project's log stream.
+     */
+
+    public void write(int c) throws IOException {
+        char cc = (char) c;
+        if (cc == '\r' || cc == '\n') {
+            // line feed
+            if (line.length() > 0) {
+                processLine();
+            }
+        } else {
+            line.append(cc);
+        }
+    }
+
+    /**
+     * Processes a line of input and determines if an error occurred.
+     */
+
+    private void processLine() {
+        String s = line.toString();
+        task.log(s, msgOutputLevel);
+        line = new StringBuffer();
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Taskdef.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Taskdef.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Taskdef.java	(revision 16139)
@@ -0,0 +1,51 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskAdapter;
+
+/**
+ * Adds a task definition to the current project, such that this new task can be
+ * used in the current project. Two attributes are needed, the name that identifies
+ * this task uniquely, and the full name of the class (including the packages) that
+ * implements this task.</p>
+ * <p>You can also define a group of tasks at once using the file or
+ * resource attributes.  These attributes point to files in the format of
+ * Java property files.  Each line defines a single task in the
+ * format:</p>
+ * <pre>
+ * taskname=fully.qualified.java.classname
+ * </pre>
+ * @since Ant 1.1
+ * @ant.task category="internal"
+ */
+public class Taskdef extends Typedef {
+
+    /**
+     * Default constructor.
+     * Creates a new Taskdef instance.
+     * This sets the adapter and the adaptto classes to
+     * TaskAdapter and Task.
+     */
+
+    public Taskdef() {
+        setAdapterClass(TaskAdapter.class);
+        setAdaptToClass(Task.class);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/TempFile.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/TempFile.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/TempFile.java	(revision 16139)
@@ -0,0 +1,123 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ *  This task sets a property to  the name of a temporary file.
+ *  Unlike the Java1.2 method to create a temporary file, this task
+ *  does work on Java1.1. Also, it does not actually create the
+ *  temporary file, but it does guarantee that the file did not
+ *  exist when the task was executed.
+ * <p>
+ * Examples
+ * <pre>&lt;tempfile property="temp.file" /&gt;</pre>
+ * create a temporary file
+ * <pre>&lt;tempfile property="temp.file" suffix=".xml" /&gt;</pre>
+ * create a temporary file with the .xml suffix.
+ * <pre>&lt;tempfile property="temp.file" destDir="build"/&gt;</pre>
+ * create a temp file in the build subdir
+ *@since       Ant 1.5
+ *@ant.task
+ */
+
+public class TempFile extends Task {
+
+    /**
+     * Name of property to set.
+     */
+    private String property;
+
+    /**
+     * Directory to create the file in. Can be null.
+     */
+    private File destDir = null;
+
+    /**
+     * Prefix for the file.
+     */
+    private String prefix;
+
+    /**
+     * Suffix for the file.
+     */
+    private String suffix = "";
+
+
+    /**
+     * Sets the property you wish to assign the temporary file to.
+     *
+     * @param  property  The property to set
+     * @ant.attribute group="required"
+     */
+    public void setProperty(String property) {
+        this.property = property;
+    }
+
+
+    /**
+     * Sets the destination directory. If not set,
+     * the basedir directory is used instead.
+     *
+     * @param  destDir  The new destDir value
+     */
+    public void setDestDir(File destDir) {
+        this.destDir = destDir;
+    }
+
+
+    /**
+     * Sets the optional prefix string for the temp file.
+     *
+     * @param  prefix  string to prepend to generated string
+     */
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+    }
+
+
+    /**
+     * Sets the optional suffix string for the temp file.
+     *
+     * @param  suffix  suffix including any "." , e.g ".xml"
+     */
+    public void setSuffix(String suffix) {
+        this.suffix = suffix;
+    }
+
+
+    /**
+     * Creates the temporary file.
+     *
+     *@exception  BuildException  if something goes wrong with the build
+     */
+    public void execute() throws BuildException {
+        if (property == null || property.length() == 0) {
+            throw new BuildException("no property specified");
+        }
+        if (destDir == null) {
+            destDir = getProject().resolveFile(".");
+        }
+        FileUtils utils = FileUtils.newFileUtils();
+        File tfile = utils.createTempFile(prefix, suffix, destDir);
+        getProject().setNewProperty(property, tfile.toString());
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Touch.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Touch.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Touch.java	(revision 16139)
@@ -0,0 +1,349 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Locale;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.FileList;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.FileNameMapper;
+
+/**
+ * Touch a file and/or fileset(s) and/or filelist(s);
+ * corresponds to the Unix touch command.
+ *
+ * <p>If the file to touch doesn't exist, an empty one is created.</p>
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="filesystem"
+ */
+public class Touch extends Task {
+
+    private interface DateFormatFactory {
+        DateFormat getPrimaryFormat();
+        DateFormat getFallbackFormat();
+    }
+
+    private static final DateFormatFactory DEFAULT_DF_FACTORY
+        = new DateFormatFactory() {
+        /*
+         * The initial version used DateFormat.SHORT for the
+         * time format, which ignores seconds.  If we want
+         * seconds as well, we need DateFormat.MEDIUM, which
+         * in turn would break all old build files.
+         *
+         * First try to parse with DateFormat.SHORT and if
+         * that fails with MEDIUM - throw an exception if both
+         * fail.
+         */
+        public DateFormat getPrimaryFormat() {
+            return DateFormat.getDateTimeInstance(DateFormat.SHORT,
+                DateFormat.SHORT, Locale.US);
+        }
+        public DateFormat getFallbackFormat() {
+            return DateFormat.getDateTimeInstance(DateFormat.SHORT,
+                DateFormat.MEDIUM, Locale.US);
+        }
+    };
+    private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+    private File file;
+    private long millis = -1;
+    private String dateTime;
+    private Vector filesets = new Vector();
+    private Vector filelists = new Vector();
+    private boolean dateTimeConfigured;
+    private boolean mkdirs;
+    private boolean verbose = true;
+    private FileNameMapper fileNameMapper = null;
+    private DateFormatFactory dfFactory = DEFAULT_DF_FACTORY;
+
+    /**
+     * Construct a new <code>Touch</code> task.
+     */
+    public Touch() {
+    }
+
+    /**
+     * Sets a single source file to touch.  If the file does not exist
+     * an empty file will be created.
+     * @param file the <code>File</code> to touch.
+     */
+    public void setFile(File file) {
+        this.file = file;
+    }
+
+    /**
+     * Set the new modification time of file(s) touched
+     * in milliseconds since midnight Jan 1 1970. Optional, default=now.
+     * @param millis the <code>long</code> timestamp to use.
+     */
+    public void setMillis(long millis) {
+        this.millis = millis;
+    }
+
+    /**
+     * Set the new modification time of file(s) touched
+     * in the format &quot;MM/DD/YYYY HH:MM AM <i>or</i> PM&quot;
+     * or &quot;MM/DD/YYYY HH:MM:SS AM <i>or</i> PM&quot;.
+     * Optional, default=now.
+     * @param dateTime the <code>String</code> date in the specified format.
+     */
+    public void setDatetime(String dateTime) {
+        if (this.dateTime != null) {
+            log("Resetting datetime attribute to " + dateTime, Project.MSG_VERBOSE);
+        }
+        this.dateTime = dateTime;
+        dateTimeConfigured = false;
+    }
+
+    /**
+     * Set whether nonexistent parent directories should be created
+     * when touching new files.
+     * @param mkdirs <code>boolean</code> whether to create parent directories.
+     * @since Ant 1.6.3
+     */
+    public void setMkdirs(boolean mkdirs) {
+        this.mkdirs = mkdirs;
+    }
+
+    /**
+     * Set whether the touch task will report every file it creates;
+     * defaults to <code>true</code>.
+     * @param verbose <code>boolean</code> flag.
+     * @since Ant 1.6.3
+     */
+    public void setVerbose(boolean verbose) {
+        this.verbose = verbose;
+    }
+
+    /**
+     * Set the format of the datetime attribute.
+     * @param pattern the <code>SimpleDateFormat</code>-compatible format pattern.
+     * @since Ant 1.6.3
+     */
+    public void setPattern(final String pattern) {
+        dfFactory = new DateFormatFactory() {
+            public DateFormat getPrimaryFormat() {
+                return new SimpleDateFormat(pattern);
+            }
+            public DateFormat getFallbackFormat() {
+                return null;
+            }
+        };
+    }
+
+    /**
+     * Add a <code>Mapper</code>.
+     * @param mapper the <code>Mapper</code> to add.
+     * @since Ant 1.6.3
+     */
+    public void addConfiguredMapper(Mapper mapper) {
+        add(mapper.getImplementation());
+    }
+
+    /**
+     * Add a <code>FileNameMapper</code>.
+     * @param fileNameMapper the <code>FileNameMapper</code> to add.
+     * @since Ant 1.6.3
+     * @throws BuildException if multiple mappers are added.
+     */
+    public void add(FileNameMapper fileNameMapper) throws BuildException {
+        if (this.fileNameMapper != null) {
+            throw new BuildException("Only one mapper may be added to the "
+                + getTaskName() + " task.");
+        }
+        this.fileNameMapper = fileNameMapper;
+    }
+
+    /**
+     * Add a set of files to touch.
+     * @param set the <code>Fileset</code> to add.
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * Add a filelist to touch.
+     * @param list the <code>Filelist</code> to add.
+     */
+    public void addFilelist(FileList list) {
+        filelists.addElement(list);
+    }
+
+    /**
+     * Check that this task has been configured properly.
+     * @throws BuildException if configuration errors are detected.
+     * @since Ant 1.6.3
+     */
+    protected synchronized void checkConfiguration() throws BuildException {
+        if (file == null && filesets.size() + filelists.size() == 0) {
+            throw new BuildException("Specify at least one source"
+                                   + "--a file, filelist or a fileset.");
+        }
+        if (file != null && file.exists() && file.isDirectory()) {
+            throw new BuildException("Use a fileset to touch directories.");
+        }
+        if (dateTime != null && !dateTimeConfigured) {
+            long workmillis = millis;
+            DateFormat df = dfFactory.getPrimaryFormat();
+            ParseException pe = null;
+            try {
+                workmillis = df.parse(dateTime).getTime();
+            } catch (ParseException peOne) {
+                df = dfFactory.getFallbackFormat();
+                if (df == null) {
+                    pe = peOne;
+                } else {
+                    try {
+                        workmillis = df.parse(dateTime).getTime();
+                    } catch (ParseException peTwo) {
+                        pe = peTwo;
+                    }
+                }
+            }
+            if (pe != null) {
+                throw new BuildException(pe.getMessage(), pe, getLocation());
+            }
+            if (workmillis < 0) {
+                throw new BuildException("Date of " + dateTime
+                                         + " results in negative "
+                                         + "milliseconds value "
+                                         + "relative to epoch "
+                                         + "(January 1, 1970, "
+                                         + "00:00:00 GMT).");
+            }
+            log("Setting millis to " + workmillis + " from datetime attribute",
+                ((millis < 0) ? Project.MSG_DEBUG : Project.MSG_VERBOSE));
+            setMillis(workmillis);
+            //only set if successful to this point:
+            dateTimeConfigured = true;
+        }
+    }
+
+    /**
+     * Execute the touch operation.
+     * @throws BuildException if an error occurs.
+     */
+    public void execute() throws BuildException {
+        checkConfiguration();
+        touch();
+    }
+
+    /**
+     * Does the actual work; assumes everything has been checked by now.
+     * @throws BuildException if an error occurs.
+     */
+    protected void touch() throws BuildException {
+        long defaultTimestamp = getTimestamp();
+
+        if (file != null) {
+            touch(file.getParentFile(), file.getName(), defaultTimestamp);
+        }
+        // deal with the filesets
+        for (int i = 0; i < filesets.size(); i++) {
+            FileSet fs = (FileSet) filesets.elementAt(i);
+            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+            File fromDir = fs.getDir(getProject());
+
+            String[] srcFiles = ds.getIncludedFiles();
+            String[] srcDirs = ds.getIncludedDirectories();
+
+            for (int j = 0; j < srcFiles.length; j++) {
+                touch(fromDir, srcFiles[j], defaultTimestamp);
+            }
+            for (int j = 0; j < srcDirs.length; j++) {
+                touch(fromDir, srcDirs[j], defaultTimestamp);
+            }
+        }
+        // deal with the filelists
+        for (int i = 0; i < filelists.size(); i++) {
+            FileList fl = (FileList) filelists.elementAt(i);
+            File fromDir = fl.getDir(getProject());
+
+            String[] srcFiles = fl.getFiles(getProject());
+
+            for (int j = 0; j < srcFiles.length; j++) {
+                touch(fromDir, srcFiles[j], defaultTimestamp);
+            }
+        }
+    }
+
+    /**
+     * Touch a single file with the current timestamp (this.millis). This method
+     * does not interact with any nested mappers and remains for reasons of
+     * backwards-compatibility only.
+     * @param file file to touch
+     * @throws BuildException
+     * @deprecated
+     */
+    protected void touch(File file) {
+        touch(file, getTimestamp());
+    }
+
+    private long getTimestamp() {
+        return (millis < 0) ? System.currentTimeMillis() : millis;
+    }
+
+    private void touch(File fromDir, String filename, long defaultTimestamp) {
+        File f = FILE_UTILS.resolveFile(fromDir, filename);
+        if (fileNameMapper == null) {
+            touch(f, defaultTimestamp);
+        } else {
+            String[] mapped = fileNameMapper.mapFileName(filename);
+            if (mapped != null && mapped.length > 0) {
+                long modTime = (f.exists()) ? f.lastModified() : defaultTimestamp;
+                for (int i = 0; i < mapped.length; i++) {
+                    touch(getProject().resolveFile(mapped[i]), modTime);
+                }
+            }
+        }
+    }
+
+    private void touch(File file, long modTime) {
+        if (!file.exists()) {
+            log("Creating " + file,
+                ((verbose) ? Project.MSG_INFO : Project.MSG_VERBOSE));
+            try {
+                FILE_UTILS.createNewFile(file, mkdirs);
+            } catch (IOException ioe) {
+                throw new BuildException("Could not create " + file, ioe,
+                                         getLocation());
+            }
+        }
+        if (!file.canWrite()) {
+            throw new BuildException("Can not change modification date of "
+                                     + "read-only file " + file);
+        }
+        FILE_UTILS.setFileLastModified(file, modTime);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Transform.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Transform.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Transform.java	(revision 16139)
@@ -0,0 +1,26 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+/**
+ * Has been merged into ExecuteOn, empty class for backwards compatibility.
+ *
+ * @ant.task ignore="true"
+ */
+public class Transform extends ExecuteOn {
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Tstamp.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Tstamp.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Tstamp.java	(revision 16139)
@@ -0,0 +1,325 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+/**
+ * Sets properties to the current time, or offsets from the current time.
+ * The default properties are TSTAMP, DSTAMP and TODAY;
+ *
+ * @since Ant 1.1
+ * @ant.task category="utility"
+ */
+public class Tstamp extends Task {
+
+    private Vector customFormats = new Vector();
+    private String prefix = "";
+
+    /**
+     * Set a prefix for the properties. If the prefix does not end with a "."
+     * one is automatically added
+     * @since Ant 1.5
+     */
+    public void setPrefix(String prefix) {
+        this.prefix = prefix;
+        if (!this.prefix.endsWith(".")) {
+            this.prefix += ".";
+        }
+    }
+
+    /**
+     * create the timestamps. Custom ones are done before
+     * the standard ones, to get their retaliation in early.
+     * @throws BuildException
+     */
+    public void execute() throws BuildException {
+        try {
+            Date d = new Date();
+
+            Enumeration i = customFormats.elements();
+            while (i.hasMoreElements()) {
+                CustomFormat cts = (CustomFormat) i.nextElement();
+                cts.execute(getProject(), d, getLocation());
+            }
+
+            SimpleDateFormat dstamp = new SimpleDateFormat ("yyyyMMdd");
+            setProperty("DSTAMP", dstamp.format(d));
+
+            SimpleDateFormat tstamp = new SimpleDateFormat ("HHmm");
+            setProperty("TSTAMP", tstamp.format(d));
+
+            SimpleDateFormat today
+                = new SimpleDateFormat ("MMMM d yyyy", Locale.US);
+            setProperty("TODAY", today.format(d));
+
+        } catch (Exception e) {
+            throw new BuildException(e);
+        }
+    }
+
+    /**
+     * create a custom format with the current prefix.
+     * @return a ready to fill-in format
+     */
+    public CustomFormat createFormat() {
+        CustomFormat cts = new CustomFormat();
+        customFormats.addElement(cts);
+        return cts;
+    }
+
+    /**
+     * helper that encapsulates prefix logic and property setting
+     * policy (i.e. we use setNewProperty instead of setProperty).
+     */
+    private void setProperty(String name, String value) {
+        getProject().setNewProperty(prefix + name, value);
+    }
+
+    /**
+     * This nested element that allows a property to be set
+     * to the current date and time in a given format.
+     * The date/time patterns are as defined in the
+     * Java SimpleDateFormat class.
+     * The format element also allows offsets to be applied to
+     * the time to generate different time values.
+     * @todo consider refactoring out into a re-usable element.
+     */
+    public class CustomFormat {
+        private TimeZone timeZone;
+        private String propertyName;
+        private String pattern;
+        private String language;
+        private String country;
+        private String variant;
+        private int offset = 0;
+        private int field = Calendar.DATE;
+
+        /**
+         * Create a format
+         */
+        public CustomFormat() {
+        }
+
+        /**
+         *  The property to receive the date/time string in the given pattern
+         * @param propertyName
+         */
+        public void setProperty(String propertyName) {
+            this.propertyName = propertyName;
+        }
+
+        /**
+         * The date/time pattern to be used. The values are as
+         * defined by the Java SimpleDateFormat class.
+         * @param pattern
+         * @see java.text.SimpleDateFormat
+         */
+        public void setPattern(String pattern) {
+            this.pattern = pattern;
+        }
+
+        /**
+         * The locale used to create date/time string.
+         * The general form is "language, country, variant" but
+         * either variant or variant and country may be omitted.
+         * For more information please refer to documentation
+         * for the java.util.Locale  class.
+         * @param locale
+         * @see java.util.Locale
+         */
+        public void setLocale(String locale) {
+            StringTokenizer st = new StringTokenizer(locale, " \t\n\r\f,");
+            try {
+                language = st.nextToken();
+                if (st.hasMoreElements()) {
+                    country = st.nextToken();
+                    if (st.hasMoreElements()) {
+                        variant = st.nextToken();
+                        if (st.hasMoreElements()) {
+                            throw new BuildException("bad locale format",
+                                                      getLocation());
+                        }
+                    }
+                } else {
+                    country = "";
+                }
+            } catch (NoSuchElementException e) {
+                throw new BuildException("bad locale format", e,
+                                         getLocation());
+            }
+        }
+
+        /**
+         * The timezone to use for displaying time.
+         * The values are as defined by the Java TimeZone class.
+         * @param id
+         * @see java.util.TimeZone
+         */
+        public void setTimezone(String id) {
+            timeZone = TimeZone.getTimeZone(id);
+        }
+
+        /**
+         * The numeric offset to the current time.
+         * @param offset
+         */
+        public void setOffset(int offset) {
+            this.offset = offset;
+        }
+
+        /**
+         * @deprecated setUnit(String) is deprecated and is replaced with
+         *             setUnit(Tstamp.Unit) to make Ant's
+         *             Introspection mechanism do the work and also to
+         *             encapsulate operations on the unit in its own
+         *             class.
+         */
+        public void setUnit(String unit) {
+            log("DEPRECATED - The setUnit(String) method has been deprecated."
+                + " Use setUnit(Tstamp.Unit) instead.");
+            Unit u = new Unit();
+            u.setValue(unit);
+            field = u.getCalendarField();
+        }
+
+        /**
+         * The unit of the offset to be applied to the current time.
+         * Valid Values are
+         * <ul>
+         *    <li>millisecond</li>
+         *    <li>second</li>
+         *    <li>minute</li>
+         *    <li>hour</li>
+         *    <li>day</li>
+         *    <li>week</li>
+         *    <li>month</li>
+         *    <li>year</li>
+         * </ul>
+         * The default unit is day.
+         * @param unit
+         */
+        public void setUnit(Unit unit) {
+            field = unit.getCalendarField();
+        }
+
+        /**
+         * validate parameter and execute the format
+         * @param project project to set property in
+         * @param date date to use as a starting point
+         * @param location line in file (for errors)
+         */
+        public void execute(Project project, Date date, Location location) {
+            if (propertyName == null) {
+                throw new BuildException("property attribute must be provided",
+                                         location);
+            }
+
+            if (pattern == null) {
+                throw new BuildException("pattern attribute must be provided",
+                                         location);
+            }
+
+            SimpleDateFormat sdf;
+            if (language == null) {
+                sdf = new SimpleDateFormat(pattern);
+            } else if (variant == null) {
+                sdf = new SimpleDateFormat(pattern,
+                                           new Locale(language, country));
+            } else {
+                sdf = new SimpleDateFormat(pattern,
+                                           new Locale(language, country,
+                                                      variant));
+            }
+            if (offset != 0) {
+                Calendar calendar = Calendar.getInstance();
+                calendar.setTime(date);
+                calendar.add(field, offset);
+                date = calendar.getTime();
+            }
+            if (timeZone != null) {
+                sdf.setTimeZone(timeZone);
+            }
+            Tstamp.this.setProperty(propertyName, sdf.format(date));
+        }
+    }
+
+    /**
+     * set of valid units to use for time offsets.
+     */
+    public static class Unit extends EnumeratedAttribute {
+
+        private static final String MILLISECOND = "millisecond";
+        private static final String SECOND = "second";
+        private static final String MINUTE = "minute";
+        private static final String HOUR = "hour";
+        private static final String DAY = "day";
+        private static final String WEEK = "week";
+        private static final String MONTH = "month";
+        private static final String YEAR = "year";
+
+        private static final String[] units = {
+                                                MILLISECOND,
+                                                SECOND,
+                                                MINUTE,
+                                                HOUR,
+                                                DAY,
+                                                WEEK,
+                                                MONTH,
+                                                YEAR
+                                              };
+
+        private Hashtable calendarFields = new Hashtable();
+
+        public Unit() {
+            calendarFields.put(MILLISECOND,
+                               new Integer(Calendar.MILLISECOND));
+            calendarFields.put(SECOND, new Integer(Calendar.SECOND));
+            calendarFields.put(MINUTE, new Integer(Calendar.MINUTE));
+            calendarFields.put(HOUR, new Integer(Calendar.HOUR_OF_DAY));
+            calendarFields.put(DAY, new Integer(Calendar.DATE));
+            calendarFields.put(WEEK, new Integer(Calendar.WEEK_OF_YEAR));
+            calendarFields.put(MONTH, new Integer(Calendar.MONTH));
+            calendarFields.put(YEAR, new Integer(Calendar.YEAR));
+        }
+
+        public int getCalendarField() {
+            String key = getValue().toLowerCase();
+            Integer i = (Integer) calendarFields.get(key);
+            return i.intValue();
+        }
+
+        public String[] getValues() {
+            return units;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Typedef.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Typedef.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Typedef.java	(revision 16139)
@@ -0,0 +1,45 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+
+
+/**
+ *
+ * Adds a data type definition to the current project.
+ * Two attributes are
+ * needed, the name that identifies this data type uniquely, and the full
+ * name of the class (including the packages) that implements this
+ * type.
+ * <p>You can also define a group of data types at once using the file or
+ * resource attributes.  These attributes point to files in the format of
+ * Java property files.   Each line defines a single data type in the
+ * format:</p>
+ * <pre>
+ * typename=fully.qualified.java.classname
+ * </pre>
+ * <p>Typedef should be used to add your own types to the system. Data
+ * types are things likepaths or filesets that can be defined at
+ * the project level and referenced via their ID attribute.</p>
+ * <p>Custom data types usually need custom tasks to put them to good use.</p>
+ *
+ * @since Ant 1.4
+ * @ant.task category="internal"
+ */
+public class Typedef extends Definer {
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Unpack.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Unpack.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Unpack.java	(revision 16139)
@@ -0,0 +1,127 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Abstract Base class for unpack tasks.
+ *
+ *
+ * @since Ant 1.5
+ */
+
+public abstract class Unpack extends Task {
+
+    protected File source;
+    protected File dest;
+
+    /**
+     * @deprecated setSrc(String) is deprecated and is replaced with
+     *             setSrc(File) to make Ant's Introspection
+     *             mechanism do the work and also to encapsulate operations on
+     *             the type in its own class.
+     * @ant.attribute ignore="true"
+     */
+    public void setSrc(String src) {
+        log("DEPRECATED - The setSrc(String) method has been deprecated."
+            + " Use setSrc(File) instead.");
+        setSrc(getProject().resolveFile(src));
+    }
+
+    /**
+     * @deprecated setDest(String) is deprecated and is replaced with
+     *             setDest(File) to make Ant's Introspection
+     *             mechanism do the work and also to encapsulate operations on
+     *             the type in its own class.
+     * @ant.attribute ignore="true"
+     */
+    public void setDest(String dest) {
+        log("DEPRECATED - The setDest(String) method has been deprecated."
+            + " Use setDest(File) instead.");
+        setDest(getProject().resolveFile(dest));
+    }
+
+    /**
+     * The file to expand; required.
+     * @param src file to expand
+     */
+    public void setSrc(File src) {
+        source = src;
+    }
+
+    /**
+     * The destination file or directory; optional.
+     * @param dest destination file or directory
+     */
+    public void setDest(File dest) {
+        this.dest = dest;
+    }
+
+    private void validate() throws BuildException {
+        if (source == null) {
+            throw new BuildException("No Src specified", getLocation());
+        }
+
+        if (!source.exists()) {
+            throw new BuildException("Src doesn't exist", getLocation());
+        }
+
+        if (source.isDirectory()) {
+            throw new BuildException("Cannot expand a directory", getLocation());
+        }
+
+        if (dest == null) {
+            dest = new File(source.getParent());
+        }
+
+        if (dest.isDirectory()) {
+            String defaultExtension = getDefaultExtension();
+            createDestFile(defaultExtension);
+        }
+    }
+
+    private void createDestFile(String defaultExtension) {
+        String sourceName = source.getName();
+        int len = sourceName.length();
+        if (defaultExtension != null
+            && len > defaultExtension.length()
+            && defaultExtension.equalsIgnoreCase(sourceName.substring(len - defaultExtension.length()))) {
+            dest = new File(dest, sourceName.substring(0,
+                                                       len - defaultExtension.length()));
+        } else {
+            dest = new File(dest, sourceName);
+        }
+    }
+
+    public void execute() throws BuildException {
+        File savedDest = dest; // may be altered in validate
+        try {
+            validate();
+            extract();
+        } finally {
+            dest = savedDest;
+        }
+    }
+
+    protected abstract String getDefaultExtension();
+    protected abstract void extract();
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Untar.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Untar.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Untar.java	(revision 16139)
@@ -0,0 +1,197 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.GZIPInputStream;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.bzip2.CBZip2InputStream;
+import org.apache.tools.tar.TarEntry;
+import org.apache.tools.tar.TarInputStream;
+
+
+
+/**
+ * Untar a file.
+ * <p>For JDK 1.1 &quot;last modified time&quot; field is set to current time instead of being
+ * carried from the archive file.</p>
+ * <p>PatternSets are used to select files to extract
+ * <I>from</I> the archive.  If no patternset is used, all files are extracted.
+ * </p>
+ * <p>FileSet>s may be used to select archived files
+ * to perform unarchival upon.
+ * </p>
+ * <p>File permissions will not be restored on extracted files.</p>
+ * <p>The untar task recognizes the long pathname entries used by GNU tar.<p>
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="packaging"
+ */
+public class Untar extends Expand {
+    /**
+     *   compression method
+     */
+    private UntarCompressionMethod compression = new UntarCompressionMethod();
+
+    /**
+     * Set decompression algorithm to use; default=none.
+     *
+     * Allowable values are
+     * <ul>
+     *   <li>none - no compression
+     *   <li>gzip - Gzip compression
+     *   <li>bzip2 - Bzip2 compression
+     * </ul>
+     *
+     * @param method compression method
+     */
+    public void setCompression(UntarCompressionMethod method) {
+        compression = method;
+    }
+
+    /**
+     * No encoding support in Untar.
+     * @param encoding not used
+     * @throws BuildException always
+     * @since Ant 1.6
+     */
+    public void setEncoding(String encoding) {
+        throw new BuildException("The " + getTaskName()
+                                 + " task doesn't support the encoding"
+                                 + " attribute", getLocation());
+    }
+
+    /**
+     * @see Expand#expandFile(FileUtils, File, File)
+     */
+    protected void expandFile(FileUtils fileUtils, File srcF, File dir) {
+        FileInputStream fis = null;
+        TarInputStream tis = null;
+        try {
+            log("Expanding: " + srcF + " into " + dir, Project.MSG_INFO);
+            fis = new FileInputStream(srcF);
+            tis = new TarInputStream(
+                compression.decompress(srcF, new BufferedInputStream(fis)));
+            TarEntry te = null;
+
+            while ((te = tis.getNextEntry()) != null) {
+                extractFile(fileUtils, srcF, dir, tis,
+                            te.getName(), te.getModTime(), te.isDirectory());
+            }
+            log("expand complete", Project.MSG_VERBOSE);
+
+        } catch (IOException ioe) {
+            throw new BuildException("Error while expanding " + srcF.getPath(),
+                                     ioe, getLocation());
+        } finally {
+            if (tis != null) {
+                try {
+                    tis.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            } else if (fis != null) {
+                try {
+                    fis.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    /**
+     * Valid Modes for Compression attribute to Untar Task
+     *
+     */
+    public static final class UntarCompressionMethod
+        extends EnumeratedAttribute {
+
+        // permissible values for compression attribute
+        /**
+         *  No compression
+         */
+        private static final String NONE = "none";
+        /**
+         *  GZIP compression
+         */
+        private static final String GZIP = "gzip";
+        /**
+         *  BZIP2 compression
+         */
+        private static final String BZIP2 = "bzip2";
+
+
+        /**
+         *  Constructor
+         */
+        public UntarCompressionMethod() {
+            super();
+            setValue(NONE);
+        }
+
+        /**
+         * Get valid enumeration values
+         *
+         * @return valid values
+         */
+        public String[] getValues() {
+            return new String[] {NONE, GZIP, BZIP2};
+        }
+
+        /**
+         *  This method wraps the input stream with the
+         *     corresponding decompression method
+         *
+         *  @param file provides location information for BuildException
+         *  @param istream input stream
+         *  @return input stream with on-the-fly decompression
+         *  @exception IOException thrown by GZIPInputStream constructor
+         *  @exception BuildException thrown if bzip stream does not
+         *     start with expected magic values
+         */
+        private InputStream decompress(final File file,
+                                       final InputStream istream)
+            throws IOException, BuildException {
+            final String value = getValue();
+            if (GZIP.equals(value)) {
+                return new GZIPInputStream(istream);
+            } else {
+                if (BZIP2.equals(value)) {
+                    final char[] magic = new char[] {'B', 'Z'};
+                    for (int i = 0; i < magic.length; i++) {
+                        if (istream.read() != magic[i]) {
+                            throw new BuildException(
+                                "Invalid bz2 file." + file.toString());
+                        }
+                    }
+                    return new CBZip2InputStream(istream);
+                }
+            }
+            return istream;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/UpToDate.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/UpToDate.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/UpToDate.java	(revision 16139)
@@ -0,0 +1,234 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.MergingMapper;
+import org.apache.tools.ant.util.SourceFileScanner;
+
+/**
+ * Sets the given property if the specified target has a timestamp
+ * greater than all of the source files.
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task category="control"
+ */
+
+public class UpToDate extends Task implements Condition {
+
+    private String property;
+    private String value;
+    private File sourceFile;
+    private File targetFile;
+    private Vector sourceFileSets = new Vector();
+
+    protected Mapper mapperElement = null;
+
+    /**
+     * The property to set if the target file is more up-to-date than
+     * (each of) the source file(s).
+     *
+     * @param property the name of the property to set if Target is up-to-date.
+     */
+    public void setProperty(final String property) {
+        this.property = property;
+    }
+
+    /**
+     * The value to set the named property to if the target file is more
+     * up-to-date than (each of) the source file(s). Defaults to 'true'.
+     *
+     * @param value the value to set the property to if Target is up-to-date
+     */
+    public void setValue(final String value) {
+        this.value = value;
+    }
+
+    /**
+     * Returns the value, or "true" if a specific value wasn't provided.
+     */
+    private String getValue() {
+        return (value != null) ? value : "true";
+    }
+
+    /**
+     * The file which must be more up-to-date than (each of) the source file(s)
+     * if the property is to be set.
+     *
+     * @param file the file we are checking against.
+     */
+    public void setTargetFile(final File file) {
+        this.targetFile = file;
+    }
+
+    /**
+     * The file that must be older than the target file
+     * if the property is to be set.
+     *
+     * @param file the file we are checking against the target file.
+     */
+    public void setSrcfile(final File file) {
+        this.sourceFile = file;
+    }
+
+    /**
+     * Nested &lt;srcfiles&gt; element.
+     * @param fs the source files
+     */
+    public void addSrcfiles(final FileSet fs) {
+        sourceFileSets.addElement(fs);
+    }
+
+    /**
+     * Defines the FileNameMapper to use (nested mapper element).
+     * @return a mapper to be configured
+     * @throws BuildException if more than one mapper is defined
+     */
+    public Mapper createMapper() throws BuildException {
+        if (mapperElement != null) {
+            throw new BuildException("Cannot define more than one mapper",
+                                     getLocation());
+        }
+        mapperElement = new Mapper(getProject());
+        return mapperElement;
+    }
+
+    /**
+     * A nested filenamemapper
+     * @param fileNameMapper the mapper to add
+     * @since Ant 1.6.3
+     */
+    public void add(FileNameMapper fileNameMapper) {
+        createMapper().add(fileNameMapper);
+    }
+
+    /**
+     * Evaluate (all) target and source file(s) to
+     * see if the target(s) is/are up-to-date.
+     * @return true if the target(s) is/are up-to-date
+     */
+    public boolean eval() {
+        if (sourceFileSets.size() == 0 && sourceFile == null) {
+            throw new BuildException("At least one srcfile or a nested "
+                                     + "<srcfiles> element must be set.");
+        }
+
+        if (sourceFileSets.size() > 0 && sourceFile != null) {
+            throw new BuildException("Cannot specify both the srcfile "
+                                     + "attribute and a nested <srcfiles> "
+                                     + "element.");
+        }
+
+        if (targetFile == null && mapperElement == null) {
+            throw new BuildException("The targetfile attribute or a nested "
+                                     + "mapper element must be set.");
+        }
+
+        // if the target file is not there, then it can't be up-to-date
+        if (targetFile != null && !targetFile.exists()) {
+            log("The targetfile \"" + targetFile.getAbsolutePath()
+                    + "\" does not exist.", Project.MSG_VERBOSE);
+            return false;
+        }
+
+        // if the source file isn't there, throw an exception
+        if (sourceFile != null && !sourceFile.exists()) {
+            throw new BuildException(sourceFile.getAbsolutePath()
+                                     + " not found.");
+        }
+
+        Enumeration e = sourceFileSets.elements();
+        boolean upToDate = true;
+        while (upToDate && e.hasMoreElements()) {
+            FileSet fs = (FileSet) e.nextElement();
+            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+            upToDate = upToDate && scanDir(fs.getDir(getProject()),
+                                           ds.getIncludedFiles());
+        }
+
+        if (sourceFile != null) {
+            if (mapperElement == null) {
+                upToDate = upToDate
+                    && (targetFile.lastModified() >= sourceFile.lastModified());
+            } else {
+                SourceFileScanner sfs = new SourceFileScanner(this);
+                upToDate = upToDate
+                    && (sfs.restrict(new String[] {sourceFile.getAbsolutePath()},
+                                  null, null,
+                                  mapperElement.getImplementation()).length == 0);
+            }
+        }
+        return upToDate;
+    }
+
+
+    /**
+     * Sets property to true if target file(s) have a more recent timestamp
+     * than (each of) the corresponding source file(s).
+     * @throws BuildException on error
+     */
+    public void execute() throws BuildException {
+        if (property == null) {
+            throw new BuildException("property attribute is required.",
+                                     getLocation());
+        }
+        boolean upToDate = eval();
+        if (upToDate) {
+            getProject().setNewProperty(property, getValue());
+            if (mapperElement == null) {
+                log("File \"" + targetFile.getAbsolutePath()
+                    + "\" is up-to-date.", Project.MSG_VERBOSE);
+            } else {
+                log("All target files are up-to-date.",
+                    Project.MSG_VERBOSE);
+            }
+        }
+    }
+
+    /**
+     * Scan a directory for files to check for "up to date"ness
+     * @param srcDir the directory
+     * @param files the files to scan for
+     * @return true if the files are up to date
+     */
+    protected boolean scanDir(File srcDir, String[] files) {
+        SourceFileScanner sfs = new SourceFileScanner(this);
+        FileNameMapper mapper = null;
+        File dir = srcDir;
+        if (mapperElement == null) {
+            MergingMapper mm = new MergingMapper();
+            mm.setTo(targetFile.getAbsolutePath());
+            mapper = mm;
+            dir = null;
+        } else {
+            mapper = mapperElement.getImplementation();
+        }
+        return sfs.restrict(files, srcDir, dir, mapper).length == 0;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/WaitFor.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/WaitFor.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/WaitFor.java	(revision 16139)
@@ -0,0 +1,175 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.util.Hashtable;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.taskdefs.condition.ConditionBase;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+/**
+ * Wait for an external event to occur.
+ *
+ * Wait for an external process to start or to complete some
+ * task. This is useful with the <code>parallel</code> task to
+ * synchronize the execution of tests with server startup.
+ *
+ * The following attributes can be specified on a waitfor task:
+ * <ul>
+ * <li>maxwait - maximum length of time to wait before giving up</li>
+ * <li>maxwaitunit - The unit to be used to interpret maxwait attribute</li>
+ * <li>checkevery - amount of time to sleep between each check</li>
+ * <li>checkeveryunit - The unit to be used to interpret checkevery attribute</li>
+ * <li>timeoutproperty - name of a property to set if maxwait has been exceeded.</li>
+ * </ul>
+ *
+ * The maxwaitunit and checkeveryunit are allowed to have the following values:
+ * millisecond, second, minute, hour, day and week. The default is millisecond.
+ *
+ * @since Ant 1.5
+ *
+ * @ant.task category="control"
+ */
+
+public class WaitFor extends ConditionBase {
+    /** default max wait time */
+    private long maxWaitMillis = 1000L * 60L * 3L;
+    private long maxWaitMultiplier = 1L;
+    private long checkEveryMillis = 500L;
+    private long checkEveryMultiplier = 1L;
+    private String timeoutProperty;
+
+    /**
+     * Set the maximum length of time to wait
+     */
+    public void setMaxWait(long time) {
+        maxWaitMillis = time;
+    }
+
+    /**
+     * Set the max wait time unit
+     */
+    public void setMaxWaitUnit(Unit unit) {
+        maxWaitMultiplier = unit.getMultiplier();
+    }
+
+    /**
+     * Set the time between each check
+     */
+    public void setCheckEvery(long time) {
+        checkEveryMillis = time;
+    }
+
+    /**
+     * Set the check every time unit
+     */
+    public void setCheckEveryUnit(Unit unit) {
+        checkEveryMultiplier = unit.getMultiplier();
+    }
+
+    /**
+     * Name the property to set after a timeout.
+     */
+    public void setTimeoutProperty(String p) {
+        timeoutProperty = p;
+    }
+
+    /**
+     * Check repeatedly for the specified conditions until they become
+     * true or the timeout expires.
+     */
+    public void execute() throws BuildException {
+        if (countConditions() > 1) {
+            throw new BuildException("You must not nest more than one "
+                                     + "condition into <waitfor>");
+        }
+        if (countConditions() < 1) {
+            throw new BuildException("You must nest a condition into "
+                                     + "<waitfor>");
+        }
+        Condition c = (Condition) getConditions().nextElement();
+
+        long savedMaxWaitMillis = maxWaitMillis;
+        long savedCheckEveryMillis = checkEveryMillis;
+        try {
+            maxWaitMillis *= maxWaitMultiplier;
+            checkEveryMillis *= checkEveryMultiplier;
+            long start = System.currentTimeMillis();
+            long end = start + maxWaitMillis;
+
+            while (System.currentTimeMillis() < end) {
+                if (c.eval()) {
+                    return;
+                }
+                try {
+                    Thread.sleep(checkEveryMillis);
+                } catch (InterruptedException e) {
+                    // ignore
+                }
+            }
+
+            if (timeoutProperty != null) {
+                getProject().setNewProperty(timeoutProperty, "true");
+            }
+        } finally {
+            maxWaitMillis = savedMaxWaitMillis;
+            checkEveryMillis = savedCheckEveryMillis;
+        }
+    }
+
+    /**
+     * The enumeration of units:
+     * millisecond, second, minute, hour, day, week
+     * @todo we use timestamps in many places, why not factor this out
+     */
+    public static class Unit extends EnumeratedAttribute {
+
+        private static final String MILLISECOND = "millisecond";
+        private static final String SECOND = "second";
+        private static final String MINUTE = "minute";
+        private static final String HOUR = "hour";
+        private static final String DAY = "day";
+        private static final String WEEK = "week";
+
+        private static final String[] units = {
+            MILLISECOND, SECOND, MINUTE, HOUR, DAY, WEEK
+        };
+
+        private Hashtable timeTable = new Hashtable();
+
+        public Unit() {
+            timeTable.put(MILLISECOND, new Long(1L));
+            timeTable.put(SECOND,      new Long(1000L));
+            timeTable.put(MINUTE,      new Long(1000L * 60L));
+            timeTable.put(HOUR,        new Long(1000L * 60L * 60L));
+            timeTable.put(DAY,         new Long(1000L * 60L * 60L * 24L));
+            timeTable.put(WEEK,        new Long(1000L * 60L * 60L * 24L * 7L));
+        }
+
+        public long getMultiplier() {
+            String key = getValue().toLowerCase();
+            Long l = (Long) timeTable.get(key);
+            return l.longValue();
+        }
+
+        public String[] getValues() {
+            return units;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/War.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/War.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/War.java	(revision 16139)
@@ -0,0 +1,173 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.ZipFileSet;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.zip.ZipOutputStream;
+
+
+/**
+ * An extension of &lt;jar&gt; to create a WAR archive.
+ * Contains special treatment for files that should end up in the
+ * <code>WEB-INF/lib</code>, <code>WEB-INF/classes</code> or
+ * <code>WEB-INF</code> directories of the Web Application Archive.</p>
+ * <p>(The War task is a shortcut for specifying the particular layout of a WAR file.
+ * The same thing can be accomplished by using the <i>prefix</i> and <i>fullpath</i>
+ * attributes of zipfilesets in a Zip or Jar task.)</p>
+ * <p>The extended zipfileset element from the zip task
+ * (with attributes <i>prefix</i>, <i>fullpath</i>, and <i>src</i>)
+ * is available in the War task.</p>
+ *
+ *
+ * @since Ant 1.2
+ *
+ * @ant.task category="packaging"
+ * @see Jar
+ */
+public class War extends Jar {
+
+    /**
+     * our web.xml deployment descriptor
+     */
+    private File deploymentDescriptor;
+
+    /**
+     * flag set if the descriptor is added
+     */
+    private boolean descriptorAdded;
+
+    private static final FileUtils fu = FileUtils.newFileUtils();
+
+    public War() {
+        super();
+        archiveType = "war";
+        emptyBehavior = "create";
+    }
+
+    /**
+     * <i>Deprecated<i> name of the file to create
+     * -use <tt>destfile</tt> instead.
+     * @deprecated Use setDestFile(File) instead
+     * @ant.attribute ignore="true"
+     */
+    public void setWarfile(File warFile) {
+        setDestFile(warFile);
+    }
+
+    /**
+     * set the deployment descriptor to use (WEB-INF/web.xml);
+     * required unless <tt>update=true</tt>
+     */
+    public void setWebxml(File descr) {
+        deploymentDescriptor = descr;
+        if (!deploymentDescriptor.exists()) {
+            throw new BuildException("Deployment descriptor: "
+                                     + deploymentDescriptor
+                                     + " does not exist.");
+        }
+
+        // Create a ZipFileSet for this file, and pass it up.
+        ZipFileSet fs = new ZipFileSet();
+        fs.setFile(deploymentDescriptor);
+        fs.setFullpath("WEB-INF/web.xml");
+        super.addFileset(fs);
+    }
+
+    /**
+     * add files under WEB-INF/lib/
+     */
+
+    public void addLib(ZipFileSet fs) {
+        // We just set the prefix for this fileset, and pass it up.
+        fs.setPrefix("WEB-INF/lib/");
+        super.addFileset(fs);
+    }
+
+    /**
+     * add files under WEB-INF/classes
+     */
+    public void addClasses(ZipFileSet fs) {
+        // We just set the prefix for this fileset, and pass it up.
+        fs.setPrefix("WEB-INF/classes/");
+        super.addFileset(fs);
+    }
+
+    /**
+     * files to add under WEB-INF;
+     */
+    public void addWebinf(ZipFileSet fs) {
+        // We just set the prefix for this fileset, and pass it up.
+        fs.setPrefix("WEB-INF/");
+        super.addFileset(fs);
+    }
+
+    /**
+     * override of  parent; validates configuration
+     * before initializing the output stream.
+     */
+    protected void initZipOutputStream(ZipOutputStream zOut)
+        throws IOException, BuildException {
+        // If no webxml file is specified, it's an error.
+        if (deploymentDescriptor == null && !isInUpdateMode()) {
+            throw new BuildException("webxml attribute is required", getLocation());
+        }
+
+        super.initZipOutputStream(zOut);
+    }
+
+    /**
+     * Overridden from Zip class to deal with web.xml
+     */
+    protected void zipFile(File file, ZipOutputStream zOut, String vPath,
+                           int mode)
+        throws IOException {
+        // If the file being added is WEB-INF/web.xml, we warn if it's
+        // not the one specified in the "webxml" attribute - or if
+        // it's being added twice, meaning the same file is specified
+        // by the "webxml" attribute and in a <fileset> element.
+        if (vPath.equalsIgnoreCase("WEB-INF/web.xml"))  {
+            if (deploymentDescriptor == null
+                || !fu.fileNameEquals(deploymentDescriptor, file)
+                || descriptorAdded) {
+                log("Warning: selected " + archiveType
+                    + " files include a WEB-INF/web.xml which will be ignored "
+                    + "(please use webxml attribute to "
+                    + archiveType + " task)", Project.MSG_WARN);
+            } else {
+                super.zipFile(file, zOut, vPath, mode);
+                descriptorAdded = true;
+            }
+        } else {
+            super.zipFile(file, zOut, vPath, mode);
+        }
+    }
+
+    /**
+     * Make sure we don't think we already have a web.xml next time this task
+     * gets executed.
+     */
+    protected void cleanUp() {
+        descriptorAdded = false;
+        super.cleanUp();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/WhichResource.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/WhichResource.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/WhichResource.java	(revision 16139)
@@ -0,0 +1,181 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.AntClassLoader;
+
+import java.net.URL;
+
+/**
+ * Find a class or resource on the supplied classpath, or the
+ * system classpath if none is supplied. The named property is set if
+ * the item can be found. For example
+ * <pre>
+ * &lt;whichresource resource="/log4j.properties"
+ *   property="log4j.url" &gt;
+ * </pre>
+ * @since Ant 1.6
+ * @ant.attribute.group name="oneof" description="Exactly one of these two"
+ */
+public class WhichResource extends Task {
+    /**
+     * our classpath
+     */
+    private Path classpath;
+
+    /**
+     * class to look for
+     */
+    private String classname;
+
+    /**
+     * resource to look for
+     */
+    private String resource;
+
+    /**
+     * property to set
+     */
+    private String property;
+
+
+    /**
+     * Set the classpath to be used for this compilation.
+     * @param cp the classpath to be used.
+     */
+    public void setClasspath(Path cp) {
+        if (classpath == null) {
+            classpath = cp;
+        } else {
+            classpath.append(cp);
+        }
+    }
+
+    /**
+     * Adds a path to the classpath.
+     * @return a classpath to be configured.
+     */
+    public Path createClasspath() {
+        if (classpath == null) {
+            classpath = new Path(getProject());
+        }
+        return classpath.createPath();
+    }
+
+
+    /**
+     * validate
+     */
+    private void validate() {
+        int setcount = 0;
+        if (classname != null) {
+            setcount++;
+        }
+        if (resource != null) {
+            setcount++;
+        }
+
+
+        if (setcount == 0) {
+            throw new BuildException(
+                    "One of classname or resource must be specified");
+        }
+        if (setcount > 1) {
+            throw new BuildException(
+                    "Only one of classname or resource can be specified");
+        }
+        if (property == null) {
+            throw new BuildException("No property defined");
+        }
+    }
+
+    /**
+     * execute it
+     * @throws BuildException on error
+     */
+    public void execute() throws BuildException {
+        validate();
+        if (classpath != null) {
+            getProject().log("using user supplied classpath: " + classpath,
+                    Project.MSG_DEBUG);
+            classpath = classpath.concatSystemClasspath("ignore");
+        } else {
+            classpath = new Path(getProject());
+            classpath = classpath.concatSystemClasspath("only");
+            getProject().log("using system classpath: " + classpath, Project.MSG_DEBUG);
+        }
+        AntClassLoader loader;
+        loader = new AntClassLoader(getProject().getCoreLoader(),
+                    getProject(),
+                    classpath, false);
+        String location = null;
+        if (classname != null) {
+            //convert a class name into a resource
+            resource = classname.replace('.', '/') + ".class";
+        }
+
+        if (resource == null) {
+            throw new BuildException("One of class or resource is required");
+        }
+
+        if (resource.startsWith("/")) {
+            resource = resource.substring(1);
+        }
+
+        log("Searching for " + resource, Project.MSG_VERBOSE);
+        URL url;
+        url = loader.getResource(resource);
+        if (url != null) {
+            //set the property
+            location = url.toExternalForm();
+            getProject().setNewProperty(property, location);
+        }
+    }
+
+    /**
+     * name the resource to look for
+     * @param resource the name of the resource to look for.
+     * @ant.attribute group="oneof"
+     */
+    public void setResource(String resource) {
+        this.resource = resource;
+    }
+
+    /**
+     * name the class to look for
+     * @param classname the name of the class to look for.
+     * @ant.attribute group="oneof"
+     */
+    public void setClass(String classname) {
+        this.classname = classname;
+    }
+
+    /**
+     * the property to fill with the URL of the resource or class
+     * @param property the property to be set.
+     * @ant.attribute group="required"
+     */
+    public void setProperty(String property) {
+        this.property = property;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison.java	(revision 16139)
@@ -0,0 +1,67 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+
+/**
+ * Proxy interface for XSLT processors.
+ *
+ * @see XSLTProcess
+ * @since Ant 1.1
+ */
+public interface XSLTLiaison {
+
+    /**
+     * the file protocol prefix for systemid.
+     * This file protocol must be appended to an absolute path.
+     * Typically: <tt>FILE_PROTOCOL_PREFIX + file.getAbsolutePath()</tt>
+     * Note that on Windows, an extra '/' must be appended to the
+     * protocol prefix so that there is always 3 consecutive slashes.
+     * @since Ant 1.4
+     */
+    String FILE_PROTOCOL_PREFIX = "file://";
+
+    /**
+     * set the stylesheet to use for the transformation.
+     * @param stylesheet the stylesheet to be used for transformation.
+     * @throws Exception thrown if any problems happens.
+     * @since Ant 1.4
+     */
+    void setStylesheet(File stylesheet) throws Exception;
+
+    /**
+     * Add a parameter to be set during the XSL transformation.
+     * @param name the parameter name.
+     * @param expression the parameter value as an expression string.
+     * @throws Exception thrown if any problems happens.
+     * @since Ant 1.3
+     */
+    void addParam(String name, String expression) throws Exception;
+
+    /**
+     * Perform the transformation of a file into another.
+     * @param infile the input file, probably an XML one. :-)
+     * @param outfile the output file resulting from the transformation
+     * @throws Exception thrown if any problems happens.
+     * @see #setStylesheet(File)
+     * @since Ant 1.4
+     */
+    void transform(File infile, File outfile) throws Exception;
+
+} //-- XSLTLiaison
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison2.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison2.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XSLTLiaison2.java	(revision 16139)
@@ -0,0 +1,31 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+/**
+ * Extended Proxy interface for XSLT processors.
+ *
+ * @see XSLTProcess
+ * @since Ant 1.6
+ */
+public interface XSLTLiaison2 extends XSLTLiaison {
+    /**
+     * Configure the liasion from the XSLTProcess task
+     */
+    void configure(XSLTProcess xsltTask);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XSLTLogger.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XSLTLogger.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XSLTLogger.java	(revision 16139)
@@ -0,0 +1,28 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+/**
+ * @since Ant 1.5
+ */
+public interface XSLTLogger {
+    /**
+     * Log a message.
+     */
+    void log(String msg);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XSLTLoggerAware.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XSLTLoggerAware.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XSLTLoggerAware.java	(revision 16139)
@@ -0,0 +1,25 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+/**
+ * @since Ant 1.5
+ */
+public interface XSLTLoggerAware {
+    void setLogger(XSLTLogger l);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XSLTProcess.java	(revision 16139)
@@ -0,0 +1,980 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.DynamicConfigurator;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.XMLCatalog;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Processes a set of XML documents via XSLT. This is
+ * useful for building views of XML based documentation.
+ *
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task name="xslt" category="xml"
+ */
+
+public class XSLTProcess extends MatchingTask implements XSLTLogger {
+    /** destination directory */
+    private File destDir = null;
+
+    /** where to find the source XML file, default is the project's basedir */
+    private File baseDir = null;
+
+    /** XSL stylesheet */
+    private String xslFile = null;
+
+    /** extension of the files produced by XSL processing */
+    private String targetExtension = ".html";
+
+    /** additional parameters to be passed to the stylesheets */
+    private Vector params = new Vector();
+
+    /** Input XML document to be used */
+    private File inFile = null;
+
+    /** Output file */
+    private File outFile = null;
+
+    /** The name of the XSL processor to use */
+    private String processor;
+
+    /** Classpath to use when trying to load the XSL processor */
+    private Path classpath = null;
+
+    /** The Liason implementation to use to communicate with the XSL
+     *  processor */
+    private XSLTLiaison liaison;
+
+    /** Flag which indicates if the stylesheet has been loaded into
+     *  the processor */
+    private boolean stylesheetLoaded = false;
+
+    /** force output of target files even if they already exist */
+    private boolean force = false;
+
+    /** Utilities used for file operations */
+    private FileUtils fileUtils;
+
+    /** XSL output properties to be used */
+    private Vector outputProperties = new Vector();
+
+    /** for resolving entities such as dtds */
+    private XMLCatalog xmlCatalog = new XMLCatalog();
+
+    /** Name of the TRAX Liaison class */
+    private static final String TRAX_LIAISON_CLASS =
+                        "org.apache.tools.ant.taskdefs.optional.TraXLiaison";
+
+    /** Name of the now-deprecated XSLP Liaison class */
+    private static final String XSLP_LIAISON_CLASS =
+                        "org.apache.tools.ant.taskdefs.optional.XslpLiaison";
+
+    /** Name of the now-deprecated Xalan liaison class */
+    private static final String XALAN_LIAISON_CLASS =
+                        "org.apache.tools.ant.taskdefs.optional.XalanLiaison";
+
+    /**
+     * Whether to style all files in the included directories as well.
+     *
+     * @since Ant 1.5
+     */
+    private boolean performDirectoryScan = true;
+
+    /**
+     * factory element for TraX processors only
+     * @since Ant 1.6
+     */
+    private Factory factory = null;
+
+    /**
+     * whether to reuse Transformer if transforming multiple files.
+     * @since 1.5.2
+     */
+    private boolean reuseLoadedStylesheet = true;
+
+    /**
+     * AntClassLoader for the nested &lt;classpath&gt; - if set.
+     *
+     * <p>We keep this here in order to reset the context classloader
+     * in execute.  We can't use liaison.getClass().getClassLoader()
+     * since the actual liaison class may have been loaded by a loader
+     * higher up (system classloader, for example).</p>
+     *
+     * @since Ant 1.6.2
+     */
+    private AntClassLoader loader = null;
+
+    /**
+     * Mapper to use when a set of files gets processed.
+     *
+     * @since Ant 1.6.2
+     */
+    private Mapper mapperElement = null;
+
+    /**
+     * Creates a new XSLTProcess Task.
+     */
+    public XSLTProcess() {
+        fileUtils = FileUtils.newFileUtils();
+    } //-- XSLTProcess
+
+    /**
+     * Whether to style all files in the included directories as well;
+     * optional, default is true.
+     *
+     * @param b true if files in included directories are processed.
+     * @since Ant 1.5
+     */
+    public void setScanIncludedDirectories(boolean b) {
+        performDirectoryScan = b;
+    }
+
+    /**
+     * Controls whether the stylesheet is reloaded for every transform.
+     *
+     * <p>Setting this to true may get around a bug in certain
+     * Xalan-J versions, default is false.</p>
+     *
+     * @since Ant 1.5.2
+     */
+    public void setReloadStylesheet(boolean b) {
+        reuseLoadedStylesheet = !b;
+    }
+
+    /**
+     * Defines the mapper to map source to destination files.
+     * @exception BuildException if more than one mapper is defined
+     * @since Ant 1.6.2
+     */
+    public void addMapper(Mapper mapper) {
+        if (mapperElement != null) {
+            throw new BuildException("Cannot define more than one mapper",
+                                     getLocation());
+        }
+        mapperElement = mapper;
+    }
+
+    /**
+     * Executes the task.
+     *
+     * @exception BuildException if there is an execution problem.
+     * @todo validate that if either in or our is defined, then both are
+     */
+    public void execute() throws BuildException {
+        File savedBaseDir = baseDir;
+
+        DirectoryScanner scanner;
+        String[]         list;
+        String[]         dirs;
+
+        if (xslFile == null) {
+            throw new BuildException("no stylesheet specified", getLocation());
+        }
+
+        if (inFile != null && !inFile.exists()) {
+            throw new BuildException("input file " + inFile.toString() + " does not exist", getLocation());
+        }
+
+        try {
+            if (baseDir == null) {
+                baseDir = getProject().resolveFile(".");
+            }
+
+            liaison = getLiaison();
+
+            // check if liaison wants to log errors using us as logger
+            if (liaison instanceof XSLTLoggerAware) {
+                ((XSLTLoggerAware) liaison).setLogger(this);
+            }
+
+            log("Using " + liaison.getClass().toString(), Project.MSG_VERBOSE);
+
+            File stylesheet = getProject().resolveFile(xslFile);
+            if (!stylesheet.exists()) {
+                stylesheet = fileUtils.resolveFile(baseDir, xslFile);
+                /*
+                 * shouldn't throw out deprecation warnings before we know,
+                 * the wrong version has been used.
+                 */
+                if (stylesheet.exists()) {
+                    log("DEPRECATED - the style attribute should be relative "
+                        + "to the project\'s");
+                    log("             basedir, not the tasks\'s basedir.");
+                }
+            }
+
+            // if we have an in file and out then process them
+            if (inFile != null && outFile != null) {
+                process(inFile, outFile, stylesheet);
+                return;
+            }
+
+            /*
+             * if we get here, in and out have not been specified, we are
+             * in batch processing mode.
+             */
+
+            //-- make sure Source directory exists...
+            if (destDir == null) {
+                String msg = "destdir attributes must be set!";
+                throw new BuildException(msg);
+            }
+            scanner = getDirectoryScanner(baseDir);
+            log("Transforming into " + destDir, Project.MSG_INFO);
+
+            // Process all the files marked for styling
+            list = scanner.getIncludedFiles();
+            for (int i = 0; i < list.length; ++i) {
+                process(baseDir, list[i], destDir, stylesheet);
+            }
+            if (performDirectoryScan) {
+                // Process all the directories marked for styling
+                dirs = scanner.getIncludedDirectories();
+                for (int j = 0; j < dirs.length; ++j) {
+                    list = new File(baseDir, dirs[j]).list();
+                    for (int i = 0; i < list.length; ++i) {
+                        process(baseDir, dirs[j] + File.separator + list[i],
+                                destDir, stylesheet);
+                    }
+                }
+            }
+        } finally {
+            if (loader != null) {
+                loader.resetThreadContextLoader();
+                loader = null;
+            }
+            liaison = null;
+            stylesheetLoaded = false;
+            baseDir = savedBaseDir;
+        }
+    }
+
+    /**
+     * Set whether to check dependencies, or always generate;
+     * optional, default is false.
+     *
+     * @param force true if always generate.
+     */
+    public void setForce(boolean force) {
+        this.force = force;
+    }
+
+    /**
+     * Set the base directory;
+     * optional, default is the project's basedir.
+     *
+     * @param dir the base directory
+     **/
+    public void setBasedir(File dir) {
+        baseDir = dir;
+    }
+
+    /**
+     * Set the destination directory into which the XSL result
+     * files should be copied to;
+     * required, unless <tt>in</tt> and <tt>out</tt> are
+     * specified.
+     * @param dir the name of the destination directory
+     **/
+    public void setDestdir(File dir) {
+        destDir = dir;
+    }
+
+    /**
+     * Set the desired file extension to be used for the target;
+     * optional, default is html.
+     * @param name the extension to use
+     **/
+    public void setExtension(String name) {
+        targetExtension = name;
+    }
+
+    /**
+     * Name of the stylesheet to use - given either relative
+     * to the project's basedir or as an absolute path; required.
+     *
+     * @param xslFile the stylesheet to use
+     */
+    public void setStyle(String xslFile) {
+        this.xslFile = xslFile;
+    }
+
+    /**
+     * Set the optional classpath to the XSL processor
+     *
+     * @param classpath the classpath to use when loading the XSL processor
+     */
+    public void setClasspath(Path classpath) {
+        createClasspath().append(classpath);
+    }
+
+    /**
+     * Set the optional classpath to the XSL processor
+     *
+     * @return a path instance to be configured by the Ant core.
+     */
+    public Path createClasspath() {
+        if (classpath == null) {
+            classpath = new Path(getProject());
+        }
+        return classpath.createPath();
+    }
+
+    /**
+     * Set the reference to an optional classpath to the XSL processor
+     *
+     * @param r the id of the Ant path instance to act as the classpath
+     *          for loading the XSL processor
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+
+    /**
+     * Set the name of the XSL processor to use; optional, default trax.
+     * Other values are "xalan" for Xalan1 and "xslp" for XSL:P, though the
+     * later is strongly deprecated.
+     *
+     * @param processor the name of the XSL processor
+     */
+    public void setProcessor(String processor) {
+        this.processor = processor;
+    }
+
+    /**
+     * Add the catalog to our internal catalog
+     *
+     * @param xmlCatalog the XMLCatalog instance to use to look up DTDs
+     */
+    public void addConfiguredXMLCatalog(XMLCatalog xmlCatalog) {
+        this.xmlCatalog.addConfiguredXMLCatalog(xmlCatalog);
+    }
+
+    /**
+     * Load processor here instead of in setProcessor - this will be
+     * called from within execute, so we have access to the latest
+     * classpath.
+     *
+     * @param proc the name of the processor to load.
+     * @exception Exception if the processor cannot be loaded.
+     */
+    private void resolveProcessor(String proc) throws Exception {
+        if (proc.equals("trax")) {
+            final Class clazz = loadClass(TRAX_LIAISON_CLASS);
+            liaison = (XSLTLiaison) clazz.newInstance();
+        } else if (proc.equals("xslp")) {
+            log("DEPRECATED - xslp processor is deprecated. Use trax "
+                + "instead.");
+            final Class clazz = loadClass(XSLP_LIAISON_CLASS);
+            liaison = (XSLTLiaison) clazz.newInstance();
+        } else if (proc.equals("xalan")) {
+            log("DEPRECATED - xalan processor is deprecated. Use trax "
+                + "instead.");
+            final Class clazz = loadClass(XALAN_LIAISON_CLASS);
+            liaison = (XSLTLiaison) clazz.newInstance();
+        } else {
+            liaison = (XSLTLiaison) loadClass(proc).newInstance();
+        }
+    }
+
+    /**
+     * Load named class either via the system classloader or a given
+     * custom classloader.
+     *
+     * @param classname the name of the class to load.
+     * @return the requested class.
+     * @exception Exception if the class could not be loaded.
+     */
+    private Class loadClass(String classname) throws Exception {
+        if (classpath == null) {
+            return Class.forName(classname);
+        } else {
+            loader = getProject().createClassLoader(classpath);
+            loader.setThreadContextLoader();
+            Class c = Class.forName(classname, true, loader);
+            return c;
+        }
+    }
+
+    /**
+     * Specifies the output name for the styled result from the
+     * <tt>in</tt> attribute; required if <tt>in</tt> is set
+     *
+     * @param outFile the output File instance.
+     */
+    public void setOut(File outFile) {
+        this.outFile = outFile;
+    }
+
+    /**
+     * specifies a single XML document to be styled. Should be used
+     * with the <tt>out</tt> attribute; ; required if <tt>out</tt> is set
+     *
+     * @param inFile the input file
+     */
+    public void setIn(File inFile) {
+        this.inFile = inFile;
+    }
+
+    /**
+     * Processes the given input XML file and stores the result
+     * in the given resultFile.
+     *
+     * @param baseDir the base directory for resolving files.
+     * @param xmlFile the input file
+     * @param destDir the destination directory
+     * @param stylesheet the stylesheet to use.
+     * @exception BuildException if the processing fails.
+     */
+    private void process(File baseDir, String xmlFile, File destDir,
+                         File stylesheet)
+        throws BuildException {
+
+        File   outFile = null;
+        File   inFile = null;
+
+        try {
+            long styleSheetLastModified = stylesheet.lastModified();
+            inFile = new File(baseDir, xmlFile);
+
+            if (inFile.isDirectory()) {
+                log("Skipping " + inFile + " it is a directory.",
+                    Project.MSG_VERBOSE);
+                return;
+            }
+
+            FileNameMapper mapper = null;
+            if (mapperElement != null) {
+                mapper = mapperElement.getImplementation();
+            } else {
+                mapper = new StyleMapper();
+            }
+
+            String[] outFileName = mapper.mapFileName(xmlFile);
+            if (outFileName == null || outFileName.length == 0) {
+                log("Skipping " + inFile + " it cannot get mapped to output.",
+                    Project.MSG_VERBOSE);
+                return;
+            } else if (outFileName == null || outFileName.length > 1) {
+                log("Skipping " + inFile + " its mapping is ambiguos.",
+                    Project.MSG_VERBOSE);
+                return;
+            }
+
+            outFile = new File(destDir, outFileName[0]);
+
+            if (force
+                || inFile.lastModified() > outFile.lastModified()
+                || styleSheetLastModified > outFile.lastModified()) {
+                ensureDirectoryFor(outFile);
+                log("Processing " + inFile + " to " + outFile);
+
+                configureLiaison(stylesheet);
+                liaison.transform(inFile, outFile);
+            }
+        } catch (Exception ex) {
+            // If failed to process document, must delete target document,
+            // or it will not attempt to process it the second time
+            log("Failed to process " + inFile, Project.MSG_INFO);
+            if (outFile != null) {
+                outFile.delete();
+            }
+
+            throw new BuildException(ex);
+        }
+
+    } //-- processXML
+
+    /**
+     * Process the input file to the output file with the given stylesheet.
+     *
+     * @param inFile the input file to process.
+     * @param outFile the destination file.
+     * @param stylesheet the stylesheet to use.
+     * @exception BuildException if the processing fails.
+     */
+    private void process(File inFile, File outFile, File stylesheet)
+         throws BuildException {
+        try {
+            long styleSheetLastModified = stylesheet.lastModified();
+            log("In file " + inFile + " time: " + inFile.lastModified(),
+                Project.MSG_DEBUG);
+            log("Out file " + outFile + " time: " + outFile.lastModified(),
+                Project.MSG_DEBUG);
+            log("Style file " + xslFile + " time: " + styleSheetLastModified,
+                Project.MSG_DEBUG);
+            if (force || inFile.lastModified() >= outFile.lastModified()
+                || styleSheetLastModified >= outFile.lastModified()) {
+                ensureDirectoryFor(outFile);
+                log("Processing " + inFile + " to " + outFile,
+                    Project.MSG_INFO);
+                configureLiaison(stylesheet);
+                liaison.transform(inFile, outFile);
+            } else {
+                log("Skipping input file " + inFile
+                    + " because it is older than output file " + outFile
+                    + " and so is the stylesheet " + stylesheet, Project.MSG_DEBUG);
+            }
+        } catch (Exception ex) {
+            log("Failed to process " + inFile, Project.MSG_INFO);
+            if (outFile != null) {
+                outFile.delete();
+            }
+            throw new BuildException(ex);
+        }
+    }
+
+    /**
+     * Ensure the directory exists for a given file
+     *
+     * @param targetFile the file for which the directories are required.
+     * @exception BuildException if the directories cannot be created.
+     */
+    private void ensureDirectoryFor(File targetFile)
+         throws BuildException {
+        File directory = fileUtils.getParentFile(targetFile);
+        if (!directory.exists()) {
+            if (!directory.mkdirs()) {
+                throw new BuildException("Unable to create directory: "
+                                         + directory.getAbsolutePath());
+            }
+        }
+    }
+
+    /**
+     * Get the factory instance configured for this processor
+     *
+     * @return the factory instance in use
+     */
+    public Factory getFactory() {
+        return factory;
+    }
+
+    /**
+     * Get the XML catalog containing entity definitions
+     *
+     * @return the XML catalog for the task.
+     */
+    public XMLCatalog getXMLCatalog() {
+        return xmlCatalog;
+    }
+
+    public Enumeration getOutputProperties() {
+        return outputProperties.elements();
+    }
+
+
+    /**
+     * Get the Liason implementation to use in processing.
+     *
+     * @return an instance of the XSLTLiason interface.
+     */
+    protected XSLTLiaison getLiaison() {
+        // if processor wasn't specified, see if TraX is available.  If not,
+        // default it to xslp or xalan, depending on which is in the classpath
+        if (liaison == null) {
+            if (processor != null) {
+                try {
+                    resolveProcessor(processor);
+                } catch (Exception e) {
+                    throw new BuildException(e);
+                }
+            } else {
+                try {
+                    resolveProcessor("trax");
+                } catch (Throwable e1) {
+                    try {
+                        resolveProcessor("xalan");
+                    } catch (Throwable e2) {
+                        try {
+                            resolveProcessor("xslp");
+                        } catch (Throwable e3) {
+                            e3.printStackTrace();
+                            e2.printStackTrace();
+                            throw new BuildException(e1);
+                        }
+                    }
+                }
+            }
+        }
+        return liaison;
+    }
+
+    /**
+     * Create an instance of an XSL parameter for configuration by Ant.
+     *
+     * @return an instance of the Param class to be configured.
+     */
+    public Param createParam() {
+        Param p = new Param();
+        params.addElement(p);
+        return p;
+    }
+
+    /**
+     * The Param inner class used to store XSL parameters
+     */
+    public static class Param {
+        /** The parameter name */
+        private String name = null;
+
+        /** The parameter's value */
+        private String expression = null;
+
+        private String ifProperty;
+        private String unlessProperty;
+        private Project project;
+
+        /**
+         * Set the current project
+         *
+         * @param project the current project
+         */
+        public void setProject(Project project) {
+            this.project = project;
+        }
+
+        /**
+         * Set the parameter name.
+         *
+         * @param name the name of the parameter.
+         */
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        /**
+         * The parameter value
+         * NOTE : was intended to be an XSL expression.
+         * @param expression the parameter's value.
+         */
+        public void setExpression(String expression) {
+            this.expression = expression;
+        }
+
+        /**
+         * Get the parameter name
+         *
+         * @return the parameter name
+         * @exception BuildException if the name is not set.
+         */
+        public String getName() throws BuildException {
+            if (name == null) {
+                throw new BuildException("Name attribute is missing.");
+            }
+            return name;
+        }
+
+        /**
+         * Get the parameter's value
+         *
+         * @return the parameter value
+         * @exception BuildException if the value is not set.
+         */
+        public String getExpression() throws BuildException {
+            if (expression == null) {
+                throw new BuildException("Expression attribute is missing.");
+            }
+            return expression;
+        }
+
+        /**
+         * Set whether this param should be used.  It will be
+         * used if the property has been set, otherwise it won't.
+         * @param ifProperty name of property
+         */
+        public void setIf(String ifProperty) {
+            this.ifProperty = ifProperty;
+        }
+
+        /**
+         * Set whether this param should NOT be used. It
+         * will not be used if the property has been set, otherwise it
+         * will be used.
+         * @param unlessProperty name of property
+         */
+        public void setUnless(String unlessProperty) {
+            this.unlessProperty = unlessProperty;
+        }
+        /**
+         * Ensures that the param passes the conditions placed
+         * on it with <code>if</code> and <code>unless</code> properties.
+         */
+        public boolean shouldUse() {
+            if (ifProperty != null && project.getProperty(ifProperty) == null) {
+                return false;
+            } else if (unlessProperty != null
+                    && project.getProperty(unlessProperty) != null) {
+                return false;
+            }
+
+            return true;
+        }
+    } // Param
+
+
+    /**
+     * Create an instance of an output property to be configured.
+     * @return the newly created output property.
+     * @since Ant 1.5
+     */
+    public OutputProperty createOutputProperty() {
+        OutputProperty p = new OutputProperty();
+        outputProperties.addElement(p);
+        return p;
+    }
+
+
+    /**
+     * Specify how the result tree should be output as specified
+     * in the <a href="http://www.w3.org/TR/xslt#output">
+     * specification</a>.
+     * @since Ant 1.5
+     */
+    public static class OutputProperty {
+        /** output property name */
+        private String name;
+
+        /** output property value */
+        private String value;
+
+        /**
+         * @return the output property name.
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * set the name for this property
+         * @param name A non-null String that specifies an
+         * output property name, which may be namespace qualified.
+         */
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        /**
+         * @return the output property value.
+         */
+        public String getValue() {
+            return value;
+        }
+
+        /**
+         * set the value for this property
+         * @param value The non-null string value of the output property.
+         */
+        public void setValue(String value) {
+            this.value = value;
+        }
+    }
+
+    /**
+     * Initialize internal instance of XMLCatalog
+     */
+    public void init() throws BuildException {
+        super.init();
+        xmlCatalog.setProject(getProject());
+    }
+
+    /**
+     * Loads the stylesheet and set xsl:param parameters.
+     *
+     * @param stylesheet the file form which to load the stylesheet.
+     * @exception BuildException if the stylesheet cannot be loaded.
+     */
+    protected void configureLiaison(File stylesheet) throws BuildException {
+        if (stylesheetLoaded && reuseLoadedStylesheet) {
+            return;
+        }
+        stylesheetLoaded = true;
+
+        try {
+            log("Loading stylesheet " + stylesheet, Project.MSG_INFO);
+            liaison.setStylesheet(stylesheet);
+            for (Enumeration e = params.elements(); e.hasMoreElements();) {
+                Param p = (Param) e.nextElement();
+                if (p.shouldUse()) {
+                    liaison.addParam(p.getName(), p.getExpression());
+                }
+            }
+            if (liaison instanceof XSLTLiaison2) {
+                ((XSLTLiaison2) liaison).configure(this);
+            }
+        } catch (Exception ex) {
+            log("Failed to transform using stylesheet " + stylesheet,
+                 Project.MSG_INFO);
+            throw new BuildException(ex);
+        }
+    }
+
+    /**
+     * Create the factory element to configure a trax liaison.
+     * @return the newly created factory element.
+     * @throws BuildException if the element is created more than one time.
+     */
+    public Factory createFactory() throws BuildException {
+        if (factory != null) {
+            throw new BuildException("'factory' element must be unique");
+        }
+        factory = new Factory();
+        return factory;
+    }
+
+    /**
+     * The factory element to configure a transformer factory
+     * @since Ant 1.6
+     */
+    public static class Factory {
+
+        /** the factory class name to use for TraXLiaison */
+        private String name;
+
+        /**
+         * the list of factory attributes to use for TraXLiaison
+         */
+        private Vector attributes = new Vector();
+
+        /**
+         * @return the name of the factory.
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * Set the name of the factory
+         * @param name the name of the factory.
+         */
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        /**
+         * Create an instance of a factory attribute.
+         * the newly created factory attribute
+         */
+        public void addAttribute(Attribute attr) {
+            attributes.addElement(attr);
+        }
+
+        /**
+         * return the attribute elements.
+         * @return the enumeration of attributes
+         */
+        public Enumeration getAttributes() {
+            return attributes.elements();
+        }
+
+        /**
+         * A JAXP factory attribute. This is mostly processor specific, for
+         * example for Xalan 2.3+, the following attributes could be set:
+         * <ul>
+         *  <li>http://xml.apache.org/xalan/features/optimize (true|false) </li>
+         *  <li>http://xml.apache.org/xalan/features/incremental (true|false) </li>
+         * </ul>
+         */
+        public static class Attribute implements DynamicConfigurator {
+
+            /** attribute name, mostly processor specific */
+            private String name;
+
+            /** attribute value, often a boolean string */
+            private Object value;
+
+            /**
+             * @return the attribute name.
+             */
+            public String getName() {
+                return name;
+            }
+
+            /**
+             * @return the output property value.
+             */
+            public Object getValue() {
+                return value;
+            }
+
+            public Object createDynamicElement(String name) throws BuildException {
+                return null;
+            }
+
+            public void setDynamicAttribute(String name, String value)
+                    throws BuildException {
+                // only 'name' and 'value' exist.
+                if ("name".equalsIgnoreCase(name)) {
+                    this.name = value;
+                } else if ("value".equalsIgnoreCase(name)) {
+                    // a value must be of a given type
+                    // say boolean|integer|string that are mostly used.
+                    if ("true".equalsIgnoreCase(value)
+                            || "false".equalsIgnoreCase(value)) {
+                        this.value = new Boolean(value);
+                    } else {
+                        try {
+                            this.value = new Integer(value);
+                        } catch (NumberFormatException e) {
+                            this.value = value;
+                        }
+                    }
+                } else {
+                    throw new BuildException("Unsupported attribute: " + name);
+                }
+            }
+        } // -- class Attribute
+
+    } // -- class Factory
+
+    /**
+     * Mapper implementation of the "traditional" way &lt;xslt&gt;
+     * mapped filenames.
+     *
+     * <p>If the file has an extension, chop it off.  Append whatever
+     * the user has specified as extension or ".html".</p>
+     *
+     * @since Ant 1.6.2
+     */
+    private class StyleMapper implements FileNameMapper {
+        public void setFrom(String from) {}
+        public void setTo(String to) {}
+        public String[] mapFileName(String xmlFile) {
+            int dotPos = xmlFile.lastIndexOf('.');
+            if (dotPos > 0) {
+                xmlFile = xmlFile.substring(0, dotPos);
+            }
+            return new String[] {xmlFile + targetExtension};
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XmlProperty.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XmlProperty.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/XmlProperty.java	(revision 16139)
@@ -0,0 +1,692 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Hashtable;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.ParserConfigurationException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.XMLCatalog;
+import org.apache.tools.ant.util.FileUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+import org.xml.sax.EntityResolver;
+
+/**
+ * Loads property values from a valid XML file, generating the
+ * property names from the file's element and attribute names.
+ *
+ * <p>Example:</p>
+ * <pre>
+ *   &lt;root-tag myattr="true"&gt;
+ *     &lt;inner-tag someattr="val"&gt;Text&lt;/inner-tag&gt;
+ *     &lt;a2&gt;&lt;a3&gt;&lt;a4&gt;false&lt;/a4&gt;&lt;/a3&gt;&lt;/a2&gt;
+ *     &lt;x&gt;x1&lt;/x&gt;
+ *     &lt;x&gt;x2&lt;/x&gt;
+ *   &lt;/root-tag&gt;
+ *</pre>
+ *
+ * <p>this generates the following properties:</p>
+ *
+ * <pre>
+ *  root-tag(myattr)=true
+ *  root-tag.inner-tag=Text
+ *  root-tag.inner-tag(someattr)=val
+ *  root-tag.a2.a3.a4=false
+ *  root-tag.x=x1,x2
+ * </pre>
+ *
+ * <p>The <i>collapseAttributes</i> property of this task can be set
+ * to true (the default is false) which will instead result in the
+ * following properties (note the difference in names of properties
+ * corresponding to XML attributes):</p>
+ *
+ * <pre>
+ *  root-tag.myattr=true
+ *  root-tag.inner-tag=Text
+ *  root-tag.inner-tag.someattr=val
+ *  root-tag.a2.a3.a4=false
+ *  root-tag.x=x1,x2
+ * </pre>
+ *
+ * <p>Optionally, to more closely mirror the abilities of the Property
+ * task, a selected set of attributes can be treated specially.  To
+ * enable this behavior, the "semanticAttributes" property of this task
+ * must be set to true (it defaults to false).  If this attribute is
+ * specified, the following attributes take on special meaning
+ * (setting this to true implicitly sets collapseAttributes to true as
+ * well):</p>
+ *
+ * <ul>
+ *  <li><b>value</b>: Identifies a text value for a property.</li>
+ *  <li><b>location</b>: Identifies a file location for a property.</li>
+ *  <li><b>id</b>: Sets an id for a property</li>
+ *  <li><b>refid</b>: Sets a property to the value of another property
+ *       based upon the provided id</li>
+ *  <li><b>pathid</b>: Defines a path rather than a property with
+ *       the given id.</li>
+ * </ul>
+ *
+ * <p>For example, with keepRoot = false, the following properties file:</p>
+ *
+ * <pre>
+ * &lt;root-tag&gt;
+ *   &lt;build&gt;
+ *   &lt;build folder="build"&gt;
+ *     &lt;classes id="build.classes" location="${build.folder}/classes"/&gt;
+ *     &lt;reference refid="build.classes"/&gt;
+ *   &lt;/build&gt;
+ *   &lt;compile&gt;
+ *     &lt;classpath pathid="compile.classpath"&gt;
+ *       &lt;pathelement location="${build.classes}"/&gt;
+ *     &lt;/classpath&gt;
+ *   &lt;/compile&gt;
+ *   &lt;run-time&gt;
+ *     &lt;jars&gt;*.jar&lt;/jars&gt;
+ *     &lt;classpath pathid="run-time.classpath"&gt;
+ *       &lt;path refid="compile.classpath"/&gt;
+ *       &lt;pathelement path="${run-time.jars}"/&gt;
+ *     &lt;/classpath&gt;
+ *   &lt;/run-time&gt;
+ * &lt;/root-tag&gt;
+ * </pre>
+ *
+ * <p>is equivalent to the following entries in a build file:</p>
+ *
+ * <pre>
+ * &lt;property name="build" location="build"/&gt;
+ * &lt;property name="build.classes" location="${build.location}/classes"/&gt;
+ * &lt;property name="build.reference" refid="build.classes"/&gt;
+ *
+ * &lt;property name="run-time.jars" value="*.jar/&gt;
+ *
+ * &lt;classpath id="compile.classpath"&gt;
+ *   &lt;pathelement location="${build.classes}"/&gt;
+ * &lt;/classpath&gt;
+ *
+ * &lt;classpath id="run-time.classpath"&gt;
+ *   &lt;path refid="compile.classpath"/&gt;
+ *   &lt;pathelement path="${run-time.jars}"/&gt;
+ * &lt;/classpath&gt;
+ * </pre>
+ *
+ * <p> This task <i>requires</i> the following attributes:</p>
+ *
+ * <ul>
+ * <li><b>file</b>: The name of the file to load.</li>
+ * </ul>
+ *
+ * <p>This task supports the following attributes:</p>
+ *
+ * <ul>
+ * <li><b>prefix</b>: Optionally specify a prefix applied to
+ *     all properties loaded.  Defaults to an empty string.</li>
+ * <li><b>keepRoot</b>: Indicate whether the root xml element
+ *     is kept as part of property name.  Defaults to true.</li>
+ * <li><b>validate</b>: Indicate whether the xml file is validated.
+ *     Defaults to false.</li>
+ * <li><b>collapseAttributes</b>: Indicate whether attributes are
+ *     stored in property names with parens or with period
+ *     delimiters.  Defaults to false, meaning properties
+ *     are stored with parens (i.e., foo(attr)).</li>
+ * <li><b>semanticAttributes</b>: Indicate whether attributes
+ *     named "location", "value", "refid" and "path"
+ *     are interpreted as ant properties.  Defaults
+ *     to false.</li>
+ * <li><b>rootDirectory</b>: Indicate the directory to use
+ *     as the root directory for resolving location
+ *     properties.  Defaults to the directory
+ *     of the project using the task.</li>
+ * <li><b>includeSemanticAttribute</b>: Indicate whether to include
+ *     the semantic attribute ("location" or "value") as
+ *     part of the property name.  Defaults to false.</li>
+ * </ul>
+ *
+ * @ant.task name="xmlproperty" category="xml"
+ */
+
+public class XmlProperty extends org.apache.tools.ant.Task {
+
+    private File src;
+    private String prefix = "";
+    private boolean keepRoot = true;
+    private boolean validate = false;
+    private boolean collapseAttributes = false;
+    private boolean semanticAttributes = false;
+    private boolean includeSemanticAttribute = false;
+    private File rootDirectory = null;
+    private FileUtils fileUtils = FileUtils.newFileUtils();
+    private Hashtable addedAttributes = new Hashtable();
+    private XMLCatalog xmlCatalog = new XMLCatalog();
+
+    private static final String ID = "id";
+    private static final String REF_ID = "refid";
+    private static final String LOCATION = "location";
+    private static final String VALUE = "value";
+    private static final String PATH = "path";
+    private static final String PATHID = "pathid";
+    private static final String[] ATTRIBUTES = new String[] {
+        ID, REF_ID, LOCATION, VALUE, PATH, PATHID
+    };
+
+    /**
+     * Constructor.
+     */
+    public XmlProperty() {
+        super();
+    }
+
+    /**
+     * Initializes the task.
+     */
+
+    public void init() {
+        super.init();
+        xmlCatalog.setProject(getProject());
+    }
+
+
+    /**
+     * @return the xmlCatalog as the entityresolver.
+     */
+    protected EntityResolver getEntityResolver() {
+        return xmlCatalog;
+    }
+
+    /**
+     * Run the task.
+     * @throws BuildException The exception raised during task execution.
+     * @todo validate the source file is valid before opening, print a better error message
+     * @todo add a verbose level log message listing the name of the file being loaded
+     */
+    public void execute()
+            throws BuildException {
+
+        if (getFile() == null) {
+            String msg = "XmlProperty task requires a file attribute";
+            throw new BuildException(msg);
+        }
+
+        try {
+            log("Loading " + src.getAbsolutePath(), Project.MSG_VERBOSE);
+
+            if (src.exists()) {
+
+              DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+              factory.setValidating(validate);
+              factory.setNamespaceAware(false);
+              DocumentBuilder builder = factory.newDocumentBuilder();
+              builder.setEntityResolver(getEntityResolver());
+              Document document = builder.parse(src);
+              Element topElement = document.getDocumentElement();
+
+              // Keep a hashtable of attributes added by this task.
+              // This task is allow to override its own properties
+              // but not other properties.  So we need to keep track
+              // of which properties we've added.
+              addedAttributes = new Hashtable();
+
+              if (keepRoot) {
+                  addNodeRecursively(topElement, prefix, null);
+              } else {
+                  NodeList topChildren = topElement.getChildNodes();
+                  int numChildren = topChildren.getLength();
+                  for (int i = 0; i < numChildren; i++) {
+                    addNodeRecursively(topChildren.item(i), prefix, null);
+                  }
+              }
+
+            } else {
+                log("Unable to find property file: " + src.getAbsolutePath(),
+                    Project.MSG_VERBOSE);
+            }
+
+        } catch (SAXException sxe) {
+            // Error generated during parsing
+            Exception x = sxe;
+            if (sxe.getException() != null) {
+                x = sxe.getException();
+            }
+            throw new BuildException(x);
+
+        } catch (ParserConfigurationException pce) {
+            // Parser with specified options can't be built
+            throw new BuildException(pce);
+        } catch (IOException ioe) {
+            // I/O error
+            throw new BuildException(ioe);
+        }
+    }
+
+    /** Iterate through all nodes in the tree. */
+    private void addNodeRecursively(Node node, String prefix,
+                                    Object container) {
+
+        // Set the prefix for this node to include its tag name.
+        String nodePrefix = prefix;
+        if (node.getNodeType() != Node.TEXT_NODE) {
+            if (prefix.trim().length() > 0) {
+                nodePrefix += ".";
+            }
+            nodePrefix += node.getNodeName();
+        }
+
+        // Pass the container to the processing of this node,
+        Object nodeObject = processNode(node, nodePrefix, container);
+
+        // now, iterate through children.
+        if (node.hasChildNodes()) {
+
+            NodeList nodeChildren = node.getChildNodes();
+            int numChildren = nodeChildren.getLength();
+
+            for (int i = 0; i < numChildren; i++) {
+                // For each child, pass the object added by
+                // processNode to its children -- in other word, each
+                // object can pass information along to its children.
+                addNodeRecursively(nodeChildren.item(i), nodePrefix,
+                                   nodeObject);
+            }
+        }
+    }
+
+    void addNodeRecursively(org.w3c.dom.Node node, String prefix) {
+        addNodeRecursively(node, prefix, null);
+    }
+
+    /**
+     * Process the given node, adding any required attributes from
+     * this child node alone -- but <em>not</em> processing any
+     * children.
+     *
+     * @param node the XML Node to parse
+     * @param prefix A string to prepend to any properties that get
+     * added by this node.
+     * @param container Optionally, an object that a parent node
+     * generated that this node might belong to.  For example, this
+     * node could be within a node that generated a Path.
+     * @return the Object created by this node.  Generally, this is
+     * either a String if this node resulted in setting an attribute,
+     * or a Path.
+     */
+    public Object processNode (Node node, String prefix, Object container) {
+
+        // Parse the attribute(s) and text of this node, adding
+        // properties for each.
+        // if the "path" attribute is specified, then return the created path
+        // which will be passed to the children of this node.
+        Object addedPath = null;
+
+        // The value of an id attribute of this node.
+        String id = null;
+
+        if (node.hasAttributes()) {
+
+            NamedNodeMap nodeAttributes = node.getAttributes();
+
+            // Is there an id attribute?
+            Node idNode = nodeAttributes.getNamedItem(ID);
+            id = (semanticAttributes && idNode != null
+                  ? idNode.getNodeValue() : null);
+
+            // Now, iterate through the attributes adding them.
+            for (int i = 0; i < nodeAttributes.getLength(); i++) {
+
+                Node attributeNode = nodeAttributes.item(i);
+
+                if (!semanticAttributes) {
+                    String attributeName = getAttributeName(attributeNode);
+                    String attributeValue = getAttributeValue(attributeNode);
+                    addProperty(prefix + attributeName, attributeValue, null);
+                } else {
+
+                    String nodeName = attributeNode.getNodeName();
+                    String attributeValue = getAttributeValue(attributeNode);
+
+                    Path containingPath = (container != null
+                        && container instanceof Path ? (Path) container : null);
+
+                    /*
+                     * The main conditional logic -- if the attribute
+                     * is somehow "special" (i.e., it has known
+                     * semantic meaning) then deal with it
+                     * appropriately.
+                     */
+                    if (nodeName.equals(ID)) {
+                        // ID has already been found above.
+                        continue;
+                    } else if (containingPath != null
+                               && nodeName.equals(PATH)) {
+                        // A "path" attribute for a node within a Path object.
+                        containingPath.setPath(attributeValue);
+                    } else if (container instanceof Path
+                               && nodeName.equals(REF_ID)) {
+                        // A "refid" attribute for a node within a Path object.
+                        containingPath.setPath(attributeValue);
+                    } else if (container instanceof Path
+                               && nodeName.equals(LOCATION)) {
+                        // A "location" attribute for a node within a
+                        // Path object.
+                        containingPath.setLocation(resolveFile(attributeValue));
+                    } else if (nodeName.equals(PATHID)) {
+                        // A node identifying a new path
+                        if (container != null) {
+                            throw new BuildException("XmlProperty does not "
+                                                     + "support nested paths");
+                        }
+
+                        addedPath = new Path(getProject());
+                        getProject().addReference(attributeValue, addedPath);
+                    } else {
+                        // An arbitrary attribute.
+                        String attributeName = getAttributeName(attributeNode);
+                        addProperty(prefix + attributeName, attributeValue, id);
+                    }
+                }
+            }
+        }
+
+        String nodeText = null;
+        if (node.getNodeType() == Node.TEXT_NODE) {
+            // For the text node, add a property.
+            nodeText = getAttributeValue(node);
+        } else if ((node.getNodeType() == Node.ELEMENT_NODE)
+            && (node.getChildNodes().getLength() == 1)
+            && (node.getFirstChild().getNodeType() == Node.CDATA_SECTION_NODE)) {
+
+            nodeText = node.getFirstChild().getNodeValue();
+        }
+
+        if (nodeText != null) {
+            // If the containing object was a String, then use it as the ID.
+            if (semanticAttributes && id == null
+                && container instanceof String) {
+                id = (String) container;
+            }
+
+            if (nodeText.trim().length() != 0) {
+                addProperty(prefix, nodeText, id);
+            }
+        }
+
+        // Return the Path we added or the ID of this node for
+        // children to reference if needed.  Path objects are
+        // definitely used by child path elements, and ID may be used
+        // for a child text node.
+        return (addedPath != null ? addedPath : id);
+    }
+
+    /**
+     * Actually add the given property/value to the project
+     * after writing a log message.
+     */
+    private void addProperty (String name, String value, String id) {
+        String msg = name + ":" + value;
+        if (id != null) {
+            msg += ("(id=" + id + ")");
+        }
+        log(msg, Project.MSG_DEBUG);
+
+        if (addedAttributes.containsKey(name)) {
+            // If this attribute was added by this task, then
+            // we append this value to the existing value.
+            // We use the setProperty method which will
+            // forcibly override the property if it already exists.
+            // We need to put these properties into the project
+            // when we read them, though (instead of keeping them
+            // outside of the project and batch adding them at the end)
+            // to allow other properties to reference them.
+            value = (String) addedAttributes.get(name) + "," + value;
+            getProject().setProperty(name, value);
+        } else {
+            getProject().setNewProperty(name, value);
+        }
+        addedAttributes.put(name, value);
+        if (id != null) {
+            getProject().addReference(id, value);
+        }
+    }
+
+    /**
+     * Return a reasonable attribute name for the given node.
+     * If we are using semantic attributes or collapsing
+     * attributes, the returned name is ".nodename".
+     * Otherwise, we return "(nodename)".  This is long-standing
+     * (and default) &lt;xmlproperty&gt; behavior.
+     */
+    private String getAttributeName (Node attributeNode) {
+        String attributeName = attributeNode.getNodeName();
+
+        if (semanticAttributes) {
+            // Never include the "refid" attribute as part of the
+            // attribute name.
+            if (attributeName.equals(REF_ID)) {
+                return "";
+            // Otherwise, return it appended unless property to hide it is set.
+            } else if (!isSemanticAttribute(attributeName)
+                       || includeSemanticAttribute) {
+                return "." + attributeName;
+            } else {
+                return "";
+            }
+        } else if (collapseAttributes) {
+            return "." + attributeName;
+        } else {
+            return "(" + attributeName + ")";
+        }
+    }
+
+    /**
+     * Return whether the provided attribute name is recognized or not.
+     */
+    private static boolean isSemanticAttribute (String attributeName) {
+        for (int i = 0; i < ATTRIBUTES.length; i++) {
+            if (attributeName.equals(ATTRIBUTES[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Return the value for the given attribute.
+     * If we are not using semantic attributes, its just the
+     * literal string value of the attribute.
+     *
+     * <p>If we <em>are</em> using semantic attributes, then first
+     * dependent properties are resolved (i.e., ${foo} is resolved
+     * based on the foo property value), and then an appropriate data
+     * type is used.  In particular, location-based properties are
+     * resolved to absolute file names.  Also for refid values, look
+     * up the referenced object from the project.</p>
+     */
+    private String getAttributeValue (Node attributeNode) {
+        String nodeValue = attributeNode.getNodeValue().trim();
+        if (semanticAttributes) {
+            String attributeName = attributeNode.getNodeName();
+            nodeValue = getProject().replaceProperties(nodeValue);
+            if (attributeName.equals(LOCATION)) {
+                File f = resolveFile(nodeValue);
+                return f.getPath();
+            } else if (attributeName.equals(REF_ID)) {
+                Object ref = getProject().getReference(nodeValue);
+                if (ref != null) {
+                    return ref.toString();
+                }
+            }
+        }
+        return nodeValue;
+    }
+
+    /**
+     * The XML file to parse; required.
+     * @param src the file to parse
+     */
+    public void setFile(File src) {
+        this.src = src;
+    }
+
+    /**
+     * the prefix to prepend to each property
+     * @param prefix the prefix to prepend to each property
+     */
+    public void setPrefix(String prefix) {
+        this.prefix = prefix.trim();
+    }
+
+    /**
+     * flag to include the xml root tag as a
+     * first value in the property name; optional,
+     * default is true
+     * @param keepRoot if true (default), include the xml root tag
+     */
+    public void setKeeproot(boolean keepRoot) {
+        this.keepRoot = keepRoot;
+    }
+
+    /**
+     * flag to validate the XML file; optional, default false
+     * @param validate if true validate the XML file, default false
+     */
+    public void setValidate(boolean validate) {
+        this.validate = validate;
+    }
+
+    /**
+     * flag to treat attributes as nested elements;
+     * optional, default false
+     * @param collapseAttributes if true treat attributes as nested elements
+     */
+    public void setCollapseAttributes(boolean collapseAttributes) {
+        this.collapseAttributes = collapseAttributes;
+    }
+
+    /**
+     * Attribute to enable special handling of attributes - see ant manual.
+     * @param semanticAttributes if true enable the special handling.
+     */
+    public void setSemanticAttributes(boolean semanticAttributes) {
+        this.semanticAttributes = semanticAttributes;
+    }
+
+    /**
+     * The directory to use for resolving file references.
+     * Ignored if semanticAttributes is not set to true.
+     * @param rootDirectory the directory.
+     */
+    public void setRootDirectory(File rootDirectory) {
+        this.rootDirectory = rootDirectory;
+    }
+
+    /**
+     * Include the semantic attribute name as part of the property name.
+     * Ignored if semanticAttributes is not set to true.
+     * @param includeSemanticAttribute if true include the sematic attribute
+     *                                 name.
+     */
+    public void setIncludeSemanticAttribute(boolean includeSemanticAttribute) {
+        this.includeSemanticAttribute = includeSemanticAttribute;
+    }
+
+    /**
+     * add an XMLCatalog as a nested element; optional.
+     * @param catalog the XMLCatalog to use
+     */
+    public void addConfiguredXMLCatalog(XMLCatalog catalog) {
+        xmlCatalog.addConfiguredXMLCatalog(catalog);
+    }
+
+    /* Expose members for extensibility */
+
+    /**
+     * @return the file attribute.
+     */
+    protected File getFile () {
+        return this.src;
+    }
+
+    /**
+     * @return the prefix attribute.
+     */
+    protected String getPrefix () {
+        return this.prefix;
+    }
+
+    /**
+     * @return the keeproot attribute.
+     */
+    protected boolean getKeeproot () {
+        return this.keepRoot;
+    }
+
+    /**
+     * @return the validate attribute.
+     */
+    protected boolean getValidate () {
+        return this.validate;
+    }
+
+    /**
+     * @return the collapse attributes attribute.
+     */
+    protected boolean getCollapseAttributes () {
+        return this.collapseAttributes;
+    }
+
+    /**
+     * @return the semantic attributes attribute.
+     */
+    protected boolean getSemanticAttributes () {
+        return this.semanticAttributes;
+    }
+
+    /**
+     * @return the root directory attribute.
+     */
+    protected File getRootDirectory () {
+        return this.rootDirectory;
+    }
+
+    /**
+     * @return the include semantic attribute.
+     */
+    protected boolean getIncludeSementicAttribute () {
+        return this.includeSemanticAttribute;
+    }
+
+    /**
+     * Let project resolve the file - or do it ourselves if
+     * rootDirectory has been set.
+     */
+    private File resolveFile(String fileName) {
+        if (rootDirectory == null) {
+            return getProject().resolveFile(fileName);
+        }
+        return fileUtils.resolveFile(rootDirectory, fileName);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Zip.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Zip.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/Zip.java	(revision 16139)
@@ -0,0 +1,1331 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Stack;
+import java.util.Vector;
+import java.util.zip.CRC32;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.FileScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ZipFileSet;
+import org.apache.tools.ant.types.ZipScanner;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.GlobPatternMapper;
+import org.apache.tools.ant.util.IdentityMapper;
+import org.apache.tools.ant.util.MergingMapper;
+import org.apache.tools.ant.util.ResourceUtils;
+import org.apache.tools.zip.ZipEntry;
+import org.apache.tools.zip.ZipExtraField;
+import org.apache.tools.zip.ZipFile;
+import org.apache.tools.zip.ZipOutputStream;
+
+/**
+ * Create a Zip file.
+ *
+ * @since Ant 1.1
+ *
+ * @ant.task category="packaging"
+ */
+public class Zip extends MatchingTask {
+
+    protected File zipFile;
+    // use to scan own archive
+    private ZipScanner zs;
+    private File baseDir;
+    protected Hashtable entries = new Hashtable();
+    private Vector groupfilesets = new Vector();
+    private Vector filesetsFromGroupfilesets = new Vector();
+    protected String duplicate = "add";
+    private boolean doCompress = true;
+    private boolean doUpdate = false;
+    // shadow of the above if the value is altered in execute
+    private boolean savedDoUpdate = false;
+    private boolean doFilesonly = false;
+    protected String archiveType = "zip";
+
+    // For directories:
+    private static final long EMPTY_CRC = new CRC32 ().getValue ();
+    protected String emptyBehavior = "skip";
+    private Vector filesets = new Vector ();
+    protected Hashtable addedDirs = new Hashtable();
+    private Vector addedFiles = new Vector();
+
+    protected boolean doubleFilePass = false;
+    protected boolean skipWriting = false;
+
+    private static FileUtils fileUtils = FileUtils.newFileUtils();
+
+    /**
+     * true when we are adding new files into the Zip file, as opposed
+     * to adding back the unchanged files
+     */
+    private boolean addingNewFiles = false;
+
+    /**
+     * Encoding to use for filenames, defaults to the platform's
+     * default encoding.
+     */
+    private String encoding;
+
+    /**
+     * Whether the original compression of entries coming from a ZIP
+     * archive should be kept (for example when updating an archive).
+     *
+     * @since Ant 1.6
+     */
+    private boolean keepCompression = false;
+
+    /**
+     * Whether the file modification times will be rounded up to the
+     * next even number of seconds.
+     *
+     * @since Ant 1.6.2
+     */
+    private boolean roundUp = true;
+
+    /**
+     * Comment for the archive.
+     * @since Ant 1.6.3
+     */
+    private String comment = "";
+
+    /**
+     * This is the name/location of where to
+     * create the .zip file.
+     *
+     * @deprecated Use setDestFile(File) instead.
+     * @ant.attribute ignore="true"
+     */
+    public void setZipfile(File zipFile) {
+        setDestFile(zipFile);
+    }
+
+    /**
+     * This is the name/location of where to
+     * create the file.
+     * @since Ant 1.5
+     * @deprecated Use setDestFile(File) instead
+     * @ant.attribute ignore="true"
+     */
+    public void setFile(File file) {
+        setDestFile(file);
+    }
+
+
+    /**
+     * The file to create; required.
+     * @since Ant 1.5
+     * @param destFile The new destination File
+     */
+    public void setDestFile(File destFile) {
+       this.zipFile = destFile;
+    }
+
+    /**
+     * The file to create.
+     * @since Ant 1.5.2
+     */
+    public File getDestFile() {
+        return zipFile;
+    }
+
+
+    /**
+     * Directory from which to archive files; optional.
+     */
+    public void setBasedir(File baseDir) {
+        this.baseDir = baseDir;
+    }
+
+    /**
+     * Whether we want to compress the files or only store them;
+     * optional, default=true;
+     */
+    public void setCompress(boolean c) {
+        doCompress = c;
+    }
+
+    /**
+     * Whether we want to compress the files or only store them;
+     *
+     * @since Ant 1.5.2
+     */
+    public boolean isCompress() {
+        return doCompress;
+    }
+
+    /**
+     * If true, emulate Sun's jar utility by not adding parent directories;
+     * optional, defaults to false.
+     */
+    public void setFilesonly(boolean f) {
+        doFilesonly = f;
+    }
+
+    /**
+     * If true, updates an existing file, otherwise overwrite
+     * any existing one; optional defaults to false.
+     */
+    public void setUpdate(boolean c) {
+        doUpdate = c;
+        savedDoUpdate = c;
+    }
+
+    /**
+     * Are we updating an existing archive?
+     */
+    public boolean isInUpdateMode() {
+        return doUpdate;
+    }
+
+    /**
+     * Adds a set of files.
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * Adds a set of files that can be
+     * read from an archive and be given a prefix/fullpath.
+     */
+    public void addZipfileset(ZipFileSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * Adds a group of zip files.
+     */
+    public void addZipGroupFileset(FileSet set) {
+        groupfilesets.addElement(set);
+    }
+
+    /**
+     * Sets behavior for when a duplicate file is about to be added -
+     * one of <code>keep</code>, <code>skip</code> or <code>overwrite</code>.
+     * Possible values are: <code>keep</code> (keep both
+     * of the files); <code>skip</code> (keep the first version
+     * of the file found); <code>overwrite</code> overwrite the file
+     * with the new file
+     * Default for zip tasks is <code>keep</code>
+     */
+    public void setDuplicate(Duplicate df) {
+        duplicate = df.getValue();
+    }
+
+    /**
+     * Possible behaviors when there are no matching files for the task:
+     * "fail", "skip", or "create".
+     */
+    public static class WhenEmpty extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[] {"fail", "skip", "create"};
+        }
+    }
+
+    /**
+     * Sets behavior of the task when no files match.
+     * Possible values are: <code>fail</code> (throw an exception
+     * and halt the build); <code>skip</code> (do not create
+     * any archive, but issue a warning); <code>create</code>
+     * (make an archive with no entries).
+     * Default for zip tasks is <code>skip</code>;
+     * for jar tasks, <code>create</code>.
+     */
+    public void setWhenempty(WhenEmpty we) {
+        emptyBehavior = we.getValue();
+    }
+
+    /**
+     * Encoding to use for filenames, defaults to the platform's
+     * default encoding.
+     *
+     * <p>For a list of possible values see <a
+     * href="http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html">http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html</a>.</p>
+     */
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * Encoding to use for filenames.
+     *
+     * @since Ant 1.5.2
+     */
+    public String getEncoding() {
+        return encoding;
+    }
+
+    /**
+     * Whether the original compression of entries coming from a ZIP
+     * archive should be kept (for example when updating an archive).
+     *
+     * @since Ant 1.6
+     */
+    public void setKeepCompression(boolean keep) {
+        keepCompression = keep;
+    }
+    
+    /**
+     * Comment to use for archive.
+     *
+     * @param comment The content of the comment.
+     * @since Ant 1.6.3
+     */
+    public void setComment(String comment) {
+        this.comment = comment;
+    }
+
+    /**
+     * Comment of the archive
+     *
+     * @return Comment of the archive.
+     * @since Ant 1.6.3
+     */
+    public String getComment() {
+        return comment;
+    }
+
+    /**
+     * Whether the file modification times will be rounded up to the
+     * next even number of seconds.
+     *
+     * <p>Zip archives store file modification times with a
+     * granularity of two seconds, so the times will either be rounded
+     * up or down.  If you round down, the archive will always seem
+     * out-of-date when you rerun the task, so the default is to round
+     * up.  Rounding up may lead to a different type of problems like
+     * JSPs inside a web archive that seem to be slightly more recent
+     * than precompiled pages, rendering precompilation useless.</p>
+     *
+     * @since Ant 1.6.2
+     */
+    public void setRoundUp(boolean r) {
+        roundUp = r;
+    }
+
+    /**
+     * validate and build
+     */
+    public void execute() throws BuildException {
+
+        if (doubleFilePass) {
+            skipWriting = true;
+            executeMain();
+            skipWriting = false;
+            executeMain();
+        } else {
+            executeMain();
+        }
+    }
+
+    public void executeMain() throws BuildException {
+
+        if (baseDir == null && filesets.size() == 0
+            && groupfilesets.size() == 0 && "zip".equals(archiveType)) {
+            throw new BuildException("basedir attribute must be set, "
+                                     + "or at least "
+                                     + "one fileset must be given!");
+        }
+
+        if (zipFile == null) {
+            throw new BuildException("You must specify the "
+                                     + archiveType + " file to create!");
+        }
+
+        if (zipFile.exists() && !zipFile.isFile()) {
+            throw new BuildException(zipFile + " is not a file.");
+        }
+
+        if (zipFile.exists() && !zipFile.canWrite()) {
+            throw new BuildException(zipFile + " is read-only.");
+        }
+
+        // Renamed version of original file, if it exists
+        File renamedFile = null;
+        // Whether or not an actual update is required -
+        // we don't need to update if the original file doesn't exist
+
+        addingNewFiles = true;
+        if (doUpdate && !zipFile.exists()) {
+            doUpdate = false;
+            log("ignoring update attribute as " + archiveType
+                + " doesn't exist.", Project.MSG_DEBUG);
+        }
+
+        // Add the files found in groupfileset to fileset
+        for (int i = 0; i < groupfilesets.size(); i++) {
+
+            log("Processing groupfileset ", Project.MSG_VERBOSE);
+            FileSet fs = (FileSet) groupfilesets.elementAt(i);
+            FileScanner scanner = fs.getDirectoryScanner(getProject());
+            String[] files = scanner.getIncludedFiles();
+            File basedir = scanner.getBasedir();
+            for (int j = 0; j < files.length; j++) {
+
+                log("Adding file " + files[j] + " to fileset",
+                    Project.MSG_VERBOSE);
+                ZipFileSet zf = new ZipFileSet();
+                zf.setProject(getProject());
+                zf.setSrc(new File(basedir, files[j]));
+                filesets.addElement(zf);
+                filesetsFromGroupfilesets.addElement(zf);
+            }
+        }
+
+        // collect filesets to pass them to getResourcesToAdd
+        Vector vfss = new Vector();
+        if (baseDir != null) {
+            FileSet fs = (FileSet) getImplicitFileSet().clone();
+            fs.setDir(baseDir);
+            vfss.addElement(fs);
+        }
+        for (int i = 0; i < filesets.size(); i++) {
+            FileSet fs = (FileSet) filesets.elementAt(i);
+            vfss.addElement(fs);
+        }
+
+        FileSet[] fss = new FileSet[vfss.size()];
+        vfss.copyInto(fss);
+        boolean success = false;
+        try {
+            // can also handle empty archives
+            ArchiveState state = getResourcesToAdd(fss, zipFile, false);
+
+            // quick exit if the target is up to date
+            if (!state.isOutOfDate()) {
+                return;
+            }
+
+            if (!zipFile.exists() && state.isWithoutAnyResources()) {
+                createEmptyZip(zipFile);
+                return;
+            }
+            Resource[][] addThem = state.getResourcesToAdd();
+
+            if (doUpdate) {
+                renamedFile =
+                    fileUtils.createTempFile("zip", ".tmp",
+                                             fileUtils.getParentFile(zipFile));
+                renamedFile.deleteOnExit();
+
+                try {
+                    fileUtils.rename(zipFile, renamedFile);
+                } catch (SecurityException e) {
+                    throw new BuildException(
+                        "Not allowed to rename old file ("
+                        + zipFile.getAbsolutePath()
+                        + ") to temporary file");
+                } catch (IOException e) {
+                    throw new BuildException(
+                        "Unable to rename old file ("
+                        + zipFile.getAbsolutePath()
+                        + ") to temporary file");
+                }
+            }
+
+            String action = doUpdate ? "Updating " : "Building ";
+
+            log(action + archiveType + ": " + zipFile.getAbsolutePath());
+
+            ZipOutputStream zOut = null;
+            try {
+
+                if (!skipWriting) {
+                    zOut = new ZipOutputStream(zipFile);
+
+                    zOut.setEncoding(encoding);
+                    if (doCompress) {
+                        zOut.setMethod(ZipOutputStream.DEFLATED);
+                    } else {
+                        zOut.setMethod(ZipOutputStream.STORED);
+                    }
+                }
+                initZipOutputStream(zOut);
+
+                // Add the explicit filesets to the archive.
+                for (int i = 0; i < fss.length; i++) {
+                    if (addThem[i].length != 0) {
+                        addResources(fss[i], addThem[i], zOut);
+                    }
+                }
+
+                if (doUpdate) {
+                    addingNewFiles = false;
+                    ZipFileSet oldFiles = new ZipFileSet();
+                    oldFiles.setProject(getProject());
+                    oldFiles.setSrc(renamedFile);
+                    oldFiles.setDefaultexcludes(false);
+
+                    for (int i = 0; i < addedFiles.size(); i++) {
+                        PatternSet.NameEntry ne = oldFiles.createExclude();
+                        ne.setName((String) addedFiles.elementAt(i));
+                    }
+                    DirectoryScanner ds =
+                        oldFiles.getDirectoryScanner(getProject());
+                    ((ZipScanner) ds).setEncoding(encoding);
+
+                    String[] f = ds.getIncludedFiles();
+                    Resource[] r = new Resource[f.length];
+                    for (int i = 0; i < f.length; i++) {
+                        r[i] = ds.getResource(f[i]);
+                    }
+
+                    if (!doFilesonly) {
+                        String[] d = ds.getIncludedDirectories();
+                        Resource[] dr = new Resource[d.length];
+                        for (int i = 0; i < d.length; i++) {
+                            dr[i] = ds.getResource(d[i]);
+                        }
+                        Resource[] tmp = r;
+                        r = new Resource[tmp.length + dr.length];
+                        System.arraycopy(dr, 0, r, 0, dr.length);
+                        System.arraycopy(tmp, 0, r, dr.length, tmp.length);
+                    }
+                    addResources(oldFiles, r, zOut);
+                }
+                if (zOut != null) {
+                    zOut.setComment(comment);
+                }
+                finalizeZipOutputStream(zOut);
+
+                // If we've been successful on an update, delete the
+                // temporary file
+                if (doUpdate) {
+                    if (!renamedFile.delete()) {
+                        log ("Warning: unable to delete temporary file "
+                            + renamedFile.getName(), Project.MSG_WARN);
+                    }
+                }
+                success = true;
+            } finally {
+                // Close the output stream.
+                try {
+                    if (zOut != null) {
+                        zOut.close();
+                    }
+                } catch (IOException ex) {
+                    // If we're in this finally clause because of an
+                    // exception, we don't really care if there's an
+                    // exception when closing the stream. E.g. if it
+                    // throws "ZIP file must have at least one entry",
+                    // because an exception happened before we added
+                    // any files, then we must swallow this
+                    // exception. Otherwise, the error that's reported
+                    // will be the close() error, which is not the
+                    // real cause of the problem.
+                    if (success) {
+                        throw ex;
+                    }
+                }
+            }
+        } catch (IOException ioe) {
+            String msg = "Problem creating " + archiveType + ": "
+                + ioe.getMessage();
+
+            // delete a bogus ZIP file (but only if it's not the original one)
+            if ((!doUpdate || renamedFile != null) && !zipFile.delete()) {
+                msg += " (and the archive is probably corrupt but I could not "
+                    + "delete it)";
+            }
+
+            if (doUpdate && renamedFile != null) {
+                try {
+                    fileUtils.rename(renamedFile, zipFile);
+                } catch (IOException e) {
+                    msg += " (and I couldn't rename the temporary file "
+                            + renamedFile.getName() + " back)";
+                }
+            }
+
+            throw new BuildException(msg, ioe, getLocation());
+        } finally {
+            cleanUp();
+        }
+    }
+
+    /**
+     * Indicates if the task is adding new files into the archive as opposed to
+     * copying back unchanged files from the backup copy
+     */
+    protected final boolean isAddingNewFiles() {
+        return addingNewFiles;
+    }
+
+    /**
+     * Add the given resources.
+     *
+     * @param fileset may give additional information like fullpath or
+     * permissions.
+     * @param resources the resources to add
+     * @param zOut the stream to write to
+     *
+     * @since Ant 1.5.2
+     */
+    protected final void addResources(FileSet fileset, Resource[] resources,
+                                      ZipOutputStream zOut)
+        throws IOException {
+
+        String prefix = "";
+        String fullpath = "";
+        int dirMode = ZipFileSet.DEFAULT_DIR_MODE;
+        int fileMode = ZipFileSet.DEFAULT_FILE_MODE;
+
+        ZipFileSet zfs = null;
+        if (fileset instanceof ZipFileSet) {
+            zfs = (ZipFileSet) fileset;
+            prefix = zfs.getPrefix(getProject());
+            fullpath = zfs.getFullpath(getProject());
+            dirMode = zfs.getDirMode(getProject());
+            fileMode = zfs.getFileMode(getProject());
+        }
+
+        if (prefix.length() > 0 && fullpath.length() > 0) {
+            throw new BuildException("Both prefix and fullpath attributes must"
+                                     + " not be set on the same fileset.");
+        }
+
+        if (resources.length != 1 && fullpath.length() > 0) {
+            throw new BuildException("fullpath attribute may only be specified"
+                                     + " for filesets that specify a single"
+                                     + " file.");
+        }
+
+        if (prefix.length() > 0) {
+            if (!prefix.endsWith("/") && !prefix.endsWith("\\")) {
+                prefix += "/";
+            }
+            addParentDirs(null, prefix, zOut, "", dirMode);
+        }
+
+        ZipFile zf = null;
+        try {
+            boolean dealingWithFiles = false;
+            File base = null;
+
+            if (zfs == null || zfs.getSrc(getProject()) == null) {
+                dealingWithFiles = true;
+                base = fileset.getDir(getProject());
+            } else {
+                zf = new ZipFile(zfs.getSrc(getProject()), encoding);
+            }
+
+            for (int i = 0; i < resources.length; i++) {
+                String name = null;
+                if (fullpath.length() > 0) {
+                    name = fullpath;
+                } else {
+                    name = resources[i].getName();
+                }
+                name = name.replace(File.separatorChar, '/');
+
+                if ("".equals(name)) {
+                    continue;
+                }
+                if (resources[i].isDirectory() && !name.endsWith("/")) {
+                    name = name + "/";
+                }
+
+                if (!doFilesonly && !dealingWithFiles
+                    && resources[i].isDirectory()
+                    && !zfs.hasDirModeBeenSet()) {
+                    int nextToLastSlash = name.lastIndexOf("/",
+                                                           name.length() - 2);
+                    if (nextToLastSlash != -1) {
+                        addParentDirs(base, name.substring(0,
+                                                           nextToLastSlash + 1),
+                                      zOut, prefix, dirMode);
+                    }
+                    ZipEntry ze = zf.getEntry(resources[i].getName());
+                    addParentDirs(base, name, zOut, prefix, ze.getUnixMode());
+
+                } else {
+                    addParentDirs(base, name, zOut, prefix, dirMode);
+                }
+
+                if (!resources[i].isDirectory() && dealingWithFiles) {
+                    File f = fileUtils.resolveFile(base,
+                                                   resources[i].getName());
+                    zipFile(f, zOut, prefix + name, fileMode);
+                } else if (!resources[i].isDirectory()) {
+                    ZipEntry ze = zf.getEntry(resources[i].getName());
+
+                    if (ze != null) {
+                        boolean oldCompress = doCompress;
+                        if (keepCompression) {
+                            doCompress = (ze.getMethod() == ZipEntry.DEFLATED);
+                        }
+                        try {
+                            zipFile(zf.getInputStream(ze), zOut, prefix + name,
+                                    ze.getTime(), zfs.getSrc(getProject()),
+                                    zfs.hasFileModeBeenSet() ? fileMode
+                                    : ze.getUnixMode());
+                        } finally {
+                            doCompress = oldCompress;
+                        }
+                    }
+                }
+            }
+        } finally {
+            if (zf != null) {
+                zf.close();
+            }
+        }
+    }
+
+    /**
+     * method for subclasses to override
+     */
+    protected void initZipOutputStream(ZipOutputStream zOut)
+        throws IOException, BuildException {
+    }
+
+    /**
+     * method for subclasses to override
+     */
+    protected void finalizeZipOutputStream(ZipOutputStream zOut)
+        throws IOException, BuildException {
+    }
+
+    /**
+     * Create an empty zip file
+     *
+     * @return true for historic reasons
+     */
+    protected boolean createEmptyZip(File zipFile) throws BuildException {
+        // In this case using java.util.zip will not work
+        // because it does not permit a zero-entry archive.
+        // Must create it manually.
+        log("Note: creating empty " + archiveType + " archive " + zipFile,
+            Project.MSG_INFO);
+        OutputStream os = null;
+        try {
+            os = new FileOutputStream(zipFile);
+            // Cf. PKZIP specification.
+            byte[] empty = new byte[22];
+            empty[0] = 80; // P
+            empty[1] = 75; // K
+            empty[2] = 5;
+            empty[3] = 6;
+            // remainder zeros
+            os.write(empty);
+        } catch (IOException ioe) {
+            throw new BuildException("Could not create empty ZIP archive "
+                                     + "(" + ioe.getMessage() + ")", ioe,
+                                     getLocation());
+        } finally {
+            if (os != null) {
+                try {
+                    os.close();
+                } catch (IOException e) {
+                    //ignore
+                }
+            }
+        }
+        return true;
+    }
+
+    /**
+     * @since Ant 1.5.2
+     */
+    private synchronized ZipScanner getZipScanner() {
+        if (zs == null) {
+            zs = new ZipScanner();
+            zs.setEncoding(encoding);
+            zs.setSrc(zipFile);
+        }
+        return zs;
+    }
+
+    /**
+     * Collect the resources that are newer than the corresponding
+     * entries (or missing) in the original archive.
+     *
+     * <p>If we are going to recreate the archive instead of updating
+     * it, all resources should be considered as new, if a single one
+     * is.  Because of this, subclasses overriding this method must
+     * call <code>super.getResourcesToAdd</code> and indicate with the
+     * third arg if they already know that the archive is
+     * out-of-date.</p>
+     *
+     * @param filesets The filesets to grab resources from
+     * @param zipFile intended archive file (may or may not exist)
+     * @param needsUpdate whether we already know that the archive is
+     * out-of-date.  Subclasses overriding this method are supposed to
+     * set this value correctly in their call to
+     * super.getResourcesToAdd.
+     * @return an array of resources to add for each fileset passed in as well
+     *         as a flag that indicates whether the archive is uptodate.
+     *
+     * @exception BuildException if it likes
+     */
+    protected ArchiveState getResourcesToAdd(FileSet[] filesets,
+                                             File zipFile,
+                                             boolean needsUpdate)
+        throws BuildException {
+
+        Resource[][] initialResources = grabResources(filesets);
+        if (isEmpty(initialResources)) {
+            if (needsUpdate && doUpdate) {
+                /*
+                 * This is a rather hairy case.
+                 *
+                 * One of our subclasses knows that we need to update the
+                 * archive, but at the same time, there are no resources
+                 * known to us that would need to be added.  Only the
+                 * subclass seems to know what's going on.
+                 *
+                 * This happens if <jar> detects that the manifest has changed,
+                 * for example.  The manifest is not part of any resources
+                 * because of our support for inline <manifest>s.
+                 *
+                 * If we invoke createEmptyZip like Ant 1.5.2 did,
+                 * we'll loose all stuff that has been in the original
+                 * archive (bugzilla report 17780).
+                 */
+                return new ArchiveState(true, initialResources);
+            }
+
+            if (emptyBehavior.equals("skip")) {
+                if (doUpdate) {
+                    log(archiveType + " archive " + zipFile
+                        + " not updated because no new files were included.",
+                        Project.MSG_VERBOSE);
+                } else {
+                    log("Warning: skipping " + archiveType + " archive "
+                        + zipFile + " because no files were included.",
+                        Project.MSG_WARN);
+                }
+            } else if (emptyBehavior.equals("fail")) {
+                throw new BuildException("Cannot create " + archiveType
+                                         + " archive " + zipFile
+                                         + ": no files were included.",
+                                         getLocation());
+            } else {
+                // Create.
+                if (!zipFile.exists())  {
+                    needsUpdate = true;
+                }
+            }
+            return new ArchiveState(needsUpdate, initialResources);
+        }
+
+        // initialResources is not empty
+
+        if (!zipFile.exists()) {
+            return new ArchiveState(true, initialResources);
+        }
+
+        if (needsUpdate && !doUpdate) {
+            // we are recreating the archive, need all resources
+            return new ArchiveState(true, initialResources);
+        }
+
+        Resource[][] newerResources = new Resource[filesets.length][];
+
+        for (int i = 0; i < filesets.length; i++) {
+            if (!(fileset instanceof ZipFileSet)
+                || ((ZipFileSet) fileset).getSrc(getProject()) == null) {
+                File base = filesets[i].getDir(getProject());
+
+                for (int j = 0; j < initialResources[i].length; j++) {
+                    File resourceAsFile =
+                        fileUtils.resolveFile(base,
+                                              initialResources[i][j].getName());
+                    if (resourceAsFile.equals(zipFile)) {
+                        throw new BuildException("A zip file cannot include "
+                                                 + "itself", getLocation());
+                    }
+                }
+            }
+        }
+
+        for (int i = 0; i < filesets.length; i++) {
+            if (initialResources[i].length == 0) {
+                newerResources[i] = new Resource[] {};
+                continue;
+            }
+
+            FileNameMapper myMapper = new IdentityMapper();
+            if (filesets[i] instanceof ZipFileSet) {
+                ZipFileSet zfs = (ZipFileSet) filesets[i];
+                if (zfs.getFullpath(getProject()) != null
+                    && !zfs.getFullpath(getProject()).equals("")) {
+                    // in this case all files from origin map to
+                    // the fullPath attribute of the zipfileset at
+                    // destination
+                    MergingMapper fm = new MergingMapper();
+                    fm.setTo(zfs.getFullpath(getProject()));
+                    myMapper = fm;
+
+                } else if (zfs.getPrefix(getProject()) != null
+                           && !zfs.getPrefix(getProject()).equals("")) {
+                    GlobPatternMapper gm = new GlobPatternMapper();
+                    gm.setFrom("*");
+                    String prefix = zfs.getPrefix(getProject());
+                    if (!prefix.endsWith("/") && !prefix.endsWith("\\")) {
+                        prefix += "/";
+                    }
+                    gm.setTo(prefix + "*");
+                    myMapper = gm;
+                }
+            }
+
+            Resource[] resources = initialResources[i];
+            if (doFilesonly) {
+                resources = selectFileResources(resources);
+            }
+
+            newerResources[i] =
+                ResourceUtils.selectOutOfDateSources(this,
+                                                     resources,
+                                                     myMapper,
+                                                     getZipScanner());
+            needsUpdate = needsUpdate || (newerResources[i].length > 0);
+
+            if (needsUpdate && !doUpdate) {
+                // we will return initialResources anyway, no reason
+                // to scan further.
+                break;
+            }
+        }
+
+        if (needsUpdate && !doUpdate) {
+            // we are recreating the archive, need all resources
+            return new ArchiveState(true, initialResources);
+        }
+
+        return new ArchiveState(needsUpdate, newerResources);
+    }
+
+    /**
+     * Fetch all included and not excluded resources from the sets.
+     *
+     * <p>Included directories will precede included files.</p>
+     *
+     * @since Ant 1.5.2
+     */
+    protected Resource[][] grabResources(FileSet[] filesets) {
+        Resource[][] result = new Resource[filesets.length][];
+        for (int i = 0; i < filesets.length; i++) {
+            boolean skipEmptyNames = true;
+            if (filesets[i] instanceof ZipFileSet) {
+                ZipFileSet zfs = (ZipFileSet) filesets[i];
+                skipEmptyNames = zfs.getPrefix(getProject()).equals("")
+                    && zfs.getFullpath(getProject()).equals("");
+            }
+            DirectoryScanner rs =
+                filesets[i].getDirectoryScanner(getProject());
+            if (rs instanceof ZipScanner) {
+                ((ZipScanner) rs).setEncoding(encoding);
+            }
+            Vector resources = new Vector();
+            String[] directories = rs.getIncludedDirectories();
+            for (int j = 0; j < directories.length; j++) {
+                if (!"".equals(directories[j]) || !skipEmptyNames) {
+                    resources.addElement(rs.getResource(directories[j]));
+                }
+            }
+            String[] files = rs.getIncludedFiles();
+            for (int j = 0; j < files.length; j++) {
+                if (!"".equals(files[j]) || !skipEmptyNames) {
+                    resources.addElement(rs.getResource(files[j]));
+                }
+            }
+
+            result[i] = new Resource[resources.size()];
+            resources.copyInto(result[i]);
+        }
+        return result;
+    }
+
+    /**
+     * @since Ant 1.5.2
+     */
+    protected void zipDir(File dir, ZipOutputStream zOut, String vPath,
+                          int mode)
+        throws IOException {
+        zipDir(dir, zOut, vPath, mode, null);
+    }
+
+    /**
+     * Add a directory to the zip stream.
+     * @param dir  the directort to add to the archive
+     * @param zOut the stream to write to
+     * @param vPath the name this entry shall have in the archive
+     * @param mode the Unix permissions to set.
+     * @param extra ZipExtraFields to add
+     * @throws IOException on error
+     * @since Ant 1.6.3
+     */
+    protected void zipDir(File dir, ZipOutputStream zOut, String vPath,
+                          int mode, ZipExtraField[] extra)
+        throws IOException {
+        if (addedDirs.get(vPath) != null) {
+            // don't add directories we've already added.
+            // no warning if we try, it is harmless in and of itself
+            return;
+        }
+
+        log("adding directory " + vPath, Project.MSG_VERBOSE);
+        addedDirs.put(vPath, vPath);
+
+        if (!skipWriting) {
+            ZipEntry ze = new ZipEntry (vPath);
+            if (dir != null && dir.exists()) {
+                // ZIPs store time with a granularity of 2 seconds, round up
+                ze.setTime(dir.lastModified() + (roundUp ? 1999 : 0));
+            } else {
+                // ZIPs store time with a granularity of 2 seconds, round up
+                ze.setTime(System.currentTimeMillis() + (roundUp ? 1999 : 0));
+            }
+            ze.setSize (0);
+            ze.setMethod (ZipEntry.STORED);
+            // This is faintly ridiculous:
+            ze.setCrc (EMPTY_CRC);
+            ze.setUnixMode(mode);
+
+            if (extra != null) {
+                ze.setExtraFields(extra);
+            }
+
+            zOut.putNextEntry(ze);
+        }
+    }
+
+    /**
+     * Adds a new entry to the archive, takes care of duplicates as well.
+     *
+     * @param in the stream to read data for the entry from.
+     * @param zOut the stream to write to.
+     * @param vPath the name this entry shall have in the archive.
+     * @param lastModified last modification time for the entry.
+     * @param fromArchive the original archive we are copying this
+     * entry from, will be null if we are not copying from an archive.
+     * @param mode the Unix permissions to set.
+     *
+     * @since Ant 1.5.2
+     */
+    protected void zipFile(InputStream in, ZipOutputStream zOut, String vPath,
+                           long lastModified, File fromArchive, int mode)
+        throws IOException {
+        if (entries.contains(vPath)) {
+
+            if (duplicate.equals("preserve")) {
+                log(vPath + " already added, skipping", Project.MSG_INFO);
+                return;
+            } else if (duplicate.equals("fail")) {
+                throw new BuildException("Duplicate file " + vPath
+                                         + " was found and the duplicate "
+                                         + "attribute is 'fail'.");
+            } else {
+                // duplicate equal to add, so we continue
+                log("duplicate file " + vPath
+                    + " found, adding.", Project.MSG_VERBOSE);
+            }
+        } else {
+            log("adding entry " + vPath, Project.MSG_VERBOSE);
+        }
+
+        entries.put(vPath, vPath);
+
+        if (!skipWriting) {
+            ZipEntry ze = new ZipEntry(vPath);
+            ze.setTime(lastModified);
+            ze.setMethod(doCompress ? ZipEntry.DEFLATED : ZipEntry.STORED);
+
+            /*
+            * ZipOutputStream.putNextEntry expects the ZipEntry to
+            * know its size and the CRC sum before you start writing
+            * the data when using STORED mode - unless it is seekable.
+            *
+            * This forces us to process the data twice.
+            */
+            if (!zOut.isSeekable() && !doCompress) {
+                long size = 0;
+                CRC32 cal = new CRC32();
+                if (!in.markSupported()) {
+                    // Store data into a byte[]
+                    ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+                    byte[] buffer = new byte[8 * 1024];
+                    int count = 0;
+                    do {
+                        size += count;
+                        cal.update(buffer, 0, count);
+                        bos.write(buffer, 0, count);
+                        count = in.read(buffer, 0, buffer.length);
+                    } while (count != -1);
+                    in = new ByteArrayInputStream(bos.toByteArray());
+
+                } else {
+                    in.mark(Integer.MAX_VALUE);
+                    byte[] buffer = new byte[8 * 1024];
+                    int count = 0;
+                    do {
+                        size += count;
+                        cal.update(buffer, 0, count);
+                        count = in.read(buffer, 0, buffer.length);
+                    } while (count != -1);
+                    in.reset();
+                }
+                ze.setSize(size);
+                ze.setCrc(cal.getValue());
+            }
+
+            ze.setUnixMode(mode);
+            zOut.putNextEntry(ze);
+
+            byte[] buffer = new byte[8 * 1024];
+            int count = 0;
+            do {
+                if (count != 0) {
+                    zOut.write(buffer, 0, count);
+                }
+                count = in.read(buffer, 0, buffer.length);
+            } while (count != -1);
+        }
+        addedFiles.addElement(vPath);
+    }
+
+    /**
+     * Method that gets called when adding from java.io.File instances.
+     *
+     * <p>This implementation delegates to the six-arg version.</p>
+     *
+     * @param file the file to add to the archive
+     * @param zOut the stream to write to
+     * @param vPath the name this entry shall have in the archive
+     * @param mode the Unix permissions to set.
+     *
+     * @since Ant 1.5.2
+     */
+    protected void zipFile(File file, ZipOutputStream zOut, String vPath,
+                           int mode)
+        throws IOException {
+        if (file.equals(zipFile)) {
+            throw new BuildException("A zip file cannot include itself",
+                                     getLocation());
+        }
+
+        FileInputStream fIn = new FileInputStream(file);
+        try {
+            // ZIPs store time with a granularity of 2 seconds, round up
+            zipFile(fIn, zOut, vPath, 
+                    file.lastModified() + (roundUp ? 1999 : 0),
+                    null, mode);
+        } finally {
+            fIn.close();
+        }
+    }
+
+    /**
+     * Ensure all parent dirs of a given entry have been added.
+     *
+     * @since Ant 1.5.2
+     */
+    protected final void addParentDirs(File baseDir, String entry,
+                                       ZipOutputStream zOut, String prefix,
+                                       int dirMode)
+        throws IOException {
+        if (!doFilesonly) {
+            Stack directories = new Stack();
+            int slashPos = entry.length();
+
+            while ((slashPos = entry.lastIndexOf('/', slashPos - 1)) != -1) {
+                String dir = entry.substring(0, slashPos + 1);
+                if (addedDirs.get(prefix + dir) != null) {
+                    break;
+                }
+                directories.push(dir);
+            }
+
+            while (!directories.isEmpty()) {
+                String dir = (String) directories.pop();
+                File f = null;
+                if (baseDir != null) {
+                    f = new File(baseDir, dir);
+                } else {
+                    f = new File(dir);
+                }
+                zipDir(f, zOut, prefix + dir, dirMode);
+            }
+        }
+    }
+
+    /**
+     * Do any clean up necessary to allow this instance to be used again.
+     *
+     * <p>When we get here, the Zip file has been closed and all we
+     * need to do is to reset some globals.</p>
+     *
+     * <p>This method will only reset globals that have been changed
+     * during execute(), it will not alter the attributes or nested
+     * child elements.  If you want to reset the instance so that you
+     * can later zip a completely different set of files, you must use
+     * the reset method.</p>
+     *
+     * @see #reset
+     */
+    protected void cleanUp() {
+        addedDirs.clear();
+        addedFiles.removeAllElements();
+        entries.clear();
+        addingNewFiles = false;
+        doUpdate = savedDoUpdate;
+        Enumeration e = filesetsFromGroupfilesets.elements();
+        while (e.hasMoreElements()) {
+            ZipFileSet zf = (ZipFileSet) e.nextElement();
+            filesets.removeElement(zf);
+        }
+        filesetsFromGroupfilesets.removeAllElements();
+    }
+
+    /**
+     * Makes this instance reset all attributes to their default
+     * values and forget all children.
+     *
+     * @since Ant 1.5
+     *
+     * @see #cleanUp
+     */
+    public void reset() {
+        filesets.removeAllElements();
+        zipFile = null;
+        baseDir = null;
+        groupfilesets.removeAllElements();
+        duplicate = "add";
+        archiveType = "zip";
+        doCompress = true;
+        emptyBehavior = "skip";
+        doUpdate = false;
+        doFilesonly = false;
+        encoding = null;
+    }
+
+    /**
+     * @return true if all individual arrays are empty
+     *
+     * @since Ant 1.5.2
+     */
+    protected static final boolean isEmpty(Resource[][] r) {
+        for (int i = 0; i < r.length; i++) {
+            if (r[i].length > 0) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Drops all non-file resources from the given array.
+     *
+     * @since Ant 1.6
+     */
+    protected Resource[] selectFileResources(Resource[] orig) {
+        if (orig.length == 0) {
+            return orig;
+        }
+
+        Vector v = new Vector(orig.length);
+        for (int i = 0; i < orig.length; i++) {
+            if (!orig[i].isDirectory()) {
+                v.addElement(orig[i]);
+            } else {
+                log("Ignoring directory " + orig[i].getName()
+                    + " as only files will be added.", Project.MSG_VERBOSE);
+            }
+        }
+
+        if (v.size() != orig.length) {
+            Resource[] r = new Resource[v.size()];
+            v.copyInto(r);
+            return r;
+        }
+        return orig;
+    }
+
+    /**
+     * Possible behaviors when a duplicate file is added:
+     * "add", "preserve" or "fail"
+     */
+    public static class Duplicate extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[] {"add", "preserve", "fail"};
+        }
+    }
+
+    /**
+     * Holds the up-to-date status and the out-of-date resources of
+     * the original archive.
+     *
+     * @since Ant 1.5.3
+     */
+    public static class ArchiveState {
+        private boolean outOfDate;
+        private Resource[][] resourcesToAdd;
+
+        ArchiveState(boolean state, Resource[][] r) {
+            outOfDate = state;
+            resourcesToAdd = r;
+        }
+
+        public boolean isOutOfDate() {
+            return outOfDate;
+        }
+
+        public Resource[][] getResourcesToAdd() {
+            return resourcesToAdd;
+        }
+        /**
+         * find out if there are absolutely no resources to add
+         * @since Ant 1.6.3
+         * @return true if there are no resources to add
+         */
+        public boolean isWithoutAnyResources() {
+            if (resourcesToAdd == null)  {
+                return true;
+            }
+            for (int counter = 0; counter < resourcesToAdd.length; counter++) {
+                if (resourcesToAdd[counter] != null) {
+                    if (resourcesToAdd[counter].length > 0) {
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapter.java	(revision 16139)
@@ -0,0 +1,49 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Javac;
+
+/**
+ * The interface that all compiler adapters must adhere to.
+ *
+ * <p>A compiler adapter is an adapter that interprets the javac's
+ * parameters in preparation to be passed off to the compiler this
+ * adapter represents.  As all the necessary values are stored in the
+ * Javac task itself, the only thing all adapters need is the javac
+ * task, the execute command and a parameterless constructor (for
+ * reflection).</p>
+ *
+ * @since Ant 1.3
+ */
+
+public interface CompilerAdapter {
+
+    /**
+     * Sets the compiler attributes, which are stored in the Javac task.
+     */
+    void setJavac(Javac attributes);
+
+    /**
+     * Executes the task.
+     *
+     * @return has the compilation been successful
+     */
+    boolean execute() throws BuildException;
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapterFactory.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapterFactory.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/CompilerAdapterFactory.java	(revision 16139)
@@ -0,0 +1,181 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Creates the necessary compiler adapter, given basic criteria.
+ *
+ * @since Ant 1.3
+ */
+public class CompilerAdapterFactory {
+    private static final String MODERN_COMPILER = "com.sun.tools.javac.Main";
+
+    /** This is a singleton -- can't create instances!! */
+    private CompilerAdapterFactory() {
+    }
+
+    /**
+     * Based on the parameter passed in, this method creates the necessary
+     * factory desired.
+     *
+     * The current mapping for compiler names are as follows:
+     * <ul><li>jikes = jikes compiler
+     * <li>classic, javac1.1, javac1.2 = the standard compiler from JDK
+     * 1.1/1.2
+     * <li>modern, javac1.3, javac1.4, javac1.5 = the compiler of JDK 1.3+
+     * <li>jvc, microsoft = the command line compiler from Microsoft's SDK
+     * for Java / Visual J++
+     * <li>kjc = the kopi compiler</li>
+     * <li>gcj = the gcj compiler from gcc</li>
+     * <li>sj, symantec = the Symantec Java compiler</li>
+     * <li><i>a fully qualified classname</i> = the name of a compiler
+     * adapter
+     * </ul>
+     *
+     * @param compilerType either the name of the desired compiler, or the
+     * full classname of the compiler's adapter.
+     * @param task a task to log through.
+     * @throws BuildException if the compiler type could not be resolved into
+     * a compiler adapter.
+     */
+    public static CompilerAdapter getCompiler(String compilerType, Task task)
+        throws BuildException {
+            boolean isClassicCompilerSupported = true;
+            //as new versions of java come out, add them to this test
+            if (!JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)
+                && !JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_2)
+                && !JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_3)) {
+                isClassicCompilerSupported = false;
+            }
+
+            if (compilerType.equalsIgnoreCase("jikes")) {
+                return new Jikes();
+            }
+            if (compilerType.equalsIgnoreCase("extJavac")) {
+                return new JavacExternal();
+            }
+            if (compilerType.equalsIgnoreCase("classic")
+                || compilerType.equalsIgnoreCase("javac1.1")
+                || compilerType.equalsIgnoreCase("javac1.2")) {
+                if (isClassicCompilerSupported) {
+                    return new Javac12();
+                } else {
+                    task.log("This version of java does "
+                                             + "not support the classic "
+                                             + "compiler; upgrading to modern",
+                                             Project.MSG_WARN);
+                    compilerType = "modern";
+                }
+            }
+            //on java<=1.3 the modern falls back to classic if it is not found
+            //but on java>=1.4 we just bail out early
+            if (compilerType.equalsIgnoreCase("modern")
+                || compilerType.equalsIgnoreCase("javac1.3")
+                || compilerType.equalsIgnoreCase("javac1.4")
+                || compilerType.equalsIgnoreCase("javac1.5")) {
+                // does the modern compiler exist?
+                if (doesModernCompilerExist()) {
+                    return new Javac13();
+                } else {
+                    if (isClassicCompilerSupported) {
+                        task.log("Modern compiler not found - looking for "
+                                 + "classic compiler", Project.MSG_WARN);
+                        return new Javac12();
+                    } else {
+                        throw new BuildException("Unable to find a javac "
+                                                 + "compiler;\n"
+                                                 + MODERN_COMPILER
+                                                 + " is not on the "
+                                                 + "classpath.\n"
+                                                 + "Perhaps JAVA_HOME does not"
+                                                 + " point to the JDK");
+                    }
+                }
+            }
+
+            if (compilerType.equalsIgnoreCase("jvc")
+                || compilerType.equalsIgnoreCase("microsoft")) {
+                return new Jvc();
+            }
+            if (compilerType.equalsIgnoreCase("kjc")) {
+                return new Kjc();
+            }
+            if (compilerType.equalsIgnoreCase("gcj")) {
+                return new Gcj();
+            }
+            if (compilerType.equalsIgnoreCase("sj")
+                || compilerType.equalsIgnoreCase("symantec")) {
+                return new Sj();
+            }
+            return resolveClassName(compilerType);
+        }
+
+    /**
+     * query for the Modern compiler existing
+     * @return true if classic os on the classpath
+     */
+    private static boolean doesModernCompilerExist() {
+        try {
+            Class.forName(MODERN_COMPILER);
+            return true;
+        } catch (ClassNotFoundException cnfe) {
+            try {
+                ClassLoader cl = CompilerAdapterFactory.class.getClassLoader();
+                if (cl != null) {
+                    cl.loadClass(MODERN_COMPILER);
+                    return true;
+                }
+            } catch (ClassNotFoundException cnfe2) {
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Tries to resolve the given classname into a compiler adapter.
+     * Throws a fit if it can't.
+     *
+     * @param className The fully qualified classname to be created.
+     * @throws BuildException This is the fit that is thrown if className
+     * isn't an instance of CompilerAdapter.
+     */
+    private static CompilerAdapter resolveClassName(String className)
+        throws BuildException {
+        try {
+            Class c = Class.forName(className);
+            Object o = c.newInstance();
+            return (CompilerAdapter) o;
+        } catch (ClassNotFoundException cnfe) {
+            throw new BuildException("Compiler Adapter '" + className
+                    + "' can\'t be found.", cnfe);
+        } catch (ClassCastException cce) {
+            throw new BuildException(className + " isn\'t the classname of "
+                    + "a compiler adapter.", cce);
+        } catch (Throwable t) {
+            // for all other possibilities
+            throw new BuildException("Compiler Adapter " + className
+                    + " caused an interesting exception.", t);
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/DefaultCompilerAdapter.java	(revision 16139)
@@ -0,0 +1,578 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.compilers;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.Javac;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * This is the default implementation for the CompilerAdapter interface.
+ * Currently, this is a cut-and-paste of the original javac task.
+ *
+ * @since Ant 1.3
+ */
+public abstract class DefaultCompilerAdapter implements CompilerAdapter {
+
+    /* jdg - TODO - all these attributes are currently protected, but they
+     * should probably be private in the near future.
+     */
+
+    protected Path src;
+    protected File destDir;
+    protected String encoding;
+    protected boolean debug = false;
+    protected boolean optimize = false;
+    protected boolean deprecation = false;
+    protected boolean depend = false;
+    protected boolean verbose = false;
+    protected String target;
+    protected Path bootclasspath;
+    protected Path extdirs;
+    protected Path compileClasspath;
+    protected Path compileSourcepath;
+    protected Project project;
+    protected Location location;
+    protected boolean includeAntRuntime;
+    protected boolean includeJavaRuntime;
+    protected String memoryInitialSize;
+    protected String memoryMaximumSize;
+
+    protected File[] compileList;
+    protected static final String lSep = System.getProperty("line.separator");
+    protected Javac attributes;
+
+    private FileUtils fileUtils = FileUtils.newFileUtils();
+
+    /**
+     * Set the Javac instance which contains the configured compilation
+     * attributes.
+     *
+     * @param attributes a configured Javac task.
+     */
+    public void setJavac(Javac attributes) {
+        this.attributes = attributes;
+        src = attributes.getSrcdir();
+        destDir = attributes.getDestdir();
+        encoding = attributes.getEncoding();
+        debug = attributes.getDebug();
+        optimize = attributes.getOptimize();
+        deprecation = attributes.getDeprecation();
+        depend = attributes.getDepend();
+        verbose = attributes.getVerbose();
+        target = attributes.getTarget();
+        bootclasspath = attributes.getBootclasspath();
+        extdirs = attributes.getExtdirs();
+        compileList = attributes.getFileList();
+        compileClasspath = attributes.getClasspath();
+        compileSourcepath = attributes.getSourcepath();
+        project = attributes.getProject();
+        location = attributes.getLocation();
+        includeAntRuntime = attributes.getIncludeantruntime();
+        includeJavaRuntime = attributes.getIncludejavaruntime();
+        memoryInitialSize = attributes.getMemoryInitialSize();
+        memoryMaximumSize = attributes.getMemoryMaximumSize();
+    }
+
+    /**
+     * Get the Javac task instance associated with this compiler adapter
+     *
+     * @return the configured Javac task instance used by this adapter.
+     */
+    public Javac getJavac() {
+        return attributes;
+    }
+
+    /**
+     * @since Ant 1.6
+     */
+    protected Project getProject() {
+        return project;
+    }
+
+    /**
+     * Builds the compilation classpath.
+     *
+     */
+    protected Path getCompileClasspath() {
+        Path classpath = new Path(project);
+
+        // add dest dir to classpath so that previously compiled and
+        // untouched classes are on classpath
+
+        if (destDir != null) {
+            classpath.setLocation(destDir);
+        }
+
+        // Combine the build classpath with the system classpath, in an
+        // order determined by the value of build.sysclasspath
+
+        Path cp = compileClasspath;
+        if (cp == null) {
+            cp = new Path(project);
+        }
+        if (includeAntRuntime) {
+            classpath.addExisting(cp.concatSystemClasspath("last"));
+        } else {
+            classpath.addExisting(cp.concatSystemClasspath("ignore"));
+        }
+
+        if (includeJavaRuntime) {
+            classpath.addJavaRuntime();
+        }
+
+        return classpath;
+    }
+
+    protected Commandline setupJavacCommandlineSwitches(Commandline cmd) {
+        return setupJavacCommandlineSwitches(cmd, false);
+    }
+
+    /**
+     * Does the command line argument processing common to classic and
+     * modern.  Doesn't add the files to compile.
+     */
+    protected Commandline setupJavacCommandlineSwitches(Commandline cmd,
+                                                        boolean useDebugLevel) {
+        Path classpath = getCompileClasspath();
+        // For -sourcepath, use the "sourcepath" value if present.
+        // Otherwise default to the "srcdir" value.
+        Path sourcepath = null;
+        if (compileSourcepath != null) {
+            sourcepath = compileSourcepath;
+        } else {
+            sourcepath = src;
+        }
+
+        String memoryParameterPrefix = assumeJava11() ? "-J-" : "-J-X";
+        if (memoryInitialSize != null) {
+            if (!attributes.isForkedJavac()) {
+                attributes.log("Since fork is false, ignoring "
+                               + "memoryInitialSize setting.",
+                               Project.MSG_WARN);
+            } else {
+                cmd.createArgument().setValue(memoryParameterPrefix
+                                              + "ms" + memoryInitialSize);
+            }
+        }
+
+        if (memoryMaximumSize != null) {
+            if (!attributes.isForkedJavac()) {
+                attributes.log("Since fork is false, ignoring "
+                               + "memoryMaximumSize setting.",
+                               Project.MSG_WARN);
+            } else {
+                cmd.createArgument().setValue(memoryParameterPrefix
+                                              + "mx" + memoryMaximumSize);
+            }
+        }
+
+        if (attributes.getNowarn()) {
+            cmd.createArgument().setValue("-nowarn");
+        }
+
+        if (deprecation == true) {
+            cmd.createArgument().setValue("-deprecation");
+        }
+
+        if (destDir != null) {
+            cmd.createArgument().setValue("-d");
+            cmd.createArgument().setFile(destDir);
+        }
+
+        cmd.createArgument().setValue("-classpath");
+
+        // Just add "sourcepath" to classpath ( for JDK1.1 )
+        // as well as "bootclasspath" and "extdirs"
+        if (assumeJava11()) {
+            Path cp = new Path(project);
+            /*
+             * XXX - This doesn't mix very well with build.systemclasspath,
+             */
+            if (bootclasspath != null) {
+                cp.append(bootclasspath);
+            }
+            if (extdirs != null) {
+                cp.addExtdirs(extdirs);
+            }
+            cp.append(classpath);
+            cp.append(sourcepath);
+            cmd.createArgument().setPath(cp);
+        } else {
+            cmd.createArgument().setPath(classpath);
+            // If the buildfile specifies sourcepath="", then don't
+            // output any sourcepath.
+            if (sourcepath.size() > 0) {
+                cmd.createArgument().setValue("-sourcepath");
+                cmd.createArgument().setPath(sourcepath);
+            }
+            if (target != null) {
+                cmd.createArgument().setValue("-target");
+                cmd.createArgument().setValue(target);
+            }
+            if (bootclasspath != null && bootclasspath.size() > 0) {
+                cmd.createArgument().setValue("-bootclasspath");
+                cmd.createArgument().setPath(bootclasspath);
+            }
+            if (extdirs != null && extdirs.size() > 0) {
+                cmd.createArgument().setValue("-extdirs");
+                cmd.createArgument().setPath(extdirs);
+            }
+        }
+
+        if (encoding != null) {
+            cmd.createArgument().setValue("-encoding");
+            cmd.createArgument().setValue(encoding);
+        }
+        if (debug) {
+            if (useDebugLevel && !assumeJava11()) {
+                String debugLevel = attributes.getDebugLevel();
+                if (debugLevel != null) {
+                    cmd.createArgument().setValue("-g:" + debugLevel);
+                } else {
+                    cmd.createArgument().setValue("-g");
+                }
+            } else {
+                cmd.createArgument().setValue("-g");
+            }
+        } else if (getNoDebugArgument() != null) {
+            cmd.createArgument().setValue(getNoDebugArgument());
+        }
+        if (optimize) {
+            cmd.createArgument().setValue("-O");
+        }
+
+        if (depend) {
+            if (assumeJava11()) {
+                cmd.createArgument().setValue("-depend");
+            } else if (assumeJava12()) {
+                cmd.createArgument().setValue("-Xdepend");
+            } else {
+                attributes.log("depend attribute is not supported by the "
+                               + "modern compiler", Project.MSG_WARN);
+            }
+        }
+
+        if (verbose) {
+            cmd.createArgument().setValue("-verbose");
+        }
+
+        addCurrentCompilerArgs(cmd);
+
+        return cmd;
+    }
+
+    /**
+     * Does the command line argument processing for modern.  Doesn't
+     * add the files to compile.
+     */
+    protected Commandline setupModernJavacCommandlineSwitches(Commandline cmd) {
+        setupJavacCommandlineSwitches(cmd, true);
+        if (attributes.getSource() != null && !assumeJava13()) {
+            cmd.createArgument().setValue("-source");
+            String source = attributes.getSource();
+            if ((assumeJava14() || assumeJava15())
+                && (source.equals("1.1") || source.equals("1.2"))) {
+                // support for -source 1.1 and -source 1.2 has been
+                // added with JDK 1.4.2 - and isn't present in 1.5.0 either
+                cmd.createArgument().setValue("1.3");
+            } else {
+                cmd.createArgument().setValue(source);
+            }
+        } else if (assumeJava15() && attributes.getTarget() != null) {
+            String t = attributes.getTarget();
+            if (t.equals("1.1") || t.equals("1.2") || t.equals("1.3")
+                || t.equals("1.4")) {
+                String s = t;
+                if (t.equals("1.1")) {
+                    // 1.5.0 doesn't support -source 1.1
+                    s = "1.2";
+                }
+                attributes.log("", Project.MSG_WARN);
+                attributes.log("          WARNING", Project.MSG_WARN);
+                attributes.log("", Project.MSG_WARN);
+                attributes.log("The -source switch defaults to 1.5 in JDK 1.5.",
+                               Project.MSG_WARN);
+                attributes.log("If you specify -target " + t
+                               + " you now must also specify -source " + s
+                               + ".", Project.MSG_WARN);
+                attributes.log("Ant will implicitly add -source " + s
+                               + " for you.  Please change your build file.",
+                               Project.MSG_WARN);
+                cmd.createArgument().setValue("-source");
+                cmd.createArgument().setValue(s);
+            }
+        }
+        return cmd;
+    }
+
+    /**
+     * Does the command line argument processing for modern and adds
+     * the files to compile as well.
+     */
+    protected Commandline setupModernJavacCommand() {
+        Commandline cmd = new Commandline();
+        setupModernJavacCommandlineSwitches(cmd);
+
+        logAndAddFilesToCompile(cmd);
+        return cmd;
+    }
+
+    protected Commandline setupJavacCommand() {
+        return setupJavacCommand(false);
+    }
+
+    /**
+     * Does the command line argument processing for classic and adds
+     * the files to compile as well.
+     */
+    protected Commandline setupJavacCommand(boolean debugLevelCheck) {
+        Commandline cmd = new Commandline();
+        setupJavacCommandlineSwitches(cmd, debugLevelCheck);
+        logAndAddFilesToCompile(cmd);
+        return cmd;
+    }
+
+    /**
+     * Logs the compilation parameters, adds the files to compile and logs the
+     * &quot;niceSourceList&quot;
+     */
+    protected void logAndAddFilesToCompile(Commandline cmd) {
+        attributes.log("Compilation " + cmd.describeArguments(),
+                       Project.MSG_VERBOSE);
+
+        StringBuffer niceSourceList = new StringBuffer("File");
+        if (compileList.length != 1) {
+            niceSourceList.append("s");
+        }
+        niceSourceList.append(" to be compiled:");
+
+        niceSourceList.append(lSep);
+
+        for (int i = 0; i < compileList.length; i++) {
+            String arg = compileList[i].getAbsolutePath();
+            cmd.createArgument().setValue(arg);
+            niceSourceList.append("    " + arg + lSep);
+        }
+
+        attributes.log(niceSourceList.toString(), Project.MSG_VERBOSE);
+    }
+
+    /**
+     * Do the compile with the specified arguments.
+     * @param args - arguments to pass to process on command line
+     * @param firstFileName - index of the first source file in args,
+     * if the index is negative, no temporary file will ever be
+     * created, but this may hit the command line length limit on your
+     * system.
+     */
+    protected int executeExternalCompile(String[] args, int firstFileName) {
+        return executeExternalCompile(args, firstFileName, true);
+    }
+
+    /**
+     * Do the compile with the specified arguments.
+     * @param args - arguments to pass to process on command line
+     * @param firstFileName - index of the first source file in args,
+     * if the index is negative, no temporary file will ever be
+     * created, but this may hit the command line length limit on your
+     * system.
+     * @param quoteFiles - if set to true, filenames containing
+     * spaces will be quoted when they appear in the external file.
+     * This is necessary when running JDK 1.4's javac and probably
+     * others.
+     *
+     * @since Ant 1.6
+     */
+    protected int executeExternalCompile(String[] args, int firstFileName,
+                                         boolean quoteFiles) {
+        String[] commandArray = null;
+        File tmpFile = null;
+
+        try {
+            /*
+             * Many system have been reported to get into trouble with
+             * long command lines - no, not only Windows ;-).
+             *
+             * POSIX seems to define a lower limit of 4k, so use a temporary
+             * file if the total length of the command line exceeds this limit.
+             */
+            if (Commandline.toString(args).length() > 4096
+                && firstFileName >= 0) {
+                PrintWriter out = null;
+                try {
+                    tmpFile = fileUtils.createTempFile(
+                        "files", "", getJavac().getTempdir());
+                    tmpFile.deleteOnExit();
+                    out = new PrintWriter(new FileWriter(tmpFile));
+                    for (int i = firstFileName; i < args.length; i++) {
+                        if (quoteFiles && args[i].indexOf(" ") > -1) {
+                            args[i] = args[i].replace(File.separatorChar, '/');
+                            out.println("\"" + args[i] + "\"");
+                        } else {
+                            out.println(args[i]);
+                        }
+                    }
+                    out.flush();
+                    commandArray = new String[firstFileName + 1];
+                    System.arraycopy(args, 0, commandArray, 0, firstFileName);
+                    commandArray[firstFileName] = "@" + tmpFile;
+                } catch (IOException e) {
+                    throw new BuildException("Error creating temporary file",
+                                             e, location);
+                } finally {
+                    if (out != null) {
+                        try {
+                            out.close();
+                        } catch (Throwable t) {
+                            // ignore
+                        }
+                    }
+                }
+            } else {
+                commandArray = args;
+            }
+
+            try {
+                Execute exe = new Execute(
+                                  new LogStreamHandler(attributes,
+                                                       Project.MSG_INFO,
+                                                       Project.MSG_WARN));
+                exe.setAntRun(project);
+                exe.setWorkingDirectory(project.getBaseDir());
+                exe.setCommandline(commandArray);
+                exe.execute();
+                return exe.getExitValue();
+            } catch (IOException e) {
+                throw new BuildException("Error running " + args[0]
+                        + " compiler", e, location);
+            }
+        } finally {
+            if (tmpFile != null) {
+                tmpFile.delete();
+            }
+        }
+    }
+
+    /**
+     * @deprecated use org.apache.tools.ant.types.Path#addExtdirs instead
+     */
+    protected void addExtdirsToClasspath(Path classpath) {
+        classpath.addExtdirs(extdirs);
+    }
+
+    /**
+     * Adds the command line arguments specific to the current implementation.
+     */
+    protected void addCurrentCompilerArgs(Commandline cmd) {
+        cmd.addArguments(getJavac().getCurrentCompilerArgs());
+    }
+
+    /**
+     * Shall we assume JDK 1.1 command line switches?
+     * @since Ant 1.5
+     */
+    protected boolean assumeJava11() {
+        return "javac1.1".equals(attributes.getCompilerVersion())
+            || ("classic".equals(attributes.getCompilerVersion())
+                && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1))
+            || ("extJavac".equals(attributes.getCompilerVersion())
+                && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1));
+    }
+
+    /**
+     * Shall we assume JDK 1.2 command line switches?
+     * @since Ant 1.5
+     */
+    protected boolean assumeJava12() {
+        return "javac1.2".equals(attributes.getCompilerVersion())
+            || ("classic".equals(attributes.getCompilerVersion())
+                && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_2))
+            || ("extJavac".equals(attributes.getCompilerVersion())
+                && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_2));
+    }
+
+    /**
+     * Shall we assume JDK 1.3 command line switches?
+     * @since Ant 1.5
+     */
+    protected boolean assumeJava13() {
+        return "javac1.3".equals(attributes.getCompilerVersion())
+            || ("classic".equals(attributes.getCompilerVersion())
+                && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_3))
+            || ("modern".equals(attributes.getCompilerVersion())
+                && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_3))
+            || ("extJavac".equals(attributes.getCompilerVersion())
+                && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_3));
+    }
+
+    /**
+     * Shall we assume JDK 1.4 command line switches?
+     * @since Ant 1.6.3
+     */
+    protected boolean assumeJava14() {
+        return "javac1.4".equals(attributes.getCompilerVersion())
+            || ("classic".equals(attributes.getCompilerVersion())
+                && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_4))
+            || ("modern".equals(attributes.getCompilerVersion())
+                && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_4))
+            || ("extJavac".equals(attributes.getCompilerVersion())
+                && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_4));
+    }
+
+    /**
+     * Shall we assume JDK 1.5 command line switches?
+     * @return true if JDK 1.5
+     * @since Ant 1.6.3
+     */
+    protected boolean assumeJava15() {
+        return "javac1.5".equals(attributes.getCompilerVersion())
+            || ("classic".equals(attributes.getCompilerVersion())
+                && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_5))
+            || ("modern".equals(attributes.getCompilerVersion())
+                && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_5))
+            || ("extJavac".equals(attributes.getCompilerVersion())
+                && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_5));
+    }
+
+    /**
+     * The argument the compiler wants to see if the debug attribute
+     * has been set to false.
+     *
+     * <p>A return value of <code>null</code> means no argument at all.</p>
+     *
+     * @return "-g:none" unless we expect to invoke a JDK 1.1 compiler.
+     *
+     * @since Ant 1.6.3
+     */
+    protected String getNoDebugArgument() {
+        return assumeJava11() ? null : "-g:none";
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Gcj.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Gcj.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Gcj.java	(revision 16139)
@@ -0,0 +1,139 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.compilers;
+
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * The implementation of the gcj compiler.
+ * This is primarily a cut-and-paste from the jikes.
+ *
+ * @since Ant 1.4
+ */
+public class Gcj extends DefaultCompilerAdapter {
+
+    /**
+     * Performs a compile using the gcj compiler.
+     */
+    public boolean execute() throws BuildException {
+        Commandline cmd;
+        attributes.log("Using gcj compiler", Project.MSG_VERBOSE);
+        cmd = setupGCJCommand();
+
+        int firstFileName = cmd.size();
+        logAndAddFilesToCompile(cmd);
+
+        return
+            executeExternalCompile(cmd.getCommandline(), firstFileName) == 0;
+    }
+
+    protected Commandline setupGCJCommand() {
+        Commandline cmd = new Commandline();
+        Path classpath = new Path(project);
+
+        // gcj doesn't support bootclasspath dir (-bootclasspath)
+        // so we'll emulate it for compatibility and convenience.
+        if (bootclasspath != null) {
+            classpath.append(bootclasspath);
+        }
+
+        // gcj doesn't support an extension dir (-extdir)
+        // so we'll emulate it for compatibility and convenience.
+        classpath.addExtdirs(extdirs);
+
+        classpath.append(getCompileClasspath());
+
+        // Gcj has no option for source-path so we
+        // will add it to classpath.
+        if (compileSourcepath != null) {
+            classpath.append(compileSourcepath);
+        } else {
+            classpath.append(src);
+        }
+
+        String exec = getJavac().getExecutable();
+        cmd.setExecutable(exec == null ? "gcj" : exec);
+
+        if (destDir != null) {
+            cmd.createArgument().setValue("-d");
+            cmd.createArgument().setFile(destDir);
+
+            if (!destDir.exists() && !destDir.mkdirs()) {
+                throw new BuildException("Can't make output directories. "
+                                         + "Maybe permission is wrong. ");
+            }
+        }
+
+        cmd.createArgument().setValue("-classpath");
+        cmd.createArgument().setPath(classpath);
+
+        if (encoding != null) {
+            cmd.createArgument().setValue("--encoding=" + encoding);
+        }
+        if (debug) {
+            cmd.createArgument().setValue("-g1");
+        }
+        if (optimize) {
+            cmd.createArgument().setValue("-O");
+        }
+
+        /**
+         *  gcj should be set for generate class.
+         * ... if no 'compile to native' argument is passed
+         */
+        if (!isNativeBuild()) {
+            cmd.createArgument().setValue("-C");
+        }
+
+        addCurrentCompilerArgs(cmd);
+
+        return cmd;
+    }
+
+    /**
+     * Whether any of the arguments given via &lt;compilerarg&gt;
+     * implies that compilation to native code is requested.
+     *
+     * @since Ant 1.6.2
+     */
+    public boolean isNativeBuild() {
+        boolean nativeBuild = false;
+        String[] additionalArguments = getJavac().getCurrentCompilerArgs();
+        int argsLength = 0;
+        while (!nativeBuild && argsLength < additionalArguments.length) {
+            int conflictLength = 0;
+            while (!nativeBuild
+                   && conflictLength < CONFLICT_WITH_DASH_C.length) {
+                nativeBuild = (additionalArguments[argsLength].startsWith
+                               (CONFLICT_WITH_DASH_C[conflictLength]));
+                conflictLength++;
+            }
+            argsLength++;
+        }
+        return nativeBuild;
+    }
+
+    private static final String [] CONFLICT_WITH_DASH_C = {
+        "-o" , "--main=", "-D", "-fjni", "-L"
+    };
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Javac12.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Javac12.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Javac12.java	(revision 16139)
@@ -0,0 +1,87 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.compilers;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * The implementation of the javac compiler for JDK 1.2
+ * This is primarily a cut-and-paste from the original javac task before it
+ * was refactored.
+ *
+ * @since Ant 1.3
+ */
+public class Javac12 extends DefaultCompilerAdapter {
+
+    /**
+     * Run the compilation.
+     *
+     * @exception BuildException if the compilation has problems.
+     */
+    public boolean execute() throws BuildException {
+        attributes.log("Using classic compiler", Project.MSG_VERBOSE);
+        Commandline cmd = setupJavacCommand(true);
+
+        OutputStream logstr = new LogOutputStream(attributes, Project.MSG_WARN);
+        try {
+            // Create an instance of the compiler, redirecting output to
+            // the project log
+            Class c = Class.forName("sun.tools.javac.Main");
+            Constructor cons =
+                c.getConstructor(new Class[] {OutputStream.class,
+                                              String.class});
+            Object compiler
+                = cons.newInstance(new Object[] {logstr, "javac"});
+
+            // Call the compile() method
+            Method compile = c.getMethod("compile",
+                                         new Class [] {String[].class});
+            Boolean ok =
+                (Boolean) compile.invoke(compiler,
+                                        new Object[] {cmd.getArguments()});
+            return ok.booleanValue();
+        } catch (ClassNotFoundException ex) {
+            throw new BuildException("Cannot use classic compiler, as it is "
+                                     + "not available.  A common solution is "
+                                     + "to set the environment variable"
+                                     + " JAVA_HOME to your jdk directory.",
+                                     location);
+        } catch (Exception ex) {
+            if (ex instanceof BuildException) {
+                throw (BuildException) ex;
+            } else {
+                throw new BuildException("Error starting classic compiler: ",
+                                         ex, location);
+            }
+        } finally {
+            try {
+                logstr.close();
+            } catch (IOException e) {
+                // plain impossible
+                throw new BuildException(e);
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Javac13.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Javac13.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Javac13.java	(revision 16139)
@@ -0,0 +1,68 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.compilers;
+
+import java.lang.reflect.Method;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+
+
+/**
+ * The implementation of the javac compiler for JDK 1.3
+ * This is primarily a cut-and-paste from the original javac task before it
+ * was refactored.
+ *
+ * @since Ant 1.3
+ */
+public class Javac13 extends DefaultCompilerAdapter {
+
+    /**
+     * Integer returned by the "Modern" jdk1.3 compiler to indicate success.
+     */
+    private static final int MODERN_COMPILER_SUCCESS = 0;
+
+    /**
+     * Run the compilation.
+     *
+     * @exception BuildException if the compilation has problems.
+     */
+    public boolean execute() throws BuildException {
+        attributes.log("Using modern compiler", Project.MSG_VERBOSE);
+        Commandline cmd = setupModernJavacCommand();
+
+        // Use reflection to be able to build on all JDKs >= 1.1:
+        try {
+            Class c = Class.forName ("com.sun.tools.javac.Main");
+            Object compiler = c.newInstance ();
+            Method compile = c.getMethod ("compile",
+                new Class [] {(new String [] {}).getClass ()});
+            int result = ((Integer) compile.invoke
+                          (compiler, new Object[] {cmd.getArguments()}))
+                .intValue ();
+            return (result == MODERN_COMPILER_SUCCESS);
+        } catch (Exception ex) {
+            if (ex instanceof BuildException) {
+                throw (BuildException) ex;
+            } else {
+                throw new BuildException("Error starting modern compiler",
+                                         ex, location);
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/JavacExternal.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/JavacExternal.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/JavacExternal.java	(revision 16139)
@@ -0,0 +1,54 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Performs a compile using javac externally.
+ *
+ * @since Ant 1.4
+ */
+public class JavacExternal extends DefaultCompilerAdapter {
+
+    /**
+     * Performs a compile using the Javac externally.
+     */
+    public boolean execute() throws BuildException {
+        attributes.log("Using external javac compiler", Project.MSG_VERBOSE);
+
+        Commandline cmd = new Commandline();
+        cmd.setExecutable(getJavac().getJavacExecutable());
+        if (!assumeJava11() && !assumeJava12()) {
+            setupModernJavacCommandlineSwitches(cmd);
+        } else {
+            setupJavacCommandlineSwitches(cmd, true);
+        }
+        int firstFileName = assumeJava11() ? -1 : cmd.size();
+        logAndAddFilesToCompile(cmd);
+
+        return
+            executeExternalCompile(cmd.getCommandline(), firstFileName,
+                                   true)
+            == 0;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Jikes.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Jikes.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Jikes.java	(revision 16139)
@@ -0,0 +1,221 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * The implementation of the jikes compiler.
+ * This is primarily a cut-and-paste from the original javac task before it
+ * was refactored.
+ *
+ * @since Ant 1.3
+ */
+public class Jikes extends DefaultCompilerAdapter {
+
+    /**
+     * Performs a compile using the Jikes compiler from IBM.
+     * Mostly of this code is identical to doClassicCompile()
+     * However, it does not support all options like
+     * extdirs, deprecation and so on, because
+     * there is no option in jikes and I don't understand
+     * what they should do.
+     *
+     * It has been successfully tested with jikes &gt;1.10
+     */
+    public boolean execute() throws BuildException {
+        attributes.log("Using jikes compiler", Project.MSG_VERBOSE);
+
+        Commandline cmd = new Commandline();
+
+        // For -sourcepath, use the "sourcepath" value if present.
+        // Otherwise default to the "srcdir" value.
+        Path sourcepath = null;
+        if (compileSourcepath != null) {
+            sourcepath = compileSourcepath;
+        } else {
+            sourcepath = src;
+        }
+        // If the buildfile specifies sourcepath="", then don't
+        // output any sourcepath.
+        if (sourcepath.size() > 0) {
+            cmd.createArgument().setValue("-sourcepath");
+            cmd.createArgument().setPath(sourcepath);
+        }
+
+        Path classpath = new Path(project);
+
+        if (bootclasspath == null || bootclasspath.size() == 0) {
+            // no bootclasspath, therefore, get one from the java runtime
+            includeJavaRuntime = true;
+        } else {
+            // there is a bootclasspath stated.  By default, the
+            // includeJavaRuntime is false.  If the user has stated a
+            // bootclasspath and said to include the java runtime, it's on
+            // their head!
+        }
+        classpath.append(getCompileClasspath());
+
+        // if the user has set JIKESPATH we should add the contents as well
+        String jikesPath = System.getProperty("jikes.class.path");
+        if (jikesPath != null) {
+            classpath.append(new Path(project, jikesPath));
+        }
+
+        if (extdirs != null && extdirs.size() > 0) {
+            cmd.createArgument().setValue("-extdirs");
+            cmd.createArgument().setPath(extdirs);
+        }
+
+        String exec = getJavac().getExecutable();
+        cmd.setExecutable(exec == null ? "jikes" : exec);
+
+        if (deprecation == true) {
+            cmd.createArgument().setValue("-deprecation");
+        }
+
+        if (destDir != null) {
+            cmd.createArgument().setValue("-d");
+            cmd.createArgument().setFile(destDir);
+        }
+
+        cmd.createArgument().setValue("-classpath");
+        cmd.createArgument().setPath(classpath);
+
+        if (encoding != null) {
+            cmd.createArgument().setValue("-encoding");
+            cmd.createArgument().setValue(encoding);
+        }
+        if (debug) {
+            String debugLevel = attributes.getDebugLevel();
+            if (debugLevel != null) {
+                cmd.createArgument().setValue("-g:" + debugLevel);
+            } else {
+                cmd.createArgument().setValue("-g");
+            }
+        } else {
+            cmd.createArgument().setValue("-g:none");
+        }
+        if (optimize) {
+            cmd.createArgument().setValue("-O");
+        }
+        if (verbose) {
+            cmd.createArgument().setValue("-verbose");
+        }
+        if (depend) {
+            cmd.createArgument().setValue("-depend");
+        }
+
+        if (target != null) {
+            cmd.createArgument().setValue("-target");
+            cmd.createArgument().setValue(target);
+        }
+
+        /**
+         * XXX
+         * Perhaps we shouldn't use properties for these
+         * three options (emacs mode, warnings and pedantic),
+         * but include it in the javac directive?
+         */
+
+        /**
+         * Jikes has the nice feature to print error
+         * messages in a form readable by emacs, so
+         * that emacs can directly set the cursor
+         * to the place, where the error occurred.
+         */
+        String emacsProperty = project.getProperty("build.compiler.emacs");
+        if (emacsProperty != null && Project.toBoolean(emacsProperty)) {
+            cmd.createArgument().setValue("+E");
+        }
+
+        /**
+         * Jikes issues more warnings that javac, for
+         * example, when you have files in your classpath
+         * that don't exist. As this is often the case, these
+         * warning can be pretty annoying.
+         */
+        String warningsProperty =
+            project.getProperty("build.compiler.warnings");
+        if (warningsProperty != null) {
+            attributes.log("!! the build.compiler.warnings property is "
+                           + "deprecated. !!", Project.MSG_WARN);
+            attributes.log("!! Use the nowarn attribute instead. !!",
+                           Project.MSG_WARN);
+            if (!Project.toBoolean(warningsProperty)) {
+                cmd.createArgument().setValue("-nowarn");
+            }
+        } if (attributes.getNowarn()) {
+            cmd.createArgument().setValue("-nowarn");
+        }
+
+        /**
+         * Jikes can issue pedantic warnings.
+         */
+        String pedanticProperty =
+            project.getProperty("build.compiler.pedantic");
+        if (pedanticProperty != null && Project.toBoolean(pedanticProperty)) {
+            cmd.createArgument().setValue("+P");
+        }
+
+        /**
+         * Jikes supports something it calls "full dependency
+         * checking", see the jikes documentation for differences
+         * between -depend and +F.
+         */
+        String fullDependProperty =
+            project.getProperty("build.compiler.fulldepend");
+        if (fullDependProperty != null
+            && Project.toBoolean(fullDependProperty)) {
+            cmd.createArgument().setValue("+F");
+        }
+
+        if (attributes.getSource() != null) {
+            cmd.createArgument().setValue("-source");
+            String source = attributes.getSource();
+            if (source.equals("1.1") || source.equals("1.2")) {
+                // support for -source 1.1 and -source 1.2 has been
+                // added with JDK 1.4.2, Jikes doesn't like it
+                attributes.log("Jikes doesn't support '-source "
+                               + source + "', will use '-source 1.3' instead");
+                cmd.createArgument().setValue("1.3");
+            } else {
+                cmd.createArgument().setValue(source);
+            }
+        }
+
+        addCurrentCompilerArgs(cmd);
+
+        int firstFileName = cmd.size();
+
+        if (bootclasspath != null) {
+            cmd.createArgument().setValue("-bootclasspath");
+            cmd.createArgument().setPath(bootclasspath);
+        }
+
+        logAndAddFilesToCompile(cmd);
+
+        return
+            executeExternalCompile(cmd.getCommandline(), firstFileName) == 0;
+    }
+
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Jvc.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Jvc.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Jvc.java	(revision 16139)
@@ -0,0 +1,113 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * The implementation of the jvc compiler from microsoft.
+ * This is primarily a cut-and-paste from the original javac task before it
+ * was refactored.
+ *
+ * @since Ant 1.3
+ */
+public class Jvc extends DefaultCompilerAdapter {
+
+    /**
+     * Run the compilation.
+     *
+     * @exception BuildException if the compilation has problems.
+     */
+    public boolean execute() throws BuildException {
+        attributes.log("Using jvc compiler", Project.MSG_VERBOSE);
+
+        Path classpath = new Path(project);
+
+        // jvc doesn't support bootclasspath dir (-bootclasspath)
+        // so we'll emulate it for compatibility and convenience.
+        if (bootclasspath != null) {
+            classpath.append(bootclasspath);
+        }
+
+        if (includeJavaRuntime) {
+            // jvc doesn't support an extension dir (-extdir)
+            // so we'll emulate it for compatibility and convenience.
+            classpath.addExtdirs(extdirs);
+        }
+
+        classpath.append(getCompileClasspath());
+
+        // jvc has no option for source-path so we
+        // will add it to classpath.
+        if (compileSourcepath != null) {
+            classpath.append(compileSourcepath);
+        } else {
+            classpath.append(src);
+        }
+
+        Commandline cmd = new Commandline();
+        String exec = getJavac().getExecutable();
+        cmd.setExecutable(exec == null ? "jvc" : exec);
+
+        if (destDir != null) {
+            cmd.createArgument().setValue("/d");
+            cmd.createArgument().setFile(destDir);
+        }
+
+        // Add the Classpath before the "internal" one.
+        cmd.createArgument().setValue("/cp:p");
+        cmd.createArgument().setPath(classpath);
+
+        boolean msExtensions = true;
+        String mse = getProject().getProperty("build.compiler.jvc.extensions");
+        if (mse != null) {
+            msExtensions = Project.toBoolean(mse);
+        }
+
+        if (msExtensions) {
+            // Enable MS-Extensions and ...
+            cmd.createArgument().setValue("/x-");
+            // ... do not display a Message about this.
+            cmd.createArgument().setValue("/nomessage");
+        }
+
+        // Do not display Logo
+        cmd.createArgument().setValue("/nologo");
+
+        if (debug) {
+            cmd.createArgument().setValue("/g");
+        }
+        if (optimize) {
+            cmd.createArgument().setValue("/O");
+        }
+        if (verbose) {
+            cmd.createArgument().setValue("/verbose");
+        }
+
+        addCurrentCompilerArgs(cmd);
+
+        int firstFileName = cmd.size();
+        logAndAddFilesToCompile(cmd);
+
+        return
+            executeExternalCompile(cmd.getCommandline(), firstFileName) == 0;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Kjc.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Kjc.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Kjc.java	(revision 16139)
@@ -0,0 +1,116 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.ExecuteJava;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * The implementation of the Java compiler for KJC.
+ * This is primarily a cut-and-paste from Jikes.java and
+ * DefaultCompilerAdapter.
+ *
+ * @since Ant 1.4
+ */
+public class Kjc extends DefaultCompilerAdapter {
+
+    /**
+     * Run the compilation.
+     *
+     * @exception BuildException if the compilation has problems.
+     */
+    public boolean execute() throws BuildException {
+        attributes.log("Using kjc compiler", Project.MSG_VERBOSE);
+        Commandline cmd = setupKjcCommand();
+        cmd.setExecutable("at.dms.kjc.Main");
+        ExecuteJava ej = new ExecuteJava();
+        ej.setJavaCommand(cmd);
+        return ej.fork(getJavac()) == 0;
+    }
+
+    /**
+     * setup kjc command arguments.
+     */
+    protected Commandline setupKjcCommand() {
+        Commandline cmd = new Commandline();
+
+        // generate classpath, because kjc doesn't support sourcepath.
+        Path classpath = getCompileClasspath();
+
+        if (deprecation == true) {
+            cmd.createArgument().setValue("-deprecation");
+        }
+
+        if (destDir != null) {
+            cmd.createArgument().setValue("-d");
+            cmd.createArgument().setFile(destDir);
+        }
+
+        // generate the clsspath
+        cmd.createArgument().setValue("-classpath");
+
+        Path cp = new Path(project);
+
+        // kjc don't have bootclasspath option.
+        if (bootclasspath != null && bootclasspath.size() > 0) {
+            cp.append(bootclasspath);
+        }
+
+        if (extdirs != null) {
+            cp.addExtdirs(extdirs);
+        }
+
+        cp.append(classpath);
+        if (compileSourcepath != null) {
+            cp.append(compileSourcepath);
+        } else {
+            cp.append(src);
+        }
+
+        cmd.createArgument().setPath(cp);
+
+        // kjc-1.5A doesn't support -encoding option now.
+        // but it will be supported near the feature.
+        if (encoding != null) {
+            cmd.createArgument().setValue("-encoding");
+            cmd.createArgument().setValue(encoding);
+        }
+
+        if (debug) {
+            cmd.createArgument().setValue("-g");
+        }
+
+        if (optimize) {
+            cmd.createArgument().setValue("-O2");
+        }
+
+        if (verbose) {
+            cmd.createArgument().setValue("-verbose");
+        }
+
+        addCurrentCompilerArgs(cmd);
+
+        logAndAddFilesToCompile(cmd);
+        return cmd;
+    }
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Sj.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Sj.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/compilers/Sj.java	(revision 16139)
@@ -0,0 +1,58 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * The implementation of the sj compiler.
+ * Uses the defaults for DefaultCompilerAdapter
+ *
+ * @since Ant 1.4
+ */
+public class Sj extends DefaultCompilerAdapter {
+
+    /**
+     * Performs a compile using the sj compiler from Symantec.
+     */
+    public boolean execute() throws BuildException {
+        attributes.log("Using symantec java compiler", Project.MSG_VERBOSE);
+
+        Commandline cmd = setupJavacCommand();
+        String exec = getJavac().getExecutable();
+        cmd.setExecutable(exec == null ? "sj" : exec);
+
+        int firstFileName = cmd.size() - compileList.length;
+
+        return
+            executeExternalCompile(cmd.getCommandline(), firstFileName) == 0;
+    }
+
+    /**
+     * Returns null since sj either has -g for debug=true or no
+     * argument at all.
+     *
+     * @since Ant 1.6.3
+     */
+    protected String getNoDebugArgument() {
+        return null;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/And.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/And.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/And.java	(revision 16139)
@@ -0,0 +1,48 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import java.util.Enumeration;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * &lt;and&gt; condition container.
+ *
+ * <p>Iterates over all conditions and returns false as soon as one
+ * evaluates to false.</p>
+ *
+ * @since Ant 1.4
+ */
+public class And extends ConditionBase implements Condition {
+
+    /**
+     * @return true if all the contained conditions evaluates to true
+     * @exception BuildException if an error occurs
+     */
+    public boolean eval() throws BuildException {
+        Enumeration e = getConditions();
+        while (e.hasMoreElements()) {
+            Condition c = (Condition) e.nextElement();
+            if (!c.eval()) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Condition.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Condition.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Condition.java	(revision 16139)
@@ -0,0 +1,34 @@
+/*
+ * Copyright  2001,2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Interface for conditions to use inside the &lt;condition&gt; task.
+ *
+ */
+public interface Condition {
+    /**
+     * Is this condition true?
+     * @return true if the condition is true
+     * @exception BuildException if an error occurs
+     */
+    boolean eval() throws BuildException;
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/ConditionBase.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/ConditionBase.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/ConditionBase.java	(revision 16139)
@@ -0,0 +1,224 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.taskdefs.Available;
+import org.apache.tools.ant.taskdefs.Checksum;
+import org.apache.tools.ant.taskdefs.UpToDate;
+
+/**
+ * Baseclass for the &lt;condition&gt; task as well as several
+ * conditions - ensures that the types of conditions inside the task
+ * and the "container" conditions are in sync.
+ *
+ * @since Ant 1.4
+ */
+public abstract class ConditionBase extends ProjectComponent {
+    private Vector conditions = new Vector();
+
+    /**
+     * Count the conditions.
+     *
+     * @return the number of conditions in the container
+     * @since 1.1
+     */
+    protected int countConditions() {
+        return conditions.size();
+    }
+
+    /**
+     * Iterate through all conditions.
+     *
+     * @return an enumeration to use for iteration
+     * @since 1.1
+     */
+    protected final Enumeration getConditions() {
+        return conditions.elements();
+    }
+
+    /**
+     * Add an &lt;available&gt; condition.
+     * @param a an available condition
+     * @since 1.1
+     */
+    public void addAvailable(Available a) {
+        conditions.addElement(a);
+    }
+
+    /**
+     * Add an &lt;checksum&gt; condition.
+     *
+     * @param c a Checksum condition
+     * @since 1.4, Ant 1.5
+     */
+    public void addChecksum(Checksum c) {
+        conditions.addElement(c);
+    }
+
+    /**
+     * Add an &lt;uptodate&gt; condition.
+     *
+     * @param u an UpToDate condition
+     * @since 1.1
+     */
+    public void addUptodate(UpToDate u) {
+        conditions.addElement(u);
+    }
+
+    /**
+     * Add an &lt;not&gt; condition "container".
+     *
+     * @param n a Not condition
+     * @since 1.1
+     */
+    public void addNot(Not n) {
+        conditions.addElement(n);
+    }
+
+    /**
+     * Add an &lt;and&gt; condition "container".
+     *
+     * @param a an And condition
+     * @since 1.1
+     */
+    public void addAnd(And a) {
+        conditions.addElement(a);
+    }
+
+    /**
+     * Add an &lt;or&gt; condition "container".
+     *
+     * @param o an Or condition
+     * @since 1.1
+     */
+    public void addOr(Or o) {
+        conditions.addElement(o);
+    }
+
+    /**
+     * Add an &lt;equals&gt; condition.
+     *
+     * @param e an Equals condition
+     * @since 1.1
+     */
+    public void addEquals(Equals e) {
+        conditions.addElement(e);
+    }
+
+    /**
+     * Add an &lt;os&gt; condition.
+     *
+     * @param o an Os condition
+     * @since 1.1
+     */
+    public void addOs(Os o) {
+        conditions.addElement(o);
+    }
+
+    /**
+     * Add an &lt;isset&gt; condition.
+     *
+     * @param i an IsSet condition
+     * @since Ant 1.5
+     */
+    public void addIsSet(IsSet i) {
+        conditions.addElement(i);
+    }
+
+    /**
+     * Add an &lt;http&gt; condition.
+     *
+     * @param h an Http condition
+     * @since Ant 1.5
+     */
+    public void addHttp(Http h) {
+        conditions.addElement(h);
+    }
+
+    /**
+     * Add a &lt;socket&gt; condition.
+     *
+     * @param s a Socket condition
+     * @since Ant 1.5
+     */
+    public void addSocket(Socket s) {
+        conditions.addElement(s);
+    }
+
+    /**
+     * Add a &lt;filesmatch&gt; condition.
+     *
+     * @param test a FilesMatch condition
+     * @since Ant 1.5
+     */
+    public void addFilesMatch(FilesMatch test) {
+        conditions.addElement(test);
+    }
+
+    /**
+     * Add a &lt;contains&gt; condition.
+     *
+     * @param test a Contains condition
+     * @since Ant 1.5
+     */
+    public void addContains(Contains test) {
+        conditions.addElement(test);
+    }
+
+    /**
+     * Add a &lt;istrue&gt; condition.
+     *
+     * @param test an IsTrue condition
+     * @since Ant 1.5
+     */
+    public void addIsTrue(IsTrue test) {
+        conditions.addElement(test);
+    }
+
+    /**
+     * Add a &lt;isfalse&gt; condition.
+     *
+     * @param test an IsFalse condition
+     * @since Ant 1.5
+     */
+    public void addIsFalse(IsFalse test) {
+        conditions.addElement(test);
+    }
+
+    /**
+     * Add an &lt;isreference&gt; condition.
+     *
+     * @param i an IsReference condition
+     * @since Ant 1.6
+     */
+    public void addIsReference(IsReference i) {
+        conditions.addElement(i);
+    }
+
+    /**
+     * Add an arbitrary condition
+     * @param c a  condition
+     * @since Ant 1.6
+     */
+    public void add(Condition c) {
+        conditions.addElement(c);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Contains.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Contains.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Contains.java	(revision 16139)
@@ -0,0 +1,75 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Is one string part of another string?
+ *
+ *
+ * @since Ant 1.5
+ */
+public class Contains implements Condition {
+
+    private String string, subString;
+    private boolean caseSensitive = true;
+
+    /**
+     * The string to search in.
+     * @param string the string to search in
+     * @since Ant 1.5
+     */
+    public void setString(String string) {
+        this.string = string;
+    }
+
+    /**
+     * The string to search for.
+     * @param subString the string to search for
+     * @since Ant 1.5
+     */
+    public void setSubstring(String subString) {
+        this.subString = subString;
+    }
+
+    /**
+     * Whether to search ignoring case or not.
+     * @param b if true, ignore case
+     * @since Ant 1.5
+     */
+    public void setCasesensitive(boolean b) {
+        caseSensitive = b;
+    }
+
+    /**
+     * @since Ant 1.5
+     * @return true if the substring is within the string
+     * @exception BuildException if the attributes are not set correctly
+     */
+    public boolean eval() throws BuildException {
+        if (string == null || subString == null) {
+            throw new BuildException("both string and substring are required "
+                                     + "in contains");
+        }
+
+        return caseSensitive
+            ? string.indexOf(subString) > -1
+            : string.toLowerCase().indexOf(subString.toLowerCase()) > -1;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Equals.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Equals.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Equals.java	(revision 16139)
@@ -0,0 +1,87 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Simple String comparison condition.
+ *
+ * @since Ant 1.4
+ */
+public class Equals implements Condition {
+
+    private String arg1, arg2;
+    private boolean trim = false;
+    private boolean caseSensitive = true;
+
+    /**
+     * Set the first string
+     *
+     * @param a1 the first string
+     */
+    public void setArg1(String a1) {
+        arg1 = a1;
+    }
+
+    /**
+     * Set the second string
+     *
+     * @param a2 the second string
+     */
+    public void setArg2(String a2) {
+        arg2 = a2;
+    }
+
+    /**
+     * Should we want to trim the arguments before comparing them?
+     * @param b if true trim the arguments
+     * @since Ant 1.5
+     */
+    public void setTrim(boolean b) {
+        trim = b;
+    }
+
+    /**
+     * Should the comparison be case sensitive?
+     * @param b if true use a case sensitive comparison (this is the
+     *          default)
+     * @since Ant 1.5
+     */
+    public void setCasesensitive(boolean b) {
+        caseSensitive = b;
+    }
+
+    /**
+     * @return true if the two strings are equal
+     * @exception BuildException if the attributes are not set correctly
+     */
+    public boolean eval() throws BuildException {
+        if (arg1 == null || arg2 == null) {
+            throw new BuildException("both arg1 and arg2 are required in "
+                                     + "equals");
+        }
+
+        if (trim) {
+            arg1 = arg1.trim();
+            arg2 = arg2.trim();
+        }
+
+        return caseSensitive ? arg1.equals(arg2) : arg1.equalsIgnoreCase(arg2);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/FilesMatch.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/FilesMatch.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/FilesMatch.java	(revision 16139)
@@ -0,0 +1,88 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+ package org.apache.tools.ant.taskdefs.condition;
+
+import java.io.File;
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Compares two files for bitwise equality based on size and
+ * content. Timestamps are not at all looked at.
+ *
+ * @since Ant 1.5
+ */
+
+public class FilesMatch implements Condition {
+
+    /**
+     * files to compare
+     */
+    private File file1, file2;
+
+    /**
+     * Helper that provides the file comparison method.
+     */
+    private FileUtils fu = FileUtils.newFileUtils();
+
+    /**
+     * Sets the File1 attribute
+     *
+     * @param file1 The new File1 value
+     */
+    public void setFile1(File file1) {
+        this.file1 = file1;
+    }
+
+
+    /**
+     * Sets the File2 attribute
+     *
+     * @param file2 The new File2 value
+     */
+    public void setFile2(File file2) {
+        this.file2 = file2;
+    }
+
+    /**
+     * comparison method of the interface
+     *
+     * @return true if the files are equal
+     * @exception BuildException if it all went pear-shaped
+     */
+    public boolean eval()
+        throws BuildException {
+
+        //validate
+        if (file1 == null || file2 == null) {
+            throw new BuildException("both file1 and file2 are required in "
+                                     + "filesmatch");
+        }
+
+        //#now match the files
+        boolean matches = false;
+        try {
+            matches = fu.contentEquals(file1, file2);
+        } catch (IOException ioe) {
+            throw new BuildException("when comparing files: "
+                + ioe.getMessage(), ioe);
+        }
+        return matches;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Http.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Http.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Http.java	(revision 16139)
@@ -0,0 +1,90 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Condition to wait for a HTTP request to succeed. Its attribute(s) are:
+ *   url - the URL of the request.
+ *   errorsBeginAt - number at which errors begin at; default=400.
+ * @since Ant 1.5
+ */
+public class Http extends ProjectComponent implements Condition {
+    private String spec = null;
+
+    /**
+     * Set the url attribute
+     *
+     * @param url the url of the request
+     */
+    public void setUrl(String url) {
+        spec = url;
+    }
+
+    private int errorsBeginAt = 400;
+
+    /**
+     * Set the errorsBeginAt attribute
+     *
+     * @param errorsBeginAt number at which errors begin at, default is
+     *                      400
+     */
+    public void setErrorsBeginAt(int errorsBeginAt) {
+        this.errorsBeginAt = errorsBeginAt;
+    }
+
+    /**
+     * @return true if the HTTP request succeeds
+     * @exception BuildException if an error occurs
+     */
+    public boolean eval() throws BuildException {
+        if (spec == null) {
+            throw new BuildException("No url specified in http condition");
+        }
+        log("Checking for " + spec, Project.MSG_VERBOSE);
+        try {
+            URL url = new URL(spec);
+            try {
+                URLConnection conn = url.openConnection();
+                if (conn instanceof HttpURLConnection) {
+                    HttpURLConnection http = (HttpURLConnection) conn;
+                    int code = http.getResponseCode();
+                    log("Result code for " + spec + " was " + code,
+                        Project.MSG_VERBOSE);
+                    if (code > 0 && code < errorsBeginAt) {
+                        return true;
+                    } else {
+                        return false;
+                    }
+                }
+            } catch (java.io.IOException e) {
+                return false;
+            }
+        } catch (MalformedURLException e) {
+            throw new BuildException("Badly formed URL: " + spec, e);
+        }
+        return true;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/IsFalse.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/IsFalse.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/IsFalse.java	(revision 16139)
@@ -0,0 +1,54 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Condition that tests whether a given string evals to false
+ *
+ * @since Ant 1.5
+ */
+public class IsFalse extends ProjectComponent implements Condition {
+    /**
+     * what we eval
+     */
+    private Boolean value = null;
+
+    /**
+     * set the value to be tested; let ant eval it to true/false
+     * @param value the value to test
+     */
+    public void setValue(boolean value) {
+        this.value = value ? Boolean.TRUE : Boolean.FALSE;
+    }
+
+    /**
+     * @return the inverted value;
+     * @throws BuildException if someone forgot to spec a value
+     */
+    public boolean eval() throws BuildException {
+        if (value == null) {
+            throw new BuildException("Nothing to test for falsehood");
+        }
+        return !value.booleanValue();
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/IsFileSelected.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/IsFileSelected.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/IsFileSelected.java	(revision 16139)
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.condition;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+import java.io.File;
+import org.apache.tools.ant.types.selectors.FileSelector;
+import org.apache.tools.ant.types.selectors.AbstractSelectorContainer;
+
+/**
+ * This is a condition that checks to see if a file passes an embedded selector.
+ */
+public class IsFileSelected extends AbstractSelectorContainer implements Condition {
+
+    private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+    private File file;
+    private File baseDir;
+
+    /**
+     * The file to check.
+     * @param file the file to check if if passes the embedded selector.
+     */
+    public void setFile(File file) {
+        this.file = file;
+    }
+
+    /**
+     * The base directory to use.
+     * @param baseDir the base directory to use, if null use the project's
+     *                basedir.
+     */
+    public void setBaseDir(File baseDir) {
+        this.baseDir = baseDir;
+    }
+
+    /**
+     * validate the parameters.
+     */
+    public void validate() {
+        if (selectorCount() != 1) {
+            throw new BuildException("Only one selector allowed");
+        }
+        super.validate();
+    }
+
+    /**
+     * Evaluate the selector with the file.
+     * @return true if the file is selected by the embedded selector.
+     */
+    public boolean eval() {
+        if (file == null) {
+            throw new BuildException("file attribute not set");
+        }
+        validate();
+        File myBaseDir = baseDir;
+        if (myBaseDir == null) {
+            myBaseDir = getProject().getBaseDir();
+        }
+
+        FileSelector f = getSelectors(getProject())[0];
+        return f.isSelected(
+            myBaseDir, FILE_UTILS.removeLeadingPath(myBaseDir, file), file);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/IsReference.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/IsReference.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/IsReference.java	(revision 16139)
@@ -0,0 +1,88 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Condition that tests whether a given reference has been defined.
+ *
+ * <p>Optionally tests whether it is of a given type/class.</p>
+ *
+ * @since Ant 1.6
+ */
+public class IsReference extends ProjectComponent implements Condition {
+    private Reference ref;
+    private String type;
+
+    /**
+     * Set the refid attribute.
+     *
+     * @param r a Reference value
+     */
+    public void setRefid(Reference r) {
+        ref = r;
+    }
+
+    /**
+     * Set the type attribute. This is optional attribute.
+     *
+     * @param type an ant component type name
+     */
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    /**
+     * @return true if the reference exists and if type is set, if
+     *              the reference is the same type
+     * @exception BuildException if an error occurs
+     */
+    public boolean eval() throws BuildException {
+        if (ref == null) {
+            throw new BuildException("No reference specified for isreference "
+                                     + "condition");
+        }
+
+        Object o = getProject().getReference(ref.getRefId());
+
+        if (o == null) {
+            return false;
+        } else if (type == null) {
+            return true;
+        } else {
+            Class typeClass =
+                (Class) getProject().getDataTypeDefinitions().get(type);
+
+            if (typeClass == null) {
+                typeClass =
+                    (Class) getProject().getTaskDefinitions().get(type);
+            }
+
+            if (typeClass == null) {
+                // don't know the type, should throw exception instead?
+                return false;
+            }
+
+            return typeClass.isAssignableFrom(o.getClass());
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/IsSet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/IsSet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/IsSet.java	(revision 16139)
@@ -0,0 +1,53 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Condition that tests whether a given property has been set.
+ *
+ * @since Ant 1.5
+ */
+public class IsSet extends ProjectComponent implements Condition {
+    private String property;
+
+    /**
+     * Set the property attribute
+     *
+     * @param p the property name
+     */
+    public void setProperty(String p) {
+        property = p;
+    }
+
+    /**
+     * @return true if the property exists
+     * @exception BuildException if the property attribute is not set
+     */
+    public boolean eval() throws BuildException {
+        if (property == null) {
+            throw new BuildException("No property specified for isset "
+                                     + "condition");
+        }
+
+        return getProject().getProperty(property) != null;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/IsTrue.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/IsTrue.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/IsTrue.java	(revision 16139)
@@ -0,0 +1,54 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Condition that tests whether a given string evals to true
+ *
+ * @since Ant 1.5
+ */
+public class IsTrue extends ProjectComponent implements Condition {
+    /**
+     * what we eval
+     */
+    private Boolean value = null;
+
+    /**
+     * set the value to be tested; let ant eval it to true/false
+     * @param value the value to test
+     */
+    public void setValue(boolean value) {
+        this.value = value ? Boolean.TRUE : Boolean.FALSE;
+    }
+
+    /**
+     * @return the value
+     * @throws BuildException if someone forgot to spec a value
+     */
+    public boolean eval() throws BuildException {
+        if (value == null) {
+            throw new BuildException("Nothing to test for truth");
+        }
+        return value.booleanValue();
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Not.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Not.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Not.java	(revision 16139)
@@ -0,0 +1,49 @@
+/*
+ * Copyright  2001-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * &lt;not&gt; condition.
+ *
+ * Evaluates to true if the single condition nested into it is false
+ * and vice versa.
+ *
+ * @since Ant 1.4
+ */
+public class Not extends ConditionBase implements Condition {
+
+    /**
+     * Evaluate condition
+     *
+     * @return true if the condition is true.
+     * @throws BuildException if the condition is not configured correctly.
+     */
+    public boolean eval() throws BuildException {
+        if (countConditions() > 1) {
+            throw new BuildException("You must not nest more than one "
+                + "condition into <not>");
+        }
+        if (countConditions() < 1) {
+            throw new BuildException("You must nest a condition into <not>");
+        }
+        return !((Condition) getConditions().nextElement()).eval();
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Or.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Or.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Or.java	(revision 16139)
@@ -0,0 +1,48 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import java.util.Enumeration;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * &lt;or&gt; condition container.
+ *
+ * <p>Iterates over all conditions and returns true as soon as one
+ * evaluates to true.</p>
+ *
+ * @since Ant 1.4
+ */
+public class Or extends ConditionBase implements Condition {
+
+    /**
+     * @return true if any of the contained conditions evaluate to true
+     * @exception BuildException if an error occurs
+     */
+    public boolean eval() throws BuildException {
+        Enumeration e = getConditions();
+        while (e.hasMoreElements()) {
+            Condition c = (Condition) e.nextElement();
+            if (c.eval()) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Os.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Os.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Os.java	(revision 16139)
@@ -0,0 +1,236 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import java.util.Locale;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Condition that tests the OS type.
+ *
+ * @since Ant 1.4
+ */
+public class Os implements Condition {
+    private static final String OS_NAME =
+        System.getProperty("os.name").toLowerCase(Locale.US);
+    private static final String OS_ARCH =
+        System.getProperty("os.arch").toLowerCase(Locale.US);
+    private static final String OS_VERSION =
+        System.getProperty("os.version").toLowerCase(Locale.US);
+    private static final String PATH_SEP =
+        System.getProperty("path.separator");
+
+    private String family;
+    private String name;
+    private String version;
+    private String arch;
+
+    /**
+     * Default constructor
+     *
+     */
+    public Os() {
+    }
+
+    /**
+     * Constructor that sets the family attribute
+     *
+     * @param family a String value
+     */
+    public Os(String family) {
+        setFamily(family);
+    }
+
+    /**
+     * Sets the desired OS family type
+     *
+     * @param f      The OS family type desired<br />
+     *               Possible values:<br />
+     *               <ul>
+     *               <li>dos</li>
+     *               <li>mac</li>
+     *               <li>netware</li>
+     *               <li>os/2</li>
+     *               <li>tandem</li>
+     *               <li>unix</li>
+     *               <li>windows</li>
+     *               <li>win9x</li>
+     *               <li>z/os</li>
+     *               <li>os/400</li>
+     *               </ul>
+     */
+    public void setFamily(String f) {
+        family = f.toLowerCase(Locale.US);
+    }
+
+    /**
+     * Sets the desired OS name
+     *
+     * @param name   The OS name
+     */
+    public void setName(String name) {
+        this.name = name.toLowerCase(Locale.US);
+    }
+
+    /**
+     * Sets the desired OS architecture
+     *
+     * @param arch   The OS architecture
+     */
+    public void setArch(String arch) {
+        this.arch = arch.toLowerCase(Locale.US);
+    }
+
+    /**
+     * Sets the desired OS version
+     *
+     * @param version   The OS version
+     */
+    public void setVersion(String version) {
+        this.version = version.toLowerCase(Locale.US);
+    }
+
+    /**
+     * Determines if the OS on which Ant is executing matches the type of
+     * that set in setFamily.
+     * @see Os#setFamily(String)
+     */
+    public boolean eval() throws BuildException {
+        return isOs(family, name, arch, version);
+    }
+
+    /**
+     * Determines if the OS on which Ant is executing matches the
+     * given OS family.
+     * @param family the family to check for
+     * @return true if the OS matches
+     * @since 1.5
+     */
+    public static boolean isFamily(String family) {
+        return isOs(family, null, null, null);
+    }
+
+    /**
+     * Determines if the OS on which Ant is executing matches the
+     * given OS name.
+     *
+     * @param name the OS name to check for
+     * @return true if the OS matches
+     * @since 1.7
+     */
+    public static boolean isName(String name) {
+        return isOs(null, name, null, null);
+    }
+
+    /**
+     * Determines if the OS on which Ant is executing matches the
+     * given OS architecture.
+     *
+     * @param arch the OS architecture to check for
+     * @return true if the OS matches
+     * @since 1.7
+     */
+    public static boolean isArch(String arch) {
+        return isOs(null, null, arch, null);
+    }
+
+    /**
+     * Determines if the OS on which Ant is executing matches the
+     * given OS version.
+     *
+     * @param version the OS version to check for
+     * @return true if the OS matches
+     * @since 1.7
+     */
+    public static boolean isVersion(String version) {
+        return isOs(null, null, null, version);
+    }
+
+    /**
+     * Determines if the OS on which Ant is executing matches the
+     * given OS family, name, architecture and version
+     *
+     * @param family   The OS family
+     * @param name   The OS name
+     * @param arch   The OS architecture
+     * @param version   The OS version
+     * @return true if the OS matches
+     * @since 1.7
+     */
+    public static boolean isOs(String family, String name, String arch,
+                               String version) {
+        boolean retValue = false;
+
+        if (family != null || name != null || arch != null
+            || version != null) {
+
+            boolean isFamily = true;
+            boolean isName = true;
+            boolean isArch = true;
+            boolean isVersion = true;
+
+            if (family != null) {
+                if (family.equals("windows")) {
+                    isFamily = OS_NAME.indexOf("windows") > -1;
+                } else if (family.equals("os/2")) {
+                    isFamily = OS_NAME.indexOf("os/2") > -1;
+                } else if (family.equals("netware")) {
+                    isFamily = OS_NAME.indexOf("netware") > -1;
+                } else if (family.equals("dos")) {
+                    isFamily = PATH_SEP.equals(";") && !isFamily("netware");
+                } else if (family.equals("mac")) {
+                    isFamily = OS_NAME.indexOf("mac") > -1;
+                } else if (family.equals("tandem")) {
+                    isFamily = OS_NAME.indexOf("nonstop_kernel") > -1;
+                } else if (family.equals("unix")) {
+                    isFamily = PATH_SEP.equals(":")
+                        && !isFamily("openvms")
+                        && (!isFamily("mac") || OS_NAME.endsWith("x"));
+                } else if (family.equals("win9x")) {
+                    isFamily = isFamily("windows")
+                        && (OS_NAME.indexOf("95") >= 0
+                            || OS_NAME.indexOf("98") >= 0
+                            || OS_NAME.indexOf("me") >= 0
+                            || OS_NAME.indexOf("ce") >= 0);
+                } else if (family.equals("z/os")) {
+                    isFamily = OS_NAME.indexOf("z/os") > -1
+                        || OS_NAME.indexOf("os/390") > -1;
+                } else if (family.equals("os/400")) {
+                    isFamily = OS_NAME.indexOf("os/400") > -1;
+                } else if (family.equals("openvms")) {
+                    isFamily = OS_NAME.indexOf("openvms") > -1;
+                } else {
+                    throw new BuildException(
+                        "Don\'t know how to detect os family \""
+                        + family + "\"");
+                }
+            }
+            if (name != null) {
+                isName = name.equals(OS_NAME);
+            }
+            if (arch != null) {
+                isArch = arch.equals(OS_ARCH);
+            }
+            if (version != null) {
+                isVersion = version.equals(OS_VERSION);
+            }
+            retValue = isFamily && isName && isArch && isVersion;
+        }
+        return retValue;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Socket.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Socket.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/condition/Socket.java	(revision 16139)
@@ -0,0 +1,85 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Condition to wait for a TCP/IP socket to have a listener. Its attributes are:
+ *   server - the name of the server.
+ *   port - the port number of the socket.
+ *
+ * @since Ant 1.5
+ */
+public class Socket extends ProjectComponent implements Condition {
+    private String server = null;
+    private int port = 0;
+
+    /**
+     * Set the server attribute
+     *
+     * @param server the server name
+     */
+    public void setServer(String server) {
+        this.server = server;
+    }
+
+    /**
+     * Set the port attribute
+     *
+     * @param port the port number of the socket
+     */
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    /**
+     * @return true if a socket can be created
+     * @exception BuildException if the attributes are not set
+     */
+    public boolean eval() throws BuildException {
+        if (server == null) {
+            throw new BuildException("No server specified in socket "
+                                     + "condition");
+        }
+        if (port == 0) {
+            throw new BuildException("No port specified in socket condition");
+        }
+        log("Checking for listener at " + server + ":" + port,
+            Project.MSG_VERBOSE);
+        java.net.Socket s = null;
+        try {
+            s = new java.net.Socket(server, port);
+        } catch (IOException e) {
+            return false;
+        } finally {
+          if (s != null) {
+            try {
+              s.close();
+            } catch (IOException ioe) {
+              // Intentionally left blank
+            }
+          }
+        }
+        return true;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/CVSEntry.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/CVSEntry.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/CVSEntry.java	(revision 16139)
@@ -0,0 +1,70 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.cvslib;
+
+import java.util.Date;
+import java.util.Vector;
+
+/**
+ * CVS Entry.
+ *
+ */
+class CVSEntry {
+    private Date m_date;
+    private String m_author;
+    private final String m_comment;
+    private final Vector m_files = new Vector();
+
+    public CVSEntry(Date date, String author, String comment) {
+        m_date = date;
+        m_author = author;
+        m_comment = comment;
+    }
+
+    public void addFile(String file, String revision) {
+        m_files.addElement(new RCSFile(file, revision));
+    }
+
+    public void addFile(String file, String revision, String previousRevision) {
+        m_files.addElement(new RCSFile(file, revision, previousRevision));
+    }
+
+    Date getDate() {
+        return m_date;
+    }
+
+    void setAuthor(final String author) {
+        m_author = author;
+    }
+
+    String getAuthor() {
+        return m_author;
+    }
+
+    String getComment() {
+        return m_comment;
+    }
+
+    Vector getFiles() {
+        return m_files;
+    }
+
+    public String toString() {
+        return getAuthor() + "\n" + getDate() + "\n" + getFiles() + "\n"
+            + getComment();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogParser.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogParser.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogParser.java	(revision 16139)
@@ -0,0 +1,239 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.cvslib;
+
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.TimeZone;
+
+/**
+ * A class used to parse the output of the CVS log command.
+ *
+ */
+class ChangeLogParser {
+    //private static final int GET_ENTRY = 0;
+    private static final int GET_FILE = 1;
+    private static final int GET_DATE = 2;
+    private static final int GET_COMMENT = 3;
+    private static final int GET_REVISION = 4;
+    private static final int GET_PREVIOUS_REV = 5;
+
+    /** input format for dates read in from cvs log */
+    private static final SimpleDateFormat c_inputDate
+        = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+
+    static {
+        TimeZone utc = TimeZone.getTimeZone("UTC");
+        c_inputDate.setTimeZone(utc);
+    }
+
+    //The following is data used while processing stdout of CVS command
+    private String m_file;
+    private String m_date;
+    private String m_author;
+    private String m_comment;
+    private String m_revision;
+    private String m_previousRevision;
+
+    private int m_status = GET_FILE;
+
+    /** rcs entries */
+    private final Hashtable m_entries = new Hashtable();
+
+    /**
+     * Get a list of rcs entries as an array.
+     *
+     * @return a list of rcs entries as an array
+     */
+    CVSEntry[] getEntrySetAsArray() {
+        final CVSEntry[] array = new CVSEntry[ m_entries.size() ];
+        Enumeration e = m_entries.elements();
+        int i = 0;
+        while (e.hasMoreElements()) {
+            array[i++] = (CVSEntry) e.nextElement();
+        }
+        return array;
+    }
+
+    /**
+     * Receive notification about the process writing
+     * to standard output.
+     */
+    public void stdout(final String line) {
+        switch(m_status) {
+            case GET_FILE:
+                // make sure attributes are reset when
+                // working on a 'new' file.
+                reset();
+                processFile(line);
+                break;
+            case GET_REVISION:
+                processRevision(line);
+                break;
+
+            case GET_DATE:
+                processDate(line);
+                break;
+
+            case GET_COMMENT:
+                processComment(line);
+                break;
+
+            case GET_PREVIOUS_REV:
+                processGetPreviousRevision(line);
+                break;
+        }
+    }
+
+    /**
+     * Process a line while in "GET_COMMENT" state.
+     *
+     * @param line the line
+     */
+    private void processComment(final String line) {
+        final String lineSeparator = System.getProperty("line.separator");
+        if (line.startsWith("======")) {
+            //We have ended changelog for that particular file
+            //so we can save it
+            final int end
+                = m_comment.length() - lineSeparator.length(); //was -1
+            m_comment = m_comment.substring(0, end);
+            saveEntry();
+            m_status = GET_FILE;
+        } else if (line.startsWith("----------------------------")) {
+            final int end
+                = m_comment.length() - lineSeparator.length(); //was -1
+            m_comment = m_comment.substring(0, end);
+            m_status = GET_PREVIOUS_REV;
+        } else {
+            m_comment += line + lineSeparator;
+        }
+    }
+
+    /**
+     * Process a line while in "GET_FILE" state.
+     *
+     * @param line the line
+     */
+    private void processFile(final String line) {
+        if (line.startsWith("Working file:")) {
+            m_file = line.substring(14, line.length());
+            m_status = GET_REVISION;
+        }
+    }
+
+    /**
+     * Process a line while in "REVISION" state.
+     *
+     * @param line the line
+     */
+    private void processRevision(final String line) {
+        if (line.startsWith("revision")) {
+            m_revision = line.substring(9);
+            m_status = GET_DATE;
+        } else if (line.startsWith("======")) {
+            //There was no revisions in this changelog
+            //entry so lets move unto next file
+            m_status = GET_FILE;
+        }
+    }
+
+    /**
+     * Process a line while in "DATE" state.
+     *
+     * @param line the line
+     */
+    private void processDate(final String line) {
+        if (line.startsWith("date:")) {
+            m_date = line.substring(6, 25);
+            String lineData = line.substring(line.indexOf(";") + 1);
+            m_author = lineData.substring(10, lineData.indexOf(";"));
+
+            m_status = GET_COMMENT;
+
+            //Reset comment to empty here as we can accumulate multiple lines
+            //in the processComment method
+            m_comment = "";
+        }
+    }
+
+    /**
+     * Process a line while in "GET_PREVIOUS_REVISION" state.
+     *
+     * @param line the line
+     */
+    private void processGetPreviousRevision(final String line) {
+        if (!line.startsWith("revision")) {
+            throw new IllegalStateException("Unexpected line from CVS: "
+                + line);
+        }
+        m_previousRevision = line.substring(9);
+
+        saveEntry();
+
+        m_revision = m_previousRevision;
+        m_status = GET_DATE;
+    }
+
+    /**
+     * Utility method that saves the current entry.
+     */
+    private void saveEntry() {
+        final String entryKey = m_date + m_author + m_comment;
+        CVSEntry entry;
+        if (!m_entries.containsKey(entryKey)) {
+            entry = new CVSEntry(parseDate(m_date), m_author, m_comment);
+            m_entries.put(entryKey, entry);
+        } else {
+            entry = (CVSEntry) m_entries.get(entryKey);
+        }
+
+        entry.addFile(m_file, m_revision, m_previousRevision);
+    }
+
+    /**
+     * Parse date out from expected format.
+     *
+     * @param date the string holding dat
+     * @return the date object or null if unknown date format
+     */
+    private Date parseDate(final String date) {
+        try {
+            return c_inputDate.parse(date);
+        } catch (ParseException e) {
+            //final String message = REZ.getString( "changelog.bat-date.error", date );
+            //getContext().error( message );
+            return null;
+        }
+    }
+
+    /**
+     * reset all internal attributes except status.
+     */
+    private void reset() {
+        m_file = null;
+        m_date = null;
+        m_author = null;
+        m_comment = null;
+        m_revision = null;
+        m_previousRevision = null;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogTask.java	(revision 16139)
@@ -0,0 +1,398 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.cvslib;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.AbstractCvsTask;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * Examines the output of cvs log and group related changes together.
+ *
+ * It produces an XML output representing the list of changes.
+ * <PRE>
+ * <FONT color=#0000ff>&lt;!-- Root element --&gt;</FONT>
+ * <FONT color=#6a5acd>&lt;!ELEMENT</FONT> changelog <FONT color=#ff00ff>(entry</FONT><FONT color=#ff00ff>+</FONT><FONT color=#ff00ff>)</FONT><FONT color=#6a5acd>&gt;</FONT>
+ * <FONT color=#0000ff>&lt;!-- CVS Entry --&gt;</FONT>
+ * <FONT color=#6a5acd>&lt;!ELEMENT</FONT> entry <FONT color=#ff00ff>(date,author,file</FONT><FONT color=#ff00ff>+</FONT><FONT color=#ff00ff>,msg)</FONT><FONT color=#6a5acd>&gt;</FONT>
+ * <FONT color=#0000ff>&lt;!-- Date of cvs entry --&gt;</FONT>
+ * <FONT color=#6a5acd>&lt;!ELEMENT</FONT> date <FONT color=#ff00ff>(#PCDATA)</FONT><FONT color=#6a5acd>&gt;</FONT>
+ * <FONT color=#0000ff>&lt;!-- Author of change --&gt;</FONT>
+ * <FONT color=#6a5acd>&lt;!ELEMENT</FONT> author <FONT color=#ff00ff>(#PCDATA)</FONT><FONT color=#6a5acd>&gt;</FONT>
+ * <FONT color=#0000ff>&lt;!-- List of files affected --&gt;</FONT>
+ * <FONT color=#6a5acd>&lt;!ELEMENT</FONT> msg <FONT color=#ff00ff>(#PCDATA)</FONT><FONT color=#6a5acd>&gt;</FONT>
+ * <FONT color=#0000ff>&lt;!-- File changed --&gt;</FONT>
+ * <FONT color=#6a5acd>&lt;!ELEMENT</FONT> file <FONT color=#ff00ff>(name,revision,prevrevision</FONT><FONT color=#ff00ff>?</FONT><FONT color=#ff00ff>)</FONT><FONT color=#6a5acd>&gt;</FONT>
+ * <FONT color=#0000ff>&lt;!-- Name of the file --&gt;</FONT>
+ * <FONT color=#6a5acd>&lt;!ELEMENT</FONT> name <FONT color=#ff00ff>(#PCDATA)</FONT><FONT color=#6a5acd>&gt;</FONT>
+ * <FONT color=#0000ff>&lt;!-- Revision number --&gt;</FONT>
+ * <FONT color=#6a5acd>&lt;!ELEMENT</FONT> revision <FONT color=#ff00ff>(#PCDATA)</FONT><FONT color=#6a5acd>&gt;</FONT>
+ * <FONT color=#0000ff>&lt;!-- Previous revision number --&gt;</FONT>
+ * <FONT color=#6a5acd>&lt;!ELEMENT</FONT> prevrevision <FONT color=#ff00ff>(#PCDATA)</FONT><FONT color=#6a5acd>&gt;</FONT>
+ * </PRE>
+ *
+ * @since Ant 1.5
+ * @ant.task name="cvschangelog" category="scm"
+ */
+public class ChangeLogTask extends AbstractCvsTask {
+    /** User list */
+    private File m_usersFile;
+
+    /** User list */
+    private Vector m_cvsUsers = new Vector();
+
+    /** Input dir */
+    private File m_dir;
+
+    /** Output file */
+    private File m_destfile;
+
+    /** The earliest date at which to start processing entries.  */
+    private Date m_start;
+
+    /** The latest date at which to stop processing entries.  */
+    private Date m_stop;
+
+    /**
+     * Filesets containing list of files against which the cvs log will be
+     * performed. If empty then all files will in the working directory will
+     * be checked.
+     */
+    private final Vector m_filesets = new Vector();
+
+
+    /**
+     * Set the base dir for cvs.
+     *
+     * @param dir The new dir value
+     */
+    public void setDir(final File dir) {
+        m_dir = dir;
+    }
+
+
+    /**
+     * Set the output file for the log.
+     *
+     * @param destfile The new destfile value
+     */
+    public void setDestfile(final File destfile) {
+        m_destfile = destfile;
+    }
+
+
+    /**
+     * Set a lookup list of user names & addresses
+     *
+     * @param usersFile The file containing the users info.
+     */
+    public void setUsersfile(final File usersFile) {
+        m_usersFile = usersFile;
+    }
+
+
+    /**
+     * Add a user to list changelog knows about.
+     *
+     * @param user the user
+     */
+    public void addUser(final CvsUser user) {
+        m_cvsUsers.addElement(user);
+    }
+
+
+    /**
+     * Set the date at which the changelog should start.
+     *
+     * @param start The date at which the changelog should start.
+     */
+    public void setStart(final Date start) {
+        m_start = start;
+    }
+
+
+    /**
+     * Set the date at which the changelog should stop.
+     *
+     * @param stop The date at which the changelog should stop.
+     */
+    public void setEnd(final Date stop) {
+        m_stop = stop;
+    }
+
+
+    /**
+     * Set the number of days worth of log entries to process.
+     *
+     * @param days the number of days of log to process.
+     */
+    public void setDaysinpast(final int days) {
+        final long time = System.currentTimeMillis()
+             - (long) days * 24 * 60 * 60 * 1000;
+
+        setStart(new Date(time));
+    }
+
+
+    /**
+     * Adds a set of files about which cvs logs will be generated.
+     *
+     * @param fileSet a set of files about which cvs logs will be generated.
+     */
+    public void addFileset(final FileSet fileSet) {
+        m_filesets.addElement(fileSet);
+    }
+
+
+    /**
+     * Execute task
+     *
+     * @exception BuildException if something goes wrong executing the
+     *            cvs command
+     */
+    public void execute() throws BuildException {
+        File savedDir = m_dir; // may be altered in validate
+
+        try {
+
+            validate();
+            final Properties userList = new Properties();
+
+            loadUserlist(userList);
+
+            for (Enumeration e = m_cvsUsers.elements();
+                e.hasMoreElements();) {
+                final CvsUser user = (CvsUser) e.nextElement();
+
+                user.validate();
+                userList.put(user.getUserID(), user.getDisplayname());
+            }
+
+
+            setCommand("log");
+
+            if (getTag() != null) {
+                CvsVersion myCvsVersion = new CvsVersion();
+                myCvsVersion.setProject(getProject());
+                myCvsVersion.setTaskName("cvsversion");
+                myCvsVersion.setCvsRoot(getCvsRoot());
+                myCvsVersion.setCvsRsh(getCvsRsh());
+                myCvsVersion.setPassfile(getPassFile());
+                myCvsVersion.setDest(m_dir);
+                myCvsVersion.execute();
+                if (myCvsVersion.supportsCvsLogWithSOption()) {
+                    addCommandArgument("-S");
+                }
+            }
+            if (null != m_start) {
+                final SimpleDateFormat outputDate =
+                    new SimpleDateFormat("yyyy-MM-dd");
+
+                // We want something of the form: -d ">=YYYY-MM-dd"
+                final String dateRange = ">=" + outputDate.format(m_start);
+
+                // Supply '-d' as a separate argument - Bug# 14397
+                addCommandArgument("-d");
+                addCommandArgument(dateRange);
+            }
+
+            // Check if list of files to check has been specified
+            if (!m_filesets.isEmpty()) {
+                final Enumeration e = m_filesets.elements();
+
+                while (e.hasMoreElements()) {
+                    final FileSet fileSet = (FileSet) e.nextElement();
+                    final DirectoryScanner scanner =
+                        fileSet.getDirectoryScanner(getProject());
+                    final String[] files = scanner.getIncludedFiles();
+
+                    for (int i = 0; i < files.length; i++) {
+                        addCommandArgument(files[i]);
+                    }
+                }
+            }
+
+            final ChangeLogParser parser = new ChangeLogParser();
+            final RedirectingStreamHandler handler =
+                new RedirectingStreamHandler(parser);
+
+            log(getCommand(), Project.MSG_VERBOSE);
+
+            setDest(m_dir);
+            setExecuteStreamHandler(handler);
+            try {
+                super.execute();
+            } finally {
+                final String errors = handler.getErrors();
+
+                if (null != errors) {
+                    log(errors, Project.MSG_ERR);
+                }
+            }
+            final CVSEntry[] entrySet = parser.getEntrySetAsArray();
+            final CVSEntry[] filteredEntrySet = filterEntrySet(entrySet);
+
+            replaceAuthorIdWithName(userList, filteredEntrySet);
+
+            writeChangeLog(filteredEntrySet);
+
+        } finally {
+            m_dir = savedDir;
+        }
+    }
+
+    /**
+     * Validate the parameters specified for task.
+     *
+     * @throws BuildException if fails validation checks
+     */
+    private void validate()
+         throws BuildException {
+        if (null == m_dir) {
+            m_dir = getProject().getBaseDir();
+        }
+        if (null == m_destfile) {
+            final String message = "Destfile must be set.";
+
+            throw new BuildException(message);
+        }
+        if (!m_dir.exists()) {
+            final String message = "Cannot find base dir "
+                 + m_dir.getAbsolutePath();
+
+            throw new BuildException(message);
+        }
+        if (null != m_usersFile && !m_usersFile.exists()) {
+            final String message = "Cannot find user lookup list "
+                 + m_usersFile.getAbsolutePath();
+
+            throw new BuildException(message);
+        }
+    }
+
+    /**
+     * Load the userlist from the userList file (if specified) and add to
+     * list of users.
+     *
+     * @param userList the file of users
+     * @throws BuildException if file can not be loaded for some reason
+     */
+    private void loadUserlist(final Properties userList)
+         throws BuildException {
+        if (null != m_usersFile) {
+            try {
+                userList.load(new FileInputStream(m_usersFile));
+            } catch (final IOException ioe) {
+                throw new BuildException(ioe.toString(), ioe);
+            }
+        }
+    }
+
+    /**
+     * Filter the specified entries according to an appropriate rule.
+     *
+     * @param entrySet the entry set to filter
+     * @return the filtered entry set
+     */
+    private CVSEntry[] filterEntrySet(final CVSEntry[] entrySet) {
+        final Vector results = new Vector();
+
+        for (int i = 0; i < entrySet.length; i++) {
+            final CVSEntry cvsEntry = entrySet[i];
+            final Date date = cvsEntry.getDate();
+
+            if (null != m_start && m_start.after(date)) {
+                //Skip dates that are too early
+                continue;
+            }
+            if (null != m_stop && m_stop.before(date)) {
+                //Skip dates that are too late
+                continue;
+            }
+            results.addElement(cvsEntry);
+        }
+
+        final CVSEntry[] resultArray = new CVSEntry[results.size()];
+
+        results.copyInto(resultArray);
+        return resultArray;
+    }
+
+    /**
+     * replace all known author's id's with their maven specified names
+     */
+    private void replaceAuthorIdWithName(final Properties userList,
+                                         final CVSEntry[] entrySet) {
+        for (int i = 0; i < entrySet.length; i++) {
+
+            final CVSEntry entry = entrySet[ i ];
+            if (userList.containsKey(entry.getAuthor())) {
+                entry.setAuthor(userList.getProperty(entry.getAuthor()));
+            }
+        }
+    }
+
+    /**
+     * Print changelog to file specified in task.
+     *
+     * @param entrySet the entry set to write.
+     * @throws BuildException if there is an error writing changelog.
+     */
+    private void writeChangeLog(final CVSEntry[] entrySet)
+         throws BuildException {
+        FileOutputStream output = null;
+
+        try {
+            output = new FileOutputStream(m_destfile);
+
+            final PrintWriter writer =
+                new PrintWriter(new OutputStreamWriter(output, "UTF-8"));
+
+            final ChangeLogWriter serializer = new ChangeLogWriter();
+
+            serializer.printChangeLog(writer, entrySet);
+        } catch (final UnsupportedEncodingException uee) {
+            getProject().log(uee.toString(), Project.MSG_ERR);
+        } catch (final IOException ioe) {
+            throw new BuildException(ioe.toString(), ioe);
+        } finally {
+            if (null != output) {
+                try {
+                    output.close();
+                } catch (final IOException ioe) {
+                }
+            }
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogWriter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogWriter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/ChangeLogWriter.java	(revision 16139)
@@ -0,0 +1,115 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.cvslib;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.Enumeration;
+import java.util.TimeZone;
+
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.apache.tools.ant.util.DOMUtils;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Class used to generate an XML changelog.
+ *
+ */
+class ChangeLogWriter {
+    /** output format for dates written to xml file */
+    private static final SimpleDateFormat c_outputDate
+        = new SimpleDateFormat("yyyy-MM-dd");
+    /** output format for times written to xml file */
+    private static final SimpleDateFormat c_outputTime
+        = new SimpleDateFormat("HH:mm");
+    /** stateless helper for writing the XML document */
+    private static final DOMElementWriter DOM_WRITER = new DOMElementWriter();
+
+    static {
+        TimeZone utc = TimeZone.getTimeZone("UTC");
+        c_outputDate.setTimeZone(utc);
+        c_outputTime.setTimeZone(utc);
+    }
+
+    /**
+     * Print out the specified entries.
+     *
+     * @param output writer to which to send output.
+     * @param entries the entries to be written.
+     */
+    public void printChangeLog(final PrintWriter output,
+                               final CVSEntry[] entries) {
+        try {
+            output.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+            Document doc = DOMUtils.newDocument();
+            Element root = doc.createElement("changelog");
+            DOM_WRITER.openElement(root, output, 0, "\t");
+            output.println();
+            for (int i = 0; i < entries.length; i++) {
+                final CVSEntry entry = entries[i];
+
+                printEntry(doc, output, entry);
+            }
+            DOM_WRITER.closeElement(root, output, 0, "\t", true);
+            output.flush();
+            output.close();
+        } catch (IOException e) {
+            throw new org.apache.tools.ant.BuildException(e);
+        }
+    }
+
+
+    /**
+     * Print out an individual entry in changelog.
+     *
+     * @param doc Document used to create elements.
+     * @param entry the entry to print
+     * @param output writer to which to send output.
+     */
+    private void printEntry(Document doc, final PrintWriter output,
+                            final CVSEntry entry) throws IOException {
+        Element ent = doc.createElement("entry");
+        DOMUtils.appendTextElement(ent, "date",
+                                   c_outputDate.format(entry.getDate()));
+        DOMUtils.appendTextElement(ent, "time",
+                                   c_outputTime.format(entry.getDate()));
+        DOMUtils.appendCDATAElement(ent, "author", entry.getAuthor());
+
+        final Enumeration enumeration = entry.getFiles().elements();
+
+        while (enumeration.hasMoreElements()) {
+            final RCSFile file = (RCSFile) enumeration.nextElement();
+
+            Element f = DOMUtils.createChildElement(ent, "file");
+            DOMUtils.appendCDATAElement(f, "name", file.getName());
+            DOMUtils.appendTextElement(f, "revision", file.getRevision());
+
+            final String previousRevision = file.getPreviousRevision();
+
+            if (previousRevision != null) {
+                DOMUtils.appendTextElement(f, "prevrevision",
+                                           previousRevision);
+            }
+        }
+        DOMUtils.appendCDATAElement(ent, "msg", entry.getComment());
+        DOM_WRITER.write(ent, output, 1, "\t");
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsTagDiff.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsTagDiff.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsTagDiff.java	(revision 16139)
@@ -0,0 +1,456 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.cvslib;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.util.Vector;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.AbstractCvsTask;
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.apache.tools.ant.util.DOMUtils;
+import org.apache.tools.ant.util.FileUtils;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * Examines the output of cvs rdiff between two tags.
+ *
+ * It produces an XML output representing the list of changes.
+ * <PRE>
+ * &lt;!-- Root element --&gt;
+ * &lt;!ELEMENT tagdiff ( entry+ ) &gt;
+ * &lt;!-- Start tag of the report --&gt;
+ * &lt;!ATTLIST tagdiff startTag NMTOKEN #IMPLIED &gt;
+ * &lt;!-- End tag of the report --&gt;
+ * &lt;!ATTLIST tagdiff endTag NMTOKEN #IMPLIED &gt;
+ * &lt;!-- Start date of the report --&gt;
+ * &lt;!ATTLIST tagdiff startDate NMTOKEN #IMPLIED &gt;
+ * &lt;!-- End date of the report --&gt;
+ * &lt;!ATTLIST tagdiff endDate NMTOKEN #IMPLIED &gt;
+ *
+ * &lt;!-- CVS tag entry --&gt;
+ * &lt;!ELEMENT entry ( file ) &gt;
+ * &lt;!-- File added, changed or removed --&gt;
+ * &lt;!ELEMENT file ( name, revision?, prevrevision? ) &gt;
+ * &lt;!-- Name of the file --&gt;
+ * &lt;!ELEMENT name ( #PCDATA ) &gt;
+ * &lt;!-- Revision number --&gt;
+ * &lt;!ELEMENT revision ( #PCDATA ) &gt;
+ * &lt;!-- Previous revision number --&gt;
+ * &lt;!ELEMENT prevrevision ( #PCDATA ) &gt;
+ * </PRE>
+ *
+ * @since Ant 1.5
+ * @ant.task name="cvstagdiff"
+ */
+public class CvsTagDiff extends AbstractCvsTask {
+
+    /** stateless helper for writing the XML document */
+    private static final DOMElementWriter DOM_WRITER = new DOMElementWriter();
+
+    /**
+     * Token to identify the word file in the rdiff log
+     */
+    static final String FILE_STRING = "File ";
+    /**
+     * Token to identify the word file in the rdiff log
+     */
+    static final String TO_STRING = " to ";
+    /**
+     * Token to identify a new file in the rdiff log
+     */
+    static final String FILE_IS_NEW = " is new;";
+    /**
+     * Token to identify the revision
+     */
+    static final String REVISION = "revision ";
+
+    /**
+     * Token to identify a modified file in the rdiff log
+     */
+    static final String FILE_HAS_CHANGED = " changed from revision ";
+
+    /**
+     * Token to identify a removed file in the rdiff log
+     */
+    static final String FILE_WAS_REMOVED = " is removed";
+
+    /**
+     * The cvs package/module to analyse
+     */
+    private String mypackage;
+
+    /**
+     * The earliest tag from which diffs are to be included in the report.
+     */
+    private String mystartTag;
+
+    /**
+     * The latest tag from which diffs are to be included in the report.
+     */
+    private String myendTag;
+
+    /**
+     * The earliest date from which diffs are to be included in the report.
+     */
+    private String mystartDate;
+
+    /**
+     * The latest date from which diffs are to be included in the report.
+     */
+    private String myendDate;
+
+    /**
+     * The file in which to write the diff report.
+     */
+    private File mydestfile;
+
+    /**
+     * Used to create the temp file for cvs log
+     */
+    private FileUtils myfileUtils = FileUtils.newFileUtils();
+
+    /**
+     * The package/module to analyze.
+     * @param p the name of the package to analyse
+     */
+    public void setPackage(String p) {
+        mypackage = p;
+    }
+
+    /**
+     * Set the start tag.
+     *
+     * @param s the start tag.
+     */
+    public void setStartTag(String s) {
+        mystartTag = s;
+    }
+
+    /**
+     * Set the start date.
+     *
+     * @param s the start date.
+     */
+    public void setStartDate(String s) {
+        mystartDate = s;
+    }
+
+    /**
+     * Set the end tag.
+     *
+     * @param s the end tag.
+     */
+    public void setEndTag(String s) {
+        myendTag = s;
+    }
+
+    /**
+     * Set the end date.
+     *
+     * @param s the end date.
+     */
+    public void setEndDate(String s) {
+        myendDate = s;
+    }
+
+    /**
+     * Set the output file for the diff.
+     *
+     * @param f the output file for the diff.
+     */
+    public void setDestFile(File f) {
+        mydestfile = f;
+    }
+
+    /**
+     * Execute task.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+        // validate the input parameters
+        validate();
+
+        // build the rdiff command
+        addCommandArgument("rdiff");
+        addCommandArgument("-s");
+        if (mystartTag != null) {
+            addCommandArgument("-r");
+            addCommandArgument(mystartTag);
+        } else {
+            addCommandArgument("-D");
+            addCommandArgument(mystartDate);
+        }
+        if (myendTag != null) {
+            addCommandArgument("-r");
+            addCommandArgument(myendTag);
+        } else {
+            addCommandArgument("-D");
+            addCommandArgument(myendDate);
+        }
+        // support multiple packages
+        StringTokenizer myTokenizer = new StringTokenizer(mypackage);
+        while (myTokenizer.hasMoreTokens()) {
+            addCommandArgument(myTokenizer.nextToken());
+        }
+        // force command not to be null
+        setCommand("");
+        File tmpFile = null;
+        try {
+            tmpFile = myfileUtils.createTempFile("cvstagdiff", ".log", null);
+            tmpFile.deleteOnExit();
+            setOutput(tmpFile);
+
+            // run the cvs command
+            super.execute();
+
+            // parse the rdiff
+            CvsTagEntry[] entries = parseRDiff(tmpFile);
+
+            // write the tag diff
+            writeTagDiff(entries);
+
+        } finally {
+            if (tmpFile != null) {
+                tmpFile.delete();
+            }
+        }
+    }
+
+    /**
+     * Parse the tmpFile and return and array of CvsTagEntry to be
+     * written in the output.
+     *
+     * @param tmpFile the File containing the output of the cvs rdiff command
+     * @return the entries in the output
+     * @exception BuildException if an error occurs
+     */
+    private CvsTagEntry[] parseRDiff(File tmpFile) throws BuildException {
+        // parse the output of the command
+        BufferedReader reader = null;
+
+        try {
+            reader = new BufferedReader(new FileReader(tmpFile));
+
+            // entries are of the form:
+            //CVS 1.11
+            // File module/filename is new; current revision 1.1
+            //CVS 1.11.9
+            // File module/filename is new; cvstag_2003_11_03_2  revision 1.1
+            // or
+            // File module/filename changed from revision 1.4 to 1.6
+            // or
+            // File module/filename is removed; not included in
+            // release tag SKINLF_12
+            //CVS 1.11.9
+            // File testantoine/antoine.bat is removed; TESTANTOINE_1 revision 1.1.1.1
+            //
+            // get rid of 'File module/"
+            String toBeRemoved = FILE_STRING + mypackage + "/";
+            int headerLength = toBeRemoved.length();
+            Vector entries = new Vector();
+
+            String line = reader.readLine();
+            int index;
+            CvsTagEntry entry = null;
+
+            while (null != line) {
+                if (line.length() > headerLength) {
+                    if (line.startsWith(toBeRemoved)) {
+                        line = line.substring(headerLength);
+                    } else {
+                        line = line.substring(FILE_STRING.length());
+                    }
+
+                    if ((index = line.indexOf(FILE_IS_NEW)) != -1) {
+                        // it is a new file
+                        // set the revision but not the prevrevision
+                        String filename = line.substring(0, index);
+                        String rev = null;
+                        int indexrev = -1;
+                        if ((indexrev = line.indexOf(REVISION, index)) != -1) {
+                            rev = line.substring(indexrev + REVISION.length());
+                        }
+                        entry = new CvsTagEntry(filename, rev);
+                        entries.addElement(entry);
+                        log(entry.toString(), Project.MSG_VERBOSE);
+                    } else if ((index = line.indexOf(FILE_HAS_CHANGED)) != -1) {
+                        // it is a modified file
+                        // set the revision and the prevrevision
+                        String filename = line.substring(0, index);
+                        int revSeparator = line.indexOf(" to ", index);
+                        String prevRevision =
+                            line.substring(index + FILE_HAS_CHANGED.length(),
+                                revSeparator);
+                        String revision = line.substring(revSeparator + TO_STRING.length());
+                        entry = new CvsTagEntry(filename,
+                            revision,
+                            prevRevision);
+                        entries.addElement(entry);
+                        log(entry.toString(), Project.MSG_VERBOSE);
+                    } else if ((index = line.indexOf(FILE_WAS_REMOVED)) != -1) {
+                        // it is a removed file
+                        String filename = line.substring(0, index);
+                        String rev = null;
+                        int indexrev = -1;
+                        if ((indexrev = line.indexOf(REVISION, index)) != -1) {
+                            rev = line.substring(indexrev + REVISION.length());
+                        }
+                        entry = new CvsTagEntry(filename, null, rev);
+                        entries.addElement(entry);
+                        log(entry.toString(), Project.MSG_VERBOSE);
+                    }
+                }
+                line = reader.readLine();
+            }
+
+            CvsTagEntry[] array = new CvsTagEntry[entries.size()];
+            entries.copyInto(array);
+
+            return array;
+        } catch (IOException e) {
+            throw new BuildException("Error in parsing", e);
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (IOException e) {
+                    log(e.toString(), Project.MSG_ERR);
+                }
+            }
+        }
+    }
+
+    /**
+     * Write the rdiff log.
+     *
+     * @param entries a <code>CvsTagEntry[]</code> value
+     * @exception BuildException if an error occurs
+     */
+    private void writeTagDiff(CvsTagEntry[] entries) throws BuildException {
+        FileOutputStream output = null;
+        try {
+            output = new FileOutputStream(mydestfile);
+            PrintWriter writer = new PrintWriter(
+                                     new OutputStreamWriter(output, "UTF-8"));
+            writer.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+            Document doc = DOMUtils.newDocument();
+            Element root = doc.createElement("tagdiff");
+            if (mystartTag != null) {
+                root.setAttribute("startTag", mystartTag);
+            } else {
+                root.setAttribute("startDate", mystartDate);
+            }
+            if (myendTag != null) {
+                root.setAttribute("endTag", myendTag);
+            } else {
+                root.setAttribute("endDate", myendDate);
+            }
+
+            root.setAttribute("cvsroot", getCvsRoot());
+            root.setAttribute("package", mypackage);
+            DOM_WRITER.openElement(root, writer, 0, "\t");
+            writer.println();
+            for (int i = 0, c = entries.length; i < c; i++) {
+                writeTagEntry(doc, writer, entries[i]);
+            }
+            DOM_WRITER.closeElement(root, writer, 0, "\t", true);
+            writer.flush();
+            writer.close();
+        } catch (UnsupportedEncodingException uee) {
+            log(uee.toString(), Project.MSG_ERR);
+        } catch (IOException ioe) {
+            throw new BuildException(ioe.toString(), ioe);
+        } finally {
+            if (null != output) {
+                try {
+                    output.close();
+                } catch (IOException ioe) {
+                    log(ioe.toString(), Project.MSG_ERR);
+                }
+            }
+        }
+    }
+
+    /**
+     * Write a single entry to the given writer.
+     *
+     * @param doc Document used to create elements.
+     * @param writer a <code>PrintWriter</code> value
+     * @param entry a <code>CvsTagEntry</code> value
+     */
+    private void writeTagEntry(Document doc, PrintWriter writer,
+                               CvsTagEntry entry)
+        throws IOException {
+        Element ent = doc.createElement("entry");
+        Element f = DOMUtils.createChildElement(ent, "file");
+        DOMUtils.appendCDATAElement(f, "name", entry.getFile());
+        if (entry.getRevision() != null) {
+            DOMUtils.appendTextElement(f, "revision", entry.getRevision());
+        }
+        if (entry.getPreviousRevision() != null) {
+            DOMUtils.appendTextElement(f, "prevrevision",
+                                       entry.getPreviousRevision());
+        }
+        DOM_WRITER.write(ent, writer, 1, "\t");
+    }
+
+    /**
+     * Validate the parameters specified for task.
+     *
+     * @exception BuildException if a parameter is not correctly set
+     */
+    private void validate() throws BuildException {
+        if (null == mypackage) {
+            throw new BuildException("Package/module must be set.");
+        }
+
+        if (null == mydestfile) {
+            throw new BuildException("Destfile must be set.");
+        }
+
+        if (null == mystartTag && null == mystartDate) {
+            throw new BuildException("Start tag or start date must be set.");
+        }
+
+        if (null != mystartTag && null != mystartDate) {
+            throw new BuildException("Only one of start tag and start date "
+                                     + "must be set.");
+        }
+
+        if (null == myendTag && null == myendDate) {
+            throw new BuildException("End tag or end date must be set.");
+        }
+
+        if (null != myendTag && null != myendDate) {
+            throw new BuildException("Only one of end tag and end date must "
+                                     + "be set.");
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsTagEntry.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsTagEntry.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsTagEntry.java	(revision 16139)
@@ -0,0 +1,71 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.cvslib;
+
+/**
+ * Holds the information of a line of rdiff
+ */
+class CvsTagEntry {
+    String m_filename;
+    String m_prevRevision;
+    String m_revision;
+
+    public CvsTagEntry(String filename) {
+        this(filename, null, null);
+    }
+
+    public CvsTagEntry(String filename, String revision) {
+        this(filename, revision, null);
+    }
+
+    public CvsTagEntry(String filename, String revision,
+                       String prevRevision) {
+        m_filename = filename;
+        m_revision = revision;
+        m_prevRevision = prevRevision;
+    }
+
+    public String getFile() {
+        return m_filename;
+    }
+
+    public String getRevision() {
+        return m_revision;
+    }
+
+    public String getPreviousRevision() {
+        return m_prevRevision;
+    }
+
+    public String toString() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(m_filename);
+        if ((m_revision == null)) {
+            buffer.append(" was removed");
+            if(m_prevRevision != null) {
+                buffer.append("; previous revision was ").append(m_prevRevision);
+            }
+        } else if (m_revision != null && m_prevRevision == null) {
+            buffer.append(" is new; current revision is ")
+                .append(m_revision);
+        } else if (m_revision != null && m_prevRevision != null) {
+            buffer.append(" has changed from ")
+                .append(m_prevRevision).append(" to ").append(m_revision);
+        }
+        return buffer.toString();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsUser.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsUser.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsUser.java	(revision 16139)
@@ -0,0 +1,92 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.cvslib;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Represents a CVS user with a userID and a full name.
+ *
+ */
+public class CvsUser {
+    /** The user's Id */
+    private String m_userID;
+    /** The user's full name */
+    private String m_displayName;
+
+
+    /**
+     * Set the user's fullname
+     *
+     * @param displayName the user's full name
+     */
+    public void setDisplayname(final String displayName) {
+        m_displayName = displayName;
+    }
+
+
+    /**
+     * Set the user's id
+     *
+     * @param userID the user's new id value.
+     */
+    public void setUserid(final String userID) {
+        m_userID = userID;
+    }
+
+
+    /**
+     * Get the user's id.
+     *
+     * @return The userID value
+     */
+    String getUserID() {
+        return m_userID;
+    }
+
+
+    /**
+     * Get the user's full name
+     *
+     * @return the user's full name
+     */
+    String getDisplayname() {
+        return m_displayName;
+    }
+
+
+    /**
+     * validate that this object is configured.
+     *
+     * @exception BuildException if the instance has not be correctly
+     *            configured.
+     */
+    void validate() throws BuildException {
+        if (null == m_userID) {
+            final String message = "Username attribute must be set.";
+
+            throw new BuildException(message);
+        }
+        if (null == m_displayName) {
+            final String message =
+                "Displayname attribute must be set for userID " + m_userID;
+
+            throw new BuildException(message);
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsVersion.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsVersion.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/CvsVersion.java	(revision 16139)
@@ -0,0 +1,152 @@
+/*
+ * Copyright  2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.cvslib;
+
+import org.apache.tools.ant.taskdefs.AbstractCvsTask;
+
+import java.io.ByteArrayOutputStream;
+import java.util.StringTokenizer;
+
+/**
+ * this task allows to find out the client and the server version of a
+ * CVS installation
+ *
+ * example usage :
+ * &lt;cvsversion
+ * cvsRoot=&quot;:pserver:anoncvs@cvs.apache.org:/home/cvspublic&quot;
+ * passfile=&quot;c:/programme/cygwin/home/antoine/.cvspass&quot;
+ * clientversionproperty=&quot;apacheclient&quot;
+ * serverversionproperty=&quot;apacheserver&quot;   /&gt;
+ *
+ * the task can be used also in the API by calling its execute method,
+ * then calling getServerVersion and/or getClientVersion
+ *
+ * @ant.task category="scm"
+ * @since ant 1.6.1
+ */
+public class CvsVersion extends AbstractCvsTask {
+    static final long VERSION_1_11_2 = 11102;
+    static final long MULTIPLY = 100;
+    private String clientVersion;
+    private String serverVersion;
+    private String clientVersionProperty;
+    private String serverVersionProperty;
+    /**
+     * get the CVS client version
+     * @return CVS client version
+     */
+    public String getClientVersion() {
+        return clientVersion;
+    }
+    /**
+     * get the CVS server version
+     * @return CVS server version
+     */
+    public String getServerVersion() {
+        return serverVersion;
+    }
+    /**
+     * set a property where to store the CVS client version
+     * @param clientVersionProperty  property for CVS client version
+     */
+    public void setClientVersionProperty(String clientVersionProperty) {
+        this.clientVersionProperty = clientVersionProperty;
+    }
+
+    /**
+     * set a property where to store the CVS server version
+     * @param serverVersionProperty  property for CVS server version
+     */
+    public void setServerVersionProperty(String serverVersionProperty) {
+        this.serverVersionProperty = serverVersionProperty;
+    }
+    /**
+     * find out if the server version supports log with S option
+     * @return  boolean indicating if the server version supports log with S option
+     */
+    public boolean supportsCvsLogWithSOption() {
+        if (serverVersion == null) {
+            return false;
+        }
+        StringTokenizer mySt = new StringTokenizer(serverVersion, ".");
+        long versionNumber;
+        long counter = MULTIPLY * MULTIPLY;
+        long version = 0;
+        while (mySt.hasMoreTokens()) {
+            String s = mySt.nextToken();
+            int i = 0;
+            for (i = 0; i < s.length(); i++) {
+                if (!Character.isDigit(s.charAt(i))) {
+                    break;
+                }
+            }
+            String s2 = s.substring(0, i);
+            version = version + counter * Long.parseLong(s2);
+            if (counter == 1) {
+                break;
+            }
+            counter = counter / MULTIPLY;
+        }
+        return (version >= VERSION_1_11_2);
+    }
+    /**
+     * the execute method running CvsVersion
+     */
+    public void execute() {
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        this.setOutputStream(bos);
+        ByteArrayOutputStream berr = new ByteArrayOutputStream();
+        this.setErrorStream(berr);
+        setCommand("version");
+        super.execute();
+        String output = bos.toString();
+        StringTokenizer st = new StringTokenizer(output);
+        boolean client = false;
+        boolean server = false;
+        boolean cvs = false;
+        while (st.hasMoreTokens()) {
+            String currentToken = st.nextToken();
+            if (currentToken.equals("Client:")) {
+                client = true;
+            } else if (currentToken.equals("Server:")) {
+                server = true;
+            } else if (currentToken.equals("(CVS)")) {
+                cvs = true;
+            }
+            if (client && cvs) {
+                if (st.hasMoreTokens()) {
+                    clientVersion = st.nextToken();
+                }
+                client = false;
+                cvs = false;
+            } else if (server && cvs) {
+                if (st.hasMoreTokens()) {
+                    serverVersion = st.nextToken();
+                }
+                server = false;
+                cvs = false;
+            }
+
+        }
+        if (clientVersionProperty != null) {
+            getProject().setNewProperty(clientVersionProperty, clientVersion);
+        }
+        if (serverVersionProperty != null) {
+            getProject().setNewProperty(serverVersionProperty, serverVersion);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/RCSFile.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/RCSFile.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/RCSFile.java	(revision 16139)
@@ -0,0 +1,59 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.cvslib;
+
+/**
+ * Represents a RCS File change.
+ *
+ */
+class RCSFile {
+    private String m_name;
+    private String m_revision;
+    private String m_previousRevision;
+
+
+    RCSFile(final String name, final String rev) {
+        this(name, rev, null);
+    }
+
+
+    RCSFile(final String name,
+                  final String revision,
+                  final String previousRevision) {
+        m_name = name;
+        m_revision = revision;
+        if (!revision.equals(previousRevision)) {
+            m_previousRevision = previousRevision;
+        }
+    }
+
+
+    String getName() {
+        return m_name;
+    }
+
+
+    String getRevision() {
+        return m_revision;
+    }
+
+
+    String getPreviousRevision() {
+        return m_previousRevision;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/RedirectingOutputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/RedirectingOutputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/RedirectingOutputStream.java	(revision 16139)
@@ -0,0 +1,50 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.cvslib;
+
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+
+/**
+ * A dummy stream that just passes stuff to the parser.
+ *
+ */
+class RedirectingOutputStream
+     extends LogOutputStream {
+    private final ChangeLogParser parser;
+
+
+    /**
+     * Creates a new instance of this class.
+     *
+     * @param parser the parser to which output is sent.
+     */
+    public RedirectingOutputStream(final ChangeLogParser parser) {
+        super(null, 0);
+        this.parser = parser;
+    }
+
+
+    /**
+     * Logs a line to the log system of ant.
+     *
+     * @param line the line to log.
+     */
+    protected void processLine(final String line) {
+        parser.stdout(line);
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/RedirectingStreamHandler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/RedirectingStreamHandler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/cvslib/RedirectingStreamHandler.java	(revision 16139)
@@ -0,0 +1,59 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.cvslib;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.PumpStreamHandler;
+
+/**
+ * A dummy stream handler that just passes stuff to the parser.
+ *
+ */
+class RedirectingStreamHandler
+     extends PumpStreamHandler {
+    RedirectingStreamHandler(final ChangeLogParser parser) {
+        super(new RedirectingOutputStream(parser),
+            new ByteArrayOutputStream());
+    }
+
+
+    String getErrors() {
+        try {
+            final ByteArrayOutputStream error
+                = (ByteArrayOutputStream) getErr();
+
+            return error.toString("ASCII");
+        } catch (final Exception e) {
+            return null;
+        }
+    }
+
+
+    public void stop() {
+        super.stop();
+        try {
+            getErr().close();
+            getOut().close();
+        } catch (final IOException e) {
+            // plain impossible
+            throw new BuildException(e);
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/defaults.properties
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/defaults.properties	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/defaults.properties	(revision 16139)
@@ -0,0 +1,212 @@
+# standard ant tasks
+mkdir=org.apache.tools.ant.taskdefs.Mkdir
+javac=org.apache.tools.ant.taskdefs.Javac
+chmod=org.apache.tools.ant.taskdefs.Chmod
+delete=org.apache.tools.ant.taskdefs.Delete
+copy=org.apache.tools.ant.taskdefs.Copy
+move=org.apache.tools.ant.taskdefs.Move
+jar=org.apache.tools.ant.taskdefs.Jar
+rmic=org.apache.tools.ant.taskdefs.Rmic
+cvs=org.apache.tools.ant.taskdefs.Cvs
+get=org.apache.tools.ant.taskdefs.Get
+unzip=org.apache.tools.ant.taskdefs.Expand
+unjar=org.apache.tools.ant.taskdefs.Expand
+unwar=org.apache.tools.ant.taskdefs.Expand
+echo=org.apache.tools.ant.taskdefs.Echo
+javadoc=org.apache.tools.ant.taskdefs.Javadoc
+zip=org.apache.tools.ant.taskdefs.Zip
+gzip=org.apache.tools.ant.taskdefs.GZip
+gunzip=org.apache.tools.ant.taskdefs.GUnzip
+replace=org.apache.tools.ant.taskdefs.Replace
+java=org.apache.tools.ant.taskdefs.Java
+tstamp=org.apache.tools.ant.taskdefs.Tstamp
+property=org.apache.tools.ant.taskdefs.Property
+xmlproperty=org.apache.tools.ant.taskdefs.XmlProperty
+taskdef=org.apache.tools.ant.taskdefs.Taskdef
+ant=org.apache.tools.ant.taskdefs.Ant
+exec=org.apache.tools.ant.taskdefs.ExecTask
+tar=org.apache.tools.ant.taskdefs.Tar
+untar=org.apache.tools.ant.taskdefs.Untar
+available=org.apache.tools.ant.taskdefs.Available
+filter=org.apache.tools.ant.taskdefs.Filter
+fixcrlf=org.apache.tools.ant.taskdefs.FixCRLF
+patch=org.apache.tools.ant.taskdefs.Patch
+style=org.apache.tools.ant.taskdefs.XSLTProcess
+xslt=org.apache.tools.ant.taskdefs.XSLTProcess
+touch=org.apache.tools.ant.taskdefs.Touch
+signjar=org.apache.tools.ant.taskdefs.SignJar
+genkey=org.apache.tools.ant.taskdefs.GenerateKey
+antstructure=org.apache.tools.ant.taskdefs.AntStructure
+execon=org.apache.tools.ant.taskdefs.ExecuteOn
+antcall=org.apache.tools.ant.taskdefs.CallTarget
+sql=org.apache.tools.ant.taskdefs.SQLExec
+mail=org.apache.tools.ant.taskdefs.email.EmailTask
+fail=org.apache.tools.ant.taskdefs.Exit
+war=org.apache.tools.ant.taskdefs.War
+uptodate=org.apache.tools.ant.taskdefs.UpToDate
+apply=org.apache.tools.ant.taskdefs.Transform
+record=org.apache.tools.ant.taskdefs.Recorder
+cvspass=org.apache.tools.ant.taskdefs.CVSPass
+typedef=org.apache.tools.ant.taskdefs.Typedef
+sleep=org.apache.tools.ant.taskdefs.Sleep
+pathconvert=org.apache.tools.ant.taskdefs.PathConvert
+ear=org.apache.tools.ant.taskdefs.Ear
+parallel=org.apache.tools.ant.taskdefs.Parallel
+sequential=org.apache.tools.ant.taskdefs.Sequential
+condition=org.apache.tools.ant.taskdefs.ConditionTask
+dependset=org.apache.tools.ant.taskdefs.DependSet
+bzip2=org.apache.tools.ant.taskdefs.BZip2
+bunzip2=org.apache.tools.ant.taskdefs.BUnzip2
+checksum=org.apache.tools.ant.taskdefs.Checksum
+waitfor=org.apache.tools.ant.taskdefs.WaitFor
+input=org.apache.tools.ant.taskdefs.Input
+loadfile=org.apache.tools.ant.taskdefs.LoadFile
+manifest=org.apache.tools.ant.taskdefs.ManifestTask
+loadproperties=org.apache.tools.ant.taskdefs.LoadProperties
+basename=org.apache.tools.ant.taskdefs.Basename
+dirname=org.apache.tools.ant.taskdefs.Dirname
+cvschangelog=org.apache.tools.ant.taskdefs.cvslib.ChangeLogTask
+cvsversion=org.apache.tools.ant.taskdefs.cvslib.CvsVersion
+buildnumber=org.apache.tools.ant.taskdefs.BuildNumber
+concat=org.apache.tools.ant.taskdefs.Concat
+cvstagdiff=org.apache.tools.ant.taskdefs.cvslib.CvsTagDiff
+tempfile=org.apache.tools.ant.taskdefs.TempFile
+import=org.apache.tools.ant.taskdefs.ImportTask
+whichresource=org.apache.tools.ant.taskdefs.WhichResource
+subant=org.apache.tools.ant.taskdefs.SubAnt
+sync=org.apache.tools.ant.taskdefs.Sync
+defaultexcludes=org.apache.tools.ant.taskdefs.DefaultExcludes
+presetdef=org.apache.tools.ant.taskdefs.PreSetDef
+macrodef=org.apache.tools.ant.taskdefs.MacroDef
+nice=org.apache.tools.ant.taskdefs.Nice
+length=org.apache.tools.ant.taskdefs.Length
+
+# optional tasks
+image=org.apache.tools.ant.taskdefs.optional.image.Image
+script=org.apache.tools.ant.taskdefs.optional.Script
+netrexxc=org.apache.tools.ant.taskdefs.optional.NetRexxC
+renameext=org.apache.tools.ant.taskdefs.optional.RenameExtensions
+ejbc=org.apache.tools.ant.taskdefs.optional.ejb.Ejbc
+ddcreator=org.apache.tools.ant.taskdefs.optional.ejb.DDCreator
+wlrun=org.apache.tools.ant.taskdefs.optional.ejb.WLRun
+wlstop=org.apache.tools.ant.taskdefs.optional.ejb.WLStop
+vssadd=org.apache.tools.ant.taskdefs.optional.vss.MSVSSADD
+vsscheckin=org.apache.tools.ant.taskdefs.optional.vss.MSVSSCHECKIN
+vsscheckout=org.apache.tools.ant.taskdefs.optional.vss.MSVSSCHECKOUT
+vsscp=org.apache.tools.ant.taskdefs.optional.vss.MSVSSCP
+vsscreate=org.apache.tools.ant.taskdefs.optional.vss.MSVSSCREATE
+vssget=org.apache.tools.ant.taskdefs.optional.vss.MSVSSGET
+vsshistory=org.apache.tools.ant.taskdefs.optional.vss.MSVSSHISTORY
+vsslabel=org.apache.tools.ant.taskdefs.optional.vss.MSVSSLABEL
+ejbjar=org.apache.tools.ant.taskdefs.optional.ejb.EjbJar
+mparse=org.apache.tools.ant.taskdefs.optional.metamata.MParse
+mmetrics=org.apache.tools.ant.taskdefs.optional.metamata.MMetrics
+maudit=org.apache.tools.ant.taskdefs.optional.metamata.MAudit
+junit=org.apache.tools.ant.taskdefs.optional.junit.JUnitTask
+cab=org.apache.tools.ant.taskdefs.optional.Cab
+ftp=org.apache.tools.ant.taskdefs.optional.net.FTP
+icontract=org.apache.tools.ant.taskdefs.optional.IContract
+javacc=org.apache.tools.ant.taskdefs.optional.javacc.JavaCC
+jjdoc=org.apache.tools.ant.taskdefs.optional.javacc.JJDoc
+jjtree=org.apache.tools.ant.taskdefs.optional.javacc.JJTree
+stcheckout=org.apache.tools.ant.taskdefs.optional.starteam.StarTeamCheckout
+stcheckin=org.apache.tools.ant.taskdefs.optional.starteam.StarTeamCheckin
+stlabel=org.apache.tools.ant.taskdefs.optional.starteam.StarTeamLabel
+stlist=org.apache.tools.ant.taskdefs.optional.starteam.StarTeamList
+wljspc=org.apache.tools.ant.taskdefs.optional.jsp.WLJspc
+jlink=org.apache.tools.ant.taskdefs.optional.jlink.JlinkTask
+native2ascii=org.apache.tools.ant.taskdefs.optional.Native2Ascii
+propertyfile=org.apache.tools.ant.taskdefs.optional.PropertyFile
+depend=org.apache.tools.ant.taskdefs.optional.depend.Depend
+antlr=org.apache.tools.ant.taskdefs.optional.ANTLR
+vajload=org.apache.tools.ant.taskdefs.optional.ide.VAJLoadProjects
+vajexport=org.apache.tools.ant.taskdefs.optional.ide.VAJExport
+vajimport=org.apache.tools.ant.taskdefs.optional.ide.VAJImport
+telnet=org.apache.tools.ant.taskdefs.optional.net.TelnetTask
+csc=org.apache.tools.ant.taskdefs.optional.dotnet.CSharp
+ilasm=org.apache.tools.ant.taskdefs.optional.dotnet.Ilasm
+WsdlToDotnet=org.apache.tools.ant.taskdefs.optional.dotnet.WsdlToDotnet
+wsdltodotnet=org.apache.tools.ant.taskdefs.optional.dotnet.WsdlToDotnet
+importtypelib=org.apache.tools.ant.taskdefs.optional.dotnet.ImportTypelib
+stylebook=org.apache.tools.ant.taskdefs.optional.StyleBook
+test=org.apache.tools.ant.taskdefs.optional.Test
+pvcs=org.apache.tools.ant.taskdefs.optional.pvcs.Pvcs
+p4change=org.apache.tools.ant.taskdefs.optional.perforce.P4Change
+p4delete=org.apache.tools.ant.taskdefs.optional.perforce.P4Delete
+p4label=org.apache.tools.ant.taskdefs.optional.perforce.P4Label
+p4labelsync=org.apache.tools.ant.taskdefs.optional.perforce.P4Labelsync
+p4have=org.apache.tools.ant.taskdefs.optional.perforce.P4Have
+p4sync=org.apache.tools.ant.taskdefs.optional.perforce.P4Sync
+p4edit=org.apache.tools.ant.taskdefs.optional.perforce.P4Edit
+p4integrate=org.apache.tools.ant.taskdefs.optional.perforce.P4Integrate
+p4resolve=org.apache.tools.ant.taskdefs.optional.perforce.P4Resolve
+p4submit=org.apache.tools.ant.taskdefs.optional.perforce.P4Submit
+p4counter=org.apache.tools.ant.taskdefs.optional.perforce.P4Counter
+p4revert=org.apache.tools.ant.taskdefs.optional.perforce.P4Revert
+p4reopen=org.apache.tools.ant.taskdefs.optional.perforce.P4Reopen
+p4fstat=org.apache.tools.ant.taskdefs.optional.perforce.P4Fstat
+javah=org.apache.tools.ant.taskdefs.optional.Javah
+ccupdate=org.apache.tools.ant.taskdefs.optional.clearcase.CCUpdate
+cccheckout=org.apache.tools.ant.taskdefs.optional.clearcase.CCCheckout
+cccheckin=org.apache.tools.ant.taskdefs.optional.clearcase.CCCheckin
+ccuncheckout=org.apache.tools.ant.taskdefs.optional.clearcase.CCUnCheckout
+ccmklbtype=org.apache.tools.ant.taskdefs.optional.clearcase.CCMklbtype
+ccmklabel=org.apache.tools.ant.taskdefs.optional.clearcase.CCMklabel
+ccrmtype=org.apache.tools.ant.taskdefs.optional.clearcase.CCRmtype
+cclock=org.apache.tools.ant.taskdefs.optional.clearcase.CCLock
+ccunlock=org.apache.tools.ant.taskdefs.optional.clearcase.CCUnlock
+ccmkbl=org.apache.tools.ant.taskdefs.optional.clearcase.CCMkbl
+ccmkattr=org.apache.tools.ant.taskdefs.optional.clearcase.CCMkattr
+ccmkelem=org.apache.tools.ant.taskdefs.optional.clearcase.CCMkelem
+ccmkdir=org.apache.tools.ant.taskdefs.optional.clearcase.CCMkdir
+sound=org.apache.tools.ant.taskdefs.optional.sound.SoundTask
+junitreport=org.apache.tools.ant.taskdefs.optional.junit.XMLResultAggregator
+blgenclient=org.apache.tools.ant.taskdefs.optional.ejb.BorlandGenerateClient
+rpm=org.apache.tools.ant.taskdefs.optional.Rpm
+xmlvalidate=org.apache.tools.ant.taskdefs.optional.XMLValidateTask
+iplanet-ejbc=org.apache.tools.ant.taskdefs.optional.ejb.IPlanetEjbcTask
+jdepend=org.apache.tools.ant.taskdefs.optional.jdepend.JDependTask
+mimemail=org.apache.tools.ant.taskdefs.optional.net.MimeMail
+ccmcheckin=org.apache.tools.ant.taskdefs.optional.ccm.CCMCheckin
+ccmcheckout=org.apache.tools.ant.taskdefs.optional.ccm.CCMCheckout
+ccmcheckintask=org.apache.tools.ant.taskdefs.optional.ccm.CCMCheckinDefault
+ccmreconfigure=org.apache.tools.ant.taskdefs.optional.ccm.CCMReconfigure
+ccmcreatetask=org.apache.tools.ant.taskdefs.optional.ccm.CCMCreateTask
+jpcoverage=org.apache.tools.ant.taskdefs.optional.sitraka.Coverage
+jpcovmerge=org.apache.tools.ant.taskdefs.optional.sitraka.CovMerge
+jpcovreport=org.apache.tools.ant.taskdefs.optional.sitraka.CovReport
+p4add=org.apache.tools.ant.taskdefs.optional.perforce.P4Add
+jspc=org.apache.tools.ant.taskdefs.optional.jsp.JspC
+replaceregexp=org.apache.tools.ant.taskdefs.optional.ReplaceRegExp
+translate=org.apache.tools.ant.taskdefs.optional.i18n.Translate
+sosget=org.apache.tools.ant.taskdefs.optional.sos.SOSGet
+soscheckin=org.apache.tools.ant.taskdefs.optional.sos.SOSCheckin
+soscheckout=org.apache.tools.ant.taskdefs.optional.sos.SOSCheckout
+soslabel=org.apache.tools.ant.taskdefs.optional.sos.SOSLabel
+echoproperties=org.apache.tools.ant.taskdefs.optional.EchoProperties
+splash=org.apache.tools.ant.taskdefs.optional.splash.SplashTask
+serverdeploy=org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy
+jarlib-display=org.apache.tools.ant.taskdefs.optional.extension.JarLibDisplayTask
+jarlib-manifest=org.apache.tools.ant.taskdefs.optional.extension.JarLibManifestTask
+jarlib-available=org.apache.tools.ant.taskdefs.optional.extension.JarLibAvailableTask
+jarlib-resolve=org.apache.tools.ant.taskdefs.optional.extension.JarLibResolveTask
+setproxy=org.apache.tools.ant.taskdefs.optional.net.SetProxy
+vbc=org.apache.tools.ant.taskdefs.optional.dotnet.VisualBasicCompile
+symlink=org.apache.tools.ant.taskdefs.optional.unix.Symlink
+chgrp=org.apache.tools.ant.taskdefs.optional.unix.Chgrp
+chown=org.apache.tools.ant.taskdefs.optional.unix.Chown
+attrib=org.apache.tools.ant.taskdefs.optional.windows.Attrib
+scp=org.apache.tools.ant.taskdefs.optional.ssh.Scp
+sshexec=org.apache.tools.ant.taskdefs.optional.ssh.SSHExec
+jsharpc=org.apache.tools.ant.taskdefs.optional.dotnet.JSharp
+rexec=org.apache.tools.ant.taskdefs.optional.net.RExecTask
+scriptdef=org.apache.tools.ant.taskdefs.optional.script.ScriptDef
+ildasm=org.apache.tools.ant.taskdefs.optional.dotnet.Ildasm
+
+# deprecated ant tasks (kept for back compatibility)
+starteam=org.apache.tools.ant.taskdefs.optional.scm.AntStarTeamCheckOut
+javadoc2=org.apache.tools.ant.taskdefs.Javadoc
+copydir=org.apache.tools.ant.taskdefs.Copydir
+copyfile=org.apache.tools.ant.taskdefs.Copyfile
+deltree=org.apache.tools.ant.taskdefs.Deltree
+rename=org.apache.tools.ant.taskdefs.Rename
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/EmailAddress.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/EmailAddress.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/EmailAddress.java	(revision 16139)
@@ -0,0 +1,196 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.email;
+
+/**
+ * Holds an email address.
+ *
+ * @since Ant 1.5
+ */
+public class EmailAddress {
+    private String name;
+    private String address;
+
+
+    /** Creates an empty email address  */
+    public EmailAddress() {
+    }
+
+
+    /**
+     * Creates a new email address based on the given string
+     *
+     * @param email the email address (with or without <>)
+     * Acceptable forms include:
+     *    address
+     *    <address>
+     *    name <address>
+     *    <address> name
+     *    (name) address
+     *    address (name)
+     */
+    // Make a limited attempt to extract a sanitized name and email address
+    // Algorithm based on the one found in Ant's MailMessage.java
+    public EmailAddress(String email) {
+        final int minLen = 9;
+        int len = email.length();
+
+        // shortcut for "<address>"
+        if (len > minLen) {
+            if ((email.charAt(0) == '<' || email.charAt(1) == '<')
+            && (email.charAt(len - 1) == '>' || email.charAt(len - 2) == '>')) {
+                this.address = trim(email, true);
+                return;
+            }
+        }
+
+        int paramDepth = 0;
+        int start = 0;
+        int end = 0;
+        int nStart = 0;
+        int nEnd = 0;
+
+        for (int i = 0; i < len; i++) {
+            char c = email.charAt(i);
+            if (c == '(') {
+                paramDepth++;
+                if (start == 0) {
+                    end = i;  // support "address (name)"
+                    nStart = i + 1;
+                }
+            } else if (c == ')') {
+                paramDepth--;
+                if (end == 0) {
+                    start = i + 1;  // support "(name) address"
+                    nEnd = i;
+                }
+            } else if (paramDepth == 0 && c == '<') {
+                if (start == 0) {
+                    nEnd = i;
+                }
+                start = i + 1;
+            } else if (paramDepth == 0 && c == '>') {
+                end = i;
+                if (end != len - 1) {
+                    nStart = i + 1;
+                }
+            }
+        }
+
+        // DEBUG: System.out.println( email );
+        if (end == 0) {
+            end = len;
+        }
+        // DEBUG: System.out.println( "address: " + start + " " + end );
+        if (nEnd == 0) {
+            nEnd = len;
+        }
+        // DEBUG: System.out.println( "name: " + nStart + " " + nEnd );
+
+        this.address = trim(email.substring(start, end), true);
+        this.name = trim(email.substring(nStart, nEnd), false);
+
+        // if the two substrings are longer than the original, then name
+        // contains address - so reset the name to null
+        if (this.name.length() + this.address.length() > len) {
+            this.name = null;
+        }
+    }
+
+    /**
+     *  A specialised trim() that trims whitespace,
+     *  '(', ')', '"', '<', '>' from the start and end of strings
+     */
+    private String trim(String t, boolean trimAngleBrackets) {
+        int start = 0;
+        int end = t.length();
+        boolean trim = false;
+        do {
+            trim = false;
+            if (t.charAt(end - 1) == ')'
+                || (t.charAt(end - 1) == '>' && trimAngleBrackets)
+                || (t.charAt(end - 1) == '"' && t.charAt(end - 2) != '\\')
+                || t.charAt(end - 1) <= '\u0020') {
+                trim = true;
+                end--;
+            }
+            if (t.charAt(start) == '('
+                || (t.charAt(start) == '<' && trimAngleBrackets)
+                || t.charAt(start) == '"'
+                || t.charAt(start) <= '\u0020') {
+                trim = true;
+                start++;
+            }
+        } while (trim);
+        return t.substring(start, end);
+    }
+
+
+    /**
+     * Sets the personal / display name of the address
+     *
+     * @param name the display name
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+
+    /**
+     * Sets the email address
+     *
+     * @param address the actual email address (without <>)
+     */
+    public void setAddress(String address) {
+        this.address = address;
+    }
+
+
+    /**
+     * Constructs a string "name &lt;address&gt;" or "address"
+     *
+     * @return a string representation of the address
+     */
+    public String toString() {
+        if (name == null) {
+            return address;
+        } else {
+            return name + " <" + address + ">";
+        }
+    }
+
+
+    /**
+     * Returns the address
+     *
+     * @return the address part
+     */
+    public String getAddress() {
+        return address;
+    }
+
+
+    /**
+     * Returns the display name
+     *
+     * @return the display name part
+     */
+    public String getName() {
+        return name;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/EmailTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/EmailTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/EmailTask.java	(revision 16139)
@@ -0,0 +1,589 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.email;
+
+// Ant imports
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * A task to send SMTP email. This is a refactoring of the SendMail and
+ * MimeMail tasks such that both are within a single task.
+ *
+ * @since Ant 1.5
+ * @ant.task name="mail" category="network"
+ */
+public class EmailTask
+     extends Task {
+    /** Constant to show that the best available mailer should be used.  */
+    public static final String AUTO = "auto";
+    /** Constant to allow the Mime mailer to be requested  */
+    public static final String MIME = "mime";
+    /** Constant to allow the UU mailer to be requested  */
+    public static final String UU = "uu";
+    /** Constant to allow the plaintext mailer to be requested  */
+    public static final String PLAIN = "plain";
+
+
+    /**
+     * Enumerates the encoding constants
+     */
+    public static class Encoding extends EnumeratedAttribute {
+        /**
+         * finds the valid encoding values
+         *
+         * @return a list of valid entries
+         */
+        public String[] getValues() {
+            return new String[] {AUTO, MIME, UU, PLAIN};
+        }
+    }
+
+    private String encoding = AUTO;
+    /** host running SMTP  */
+    private String host = "localhost";
+    private int port = 25;
+    /** subject field  */
+    private String subject = null;
+    /** any text  */
+    private Message message = null;
+    /** failure flag */
+    private boolean failOnError = true;
+    private boolean includeFileNames = false;
+    private String messageMimeType = null;
+    /** special headers */
+    /** sender  */
+    private EmailAddress from = null;
+    /** replyto */
+    private Vector replyToList = new Vector();
+    /** TO recipients  */
+    private Vector toList = new Vector();
+    /** CC (Carbon Copy) recipients  */
+    private Vector ccList = new Vector();
+    /** BCC (Blind Carbon Copy) recipients  */
+    private Vector bccList = new Vector();
+
+    /** file list  */
+    private Vector files = new Vector();
+    private Vector filesets = new Vector();
+    /** Character set for MimeMailer*/
+    private String charset = null;
+    /** User for SMTP auth */
+    private String user = null;
+    /** Password for SMTP auth */
+    private String password = null;
+    /** indicate if the user wishes SSL-TLS */
+    private boolean SSL = false;
+
+    /**
+     * sets the user for SMTP auth; this requires JavaMail
+     * @param user
+     * @since ant 1.6
+     */
+    public void setUser(String user) {
+        this.user = user;
+    }
+
+    /**
+     * sets the password for SMTP auth; this requires JavaMail
+     * @param password
+     * @since ant 1.6
+     */
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    /**
+     * tells if the user needs to send his data over SSL
+     * @param SSL
+     * @since ant 1.6
+     */
+    public void setSSL(boolean SSL) {
+        this.SSL = SSL;
+    }
+
+    /**
+     * Allows the build writer to choose the preferred encoding method
+     *
+     * @param encoding The encoding (one of AUTO,MIME,UU,PLAIN)
+     */
+    public void setEncoding(Encoding encoding) {
+        this.encoding = encoding.getValue();
+    }
+
+
+    /**
+     * Sets the mail server port
+     *
+     * @param port The port to use
+     */
+    public void setMailport(int port) {
+        this.port = port;
+    }
+
+
+    /**
+     * Sets the host
+     *
+     * @param host The host to connect to
+     */
+    public void setMailhost(String host) {
+        this.host = host;
+    }
+
+
+    /**
+     * Sets the subject line of the email
+     *
+     * @param subject Subject of this email.
+     */
+    public void setSubject(String subject) {
+        this.subject = subject;
+    }
+
+
+    /**
+     * Shorthand method to set the message
+     *
+     * @param message Message body of this email.
+     */
+    public void setMessage(String message) {
+        if (this.message != null) {
+            throw new BuildException("Only one message can be sent in an "
+                 + "email");
+        }
+
+        this.message = new Message(message);
+        this.message.setProject(getProject());
+    }
+
+
+    /**
+     * Shorthand method to set the message from a file
+     *
+     * @param file The file from which to take the message
+     */
+    public void setMessageFile(File file) {
+        if (this.message != null) {
+            throw new BuildException("Only one message can be sent in an "
+                 + "email");
+        }
+
+        this.message = new Message(file);
+        this.message.setProject(getProject());
+    }
+
+
+    /**
+     * Shorthand method to set type of the text message, text/plain by default
+     * but text/html or text/xml is quite feasible.
+     *
+     * @param type The new MessageMimeType value
+     */
+    public void setMessageMimeType(String type) {
+        this.messageMimeType = type;
+    }
+
+
+    /**
+     * Add a message element
+     *
+     * @param message The message object
+     * @throws BuildException if a message has already been added
+     */
+    public void addMessage(Message message)
+         throws BuildException {
+        if (this.message != null) {
+            throw new BuildException("Only one message can be sent in an "
+                 + "email");
+        }
+
+        this.message = message;
+    }
+
+
+    /**
+     * Adds a from address element
+     *
+     * @param address The address to send from
+     */
+    public void addFrom(EmailAddress address) {
+        if (this.from != null) {
+            throw new BuildException("Emails can only be from one address");
+        }
+
+        this.from = address;
+    }
+
+
+    /**
+     * Shorthand to set the from address element
+     *
+     * @param address The address to send mail from
+     */
+    public void setFrom(String address) {
+        if (this.from != null) {
+            throw new BuildException("Emails can only be from one address");
+        }
+
+        this.from = new EmailAddress(address);
+    }
+
+
+    /**
+     * Adds a replyto address element
+     *
+     * @param address The address to reply to
+     * @since ant 1.6
+     */
+    public void addReplyTo(EmailAddress address) {
+        this.replyToList.add(address);
+    }
+
+
+    /**
+     * Shorthand to set the replyto address element
+     *
+     * @param address The address to which replies should be directed
+     * @since ant 1.6
+     */
+    public void setReplyTo(String address) {
+        this.replyToList.add(new EmailAddress(address));
+    }
+
+
+    /**
+     * Adds a to address element
+     *
+     * @param address An email address
+     */
+    public void addTo(EmailAddress address) {
+        toList.addElement(address);
+    }
+
+
+    /**
+     * Adds "to" address elements
+     *
+     * @param list Comma separated list of addresses
+     */
+    public void setToList(String list) {
+        StringTokenizer tokens = new StringTokenizer(list, ",");
+
+        while (tokens.hasMoreTokens()) {
+            toList.addElement(new EmailAddress(tokens.nextToken()));
+        }
+    }
+
+
+    /**
+     * Adds "cc" address element
+     *
+     * @param address The email address
+     */
+    public void addCc(EmailAddress address) {
+        ccList.addElement(address);
+    }
+
+
+    /**
+     * Adds "cc" address elements
+     *
+     * @param list Comma separated list of addresses
+     */
+    public void setCcList(String list) {
+        StringTokenizer tokens = new StringTokenizer(list, ",");
+
+        while (tokens.hasMoreTokens()) {
+            ccList.addElement(new EmailAddress(tokens.nextToken()));
+        }
+    }
+
+
+    /**
+     * Adds "bcc" address elements
+     *
+     * @param address The email address
+     */
+    public void addBcc(EmailAddress address) {
+        bccList.addElement(address);
+    }
+
+
+    /**
+     * Adds "bcc" address elements
+     *
+     * @param list comma separated list of addresses
+     */
+    public void setBccList(String list) {
+        StringTokenizer tokens = new StringTokenizer(list, ",");
+
+        while (tokens.hasMoreTokens()) {
+            bccList.addElement(new EmailAddress(tokens.nextToken()));
+        }
+    }
+
+
+    /**
+     * Indicates whether BuildExceptions should be passed back to the core
+     *
+     * @param failOnError The new FailOnError value
+     */
+    public void setFailOnError(boolean failOnError) {
+        this.failOnError = failOnError;
+    }
+
+
+    /**
+     * Adds a list of files to be attached
+     *
+     * @param filenames Comma separated list of files
+     */
+    public void setFiles(String filenames) {
+        StringTokenizer t = new StringTokenizer(filenames, ", ");
+
+        while (t.hasMoreTokens()) {
+            files.addElement(getProject().resolveFile(t.nextToken()));
+        }
+    }
+
+
+    /**
+     * Adds a set of files (nested fileset attribute).
+     *
+     * @param fs The fileset
+     */
+    public void addFileset(FileSet fs) {
+        filesets.addElement(fs);
+    }
+
+
+    /**
+     * Sets Includefilenames attribute
+     *
+     * @param includeFileNames Whether to include filenames in the text of the
+     *      message
+     */
+    public void setIncludefilenames(boolean includeFileNames) {
+        this.includeFileNames = includeFileNames;
+    }
+
+
+    /**
+     * Identifies whether file names should be included
+     *
+     * @return Identifies whether file names should be included
+     */
+    public boolean getIncludeFileNames() {
+        return includeFileNames;
+    }
+
+
+    /** Sends an email  */
+    public void execute() {
+        Message savedMessage = message;
+        Vector savedFiles = (Vector) files.clone();
+
+        try {
+            Mailer mailer = null;
+
+            // prepare for the auto select mechanism
+            boolean autoFound = false;
+            // try MIME format
+            if (encoding.equals(MIME)
+                 || (encoding.equals(AUTO) && !autoFound)) {
+                try {
+                    mailer =
+                        (Mailer) Class.forName("org.apache.tools.ant.taskdefs.email.MimeMailer")
+                        .newInstance();
+                    autoFound = true;
+                    log("Using MIME mail", Project.MSG_VERBOSE);
+                } catch (Throwable e) {
+                    log("Failed to initialise MIME mail: "
+                        + e.getMessage(), Project.MSG_WARN);
+                }
+            }
+            // SMTP auth only allowed with MIME mail
+            if (autoFound == false && ((user != null) || (password != null))
+                && (encoding.equals(UU) || encoding.equals(PLAIN))) {
+                throw new BuildException("SMTP auth only possible with MIME mail");
+            }
+            // SSL only allowed with MIME mail
+            if (autoFound == false && (SSL)
+                && (encoding.equals(UU) || encoding.equals(PLAIN))) {
+                throw new BuildException("SSL only possible with MIME mail");
+            }
+
+
+            // try UU format
+            if (encoding.equals(UU)
+                 || (encoding.equals(AUTO) && !autoFound)) {
+                try {
+                    mailer =
+                        (Mailer) Class.forName("org.apache.tools.ant.taskdefs.email.UUMailer")
+                        .newInstance();
+                    autoFound = true;
+                    log("Using UU mail", Project.MSG_VERBOSE);
+                } catch (Throwable e) {
+                    log("Failed to initialise UU mail", Project.MSG_WARN);
+                }
+            }
+
+            // try plain format
+            if (encoding.equals(PLAIN)
+                 || (encoding.equals(AUTO) && !autoFound)) {
+                mailer = new PlainMailer();
+                autoFound = true;
+                log("Using plain mail", Project.MSG_VERBOSE);
+            }
+
+            // a valid mailer must be present by now
+            if (mailer == null) {
+                throw new BuildException("Failed to initialise encoding: "
+                     + encoding);
+            }
+
+            // a valid message is required
+            if (message == null) {
+                message = new Message();
+                message.setProject(getProject());
+            }
+
+            // an address to send from is required
+            if (from == null || from.getAddress() == null) {
+                throw new BuildException("A from element is required");
+            }
+
+            // at least one address to send to/cc/bcc is required
+            if (toList.isEmpty() && ccList.isEmpty() && bccList.isEmpty()) {
+                throw new BuildException("At least one of to,cc or bcc must "
+                     + "be supplied");
+            }
+
+            // set the mimetype if not done already (and required)
+            if (messageMimeType != null) {
+                if (message.isMimeTypeSpecified()) {
+                    throw new BuildException("The mime type can only be "
+                         + "specified in one location");
+                } else {
+                    message.setMimeType(messageMimeType);
+                }
+            }
+            // set the character set if not done already (and required)
+            if (charset != null) {
+                if (message.getCharset() != null) {
+                    throw new BuildException("The charset can only be "
+                         + "specified in one location");
+                } else {
+                    message.setCharset(charset);
+                }
+            }
+
+            // identify which files should be attached
+            Enumeration e = filesets.elements();
+
+            while (e.hasMoreElements()) {
+                FileSet fs = (FileSet) e.nextElement();
+
+                DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+                String[] includedFiles = ds.getIncludedFiles();
+                File baseDir = ds.getBasedir();
+
+                for (int j = 0; j < includedFiles.length; ++j) {
+                    File file = new File(baseDir, includedFiles[j]);
+
+                    files.addElement(file);
+                }
+            }
+
+            // let the user know what's going to happen
+            log("Sending email: " + subject, Project.MSG_INFO);
+            log("From " + from, Project.MSG_VERBOSE);
+            log("ReplyTo " + replyToList, Project.MSG_VERBOSE);
+            log("To " + toList, Project.MSG_VERBOSE);
+            log("Cc " + ccList, Project.MSG_VERBOSE);
+            log("Bcc " + bccList, Project.MSG_VERBOSE);
+
+            // pass the params to the mailer
+            mailer.setHost(host);
+            mailer.setPort(port);
+            mailer.setUser(user);
+            mailer.setPassword(password);
+            mailer.setSSL(SSL);
+            mailer.setMessage(message);
+            mailer.setFrom(from);
+            mailer.setReplyToList(replyToList);
+            mailer.setToList(toList);
+            mailer.setCcList(ccList);
+            mailer.setBccList(bccList);
+            mailer.setFiles(files);
+            mailer.setSubject(subject);
+            mailer.setTask(this);
+            mailer.setIncludeFileNames(includeFileNames);
+
+            // send the email
+            mailer.send();
+
+            // let the user know what happened
+            int count = files.size();
+
+            log("Sent email with " + count + " attachment"
+                 + (count == 1 ? "" : "s"), Project.MSG_INFO);
+        } catch (BuildException e) {
+            log("Failed to send email", Project.MSG_WARN);
+            if (failOnError) {
+                throw e;
+            }
+        } catch (Exception e) {
+          log("Failed to send email", Project.MSG_WARN);
+          if (failOnError) {
+            throw new BuildException(e);
+          }
+        } finally {
+            message = savedMessage;
+            files = savedFiles;
+        }
+    }
+    /**
+     * Sets the character set of mail message.
+     * Will be ignored if mimeType contains ....; Charset=... substring or
+     * encoding is not a <code>mime</code>
+     * @since Ant 1.6
+     */
+    public void setCharset(String charset) {
+      this.charset = charset;
+    }
+    /**
+     * Returns the character set of mail message.
+     *
+     * @return Charset of mail message.
+     * @since Ant 1.6
+     */
+    public String getCharset() {
+      return charset;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/Mailer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/Mailer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/Mailer.java	(revision 16139)
@@ -0,0 +1,216 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.email;
+
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.DateUtils;
+
+/**
+ * Base class for the various emailing implementations.
+ *
+ * @since Ant 1.5
+ */
+public abstract class Mailer {
+    protected String host = null;
+    protected int port = -1;
+    protected String user = null;
+    protected String password = null;
+    protected boolean SSL = false;
+    protected Message message;
+    protected EmailAddress from;
+    protected Vector replyToList = null;
+    protected Vector toList = null;
+    protected Vector ccList = null;
+    protected Vector bccList = null;
+    protected Vector files = null;
+    protected String subject = null;
+    protected Task task;
+    protected boolean includeFileNames = false;
+
+    /**
+     * Sets the mail server
+     *
+     * @param host the mail server name
+     */
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+
+    /**
+     * Sets the smtp port
+     *
+     * @param port the SMTP port
+     */
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    /**
+     * Sets the user for smtp auth
+     *
+     * @param user the username
+     * @since ant 1.6
+     */
+    public void setUser(String user) {
+        this.user = user;
+    }
+
+    /**
+     * Sets the password for smtp auth
+     *
+     * @param password the authentication password
+     * @since ant 1.6
+     */
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    /**
+     * Sets whether the user wants to send the mail through SSL
+     *
+     * @param SSL if true use SSL transport
+     * @since ant 1.6
+     */
+    public void setSSL(boolean SSL) {
+        this.SSL = SSL;
+    }
+
+    /**
+     * Sets the message
+     *
+     * @param m the message content
+     */
+    public void setMessage(Message m) {
+        this.message = m;
+    }
+
+
+    /**
+     * Sets the address to send from
+     *
+     * @param from the sender
+     */
+    public void setFrom(EmailAddress from) {
+        this.from = from;
+    }
+
+
+    /**
+     * Sets the replyto addresses
+     *
+     * @param list a vector of reployTo addresses
+     * @since ant 1.6
+     */
+    public void setReplyToList(Vector list) {
+        this.replyToList = list;
+    }
+
+
+    /**
+     * Set the to addresses
+     *
+     * @param list a vector of recipient addresses
+     */
+    public void setToList(Vector list) {
+        this.toList = list;
+    }
+
+
+    /**
+     * Sets the cc addresses
+     *
+     * @param list a vector of cc addresses
+     */
+    public void setCcList(Vector list) {
+        this.ccList = list;
+    }
+
+
+    /**
+     * Sets the bcc addresses
+     *
+     * @param list a vector of the bcc addresses
+     */
+    public void setBccList(Vector list) {
+        this.bccList = list;
+    }
+
+
+    /**
+     * Sets the files to attach
+     *
+     * @param files list of files to attach to the email.
+     */
+    public void setFiles(Vector files) {
+        this.files = files;
+    }
+
+
+    /**
+     * Sets the subject
+     *
+     * @param subject the subject line
+     */
+    public void setSubject(String subject) {
+        this.subject = subject;
+    }
+
+
+    /**
+     * Sets the owning task
+     *
+     * @param task the owning task instance
+     */
+    public void setTask(Task task) {
+        this.task = task;
+    }
+
+
+    /**
+     * Indicates whether filenames should be listed in the body
+     *
+     * @param b if true list attached file names in the body content.
+     */
+    public void setIncludeFileNames(boolean b) {
+        this.includeFileNames = b;
+    }
+
+
+    /**
+     * This method should send the email
+     *
+     * @throws BuildException if the email can't be sent.
+     */
+    public abstract void send()
+         throws BuildException;
+
+    /**
+     * Returns the current Date in a format suitable for a SMTP date
+     * header.
+     *
+     * @return the current date in SMTP suitable format.
+     *
+     * @since Ant 1.5
+     */
+    protected final String getDate() {
+        return DateUtils.getDateForHeader();
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/Message.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/Message.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/Message.java	(revision 16139)
@@ -0,0 +1,166 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.email;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Class representing an email message.
+ *
+ * @since Ant 1.5
+ */
+public class Message extends ProjectComponent {
+    private File messageSource = null;
+    private StringBuffer buffer = new StringBuffer();
+    private String mimeType = "text/plain";
+    private boolean specified = false;
+    private String charset = null;
+
+    /** Creates a new empty message  */
+    public Message() {
+    }
+
+
+    /**
+     * Creates a new message based on the given string
+     *
+     * @param text the message
+     */
+    public Message(String text) {
+        addText(text);
+    }
+
+
+    /**
+     * Creates a new message using the contents of the given file.
+     *
+     * @param file the source of the message
+     */
+    public Message(File file) {
+        messageSource = file;
+    }
+
+
+    /**
+     * Adds a textual part of the message
+     *
+     * @param text some text to add
+     */
+    public void addText(String text) {
+        buffer.append(text);
+    }
+
+
+    /**
+     * Sets the source file of the message
+     *
+     * @param src the source of the message
+     */
+    public void setSrc(File src) {
+        this.messageSource = src;
+    }
+
+
+    /**
+     * Sets the content type for the message
+     *
+     * @param mimeType a mime type e.g. "text/plain"
+     */
+    public void setMimeType(String mimeType) {
+        this.mimeType = mimeType;
+        specified = true;
+    }
+
+
+    /**
+     * Returns the content type
+     *
+     * @return the mime type
+     */
+    public String getMimeType() {
+        return mimeType;
+    }
+
+
+    /**
+     * Prints the message onto an output stream
+     *
+     * @param ps The print stream to write to
+     * @throws IOException if an error occurs
+     */
+    public void print(PrintStream ps)
+         throws IOException {
+        // We need character encoding aware printing here.
+        // So, using PrintWriter over OutputStreamWriter instead of PrintStream
+        PrintWriter out
+            = charset != null ? new PrintWriter(new OutputStreamWriter(ps, charset))
+                              : new PrintWriter(ps);
+        if (messageSource != null) {
+            // Read message from a file
+            FileReader freader = new FileReader(messageSource);
+
+            try {
+                BufferedReader in = new BufferedReader(freader);
+                String line = null;
+                while ((line = in.readLine()) != null) {
+                    out.println(getProject().replaceProperties(line));
+                }
+            } finally {
+                freader.close();
+            }
+        } else {
+            out.println(getProject().replaceProperties(buffer.substring(0)));
+        }
+        out.flush();
+    }
+
+
+    /**
+     * Returns true if the mimeType has been set.
+     *
+     * @return false if the default value is in use
+     */
+    public boolean isMimeTypeSpecified() {
+        return specified;
+    }
+    /**
+     * Sets the character set of mail message.
+     * Will be ignored if mimeType contains ....; Charset=... substring.
+     * @since Ant 1.6
+     */
+    public void setCharset(String charset) {
+      this.charset = charset;
+    }
+    /**
+     * Returns the charset of mail message.
+     *
+     * @return Charset of mail message.
+     * @since Ant 1.6
+     */
+    public String getCharset() {
+      return charset;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/MimeMailer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/MimeMailer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/MimeMailer.java	(revision 16139)
@@ -0,0 +1,279 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.email;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.security.Security;
+import java.security.Provider;
+
+import javax.activation.DataHandler;
+import javax.activation.FileDataSource;
+
+import javax.mail.Authenticator;
+import javax.mail.PasswordAuthentication;
+import javax.mail.Session;
+import javax.mail.Message;
+import javax.mail.Transport;
+import javax.mail.MessagingException;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Uses the JavaMail classes to send Mime format email.
+ *
+ * @since Ant 1.5
+ */
+public class MimeMailer extends Mailer {
+    /** Default character set */
+    private static final String DEFAULT_CHARSET
+        = System.getProperty("file.encoding");
+
+    // To work properly with national charsets we have to use
+    // implementation of interface javax.activation.DataSource
+    /**
+     * @since Ant 1.6
+     */
+    class StringDataSource implements javax.activation.DataSource {
+      private String data = null;
+      private String type = null;
+      private String charset = null;
+      private ByteArrayOutputStream out;
+
+      public InputStream getInputStream() throws IOException {
+        if (data == null && out == null) {
+          throw new IOException("No data");
+        } else {
+          if (out != null) {
+            data = (data != null) ? data.concat(out.toString(charset)) : out.toString(charset);
+            out = null;
+          }
+          return new ByteArrayInputStream(data.getBytes(charset));
+        }
+      }
+
+      public OutputStream getOutputStream() throws IOException {
+        if (out == null) {
+          out = new ByteArrayOutputStream();
+        }
+        return out;
+      }
+
+      public void setContentType(String type) {
+        this.type = type.toLowerCase();
+      }
+
+      public String getContentType() {
+        if (type != null && type.indexOf("charset") > 0 && type.startsWith("text/")) {
+          return type;
+        }
+        // Must be like "text/plain; charset=windows-1251"
+        return type != null ? type.concat("; charset=".concat(charset))
+                            : "text/plain".concat("; charset=".concat(charset));
+      }
+
+      public String getName() {
+        return "StringDataSource";
+      }
+      public void setCharset(String charset) {
+        this.charset = charset;
+      }
+      public String getCharset() {
+        return charset;
+      }
+  }
+
+  /** Sends the email  */
+  public void send() {
+        try {
+            Properties props = new Properties();
+
+            props.put("mail.smtp.host", host);
+            props.put("mail.smtp.port", String.valueOf(port));
+
+            // Aside, the JDK is clearly unaware of the Scottish
+            // 'session', which involves excessive quantities of
+            // alcohol :-)
+            Session sesh;
+            Authenticator auth;
+            if (SSL) {
+                try {
+                    Provider p
+                        = (Provider) Class.forName("com.sun.net.ssl.internal.ssl.Provider").newInstance();
+                    Security.addProvider(p);
+                } catch (Exception e) {
+                    throw new BuildException("could not instantiate ssl "
+                        + "security provider, check that you have JSSE in "
+                        + "your classpath");
+                }
+                final String SSL_FACTORY = "javax.net.ssl.SSLSocketFactory";
+                // SMTP provider
+                props.put("mail.smtp.socketFactory.class", SSL_FACTORY);
+                props.put("mail.smtp.socketFactory.fallback", "false");
+            }
+            if (user == null && password == null) {
+                sesh = Session.getDefaultInstance(props, null);
+            } else {
+                props.put("mail.smtp.auth", "true");
+                auth = new SimpleAuthenticator(user, password);
+                sesh = Session.getInstance(props, auth);
+            }
+            //create the message
+            MimeMessage msg = new MimeMessage(sesh);
+            MimeMultipart attachments = new MimeMultipart();
+
+            //set the sender
+            if (from.getName() == null) {
+                msg.setFrom(new InternetAddress(from.getAddress()));
+            } else {
+                msg.setFrom(new InternetAddress(from.getAddress(),
+                    from.getName()));
+            }
+            // set the reply to addresses
+            msg.setReplyTo(internetAddresses(replyToList));
+            msg.setRecipients(Message.RecipientType.TO,
+                internetAddresses(toList));
+            msg.setRecipients(Message.RecipientType.CC,
+                internetAddresses(ccList));
+            msg.setRecipients(Message.RecipientType.BCC,
+                internetAddresses(bccList));
+
+            // Choosing character set of the mail message
+            // First: looking it from MimeType
+            String charset = parseCharSetFromMimeType(message.getMimeType());
+            if (charset != null) {
+                // Assign/reassign message charset from MimeType
+                message.setCharset(charset);
+            } else {
+                // Next: looking if charset having explicit definition
+                charset = message.getCharset();
+                if (charset == null) {
+                    // Using default
+                    charset = DEFAULT_CHARSET;
+                    message.setCharset(charset);
+                }
+            }
+
+            // Using javax.activation.DataSource paradigm
+            StringDataSource sds = new StringDataSource();
+            sds.setContentType(message.getMimeType());
+            sds.setCharset(charset);
+
+            if (subject != null) {
+                msg.setSubject(subject, charset);
+            }
+            msg.addHeader("Date", getDate());
+
+            PrintStream out = new PrintStream(sds.getOutputStream());
+            message.print(out);
+            out.close();
+
+            MimeBodyPart textbody = new MimeBodyPart();
+            textbody.setDataHandler(new DataHandler(sds));
+            attachments.addBodyPart(textbody);
+
+            Enumeration e = files.elements();
+
+            while (e.hasMoreElements()) {
+                File file = (File) e.nextElement();
+
+                MimeBodyPart body;
+
+                body = new MimeBodyPart();
+                if (!file.exists() || !file.canRead()) {
+                    throw new BuildException("File \"" + file.getAbsolutePath()
+                         + "\" does not exist or is not "
+                         + "readable.");
+                }
+                FileDataSource fileData = new FileDataSource(file);
+                DataHandler fileDataHandler = new DataHandler(fileData);
+
+                body.setDataHandler(fileDataHandler);
+                body.setFileName(file.getName());
+                attachments.addBodyPart(body);
+            }
+
+            msg.setContent(attachments);
+            Transport.send(msg);
+        } catch (MessagingException e) {
+            throw new BuildException("Problem while sending mime mail:", e);
+        } catch (IOException e) {
+            throw new BuildException("Problem while sending mime mail:", e);
+        }
+    }
+
+
+    private static InternetAddress[] internetAddresses(Vector list)
+         throws AddressException, UnsupportedEncodingException {
+        InternetAddress[] addrs = new InternetAddress[list.size()];
+
+        for (int i = 0; i < list.size(); ++i) {
+            EmailAddress addr = (EmailAddress) list.elementAt(i);
+
+            if (addr.getName() == null) {
+                addrs[i] = new InternetAddress(addr.getAddress());
+            } else {
+                addrs[i] = new InternetAddress(addr.getAddress(),
+                    addr.getName());
+            }
+        }
+
+        return addrs;
+
+    }
+
+    private String parseCharSetFromMimeType(String type) {
+        int pos;
+        if (type == null || (pos = type.indexOf("charset")) < 0) {
+          return null;
+        }
+        // Assuming mime type in form "text/XXXX; charset=XXXXXX"
+        StringTokenizer token = new StringTokenizer(type.substring(pos), "=; ");
+        token.nextToken(); // Skip 'charset='
+        return token.nextToken();
+    }
+
+    static class SimpleAuthenticator extends Authenticator {
+        private String user = null;
+        private String password = null;
+        public SimpleAuthenticator(String user, String password) {
+            this.user = user;
+            this.password = password;
+        }
+        public PasswordAuthentication getPasswordAuthentication() {
+
+            return new PasswordAuthentication(user, password);
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/PlainMailer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/PlainMailer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/PlainMailer.java	(revision 16139)
@@ -0,0 +1,140 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.email;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Enumeration;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.mail.MailMessage;
+
+/**
+ * Class responsible for sending email through raw protocol methods.
+ *
+ * @since Ant 1.5
+ */
+class PlainMailer extends Mailer {
+    /**
+     * Sends the email using the apache MailMessage class.
+     *
+     * @see org.apache.tools.mail.MailMessage
+     */
+    public void send() {
+        try {
+            MailMessage mailMessage = new MailMessage(host, port);
+
+            mailMessage.from(from.toString());
+
+            Enumeration e;
+
+            e = replyToList.elements();
+            while (e.hasMoreElements()) {
+                mailMessage.replyto(e.nextElement().toString());
+            }
+
+            e = toList.elements();
+            while (e.hasMoreElements()) {
+                mailMessage.to(e.nextElement().toString());
+            }
+
+            e = ccList.elements();
+            while (e.hasMoreElements()) {
+                mailMessage.cc(e.nextElement().toString());
+            }
+
+            e = bccList.elements();
+            while (e.hasMoreElements()) {
+                mailMessage.bcc(e.nextElement().toString());
+            }
+
+            if (subject != null) {
+                mailMessage.setSubject(subject);
+            }
+
+            mailMessage.setHeader("Date", getDate());
+            if (message.getCharset() != null) {
+                mailMessage.setHeader("Content-Type", message.getMimeType()
+                    + "; charset=\"" + message.getCharset() + "\"");
+
+            } else {
+                mailMessage.setHeader("Content-Type", message.getMimeType());
+            }
+            PrintStream out = mailMessage.getPrintStream();
+            message.print(out);
+
+            e = files.elements();
+            while (e.hasMoreElements()) {
+                File file = (File) e.nextElement();
+
+                attach(file, out);
+            }
+
+            mailMessage.sendAndClose();
+        } catch (IOException ioe) {
+            throw new BuildException("IO error sending mail", ioe);
+        }
+
+    }
+
+    /**
+     * Attaches a file to this email
+     *
+     * @param file The file to attache
+     * @param out The message stream to add to
+     * @throws IOException if errors occur
+     */
+    protected void attach(File file, PrintStream out)
+         throws IOException {
+        if (!file.exists() || !file.canRead()) {
+            throw new BuildException("File \"" + file.getName()
+                 + "\" does not exist or is not "
+                 + "readable.");
+        }
+
+        if (includeFileNames) {
+            out.println();
+
+            String filename = file.getName();
+            int filenamelength = filename.length();
+
+            out.println(filename);
+            for (int star = 0; star < filenamelength; star++) {
+                out.print('=');
+            }
+            out.println();
+        }
+
+        int length;
+        final int maxBuf = 1024;
+        byte[] buf = new byte[maxBuf];
+        FileInputStream finstr = new FileInputStream(file);
+
+        try {
+            BufferedInputStream in = new BufferedInputStream(finstr, buf.length);
+
+            while ((length = in.read(buf)) != -1) {
+                out.write(buf, 0, length);
+            }
+        } finally {
+            finstr.close();
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/UUMailer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/UUMailer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/email/UUMailer.java	(revision 16139)
@@ -0,0 +1,54 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.email;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import org.apache.tools.ant.BuildException;
+import sun.misc.UUEncoder;
+
+/**
+ * An emailer that uuencodes attachments.
+ *
+ * @since Ant 1.5
+ */
+class UUMailer extends PlainMailer {
+    protected void attach(File file, PrintStream out)
+         throws IOException {
+        if (!file.exists() || !file.canRead()) {
+            throw new BuildException("File \"" + file.getName()
+                 + "\" does not exist or is not "
+                 + "readable.");
+        }
+
+        FileInputStream finstr = new FileInputStream(file);
+
+        try {
+            BufferedInputStream in = new BufferedInputStream(finstr);
+            UUEncoder encoder = new UUEncoder(file.getName());
+
+            encoder.encode(in, out);
+
+        } finally {
+            finstr.close();
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ANTLR.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ANTLR.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ANTLR.java	(revision 16139)
@@ -0,0 +1,414 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.taskdefs.PumpStreamHandler;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.LoaderUtils;
+import org.apache.tools.ant.util.TeeOutputStream;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ *  Invokes the ANTLR Translator generator on a grammar file.
+ *
+ */
+public class ANTLR extends Task {
+
+    private CommandlineJava commandline = new CommandlineJava();
+
+    /** the file to process */
+    private File target;
+
+    /** where to output the result */
+    private File outputDirectory;
+
+    /** an optional super grammar file */
+    private File superGrammar;
+
+    /** optional flag to enable html output */
+    private boolean html;
+
+    /** optional flag to print out a diagnostic file */
+    private boolean diagnostic;
+
+    /** optional flag to add trace methods */
+    private boolean trace;
+
+    /** optional flag to add trace methods to the parser only */
+    private boolean traceParser;
+
+    /** optional flag to add trace methods to the lexer only */
+    private boolean traceLexer;
+
+    /** optional flag to add trace methods to the tree walker only */
+    private boolean traceTreeWalker;
+
+    /** working directory */
+    private File workingdir = null;
+
+    /** captures ANTLR's output */
+    private ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+    /** The debug attribute */
+    private boolean debug;
+
+
+    /** Instance of a utility class to use for file operations. */
+    private FileUtils fileUtils;
+
+    public ANTLR() {
+        commandline.setVm(JavaEnvUtils.getJreExecutable("java"));
+        commandline.setClassname("antlr.Tool");
+        fileUtils = FileUtils.newFileUtils();
+    }
+
+    /**
+     * The grammar file to process.
+     */
+    public void setTarget(File target) {
+        log("Setting target to: " + target.toString(), Project.MSG_VERBOSE);
+        this.target = target;
+    }
+
+    /**
+     * The directory to write the generated files to.
+     */
+    public void setOutputdirectory(File outputDirectory) {
+        log("Setting output directory to: " + outputDirectory.toString(), Project.MSG_VERBOSE);
+        this.outputDirectory = outputDirectory;
+    }
+
+    /**
+     * Sets an optional super grammar file.
+     * Use setGlib(File superGrammar) instead.
+     * @deprecated  since ant 1.6
+     */
+    public void setGlib(String superGrammar) {
+        String sg = null;
+        if (Os.isFamily("dos")) {
+            sg = superGrammar.replace('\\', '/');
+        } else {
+            sg = superGrammar;
+        }
+        setGlib(fileUtils.resolveFile(getProject().getBaseDir(), sg));
+    }
+    /**
+     * Sets an optional super grammar file
+     * @since ant 1.6
+     */
+    public void setGlib(File superGrammar) {
+        this.superGrammar = superGrammar;
+    }
+    /**
+     * Sets a flag to enable ParseView debugging
+     */
+    public void setDebug(boolean enable) {
+        this.debug = enable;
+    }
+
+    /**
+     * If true, emit html
+     */
+    public void setHtml(boolean enable) {
+        html = enable;
+    }
+
+    /**
+     * Sets a flag to emit diagnostic text
+     */
+    public void setDiagnostic(boolean enable) {
+        diagnostic = enable;
+    }
+
+    /**
+     * If true, enables all tracing.
+     */
+    public void setTrace(boolean enable) {
+        trace = enable;
+    }
+
+    /**
+     * If true, enables parser tracing.
+     */
+    public void setTraceParser(boolean enable) {
+        traceParser = enable;
+    }
+
+    /**
+     * If true, enables lexer tracing.
+     */
+    public void setTraceLexer(boolean enable) {
+        traceLexer = enable;
+    }
+
+    /**
+     * Sets a flag to allow the user to enable tree walker tracing
+     */
+    public void setTraceTreeWalker(boolean enable) {
+        traceTreeWalker = enable;
+    }
+
+    // we are forced to fork ANTLR since there is a call
+    // to System.exit() and there is nothing we can do
+    // right now to avoid this. :-( (SBa)
+    // I'm not removing this method to keep backward compatibility
+    /**
+     * @ant.attribute ignore="true"
+     */
+    public void setFork(boolean s) {
+        //this.fork = s;
+    }
+
+    /**
+     * The working directory of the process
+     */
+    public void setDir(File d) {
+        this.workingdir = d;
+    }
+
+    /**
+     * Adds a classpath to be set
+     * because a directory might be given for Antlr debug.
+     */
+    public Path createClasspath() {
+        return commandline.createClasspath(getProject()).createPath();
+    }
+
+    /**
+     * Adds a new JVM argument.
+     * @return  create a new JVM argument so that any argument can be passed to the JVM.
+     * @see #setFork(boolean)
+     */
+    public Commandline.Argument createJvmarg() {
+        return commandline.createVmArgument();
+    }
+
+    /**
+     * Adds the jars or directories containing Antlr
+     * this should make the forked JVM work without having to
+     * specify it directly.
+     */
+    public void init() throws BuildException {
+        addClasspathEntry("/antlr/ANTLRGrammarParseBehavior.class");
+    }
+
+    /**
+     * Search for the given resource and add the directory or archive
+     * that contains it to the classpath.
+     *
+     * <p>Doesn't work for archives in JDK 1.1 as the URL returned by
+     * getResource doesn't contain the name of the archive.</p>
+     */
+    protected void addClasspathEntry(String resource) {
+        /*
+         * pre Ant 1.6 this method used to call getClass().getResource
+         * while Ant 1.6 will call ClassLoader.getResource().
+         *
+         * The difference is that Class.getResource expects a leading
+         * slash for "absolute" resources and will strip it before
+         * delegating to ClassLoader.getResource - so we now have to
+         * emulate Class's behavior.
+         */
+        if (resource.startsWith("/")) {
+            resource = resource.substring(1);
+        } else {
+            resource = "org/apache/tools/ant/taskdefs/optional/"
+                + resource;
+        }
+
+        File f = LoaderUtils.getResourceSource(getClass().getClassLoader(),
+                                               resource);
+        if (f != null) {
+            log("Found " + f.getAbsolutePath(), Project.MSG_DEBUG);
+            createClasspath().setLocation(f);
+        } else {
+            log("Couldn\'t find " + resource, Project.MSG_VERBOSE);
+        }
+    }
+
+    public void execute() throws BuildException {
+        validateAttributes();
+
+        //TODO: use ANTLR to parse the grammar file to do this.
+        File generatedFile = getGeneratedFile();
+        boolean targetIsOutOfDate =
+            target.lastModified() > generatedFile.lastModified();
+        boolean superGrammarIsOutOfDate  = superGrammar != null
+                && (superGrammar.lastModified() > generatedFile.lastModified());
+        if (targetIsOutOfDate || superGrammarIsOutOfDate) {
+            if (targetIsOutOfDate) {
+                log("Compiling " + target + " as it is newer than "
+                    + generatedFile, Project.MSG_VERBOSE);
+            } else if (superGrammarIsOutOfDate) {
+                log("Compiling " + target + " as " + superGrammar
+                    + " is newer than " + generatedFile, Project.MSG_VERBOSE);
+            }
+            populateAttributes();
+            commandline.createArgument().setValue(target.toString());
+
+            log(commandline.describeCommand(), Project.MSG_VERBOSE);
+            int err = run(commandline.getCommandline());
+            if (err != 0) {
+                throw new BuildException("ANTLR returned: " + err, getLocation());
+            } else {
+                String output = bos.toString();
+                if (output.indexOf("error:") > -1) {
+                    throw new BuildException("ANTLR signaled an error: "
+                                             + output, getLocation());
+                }
+            }
+        } else {
+            log("Skipped grammar file. Generated file " + generatedFile
+                + " is newer.", Project.MSG_VERBOSE);
+        }
+    }
+
+    /**
+     * A refactored method for populating all the command line arguments based
+     * on the user-specified attributes.
+     */
+    private void populateAttributes() {
+        commandline.createArgument().setValue("-o");
+        commandline.createArgument().setValue(outputDirectory.toString());
+        if (superGrammar != null) {
+            commandline.createArgument().setValue("-glib");
+            commandline.createArgument().setValue(superGrammar.toString());
+        }
+        if (html) {
+            commandline.createArgument().setValue("-html");
+        }
+        if (diagnostic) {
+            commandline.createArgument().setValue("-diagnostic");
+        }
+        if (trace) {
+            commandline.createArgument().setValue("-trace");
+        }
+        if (traceParser) {
+            commandline.createArgument().setValue("-traceParser");
+        }
+        if (traceLexer) {
+            commandline.createArgument().setValue("-traceLexer");
+        }
+        if (traceTreeWalker) {
+            if (is272()) {
+                commandline.createArgument().setValue("-traceTreeParser");
+            } else {
+                commandline.createArgument().setValue("-traceTreeWalker");
+            }
+        }
+        if (debug) {
+            commandline.createArgument().setValue("-debug");
+        }
+    }
+
+    private void validateAttributes() throws BuildException {
+        if (target == null || !target.isFile()) {
+            throw new BuildException("Invalid target: " + target);
+        }
+
+        // if no output directory is specified, used the target's directory
+        if (outputDirectory == null) {
+            setOutputdirectory(new File(target.getParent()));
+        }
+        if (!outputDirectory.isDirectory()) {
+            throw new BuildException("Invalid output directory: " + outputDirectory);
+        }
+    }
+
+    private File getGeneratedFile() throws BuildException {
+        String generatedFileName = null;
+        try {
+            BufferedReader in = new BufferedReader(new FileReader(target));
+            String line;
+            while ((line = in.readLine()) != null) {
+                int extendsIndex = line.indexOf(" extends ");
+                if (line.startsWith("class ") && extendsIndex > -1) {
+                    generatedFileName = line.substring(6, extendsIndex).trim();
+                    break;
+                }
+            }
+            in.close();
+        } catch (Exception e) {
+            throw new BuildException("Unable to determine generated class", e);
+        }
+        if (generatedFileName == null) {
+            throw new BuildException("Unable to determine generated class");
+        }
+        return new File(outputDirectory, generatedFileName + ".java");
+    }
+
+    /** execute in a forked VM */
+    private int run(String[] command) throws BuildException {
+        PumpStreamHandler psh =
+            new PumpStreamHandler(new LogOutputStream(this, Project.MSG_INFO),
+                                  new TeeOutputStream(
+                                                      new LogOutputStream(this,
+                                                                          Project.MSG_WARN),
+                                                      bos)
+                                  );
+        Execute exe = new Execute(psh, null);
+        exe.setAntRun(getProject());
+        if (workingdir != null) {
+            exe.setWorkingDirectory(workingdir);
+        }
+        exe.setCommandline(command);
+        try {
+            return exe.execute();
+        } catch (IOException e) {
+            throw new BuildException(e, getLocation());
+        } finally {
+            try {
+                bos.close();
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+    }
+
+    /**
+     * Whether the antlr version is 2.7.2 (or higher).
+     *
+     * @return true if the version of Antlr present is 2.7.2 or later.
+     * @since Ant 1.6
+     */
+    protected boolean is272() {
+        try {
+            AntClassLoader l = new AntClassLoader(getProject(),
+                                                  commandline.getClasspath());
+            l.loadClass("antlr.Version");
+            return true;
+        } catch (ClassNotFoundException e) {
+            return false;
+        } // end of try-catch
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Cab.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Cab.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Cab.java	(revision 16139)
@@ -0,0 +1,325 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.ExecTask;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.taskdefs.StreamPumper;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.FileUtils;
+
+
+/**
+ * Create a CAB archive.
+ *
+ */
+
+public class Cab extends MatchingTask {
+
+    private File cabFile;
+    private File baseDir;
+    private Vector filesets = new Vector();
+    private boolean doCompress = true;
+    private boolean doVerbose = false;
+    private String cmdOptions;
+
+    protected String archiveType = "cab";
+
+    private FileUtils fileUtils = FileUtils.newFileUtils();
+
+    /**
+     * The name/location of where to create the .cab file.
+     */
+    public void setCabfile(File cabFile) {
+        this.cabFile = cabFile;
+    }
+
+    /**
+     * Base directory to look in for files to CAB.
+     */
+    public void setBasedir(File baseDir) {
+        this.baseDir = baseDir;
+    }
+
+    /**
+     * If true, compress the files otherwise only store them.
+     */
+    public void setCompress(boolean compress) {
+        doCompress = compress;
+    }
+
+    /**
+     * If true, display cabarc output.
+     */
+    public void setVerbose(boolean verbose) {
+        doVerbose = verbose;
+    }
+
+    /**
+     * Sets additional cabarc options that are not supported directly.
+     */
+    public void setOptions(String options) {
+        cmdOptions = options;
+    }
+
+    /**
+     * Adds a set of files to archive.
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+    /*
+     * I'm not fond of this pattern: "sub-method expected to throw
+     * task-cancelling exceptions".  It feels too much like programming
+     * for side-effects to me...
+     */
+    protected void checkConfiguration() throws BuildException {
+        if (baseDir == null && filesets.size() == 0) {
+            throw new BuildException("basedir attribute or at least one "
+                                     + "nested filest is required!",
+                                     getLocation());
+        }
+        if (baseDir != null && !baseDir.exists()) {
+            throw new BuildException("basedir does not exist!", getLocation());
+        }
+        if (cabFile == null) {
+            throw new BuildException("cabfile attribute must be set!",
+                                     getLocation());
+        }
+    }
+
+    /**
+     * Create a new exec delegate.  The delegate task is populated so that
+     * it appears in the logs to be the same task as this one.
+     */
+    protected ExecTask createExec() throws BuildException {
+        ExecTask exec = (ExecTask) getProject().createTask("exec");
+        exec.setOwningTarget(this.getOwningTarget());
+        exec.setTaskName(this.getTaskName());
+        exec.setDescription(this.getDescription());
+
+        return exec;
+    }
+
+    /**
+     * Check to see if the target is up to date with respect to input files.
+     * @return true if the cab file is newer than its dependents.
+     */
+    protected boolean isUpToDate(Vector files) {
+        boolean upToDate = true;
+        for (int i = 0; i < files.size() && upToDate; i++) {
+            String file = files.elementAt(i).toString();
+            if (fileUtils.resolveFile(baseDir, file).lastModified()
+                    > cabFile.lastModified()) {
+                upToDate = false;
+            }
+        }
+        return upToDate;
+    }
+
+    /**
+     * Creates a list file.  This temporary file contains a list of all files
+     * to be included in the cab, one file per line.
+     *
+     * <p>This method expects to only be called on Windows and thus
+     * quotes the file names.</p>
+     */
+    protected File createListFile(Vector files)
+        throws IOException {
+        File listFile = fileUtils.createTempFile("ant", "", null);
+        listFile.deleteOnExit();
+
+        PrintWriter writer = new PrintWriter(new FileOutputStream(listFile));
+
+        int size = files.size();
+        for (int i = 0; i < size; i++) {
+            writer.println('\"' + files.elementAt(i).toString() + '\"');
+        }
+        writer.close();
+
+        return listFile;
+    }
+
+    /**
+     * Append all files found by a directory scanner to a vector.
+     */
+    protected void appendFiles(Vector files, DirectoryScanner ds) {
+        String[] dsfiles = ds.getIncludedFiles();
+
+        for (int i = 0; i < dsfiles.length; i++) {
+            files.addElement(dsfiles[i]);
+        }
+    }
+
+    /**
+     * Get the complete list of files to be included in the cab.  Filenames
+     * are gathered from filesets if any have been added, otherwise from the
+     * traditional include parameters.
+     */
+    protected Vector getFileList() throws BuildException {
+        Vector files = new Vector();
+
+        if (baseDir != null) {
+            // get files from old methods - includes and nested include
+            appendFiles(files, super.getDirectoryScanner(baseDir));
+        }
+
+        // get files from filesets
+        for (int i = 0; i < filesets.size(); i++) {
+            FileSet fs = (FileSet) filesets.elementAt(i);
+            if (fs != null) {
+                appendFiles(files, fs.getDirectoryScanner(getProject()));
+            }
+        }
+
+        return files;
+    }
+
+    public void execute() throws BuildException {
+
+        checkConfiguration();
+
+        Vector files = getFileList();
+
+        // quick exit if the target is up to date
+        if (isUpToDate(files)) {
+            return;
+        }
+
+        log("Building " + archiveType + ": " + cabFile.getAbsolutePath());
+
+        if (!Os.isFamily("windows")) {
+            log("Using listcab/libcabinet", Project.MSG_VERBOSE);
+
+            StringBuffer sb = new StringBuffer();
+
+            Enumeration fileEnum = files.elements();
+
+            while (fileEnum.hasMoreElements()) {
+                sb.append(fileEnum.nextElement()).append("\n");
+            }
+            sb.append("\n").append(cabFile.getAbsolutePath()).append("\n");
+
+            try {
+                Process p = Execute.launch(getProject(),
+                                           new String[] {"listcab"}, null,
+                                           baseDir != null ? baseDir
+                                                   : getProject().getBaseDir(),
+                                           true);
+                OutputStream out = p.getOutputStream();
+
+                // Create the stream pumpers to forward listcab's stdout and stderr to the log
+                // note: listcab is an interactive program, and issues prompts for every new line.
+                //       Therefore, make it show only with verbose logging turned on.
+                LogOutputStream outLog = new LogOutputStream(this, Project.MSG_VERBOSE);
+                LogOutputStream errLog = new LogOutputStream(this, Project.MSG_ERR);
+                StreamPumper    outPump = new StreamPumper(p.getInputStream(), outLog);
+                StreamPumper    errPump = new StreamPumper(p.getErrorStream(), errLog);
+
+                // Pump streams asynchronously
+                (new Thread(outPump)).start();
+                (new Thread(errPump)).start();
+
+                out.write(sb.toString().getBytes());
+                out.flush();
+                out.close();
+
+                int result = -99; // A wild default for when the thread is interrupted
+
+                try {
+                    // Wait for the process to finish
+                    result = p.waitFor();
+
+                    // Wait for the end of output and error streams
+                    outPump.waitFor();
+                    outLog.close();
+                    errPump.waitFor();
+                    errLog.close();
+                } catch (InterruptedException ie) {
+                    log("Thread interrupted: " + ie);
+                }
+
+                // Informative summary message in case of errors
+                if (Execute.isFailure(result)) {
+                    log("Error executing listcab; error code: " + result);
+                }
+            } catch (IOException ex) {
+                String msg = "Problem creating " + cabFile + " " + ex.getMessage();
+                throw new BuildException(msg, getLocation());
+            }
+        } else {
+            try {
+                File listFile = createListFile(files);
+                ExecTask exec = createExec();
+                File outFile = null;
+
+                // die if cabarc fails
+                exec.setFailonerror(true);
+                exec.setDir(baseDir);
+
+                if (!doVerbose) {
+                    outFile = fileUtils.createTempFile("ant", "", null);
+                    outFile.deleteOnExit();
+                    exec.setOutput(outFile);
+                }
+
+                exec.setExecutable("cabarc");
+                exec.createArg().setValue("-r");
+                exec.createArg().setValue("-p");
+
+                if (!doCompress) {
+                    exec.createArg().setValue("-m");
+                    exec.createArg().setValue("none");
+                }
+
+                if (cmdOptions != null) {
+                    exec.createArg().setLine(cmdOptions);
+                }
+
+                exec.createArg().setValue("n");
+                exec.createArg().setFile(cabFile);
+                exec.createArg().setValue("@" + listFile.getAbsolutePath());
+
+                exec.execute();
+
+                if (outFile != null) {
+                    outFile.delete();
+                }
+
+                listFile.delete();
+            } catch (IOException ioe) {
+                String msg = "Problem creating " + cabFile + " " + ioe.getMessage();
+                throw new BuildException(msg, getLocation());
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/EchoProperties.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/EchoProperties.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/EchoProperties.java	(revision 16139)
@@ -0,0 +1,419 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Vector;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.PropertySet;
+import org.apache.tools.ant.util.CollectionUtils;
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ *  Displays all the current properties in the build. The output can be sent to
+ *  a file if desired. <P>
+ *
+ *  Attribute "destfile" defines a file to send the properties to. This can be
+ *  processed as a standard property file later. <P>
+ *
+ *  Attribute "prefix" defines a prefix which is used to filter the properties
+ *  only those properties starting with this prefix will be echoed. <P>
+ *
+ *  By default, the "failonerror" attribute is enabled. If an error occurs while
+ *  writing the properties to a file, and this attribute is enabled, then a
+ *  BuildException will be thrown. If disabled, then IO errors will be reported
+ *  as a log statement, but no error will be thrown. <P>
+ *
+ *  Examples: <pre>
+ *  &lt;echoproperties  /&gt;
+ * </pre> Report the current properties to the log. <P>
+ *
+ *  <pre>
+ *  &lt;echoproperties destfile="my.properties" /&gt;
+ * </pre> Report the current properties to the file "my.properties", and will
+ *  fail the build if the file could not be created or written to. <P>
+ *
+ *  <pre>
+ *  &lt;echoproperties destfile="my.properties" failonerror="false"
+ *      prefix="ant" /&gt;
+ * </pre> Report all properties beginning with 'ant' to the file
+ *  "my.properties", and will log a message if the file could not be created or
+ *  written to, but will still allow the build to continue.
+ *
+ *@since      Ant 1.5
+ */
+public class EchoProperties extends Task {
+
+    /**
+     * the properties element.
+     */
+    private static final String PROPERTIES = "properties";
+
+    /**
+     * the property element.
+     */
+    private static final String PROPERTY = "property";
+
+    /**
+     * name attribute for property, testcase and testsuite elements.
+     */
+    private static final String ATTR_NAME = "name";
+
+    /**
+     * value attribute for property elements.
+     */
+    private static final String ATTR_VALUE = "value";
+
+    /**
+     * the input file.
+     */
+    private File inFile = null;
+
+    /**
+     *  File object pointing to the output file. If this is null, then
+     *  we output to the project log, not to a file.
+     */
+    private File destfile = null;
+
+    /**
+     *  If this is true, then errors generated during file output will become
+     *  build errors, and if false, then such errors will be logged, but not
+     *  thrown.
+     */
+    private boolean failonerror = true;
+
+    private Vector propertySets = new Vector();
+
+    private String format = "text";
+
+    /**
+     * Sets the input file.
+     *
+     * @param file  the input file
+     */
+    public void setSrcfile(File file) {
+        inFile = file;
+    }
+
+    /**
+     *  Set a file to store the property output.  If this is never specified,
+     *  then the output will be sent to the Ant log.
+     *
+     *@param destfile file to store the property output
+     */
+    public void setDestfile(File destfile) {
+        this.destfile = destfile;
+    }
+
+
+    /**
+     * If true, the task will fail if an error occurs writing the properties
+     * file, otherwise errors are just logged.
+     *
+     *@param  failonerror  <tt>true</tt> if IO exceptions are reported as build
+     *      exceptions, or <tt>false</tt> if IO exceptions are ignored.
+     */
+    public void setFailOnError(boolean failonerror) {
+        this.failonerror = failonerror;
+    }
+
+
+    /**
+     *  If the prefix is set, then only properties which start with this
+     *  prefix string will be recorded.  If this is never set, or it is set
+     *  to an empty string or <tt>null</tt>, then all properties will be
+     *  recorded. <P>
+     *
+     *  For example, if the property is set as:
+     *    <PRE>&lt;echoproperties  prefix="ant." /&gt;</PRE>
+     *  then the property "ant.home" will be recorded, but "ant-example"
+     *  will not.
+     *
+     *@param  prefix  The new prefix value
+     */
+    public void setPrefix(String prefix) {
+        PropertySet ps = new PropertySet();
+        ps.setProject(getProject());
+        ps.appendPrefix(prefix);
+        addPropertyset(ps);
+    }
+
+    /**
+     * A set of properties to write.
+     *
+     * @since Ant 1.6
+     */
+    public void addPropertyset(PropertySet ps) {
+        propertySets.addElement(ps);
+    }
+
+    public void setFormat(FormatAttribute ea) {
+        format = ea.getValue();
+    }
+
+    public static class FormatAttribute extends EnumeratedAttribute {
+        private String [] formats = new String[]{"xml", "text"};
+
+        public String[] getValues() {
+            return formats;
+        }
+    }
+
+    /**
+     *  Run the task.
+     *
+     *@exception  BuildException  trouble, probably file IO
+     */
+    public void execute() throws BuildException {
+        //copy the properties file
+        Hashtable allProps = new Hashtable();
+
+        /* load properties from file if specified, otherwise
+        use Ant's properties */
+        if (inFile == null && propertySets.size() == 0) {
+            // add ant properties
+            CollectionUtils.putAll(allProps, getProject().getProperties());
+        } else if (inFile != null) {
+            if (inFile.exists() && inFile.isDirectory()) {
+                String message = "srcfile is a directory!";
+                if (failonerror) {
+                    throw new BuildException(message, getLocation());
+                } else {
+                    log(message, Project.MSG_ERR);
+                }
+                return;
+            }
+
+            if (inFile.exists() && !inFile.canRead()) {
+                String message = "Can not read from the specified srcfile!";
+                if (failonerror) {
+                    throw new BuildException(message, getLocation());
+                } else {
+                    log(message, Project.MSG_ERR);
+                }
+                return;
+            }
+
+            FileInputStream in = null;
+            try {
+                in = new FileInputStream(inFile);
+                Properties props = new Properties();
+                props.load(in);
+                CollectionUtils.putAll(allProps, props);
+            } catch (FileNotFoundException fnfe) {
+                String message =
+                    "Could not find file " + inFile.getAbsolutePath();
+                if (failonerror) {
+                    throw new BuildException(message, fnfe, getLocation());
+                } else {
+                    log(message, Project.MSG_WARN);
+                }
+                return;
+            } catch (IOException ioe) {
+                String message =
+                    "Could not read file " + inFile.getAbsolutePath();
+                if (failonerror) {
+                    throw new BuildException(message, ioe, getLocation());
+                } else {
+                    log(message, Project.MSG_WARN);
+                }
+                return;
+            } finally {
+                try {
+                    if (null != in) {
+                        in.close();
+                    }
+                } catch (IOException ioe) {
+                    //ignore
+                }
+            }
+        }
+
+        Enumeration e = propertySets.elements();
+        while (e.hasMoreElements()) {
+            PropertySet ps = (PropertySet) e.nextElement();
+            CollectionUtils.putAll(allProps, ps.getProperties());
+        }
+
+        OutputStream os = null;
+        try {
+            if (destfile == null) {
+                os = new ByteArrayOutputStream();
+                saveProperties(allProps, os);
+                log(os.toString(), Project.MSG_INFO);
+            } else {
+                if (destfile.exists() && destfile.isDirectory()) {
+                    String message = "destfile is a directory!";
+                    if (failonerror) {
+                        throw new BuildException(message, getLocation());
+                    } else {
+                        log(message, Project.MSG_ERR);
+                    }
+                    return;
+                }
+
+                if (destfile.exists() && !destfile.canWrite()) {
+                    String message =
+                        "Can not write to the specified destfile!";
+                    if (failonerror) {
+                        throw new BuildException(message, getLocation());
+                    } else {
+                        log(message, Project.MSG_ERR);
+                    }
+                    return;
+                }
+                os = new FileOutputStream(this.destfile);
+                saveProperties(allProps, os);
+            }
+        } catch (IOException ioe) {
+            if (failonerror) {
+                throw new BuildException(ioe, getLocation());
+            } else {
+                log(ioe.getMessage(), Project.MSG_INFO);
+            }
+        } finally {
+            if (os != null) {
+                try {
+                    os.close();
+                } catch (IOException ex) {
+                    //ignore
+                }
+            }
+        }
+    }
+
+
+    /**
+     *  Send the key/value pairs in the hashtable to the given output stream.
+     *  Only those properties matching the <tt>prefix</tt> constraint will be
+     *  sent to the output stream.
+     *  The output stream will be closed when this method returns.
+     *
+     *@param  allProps         propfile to save
+     *@param  os               output stream
+     *@exception  IOException  trouble
+     */
+    protected void saveProperties(Hashtable allProps, OutputStream os)
+             throws IOException, BuildException {
+        Properties props = new Properties();
+        Enumeration e = allProps.keys();
+        while (e.hasMoreElements()) {
+            String name = e.nextElement().toString();
+            String value = allProps.get(name).toString();
+            props.put(name, value);
+        }
+
+        if ("text".equals(format)) {
+            jdkSaveProperties(props, os, "Ant properties");
+        } else if ("xml".equals(format)) {
+            xmlSaveProperties(props, os);
+        }
+    }
+
+    protected void xmlSaveProperties(Properties props,
+                                     OutputStream os) throws IOException {
+        // create XML document
+        Document doc = getDocumentBuilder().newDocument();
+        Element rootElement = doc.createElement(PROPERTIES);
+
+        // output properties
+        String name;
+        Enumeration e = props.propertyNames();
+        while (e.hasMoreElements()) {
+            name = (String) e.nextElement();
+            Element propElement = doc.createElement(PROPERTY);
+            propElement.setAttribute(ATTR_NAME, name);
+            propElement.setAttribute(ATTR_VALUE, props.getProperty(name));
+            rootElement.appendChild(propElement);
+        }
+
+        Writer wri = null;
+        try {
+            wri = new OutputStreamWriter(os, "UTF8");
+            wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+            (new DOMElementWriter()).write(rootElement, wri, 0, "\t");
+            wri.flush();
+        } catch (IOException ioe) {
+            throw new BuildException("Unable to write XML file", ioe);
+        } finally {
+            if (wri != null) {
+                wri.close();
+            }
+        }
+    }
+
+    /**
+     *  JDK 1.2 allows for the safer method
+     *  <tt>Properties.store(OutputStream, String)</tt>, which throws an
+     *  <tt>IOException</tt> on an output error.
+     *
+     *@param props the properties to record
+     *@param os record the properties to this output stream
+     *@param header prepend this header to the property output
+     *@exception IOException on an I/O error during a write.
+     */
+    protected void jdkSaveProperties(Properties props, OutputStream os,
+                                     String header) throws IOException {
+       try {
+           props.store(os, header);
+
+       } catch (IOException ioe) {
+           throw new BuildException(ioe, getLocation());
+       } finally {
+           if (os != null) {
+               try {
+                   os.close();
+               } catch (IOException ioex) {
+                   log("Failed to close output stream");
+               }
+           }
+       }
+    }
+
+
+    /**
+     * Uses the DocumentBuilderFactory to get a DocumentBuilder instance.
+     *
+     * @return   The DocumentBuilder instance
+     */
+    private static DocumentBuilder getDocumentBuilder() {
+        try {
+            return DocumentBuilderFactory.newInstance().newDocumentBuilder();
+        } catch (Exception e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/IContract.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/IContract.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/IContract.java	(revision 16139)
@@ -0,0 +1,938 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Date;
+import java.util.Properties;
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.taskdefs.Javac;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.taskdefs.Mkdir;
+import org.apache.tools.ant.taskdefs.compilers.DefaultCompilerAdapter;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Instruments Java classes with iContract DBC preprocessor.
+ * <br/>
+ * The task can generate a properties file for
+ * <a href="http://hjem.sol.no/hellesoy/icontrol.html">iControl</a>,
+ * a graphical user interface that lets you turn on/off assertions.
+ * iControl generates a control file that you can refer to
+ * from this task using the controlfile attribute.
+ * iContract is at
+ * <a href="http://www.reliable-systems.com/tools/">
+ *   http://www.reliable-systems.com/tools/</a>
+ * <p/>
+ * Thanks to Rainer Schmitz for enhancements and comments.
+ *
+ *
+ * <p/>
+ * <table border="1" cellpadding="2" cellspacing="0">
+ *   <tr>
+ *     <td valign="top"><b>Attribute</b></td>
+ *     <td valign="top"><b>Description</b></td>
+ *     <td align="center" valign="top"><b>Required</b></td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top">srcdir</td>
+ *     <td valign="top">Location of the java files.</td>
+ *     <td valign="top" align="center">Yes</td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top">instrumentdir</td>
+ *     <td valign="top">Indicates where the instrumented source
+ *                      files should go.</td>
+ *     <td valign="top" align="center">Yes</td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top">repositorydir</td>
+ *     <td valign="top">Indicates where the repository source
+ *                      files should go.</td>
+ *     <td valign="top" align="center">Yes</td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top">builddir</td>
+ *     <td valign="top">Indicates where the compiled instrumented
+ *                      classes should go. Defaults to the value of
+ *                      instrumentdir.
+ *       </p>
+ *       <em>NOTE:</em> Don't use the same directory for compiled
+ *       instrumented classes and uninstrumented classes. It will break the
+ *       dependency checking. (Classes will not be reinstrumented if you
+ *       change them).</td>
+ *     <td valign="top" align="center">No</td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top">repbuilddir</td>
+ *     <td valign="top">Indicates where the compiled repository classes
+ *                      should go. Defaults to the value of repositorydir.</td>
+ *     <td valign="top" align="center">No</td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top">pre</td>
+ *     <td valign="top">Indicates whether or not to instrument for
+ *                      preconditions. Defaults to <code>true</code> unless
+ *                      controlfile is specified, in which case it defaults
+ *                      to <code>false</code>.</td>
+ *     <td valign="top" align="center">No</td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top">post</td>
+ *     <td valign="top">Indicates whether or not to instrument for
+ *                      postconditions. Defaults to <code>true</code> unless
+ *                      controlfile is specified, in which case it defaults
+ *                      to <code>false</code>.</td>
+ *     <td valign="top" align="center">No</td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top">invariant</td>
+ *     <td valign="top">Indicates whether or not to instrument for invariants.
+ *                      Defaults to <code>true</code> unless controlfile is
+ *                      specified, in which case it defaults to
+ *                      <code>false</code>.</td>
+ *     <td valign="top" align="center">No</td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top">failthrowable</td>
+ *     <td valign="top">The full name of the Throwable (Exception) that
+ *                      should be thrown when an assertion is violated.
+ *                      Defaults to <code>java.lang.Error</code></td>
+ *     <td valign="top" align="center">No</td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top">verbosity</td>
+ *     <td valign="top">Indicates the verbosity level of iContract.
+ *       Any combination of
+ *       <code>error*,warning*,note*,info*,progress*,debug*</code>
+ *       (comma separated) can be used. Defaults to <code>error*</code></td>
+ *     <td valign="top" align="center">No</td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top">quiet</td>
+ *     <td valign="top">Indicates if iContract should be quiet. Turn it off
+ *       if many your classes extend uninstrumented classes and you don't
+ *       want warnings about this. Defaults to <code>false</code></td>
+ *     <td valign="top" align="center">No</td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top">updateicontrol</td>
+ *     <td valign="top">If set to true, it indicates that the properties
+ *       file for iControl in the current directory should be updated
+ *       (or created if it doesn't exist). Defaults to <code>false</code>.
+ *     </td>
+ *     <td valign="top" align="center">No</td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top">controlfile</td>
+ *     <td valign="top">The name of the control file to pass to iContract.
+ *                      Consider using iControl to generate the file.
+ *                      Default is not to pass a file. </td>
+ *     <td valign="top" align="center">
+ *                      Only if <code>updateicontrol=true</code></td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top">classdir</td>
+ *     <td valign="top">Indicates where compiled (unistrumented) classes are
+ *                      located. This is required in order to properly update
+ *                      the icontrol.properties file, not for instrumentation.
+ *     </td>
+ *     <td valign="top" align="center">Only if
+ *                                     <code>updateicontrol=true</code></td>
+ *   </tr>
+ *   <tr>
+ *     <td valign="top">targets</td>
+ *     <td valign="top">Name of the file that will be generated by this task,
+ *                      which lists all the classes that iContract will
+ *                      instrument. If specified, the file will not be deleted
+ *                      after execution. If not specified, a file will still
+ *                      be created, but it will be deleted after execution.</td>
+ *     <td valign="top" align="center">No</td>
+ *   </tr>
+ * </table>
+ *
+ * <p/>
+ * <b>Note:</b> iContract will use the java compiler indicated by the project's
+ * <code>build.compiler</code> property. See documentation of the Javac task for
+ * more information.
+ * <p/>
+ * Nested includes and excludes are also supported.
+ *
+ * <p><b>Example:</b></p>
+ * <pre>
+ * &lt;icontract
+ *    srcdir="${build.src}"
+ *    instrumentdir="${build.instrument}"
+ *    repositorydir="${build.repository}"
+ *    builddir="${build.instrclasses}"
+ *    updateicontrol="true"
+ *    classdir="${build.classes}"
+ *    controlfile="control"
+ *    targets="targets"
+ *    verbosity="error*,warning*"
+ *    quiet="true"
+ * >
+ *    &lt;classpath refid="compile-classpath"/>
+ * &lt;/icontract>
+ * </pre>
+ *
+ */
+public class IContract extends MatchingTask {
+
+    private static final String ICONTROL_PROPERTIES_HEADER
+        = "You might want to set classRoot to point to your normal "
+            + "compilation class root directory.";
+
+    /** compiler to use for instrumenation */
+    private String icCompiler = "javac";
+
+    /** temporary file with file names of all java files to be instrumented */
+    private File targets = null;
+
+    /**
+     * will be set to true if any of the source files are newer than the
+     * instrumented files
+     */
+    private boolean dirty = false;
+
+    /** set to true if the iContract jar is missing */
+    private boolean iContractMissing = false;
+
+    /** source file root */
+    private File srcDir = null;
+
+    /** instrumentation src root */
+    private File instrumentDir = null;
+
+    /** instrumentation build root */
+    private File buildDir = null;
+
+    /** repository src root */
+    private File repositoryDir = null;
+
+    /** repository build root */
+    private File repBuildDir = null;
+
+    /** classpath */
+    private Path classpath = null;
+
+    /** The class of the Throwable to be thrown on failed assertions */
+    private String failThrowable = "java.lang.Error";
+
+    /** The -v option */
+    private String verbosity = "error*";
+
+    /** The -q option */
+    private boolean quiet = false;
+
+    /** The -m option */
+    private File controlFile = null;
+
+    /** Indicates whether or not to instrument for preconditions */
+    private boolean pre = true;
+    private boolean preModified = false;
+
+    /** Indicates whether or not to instrument for postconditions */
+    private boolean post = true;
+    private boolean postModified = false;
+
+    /** Indicates whether or not to instrument for invariants */
+    private boolean invariant = true;
+    private boolean invariantModified = false;
+
+    /**
+     * Indicates whether or not to instrument all files regardless of timestamp
+     *
+     * Can't be explicitly set, is set if control file exists and is newer
+     * than any source file
+     */
+    private boolean instrumentall = false;
+
+    /**
+     * Indicates the name of a properties file (intentionally for iControl)
+     * where the classpath property should be updated.
+     */
+    private boolean updateIcontrol = false;
+
+    /** Regular compilation class root  */
+    private File classDir = null;
+
+    /**
+     * Sets the source directory.
+     *
+     * @param srcDir the source directory
+     */
+    public void setSrcdir(File srcDir) {
+        this.srcDir = srcDir;
+    }
+
+
+    /**
+     * Sets the class directory (uninstrumented classes).
+     *
+     * @param classDir the source directory
+     */
+    public void setClassdir(File classDir) {
+        this.classDir = classDir;
+    }
+
+
+    /**
+     * Sets the instrumentation directory.
+     *
+     * @param instrumentDir the source directory
+     */
+    public void setInstrumentdir(File instrumentDir) {
+        this.instrumentDir = instrumentDir;
+        if (this.buildDir == null) {
+            setBuilddir(instrumentDir);
+        }
+    }
+
+
+    /**
+     * Sets the build directory for instrumented classes.
+     *
+     * @param buildDir the build directory
+     */
+    public void setBuilddir(File buildDir) {
+        this.buildDir = buildDir;
+    }
+
+
+    /**
+     * Sets the build directory for repository classes.
+     *
+     * @param repositoryDir the source directory
+     */
+    public void setRepositorydir(File repositoryDir) {
+        this.repositoryDir = repositoryDir;
+        if (this.repBuildDir == null) {
+            setRepbuilddir(repositoryDir);
+        }
+    }
+
+
+    /**
+     * Sets the build directory for instrumented classes.
+     *
+     * @param repBuildDir the build directory
+     */
+    public void setRepbuilddir(File repBuildDir) {
+        this.repBuildDir = repBuildDir;
+    }
+
+
+    /**
+     * Turns on/off precondition instrumentation.
+     *
+     * @param pre true turns it on
+     */
+    public void setPre(boolean pre) {
+        this.pre = pre;
+        preModified = true;
+    }
+
+
+    /**
+     * Turns on/off postcondition instrumentation.
+     *
+     * @param post true turns it on
+     */
+    public void setPost(boolean post) {
+        this.post = post;
+        postModified = true;
+    }
+
+
+    /**
+     * Turns on/off invariant instrumentation.
+     *
+     * @param invariant true turns it on
+     */
+    public void setInvariant(boolean invariant) {
+        this.invariant = invariant;
+        invariantModified = true;
+    }
+
+
+    /**
+     * Sets the Throwable (Exception) to be thrown on assertion violation.
+     *
+     * @param clazz the fully qualified Throwable class name
+     */
+    public void setFailthrowable(String clazz) {
+        this.failThrowable = clazz;
+    }
+
+
+    /**
+     * Sets the verbosity level of iContract. Any combination of
+     * error*,warning*,note*,info*,progress*,debug* (comma separated) can be
+     * used. Defaults to error*,warning*
+     *
+     * @param verbosity verbosity level
+     */
+    public void setVerbosity(String verbosity) {
+        this.verbosity = verbosity;
+    }
+
+
+    /**
+     * Tells iContract to be quiet.
+     *
+     * @param quiet true if iContract should be quiet.
+     */
+    public void setQuiet(boolean quiet) {
+        this.quiet = quiet;
+    }
+
+
+    /**
+     * Sets the name of the file where targets will be written. That is the
+     * file that tells iContract what files to process.
+     *
+     * @param targets the targets file name
+     */
+    public void setTargets(File targets) {
+        this.targets = targets;
+    }
+
+
+    /**
+     * Sets the control file to pass to iContract.
+     *
+     * @param controlFile the control file
+     */
+    public void setControlfile(File controlFile) {
+        if (!controlFile.exists()) {
+            log("WARNING: Control file " + controlFile.getAbsolutePath()
+                 + " doesn't exist. iContract will be run "
+                 + "without control file.");
+        }
+        this.controlFile = controlFile;
+    }
+
+
+    /**
+     * Sets the classpath to be used for invocation of iContract.
+     *
+     * @param path the classpath
+     */
+    public void setClasspath(Path path) {
+        createClasspath().append(path);
+    }
+
+
+    /**
+     * Sets the classpath.
+     *
+     * @return the nested classpath element
+     * @todo this overwrites the classpath so only one
+     *       effective classpath element would work. This
+     *       is not how we do this elsewhere.
+     */
+    public Path createClasspath() {
+        if (classpath == null) {
+            classpath = new Path(getProject());
+        }
+        return classpath;
+    }
+
+
+    /**
+     * Adds a reference to a classpath defined elsewhere.
+     *
+     * @param reference referenced classpath
+     */
+    public void setClasspathRef(Reference reference) {
+        createClasspath().setRefid(reference);
+    }
+
+
+    /**
+     * If true, updates iControl properties file
+     *
+     * @param updateIcontrol true if iControl properties file should be
+     *      updated
+     */
+    public void setUpdateicontrol(boolean updateIcontrol) {
+        this.updateIcontrol = updateIcontrol;
+    }
+
+
+    /**
+     * Executes the task
+     *
+     * @exception BuildException if the instrumentation fails
+     */
+    public void execute() throws BuildException {
+        preconditions();
+        scan();
+        if (dirty) {
+
+            // turn off assertions if we're using controlfile, unless they are not explicitly set.
+            boolean useControlFile = (controlFile != null) && controlFile.exists();
+
+            if (useControlFile && !preModified) {
+                pre = false;
+            }
+            if (useControlFile && !postModified) {
+                post = false;
+            }
+            if (useControlFile && !invariantModified) {
+                invariant = false;
+            }
+            // issue warning if pre,post or invariant is used together with controlfile
+            if ((pre || post || invariant) && controlFile != null) {
+                log("WARNING: specifying pre,post or invariant will "
+                     + "override control file settings");
+            }
+
+
+            // We want to be notified if iContract jar is missing.
+            // This makes life easier for the user who didn't understand
+            // that iContract is a separate library (duh!)
+            getProject().addBuildListener(new IContractPresenceDetector());
+
+            // Prepare the directories for iContract. iContract will make
+            // them if they don't exist, but for some reason I don't know,
+            // it will complain about the REP files afterwards
+            Mkdir mkdir = (Mkdir) getProject().createTask("mkdir");
+
+            mkdir.setDir(instrumentDir);
+            mkdir.execute();
+            mkdir.setDir(buildDir);
+            mkdir.execute();
+            mkdir.setDir(repositoryDir);
+            mkdir.execute();
+
+            // Set the classpath that is needed for regular Javac compilation
+            Path baseClasspath = createClasspath();
+
+            // Might need to add the core classes if we're not using
+            // Sun's Javac (like Jikes)
+            String compiler = getProject().getProperty("build.compiler");
+            ClasspathHelper classpathHelper = new ClasspathHelper(compiler);
+
+            classpathHelper.modify(baseClasspath);
+
+            // Create the classpath required to compile the sourcefiles
+            // BEFORE instrumentation
+            Path beforeInstrumentationClasspath = ((Path) baseClasspath.clone());
+
+            beforeInstrumentationClasspath.append(new Path(getProject(),
+                srcDir.getAbsolutePath()));
+
+            // Create the classpath required to compile the sourcefiles
+            // AFTER instrumentation
+            Path afterInstrumentationClasspath = ((Path) baseClasspath.clone());
+
+            afterInstrumentationClasspath.append(new Path(getProject(),
+                instrumentDir.getAbsolutePath()));
+            afterInstrumentationClasspath.append(new Path(getProject(),
+                repositoryDir.getAbsolutePath()));
+            afterInstrumentationClasspath.append(new Path(getProject(),
+                srcDir.getAbsolutePath()));
+            afterInstrumentationClasspath.append(new Path(getProject(),
+                buildDir.getAbsolutePath()));
+
+            // Create the classpath required to automatically compile the
+            // repository files
+            Path repositoryClasspath = ((Path) baseClasspath.clone());
+
+            repositoryClasspath.append(new Path(getProject(),
+                instrumentDir.getAbsolutePath()));
+            repositoryClasspath.append(new Path(getProject(),
+                srcDir.getAbsolutePath()));
+            repositoryClasspath.append(new Path(getProject(),
+                repositoryDir.getAbsolutePath()));
+            repositoryClasspath.append(new Path(getProject(),
+                buildDir.getAbsolutePath()));
+
+            // Create the classpath required for iContract itself
+            Path iContractClasspath = ((Path) baseClasspath.clone());
+
+            iContractClasspath.append(new Path(getProject(),
+                System.getProperty("java.home") + File.separator + ".."
+                    + File.separator + "lib" + File.separator + "tools.jar"));
+            iContractClasspath.append(new Path(getProject(),
+                srcDir.getAbsolutePath()));
+            iContractClasspath.append(new Path(getProject(),
+                repositoryDir.getAbsolutePath()));
+            iContractClasspath.append(new Path(getProject(),
+                instrumentDir.getAbsolutePath()));
+            iContractClasspath.append(new Path(getProject(),
+                buildDir.getAbsolutePath()));
+
+            // Create a forked java process
+            Java iContract = (Java) getProject().createTask("java");
+
+            iContract.setTaskName(getTaskName());
+            iContract.setFork(true);
+            iContract.setClassname("com.reliablesystems.iContract.Tool");
+            iContract.setClasspath(iContractClasspath);
+
+            // Build the arguments to iContract
+            StringBuffer args = new StringBuffer();
+
+            args.append(directiveString());
+            args.append("-v").append(verbosity).append(" ");
+
+            args.append("-b").append("\"").append(icCompiler);
+            args.append(" -classpath ").append(beforeInstrumentationClasspath);
+            args.append("\" ");
+
+            args.append("-c").append("\"").append(icCompiler);
+            args.append(" -classpath ").append(afterInstrumentationClasspath);
+            args.append(" -d ").append(buildDir).append("\" ");
+
+            args.append("-n").append("\"").append(icCompiler);
+            args.append(" -classpath ").append(repositoryClasspath);
+            args.append("\" ");
+
+            args.append("-d").append(failThrowable).append(" ");
+
+            args.append("-o").append(instrumentDir).append(File.separator);
+            args.append("@p").append(File.separator).append("@f.@e ");
+
+            args.append("-k").append(repositoryDir).append(File.separator);
+            args.append("@p ");
+
+            args.append(quiet ? "-q " : "");
+            // reinstrument everything if controlFile exists and is newer
+            // than any class
+            args.append(instrumentall ? "-a " : "");
+            args.append("@").append(targets.getAbsolutePath());
+            iContract.createArg().setLine(args.toString());
+
+            //System.out.println( "JAVA -classpath " + iContractClasspath
+            //    + " com.reliablesystems.iContract.Tool " + args.toString() );
+
+            // update iControlProperties if it's set.
+            if (updateIcontrol) {
+                Properties iControlProps = new Properties();
+
+                try {
+                    // to read existing propertiesfile
+                    iControlProps.load(new FileInputStream("icontrol.properties"));
+                } catch (IOException e) {
+                    log("File icontrol.properties not found. That's ok. "
+                        + "Writing a default one.");
+                }
+                iControlProps.setProperty("sourceRoot",
+                    srcDir.getAbsolutePath());
+                iControlProps.setProperty("classRoot",
+                    classDir.getAbsolutePath());
+                iControlProps.setProperty("classpath",
+                    afterInstrumentationClasspath.toString());
+                iControlProps.setProperty("controlFile",
+                    controlFile.getAbsolutePath());
+                iControlProps.setProperty("targetsFile",
+                    targets.getAbsolutePath());
+
+                try {
+                    // to read existing propertiesfile
+                    iControlProps.store(new FileOutputStream("icontrol.properties"),
+                        ICONTROL_PROPERTIES_HEADER);
+                    log("Updated icontrol.properties");
+                } catch (IOException e) {
+                    log("Couldn't write icontrol.properties.");
+                }
+            }
+
+            // do it!
+            int result = iContract.executeJava();
+
+            if (result != 0) {
+                if (iContractMissing) {
+                    log("iContract can't be found on your classpath. "
+                        + "Your classpath is:");
+                    log(classpath.toString());
+                    log("If you don't have the iContract jar, go get it at "
+                        + "http://www.reliable-systems.com/tools/");
+                }
+                throw new BuildException("iContract instrumentation failed. "
+                    + "Code = " + result);
+            }
+        } else {
+            // not dirty
+            //log( "Nothing to do. Everything up to date." );
+        }
+    }
+
+
+    /** Checks that the required attributes are set.  */
+    private void preconditions() throws BuildException {
+        if (srcDir == null) {
+            throw new BuildException("srcdir attribute must be set!",
+                getLocation());
+        }
+        if (!srcDir.exists()) {
+            throw new BuildException("srcdir \"" + srcDir.getPath()
+                + "\" does not exist!", getLocation());
+        }
+        if (instrumentDir == null) {
+            throw new BuildException("instrumentdir attribute must be set!",
+                getLocation());
+        }
+        if (repositoryDir == null) {
+            throw new BuildException("repositorydir attribute must be set!",
+                getLocation());
+        }
+        if (updateIcontrol && classDir == null) {
+            throw new BuildException("classdir attribute must be specified "
+                + "when updateicontrol=true!", getLocation());
+        }
+        if (updateIcontrol && controlFile == null) {
+            throw new BuildException("controlfile attribute must be specified "
+                + "when updateicontrol=true!", getLocation());
+        }
+    }
+
+
+    /**
+     * Verifies whether any of the source files have changed. Done by
+     * comparing date of source/class files. The whole lot is "dirty" if at
+     * least one source file or the control file is newer than the
+     * instrumented files. If not dirty, iContract will not be executed. <br/>
+     * Also creates a temporary file with a list of the source files, that
+     * will be deleted upon exit.
+     */
+    private void scan() throws BuildException {
+        long now = (new Date()).getTime();
+
+        DirectoryScanner ds = null;
+
+        ds = getDirectoryScanner(srcDir);
+
+        String[] files = ds.getIncludedFiles();
+
+        FileOutputStream targetOutputStream = null;
+        PrintStream targetPrinter = null;
+        boolean writeTargets = false;
+
+        try {
+            if (targets == null) {
+                targets = new File("targets");
+                log("Warning: targets file not specified. generating file: "
+                    + targets.getName());
+                writeTargets = true;
+            } else if (!targets.exists()) {
+                log("Specified targets file doesn't exist. generating file: "
+                    + targets.getName());
+                writeTargets = true;
+            }
+            if (writeTargets) {
+                log("You should consider using iControl to create a target file.");
+                targetOutputStream = new FileOutputStream(targets);
+                targetPrinter = new PrintStream(targetOutputStream);
+            }
+            for (int i = 0; i < files.length; i++) {
+                File srcFile = new File(srcDir, files[i]);
+
+                if (files[i].endsWith(".java")) {
+                    // print the target, while we're at here. (Only if generatetarget=true).
+                    if (targetPrinter != null) {
+                        targetPrinter.println(srcFile.getAbsolutePath());
+                    }
+                    File classFile
+                        = new File(buildDir, files[i].substring(0, files[i].indexOf(".java")) + ".class");
+
+                    if (srcFile.lastModified() > now) {
+                        log("Warning: file modified in the future: "
+                            + files[i], Project.MSG_WARN);
+                    }
+
+                    if (!classFile.exists() || srcFile.lastModified() > classFile.lastModified()) {
+                        //log( "Found a file newer than the instrumentDir class file: "
+                        //    + srcFile.getPath() + " newer than " + classFile.getPath()
+                        //    + ". Running iContract again..." );
+                        dirty = true;
+                    }
+                }
+            }
+            if (targetPrinter != null) {
+                targetPrinter.flush();
+                targetPrinter.close();
+            }
+        } catch (IOException e) {
+            throw new BuildException("Could not create target file:" + e.getMessage());
+        }
+
+        // also, check controlFile timestamp
+        long controlFileTime = -1;
+
+        try {
+            if (controlFile != null) {
+                if (controlFile.exists() && buildDir.exists()) {
+                    controlFileTime = controlFile.lastModified();
+                    ds = getDirectoryScanner(buildDir);
+                    files = ds.getIncludedFiles();
+                    for (int i = 0; i < files.length; i++) {
+                        File srcFile = new File(srcDir, files[i]);
+
+                        if (files[i].endsWith(".class")) {
+                            if (controlFileTime > srcFile.lastModified()) {
+                                if (!dirty) {
+                                    log("Control file "
+                                        + controlFile.getAbsolutePath()
+                                        + " has been updated. "
+                                        + "Instrumenting all files...");
+                                }
+                                dirty = true;
+                                instrumentall = true;
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (Throwable t) {
+            throw new BuildException("Got an interesting exception:"
+                + t.getMessage());
+        }
+    }
+
+
+    /**
+     * Creates the -m option based on the values of controlFile, pre, post and
+     * invariant.
+     */
+    private final String directiveString() {
+        StringBuffer sb = new StringBuffer();
+        boolean comma = false;
+
+        boolean useControlFile = (controlFile != null) && controlFile.exists();
+
+        if (useControlFile || pre || post || invariant) {
+            sb.append("-m");
+        }
+        if (useControlFile) {
+            sb.append("@").append(controlFile);
+            comma = true;
+        }
+        if (pre) {
+            if (comma) {
+                sb.append(",");
+            }
+            sb.append("pre");
+            comma = true;
+        }
+        if (post) {
+            if (comma) {
+                sb.append(",");
+            }
+            sb.append("post");
+            comma = true;
+        }
+        if (invariant) {
+            if (comma) {
+                sb.append(",");
+            }
+            sb.append("inv");
+        }
+        sb.append(" ");
+        return sb.toString();
+    }
+
+
+    /**
+     * BuildListener that sets the iContractMissing flag to true if a message
+     * about missing iContract is missing. Used to indicate a more verbose
+     * error to the user, with advice about how to solve the problem
+     *
+     */
+    private class IContractPresenceDetector implements BuildListener {
+        public void buildFinished(BuildEvent event) {
+        }
+
+
+        public void buildStarted(BuildEvent event) {
+        }
+
+
+        public void messageLogged(BuildEvent event) {
+            if ("java.lang.NoClassDefFoundError: com/reliablesystems/iContract/Tool".equals(event.getMessage())) {
+                iContractMissing = true;
+            }
+        }
+
+
+        public void targetFinished(BuildEvent event) {
+        }
+
+
+        public void targetStarted(BuildEvent event) {
+        }
+
+
+        public void taskFinished(BuildEvent event) {
+        }
+
+
+        public void taskStarted(BuildEvent event) {
+        }
+    }
+
+
+    /**
+     * This class is a helper to set correct classpath for other compilers,
+     * like Jikes. It reuses the logic from DefaultCompilerAdapter, which is
+     * protected, so we have to subclass it.
+     *
+     */
+    private class ClasspathHelper extends DefaultCompilerAdapter {
+        private final String compiler;
+
+
+        public ClasspathHelper(String compiler) {
+            super();
+            this.compiler = compiler;
+        }
+
+        // make it public
+        public void modify(Path path) {
+            // depending on what compiler to use, set the
+            // includeJavaRuntime flag
+            if ("jikes".equals(compiler)) {
+                icCompiler = compiler;
+                includeJavaRuntime = true;
+                path.append(getCompileClasspath());
+            }
+        }
+
+        // dummy implementation. Never called
+        public void setJavac(Javac javac) {
+        }
+
+
+        public boolean execute() {
+            return true;
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Javah.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Javah.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Javah.java	(revision 16139)
@@ -0,0 +1,430 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.optional.javah.JavahAdapter;
+import org.apache.tools.ant.taskdefs.optional.javah.JavahAdapterFactory;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.util.facade.FacadeTaskHelper;
+import org.apache.tools.ant.util.facade.ImplementationSpecificArgument;
+
+/**
+ * Generates JNI header files using javah.
+ *
+ * This task can take the following arguments:
+ * <ul>
+ * <li>classname - the fully-qualified name of a class</li>
+ * <li>outputFile - Concatenates the resulting header or source files for all
+ *     the classes listed into this file</li>
+ * <li>destdir - Sets the directory where javah saves the header files or the
+ *     stub files</li>
+ * <li>classpath</li>
+ * <li>bootclasspath</li>
+ * <li>force - Specifies that output files should always be written
+       (JDK1.2 only)</li>
+ * <li>old - Specifies that old JDK1.0-style header files should be generated
+ *     (otherwise output file contain JNI-style native method
+ *      function prototypes) (JDK1.2 only)</li>
+ * <li>stubs - generate C declarations from the Java object file (used with old)</li>
+ * <li>verbose - causes javah to print a message to stdout concerning the status
+ *     of the generated files</li>
+ * <li>extdirs - Override location of installed extensions</li>
+ * </ul>
+ * Of these arguments, either <b>outputFile</b> or <b>destdir</b> is required,
+ * but not both. More than one classname may be specified, using a comma-separated
+ * list or by using <code>&lt;class name="xxx"&gt;</code> elements within the task.
+ * <p>
+ * When this task executes, it will generate C header and source files that
+ * are needed to implement native methods.
+ *
+ */
+
+public class Javah extends Task {
+
+    private Vector classes = new Vector(2);
+    private String cls;
+    private File destDir;
+    private Path classpath = null;
+    private File outputFile = null;
+    private boolean verbose = false;
+    private boolean force   = false;
+    private boolean old     = false;
+    private boolean stubs   = false;
+    private Path bootclasspath;
+    //private Path extdirs;
+    private static String lSep = System.getProperty("line.separator");
+    private FacadeTaskHelper facade = null;
+
+    public Javah() {
+        facade = new FacadeTaskHelper(JavahAdapterFactory.getDefault());
+    }
+
+    /**
+     * the fully-qualified name of the class (or classes, separated by commas).
+     */
+    public void setClass(String cls) {
+        this.cls = cls;
+    }
+
+    /**
+     * Adds class to process.
+     */
+    public ClassArgument createClass() {
+        ClassArgument ga = new ClassArgument();
+        classes.addElement(ga);
+        return ga;
+    }
+
+    public class ClassArgument {
+        private String name;
+
+        public ClassArgument() {
+        }
+
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        public String getName() {
+            return name;
+        }
+    }
+
+    /**
+     * Names of the classes to process.
+     *
+     * @since Ant 1.6.3
+     */
+    public String[] getClasses() {
+        ArrayList al = new ArrayList();
+        if (cls != null) {
+            StringTokenizer tok = new StringTokenizer(cls, ",", false);
+            while (tok.hasMoreTokens()) {
+                al.add(tok.nextToken().trim());
+            }
+        }
+
+        Enumeration e = classes.elements();
+        while (e.hasMoreElements()) {
+            ClassArgument arg = (ClassArgument) e.nextElement();
+            al.add(arg.getName());
+        }
+        return (String[]) al.toArray(new String[0]);
+    }
+
+    /**
+     * Set the destination directory into which the Java source
+     * files should be compiled.
+     */
+    public void setDestdir(File destDir) {
+        this.destDir = destDir;
+    }
+
+    /**
+     * The destination directory, if any.
+     *
+     * @since Ant 1.6.3
+     */
+    public File getDestdir() {
+        return destDir;
+    }
+
+    /**
+     * the classpath to use.
+     */
+    public void setClasspath(Path src) {
+        if (classpath == null) {
+            classpath = src;
+        } else {
+            classpath.append(src);
+        }
+    }
+
+    /**
+     * Path to use for classpath.
+     */
+    public Path createClasspath() {
+        if (classpath == null) {
+            classpath = new Path(getProject());
+        }
+        return classpath.createPath();
+    }
+
+    /**
+     * Adds a reference to a classpath defined elsewhere.
+     * @todo this needs to be documented in the HTML docs
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+
+    /**
+     * The classpath to use.
+     *
+     * @since Ant 1.6.3
+     */
+    public Path getClasspath() {
+        return classpath;
+    }
+
+    /**
+     * location of bootstrap class files.
+     */
+    public void setBootclasspath(Path src) {
+        if (bootclasspath == null) {
+            bootclasspath = src;
+        } else {
+            bootclasspath.append(src);
+        }
+    }
+
+    /**
+     * Adds path to bootstrap class files.
+     */
+    public Path createBootclasspath() {
+        if (bootclasspath == null) {
+            bootclasspath = new Path(getProject());
+        }
+        return bootclasspath.createPath();
+    }
+
+    /**
+     * Adds a reference to a classpath defined elsewhere.
+     * @todo this needs to be documented in the HTML
+     */
+    public void setBootClasspathRef(Reference r) {
+        createBootclasspath().setRefid(r);
+    }
+
+    /**
+     * The bootclasspath to use.
+     *
+     * @since Ant 1.6.3
+     */
+    public Path getBootclasspath() {
+        return bootclasspath;
+    }
+
+    /**
+     * Concatenates the resulting header or source files for all
+     * the classes listed into this file.
+     */
+    public void setOutputFile(File outputFile) {
+        this.outputFile = outputFile;
+    }
+
+    /**
+     * The destination file, if any.
+     *
+     * @since Ant 1.6.3
+     */
+    public File getOutputfile() {
+        return outputFile;
+    }
+
+    /**
+     * If true, output files should always be written (JDK1.2 only).
+     */
+    public void setForce(boolean force) {
+        this.force = force;
+    }
+
+    /**
+     * Whether output files should always be written.
+     *
+     * @since Ant 1.6.3
+     */
+    public boolean getForce() {
+        return force;
+    }
+
+    /**
+     * If true, specifies that old JDK1.0-style header files should be
+     * generated.
+     * (otherwise output file contain JNI-style native method function prototypes) (JDK1.2 only)
+     */
+    public void setOld(boolean old) {
+        this.old = old;
+    }
+
+    /**
+     * Whether old JDK1.0-style header files should be generated.
+     *
+     * @since Ant 1.6.3
+     */
+    public boolean getOld() {
+        return old;
+    }
+
+    /**
+     * If true, generate C declarations from the Java object file (used with old).
+     */
+    public void setStubs(boolean stubs) {
+        this.stubs = stubs;
+    }
+
+    /**
+     * Whether C declarations from the Java object file should be generated.
+     *
+     * @since Ant 1.6.3
+     */
+    public boolean getStubs() {
+        return stubs;
+    }
+
+    /**
+     * If true, causes Javah to print a message concerning
+     * the status of the generated files.
+     */
+    public void setVerbose(boolean verbose) {
+        this.verbose = verbose;
+    }
+
+    /**
+     * Whether verbose output should get generated.
+     *
+     * @since Ant 1.6.3
+     */
+    public boolean getVerbose() {
+        return verbose;
+    }
+
+    /**
+     * Choose the implementation for this particular task.
+     * @param impl the name of the implemenation
+     * @since Ant 1.6.3
+     */
+    public void setImplementation(String impl) {
+        if ("default".equals(impl)) {
+            facade.setImplementation(JavahAdapterFactory.getDefault());
+        } else {
+            facade.setImplementation(impl);
+        }        
+    }
+
+    /**
+     * Adds an implementation specific command-line argument.
+     * @return a ImplementationSpecificArgument to be configured
+     *
+     * @since Ant 1.6.3
+     */
+    public ImplementationSpecificArgument createArg() {
+        ImplementationSpecificArgument arg =
+            new ImplementationSpecificArgument();
+        facade.addImplementationArgument(arg);
+        return arg;
+    }
+
+    /**
+     * Returns the (implementation specific) settings given as nested
+     * arg elements.
+     *
+     * @since Ant 1.6.3
+     */
+    public String[] getCurrentArgs() {
+        return facade.getArgs();
+    }
+
+    /**
+     * Execute the task
+     *
+     * @throws BuildException is there is a problem in the task execution.
+     */
+    public void execute() throws BuildException {
+        // first off, make sure that we've got a srcdir
+
+        if ((cls == null) && (classes.size() == 0)) {
+            throw new BuildException("class attribute must be set!",
+                getLocation());
+        }
+
+        if ((cls != null) && (classes.size() > 0)) {
+            throw new BuildException("set class attribute or class element, "
+                + "not both.", getLocation());
+        }
+
+        if (destDir != null) {
+            if (!destDir.isDirectory()) {
+                throw new BuildException("destination directory \"" + destDir
+                    + "\" does not exist or is not a directory", getLocation());
+            }
+            if (outputFile != null) {
+                throw new BuildException("destdir and outputFile are mutually "
+                    + "exclusive", getLocation());
+            }
+        }
+
+        if (classpath == null) {
+            classpath = (new Path(getProject())).concatSystemClasspath("last");
+        } else {
+            classpath = classpath.concatSystemClasspath("ignore");
+        }
+
+        JavahAdapter ad = 
+            JavahAdapterFactory.getAdapter(facade.getImplementation(),
+                                           this);
+        if (!ad.compile(this)) {
+            throw new BuildException("compilation failed");
+        }
+    }
+
+    /**
+     * Logs the compilation parameters, adds the files to compile and logs the
+     * &quot;niceSourceList&quot;
+     */
+    public void logAndAddFiles(Commandline cmd) {
+        logAndAddFilesToCompile(cmd);
+    }
+
+    /**
+     * Logs the compilation parameters, adds the files to compile and logs the
+     * &quot;niceSourceList&quot;
+     */
+    protected void logAndAddFilesToCompile(Commandline cmd) {
+        log("Compilation " + cmd.describeArguments(),
+            Project.MSG_VERBOSE);
+
+        StringBuffer niceClassList = new StringBuffer();
+        String[] c = getClasses();
+        for (int i = 0; i < c.length; i++) {
+            cmd.createArgument().setValue(c[i]);
+            niceClassList.append("    " + c[i] + lSep);
+        }
+
+        StringBuffer prefix = new StringBuffer("Class");
+        if (c.length > 1) {
+            prefix.append("es");
+        }
+        prefix.append(" to be compiled:");
+        prefix.append(lSep);
+
+        log(prefix.toString() + niceClassList.toString(), Project.MSG_VERBOSE);
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Native2Ascii.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Native2Ascii.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Native2Ascii.java	(revision 16139)
@@ -0,0 +1,299 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.taskdefs.optional.native2ascii.Native2AsciiAdapter;
+import org.apache.tools.ant.taskdefs.optional.native2ascii.Native2AsciiAdapterFactory;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.IdentityMapper;
+import org.apache.tools.ant.util.SourceFileScanner;
+import org.apache.tools.ant.util.facade.FacadeTaskHelper;
+import org.apache.tools.ant.util.facade.ImplementationSpecificArgument;
+
+/**
+ * Converts files from native encodings to ASCII.
+ *
+ * @since Ant 1.2
+ */
+public class Native2Ascii extends MatchingTask {
+
+    private boolean reverse = false;  // convert from ascii back to native
+    private String encoding = null;   // encoding to convert to/from
+    private File srcDir = null;       // Where to find input files
+    private File destDir = null;      // Where to put output files
+    private String extension = null;  // Extension of output files if different
+
+    private Mapper mapper;
+    private FacadeTaskHelper facade = null;
+
+    public Native2Ascii() {
+        facade = new FacadeTaskHelper(Native2AsciiAdapterFactory.getDefault());
+    }
+
+    /**
+     * Flag the conversion to run in the reverse sense,
+     * that is Ascii to Native encoding.
+     *
+     * @param reverse True if the conversion is to be reversed,
+     *                otherwise false;
+     */
+    public void setReverse(boolean reverse) {
+        this.reverse = reverse;
+    }
+
+    /**
+     * The value of the reverse attribute.
+     *
+     * @since Ant 1.6.3
+     */
+    public boolean getReverse() {
+        return reverse;
+    }
+
+    /**
+     * Set the encoding to translate to/from.
+     * If unset, the default encoding for the JVM is used.
+     *
+     * @param encoding String containing the name of the Native
+     *                 encoding to convert from or to.
+     */
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * The value of the reverse attribute.
+     *
+     * @since Ant 1.6.3
+     */
+    public String getEncoding() {
+        return encoding;
+    }
+
+    /**
+     * Set the source directory in which to find files to convert.
+     *
+     * @param srcDir directory to find input file in.
+     */
+    public void setSrc(File srcDir) {
+        this.srcDir = srcDir;
+    }
+
+
+    /**
+     * Set the destination directory to place converted files into.
+     *
+     * @param destDir directory to place output file into.
+     */
+    public void setDest(File destDir) {
+        this.destDir = destDir;
+    }
+
+    /**
+     * Set the extension which converted files should have.
+     * If unset, files will not be renamed.
+     *
+     * @param ext File extension to use for converted files.
+     */
+    public void setExt(String ext) {
+        this.extension = ext;
+    }
+
+    /**
+     * Choose the implementation for this particular task.
+     * @param impl the name of the implemenation
+     * @since Ant 1.6.3
+     */
+    public void setImplementation(String impl) {
+        if ("default".equals(impl)) {
+            facade.setImplementation(Native2AsciiAdapterFactory.getDefault());
+        } else {
+            facade.setImplementation(impl);
+        }        
+    }
+
+    /**
+     * Defines the FileNameMapper to use (nested mapper element).
+     *
+     * @return the mapper to use for file name translations.
+     *
+     * @throws BuildException if more than one mapper is defined.
+     */
+    public Mapper createMapper() throws BuildException {
+        if (mapper != null) {
+            throw new BuildException("Cannot define more than one mapper",
+                                     getLocation());
+        }
+        mapper = new Mapper(getProject());
+        return mapper;
+    }
+
+    /**
+     * A nested filenamemapper
+     * @param fileNameMapper the mapper to add
+     * @since Ant 1.6.3
+     */
+    public void add(FileNameMapper fileNameMapper) {
+        createMapper().add(fileNameMapper);
+    }
+
+    /**
+     * Adds an implementation specific command-line argument.
+     * @return a ImplementationSpecificArgument to be configured
+     *
+     * @since Ant 1.6.3
+     */
+    public ImplementationSpecificArgument createArg() {
+        ImplementationSpecificArgument arg =
+            new ImplementationSpecificArgument();
+        facade.addImplementationArgument(arg);
+        return arg;
+    }
+
+    /**
+     * Execute the task
+     *
+     * @throws BuildException is there is a problem in the task execution.
+     */
+    public void execute() throws BuildException {
+
+        DirectoryScanner scanner = null; // Scanner to find our inputs
+        String[] files;                  // list of files to process
+
+        // default srcDir to basedir
+        if (srcDir == null) {
+            srcDir = getProject().resolveFile(".");
+        }
+
+        // Require destDir
+        if (destDir == null) {
+            throw new BuildException("The dest attribute must be set.");
+        }
+
+        // if src and dest dirs are the same, require the extension
+        // to be set, so we don't stomp every file.  One could still
+        // include a file with the same extension, but ....
+        if (srcDir.equals(destDir) && extension == null && mapper == null) {
+            throw new BuildException("The ext attribute or a mapper must be set if"
+                                     + " src and dest dirs are the same.");
+        }
+
+        FileNameMapper m = null;
+        if (mapper == null) {
+            if (extension == null) {
+                m = new IdentityMapper();
+            } else {
+                m = new ExtMapper();
+            }
+        } else {
+            m = mapper.getImplementation();
+        }
+
+        scanner = getDirectoryScanner(srcDir);
+        files = scanner.getIncludedFiles();
+        SourceFileScanner sfs = new SourceFileScanner(this);
+        files = sfs.restrict(files, srcDir, destDir, m);
+        int count = files.length;
+        if (count == 0) {
+            return;
+        }
+        String message = "Converting " + count + " file"
+            + (count != 1 ? "s" : "") + " from ";
+        log(message + srcDir + " to " + destDir);
+        for (int i = 0; i < files.length; i++) {
+            convert(files[i], m.mapFileName(files[i])[0]);
+        }
+    }
+
+    /**
+     * Convert a single file.
+     *
+     * @param srcName name of the input file.
+     * @param destName name of the input file.
+     */
+    private void convert(String srcName, String destName) 
+        throws BuildException {
+        File srcFile;                         // File to convert
+        File destFile;                        // where to put the results
+
+        // Build the full file names
+        srcFile = new File(srcDir, srcName);
+        destFile = new File(destDir, destName);
+
+        // Make sure we're not about to clobber something
+        if (srcFile.equals(destFile)) {
+            throw new BuildException("file " + srcFile
+                                     + " would overwrite its self");
+        }
+
+        // Make intermediate directories if needed
+        // XXX JDK 1.1 doesn't have File.getParentFile,
+        String parentName = destFile.getParent();
+        if (parentName != null) {
+            File parentFile = new File(parentName);
+
+            if ((!parentFile.exists()) && (!parentFile.mkdirs())) {
+                throw new BuildException("cannot create parent directory "
+                                         + parentName);
+            }
+        }
+
+        log("converting " + srcName, Project.MSG_VERBOSE);
+        Native2AsciiAdapter ad = 
+            Native2AsciiAdapterFactory.getAdapter(facade.getImplementation(),
+                                                  this);
+        if (!ad.convert(this, srcFile, destFile)) {
+            throw new BuildException("conversion failed");
+        }
+    }
+
+    /**
+     * Returns the (implementation specific) settings given as nested
+     * arg elements.
+     *
+     * @since Ant 1.6.3
+     */
+    public String[] getCurrentArgs() {
+        return facade.getArgs();
+    }
+
+    private class ExtMapper implements FileNameMapper {
+
+        public void setFrom(String s) {
+        }
+        public void setTo(String s) {
+        }
+
+        public String[] mapFileName(String fileName) {
+            int lastDot = fileName.lastIndexOf('.');
+            if (lastDot >= 0) {
+                return new String[] {fileName.substring(0, lastDot)
+                                         + extension};
+            } else {
+                return new String[] {fileName + extension};
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/NetRexxC.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/NetRexxC.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/NetRexxC.java	(revision 16139)
@@ -0,0 +1,903 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import netrexx.lang.Rexx;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+/**
+ * Compiles NetRexx source files.
+ * This task can take the following
+ * arguments:
+ * <ul>
+ * <li>binary</li>
+ * <li>classpath</li>
+ * <li>comments</li>
+ * <li>compile</li>
+ * <li>console</li>
+ * <li>crossref</li>
+ * <li>decimal</li>
+ * <li>destdir</li>
+ * <li>diag</li>
+ * <li>explicit</li>
+ * <li>format</li>
+ * <li>keep</li>
+ * <li>logo</li>
+ * <li>replace</li>
+ * <li>savelog</li>
+ * <li>srcdir</li>
+ * <li>sourcedir</li>
+ * <li>strictargs</li>
+ * <li>strictassign</li>
+ * <li>strictcase</li>
+ * <li>strictimport</li>
+ * <li>symbols</li>
+ * <li>time</li>
+ * <li>trace</li>
+ * <li>utf8</li>
+ * <li>verbose</li>
+ * <li>suppressMethodArgumentNotUsed</li>
+ * <li>suppressPrivatePropertyNotUsed</li>
+ * <li>suppressVariableNotUsed</li>
+ * <li>suppressExceptionNotSignalled</li>
+ * <li>suppressDeprecation</li>
+ * </ul>
+ * Of these arguments, the <b>srcdir</b> argument is required.
+ *
+ * <p>When this task executes, it will recursively scan the srcdir
+ * looking for NetRexx source files to compile. This task makes its
+ * compile decision based on timestamp.
+ * <p>Before files are compiled they and any other file in the
+ * srcdir will be copied to the destdir allowing support files to be
+ * located properly in the classpath. The reason for copying the source files
+ * before the compile is that NetRexxC has only two destinations for classfiles:
+ * <ol>
+ * <li>The current directory, and,</li>
+ * <li>The directory the source is in (see sourcedir option)
+ * </ol>
+ *
+ */
+public class NetRexxC extends MatchingTask {
+
+    // variables to hold arguments
+    private boolean binary;
+    private String classpath;
+    private boolean comments;
+    private boolean compact = true;// should be the default, as it integrates better in ant.
+    private boolean compile = true;
+    private boolean console;
+    private boolean crossref;
+    private boolean decimal = true;
+    private File destDir;
+    private boolean diag;
+    private boolean explicit;
+    private boolean format;
+    private boolean keep;
+    private boolean logo = true;
+    private boolean replace;
+    private boolean savelog;
+    private File srcDir;
+    private boolean sourcedir = true;// ?? Should this be the default for ant?
+    private boolean strictargs;
+    private boolean strictassign;
+    private boolean strictcase;
+    private boolean strictimport;
+    private boolean strictprops;
+    private boolean strictsignal;
+    private boolean symbols;
+    private boolean time;
+    private String trace = "trace2";
+    private boolean utf8;
+    private String verbose = "verbose3";
+    private boolean suppressMethodArgumentNotUsed = false;
+    private boolean suppressPrivatePropertyNotUsed = false;
+    private boolean suppressVariableNotUsed = false;
+    private boolean suppressExceptionNotSignalled = false;
+    private boolean suppressDeprecation = false;
+
+    // constants for the messages to suppress by flags and their corresponding properties
+    static final String MSG_METHOD_ARGUMENT_NOT_USED
+        = "Warning: Method argument is not used";
+    static final String MSG_PRIVATE_PROPERTY_NOT_USED
+        = "Warning: Private property is defined but not used";
+    static final String MSG_VARIABLE_NOT_USED
+        = "Warning: Variable is set but not used";
+    static final String MSG_EXCEPTION_NOT_SIGNALLED
+        = "is in SIGNALS list but is not signalled within the method";
+    static final String MSG_DEPRECATION = "has been deprecated";
+
+    // other implementation variables
+    private Vector compileList = new Vector();
+    private Hashtable filecopyList = new Hashtable();
+
+    /**
+     * Set whether literals are treated as binary, rather than NetRexx types
+     */
+    public void setBinary(boolean binary) {
+        this.binary = binary;
+    }
+
+
+    /** Set the classpath used for NetRexx compilation  */
+    public void setClasspath(String classpath) {
+        this.classpath = classpath;
+    }
+
+
+    /**
+     * Set whether comments are passed through to the generated java source.
+     * Valid true values are "on" or "true". Anything else sets the flag to
+     * false. The default value is false
+     */
+    public void setComments(boolean comments) {
+        this.comments = comments;
+    }
+
+
+    /**
+     * Set whether error messages come out in compact or verbose format. Valid
+     * true values are "on" or "true". Anything else sets the flag to false.
+     * The default value is false
+     */
+    public void setCompact(boolean compact) {
+        this.compact = compact;
+    }
+
+
+    /**
+     * Set whether the NetRexx compiler should compile the generated java code
+     * Valid true values are "on" or "true". Anything else sets the flag to
+     * false. The default value is true. Setting this flag to false, will
+     * automatically set the keep flag to true.
+     */
+    public void setCompile(boolean compile) {
+        this.compile = compile;
+        if (!this.compile && !this.keep) {
+            this.keep = true;
+        }
+    }
+
+
+    /**
+     * Set whether or not messages should be displayed on the 'console' Valid
+     * true values are "on" or "true". Anything else sets the flag to false.
+     * The default value is true.
+     */
+    public void setConsole(boolean console) {
+        this.console = console;
+    }
+
+
+    /** Whether variable cross references are generated  */
+    public void setCrossref(boolean crossref) {
+        this.crossref = crossref;
+    }
+
+
+    /**
+     * Set whether decimal arithmetic should be used for the netrexx code.
+     * Binary arithmetic is used when this flag is turned off. Valid true
+     * values are "on" or "true". Anything else sets the flag to false. The
+     * default value is true.
+     */
+    public void setDecimal(boolean decimal) {
+        this.decimal = decimal;
+    }
+
+
+    /**
+     * Set the destination directory into which the NetRexx source files
+     * should be copied and then compiled.
+     */
+    public void setDestDir(File destDirName) {
+        destDir = destDirName;
+    }
+
+
+    /**
+     * Whether diagnostic information about the compile is generated
+     */
+    public void setDiag(boolean diag) {
+        this.diag = diag;
+    }
+
+
+    /**
+     * Sets whether variables must be declared explicitly before use. Valid
+     * true values are "on" or "true". Anything else sets the flag to false.
+     * The default value is false.
+     */
+    public void setExplicit(boolean explicit) {
+        this.explicit = explicit;
+    }
+
+
+    /**
+     * Whether the generated java code is formatted nicely or left to match
+     * NetRexx line numbers for call stack debugging
+     */
+    public void setFormat(boolean format) {
+        this.format = format;
+    }
+
+
+    /**
+     * Whether the generated java code is produced Valid true values are "on"
+     * or "true". Anything else sets the flag to false. The default value is
+     * false.
+     */
+    public void setJava(boolean java) {
+        log("The attribute java is currently unused.", Project.MSG_WARN);
+    }
+
+
+    /**
+     * Sets whether the generated java source file should be kept after
+     * compilation. The generated files will have an extension of .java.keep,
+     * <b>not</b> .java Valid true values are "on" or "true". Anything else
+     * sets the flag to false. The default value is false.
+     */
+    public void setKeep(boolean keep) {
+        this.keep = keep;
+    }
+
+
+    /** Whether the compiler text logo is displayed when compiling  */
+    public void setLogo(boolean logo) {
+        this.logo = logo;
+    }
+
+
+    /**
+     * Whether the generated .java file should be replaced when compiling
+     * Valid true values are "on" or "true". Anything else sets the flag to
+     * false. The default value is false.
+     */
+    public void setReplace(boolean replace) {
+        this.replace = replace;
+    }
+
+
+    /**
+     * Sets whether the compiler messages will be written to NetRexxC.log as
+     * well as to the console Valid true values are "on" or "true". Anything
+     * else sets the flag to false. The default value is false.
+     */
+    public void setSavelog(boolean savelog) {
+        this.savelog = savelog;
+    }
+
+
+    /**
+     * Tells the NetRexx compiler to store the class files in the same
+     * directory as the source files. The alternative is the working directory
+     * Valid true values are "on" or "true". Anything else sets the flag to
+     * false. The default value is true.
+     */
+    public void setSourcedir(boolean sourcedir) {
+        this.sourcedir = sourcedir;
+    }
+
+
+    /** Set the source dir to find the source Java files.  */
+    public void setSrcDir(File srcDirName) {
+        srcDir = srcDirName;
+    }
+
+
+    /**
+     * Tells the NetRexx compiler that method calls always need parentheses,
+     * even if no arguments are needed, e.g. <code>aStringVar.getBytes</code>
+     * vs. <code>aStringVar.getBytes()</code> Valid true values are "on" or
+     * "true". Anything else sets the flag to false. The default value is
+     * false.
+     */
+    public void setStrictargs(boolean strictargs) {
+        this.strictargs = strictargs;
+    }
+
+
+    /**
+     * Tells the NetRexx compile that assignments must match exactly on type
+     */
+    public void setStrictassign(boolean strictassign) {
+        this.strictassign = strictassign;
+    }
+
+
+    /**
+     * Specifies whether the NetRexx compiler should be case sensitive or not
+     */
+    public void setStrictcase(boolean strictcase) {
+        this.strictcase = strictcase;
+    }
+
+
+    /**
+     * Sets whether classes need to be imported explicitly using an <code>import</code>
+     * statement. By default the NetRexx compiler will import certain packages
+     * automatically Valid true values are "on" or "true". Anything else sets
+     * the flag to false. The default value is false.
+     */
+    public void setStrictimport(boolean strictimport) {
+        this.strictimport = strictimport;
+    }
+
+
+    /**
+     * Sets whether local properties need to be qualified explicitly using
+     * <code>this</code> Valid true values are "on" or "true". Anything else
+     * sets the flag to false. The default value is false.
+     */
+    public void setStrictprops(boolean strictprops) {
+        this.strictprops = strictprops;
+    }
+
+
+    /**
+     * Whether the compiler should force catching of exceptions by explicitly
+     * named types
+     */
+    public void setStrictsignal(boolean strictsignal) {
+        this.strictsignal = strictsignal;
+    }
+
+
+    /**
+     * Sets whether debug symbols should be generated into the class file
+     * Valid true values are "on" or "true". Anything else sets the flag to
+     * false. The default value is false.
+     */
+    public void setSymbols(boolean symbols) {
+        this.symbols = symbols;
+    }
+
+
+    /**
+     * Asks the NetRexx compiler to print compilation times to the console
+     * Valid true values are "on" or "true". Anything else sets the flag to
+     * false. The default value is false.
+     */
+    public void setTime(boolean time) {
+        this.time = time;
+    }
+
+
+    public void setTrace(TraceAttr trace) {
+        this.trace = trace.getValue();
+    }
+
+
+    /**
+     * Turns on or off tracing and directs the resultant trace output Valid
+     * values are: "trace", "trace1", "trace2" and "notrace". "trace" and
+     * "trace2"
+     */
+    public void setTrace(String trace) {
+        TraceAttr t = new TraceAttr();
+
+        t.setValue(trace);
+        setTrace(t);
+    }
+
+
+    /**
+     * Tells the NetRexx compiler that the source is in UTF8 Valid true values
+     * are "on" or "true". Anything else sets the flag to false. The default
+     * value is false.
+     */
+    public void setUtf8(boolean utf8) {
+        this.utf8 = utf8;
+    }
+
+
+    /**
+     * Whether lots of warnings and error messages should be generated
+     */
+    public void setVerbose(VerboseAttr verbose) {
+        this.verbose = verbose.getValue();
+    }
+
+
+    /**
+     * Whether lots of warnings and error messages should be generated
+     */
+    public void setVerbose(String verbose) {
+        VerboseAttr v = new VerboseAttr();
+
+        v.setValue(verbose);
+        setVerbose(v);
+    }
+
+
+    /**
+     * Whether the task should suppress the "Method argument is not used" in
+     * strictargs-Mode, which can not be suppressed by the compiler itself.
+     * The warning is logged as verbose message, though.
+     */
+    public void setSuppressMethodArgumentNotUsed(boolean suppressMethodArgumentNotUsed) {
+        this.suppressMethodArgumentNotUsed = suppressMethodArgumentNotUsed;
+    }
+
+
+    /**
+     * Whether the task should suppress the "Private property is defined but
+     * not used" in strictargs-Mode, which can be quite annoying while
+     * developing. The warning is logged as verbose message, though.
+     */
+    public void setSuppressPrivatePropertyNotUsed(boolean suppressPrivatePropertyNotUsed) {
+        this.suppressPrivatePropertyNotUsed = suppressPrivatePropertyNotUsed;
+    }
+
+
+    /**
+     * Whether the task should suppress the "Variable is set but not used" in
+     * strictargs-Mode. Be careful with this one! The warning is logged as
+     * verbose message, though.
+     */
+    public void setSuppressVariableNotUsed(boolean suppressVariableNotUsed) {
+        this.suppressVariableNotUsed = suppressVariableNotUsed;
+    }
+
+
+    /**
+     * Whether the task should suppress the "FooException is in SIGNALS list
+     * but is not signalled within the method", which is sometimes rather
+     * useless. The warning is logged as verbose message, though.
+     */
+    public void setSuppressExceptionNotSignalled(boolean suppressExceptionNotSignalled) {
+        this.suppressExceptionNotSignalled = suppressExceptionNotSignalled;
+    }
+
+
+    /**
+     * Tells whether we should filter out any deprecation-messages
+     * of the compiler out.
+     */
+    public void setSuppressDeprecation(boolean suppressDeprecation) {
+        this.suppressDeprecation = suppressDeprecation;
+    }
+
+
+    /**
+     * init-Method sets defaults from Properties. That way, when ant is called
+     * with arguments like -Dant.netrexxc.verbose=verbose5 one can easily take
+     * control of all netrexxc-tasks.
+     */
+    public void init() {
+        String p;
+
+        if ((p = getProject().getProperty("ant.netrexxc.binary")) != null) {
+            this.binary = Project.toBoolean(p);
+        }
+        // classpath makes no sense
+        if ((p = getProject().getProperty("ant.netrexxc.comments")) != null) {
+            this.comments = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.compact")) != null) {
+            this.compact = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.compile")) != null) {
+            this.compile = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.console")) != null) {
+            this.console = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.crossref")) != null) {
+            this.crossref = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.decimal")) != null) {
+            this.decimal = Project.toBoolean(p);
+            // destDir
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.diag")) != null) {
+            this.diag = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.explicit")) != null) {
+            this.explicit = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.format")) != null) {
+            this.format = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.keep")) != null) {
+            this.keep = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.logo")) != null) {
+            this.logo = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.replace")) != null) {
+            this.replace = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.savelog")) != null) {
+            this.savelog = Project.toBoolean(p);
+            // srcDir
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.sourcedir")) != null) {
+            this.sourcedir = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.strictargs")) != null) {
+            this.strictargs = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.strictassign")) != null) {
+            this.strictassign = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.strictcase")) != null) {
+            this.strictcase = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.strictimport")) != null) {
+            this.strictimport = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.strictprops")) != null) {
+            this.strictprops = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.strictsignal")) != null) {
+            this.strictsignal = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.symbols")) != null) {
+            this.symbols = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.time")) != null) {
+            this.time = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.trace")) != null) {
+            setTrace(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.utf8")) != null) {
+            this.utf8 = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.verbose")) != null) {
+            setVerbose(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.suppressMethodArgumentNotUsed")) != null) {
+            this.suppressMethodArgumentNotUsed = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.suppressPrivatePropertyNotUsed")) != null) {
+            this.suppressPrivatePropertyNotUsed = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.suppressVariableNotUsed")) != null) {
+            this.suppressVariableNotUsed = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.suppressExceptionNotSignalled")) != null) {
+            this.suppressExceptionNotSignalled = Project.toBoolean(p);
+        }
+        if ((p = getProject().getProperty("ant.netrexxc.suppressDeprecation")) != null) {
+            this.suppressDeprecation = Project.toBoolean(p);
+        }
+    }
+
+
+    /** Executes the task - performs the actual compiler call.  */
+    public void execute() throws BuildException {
+
+        // first off, make sure that we've got a srcdir and destdir
+        if (srcDir == null || destDir == null) {
+            throw new BuildException("srcDir and destDir attributes must be set!");
+        }
+
+        // scan source and dest dirs to build up both copy lists and
+        // compile lists
+        //        scanDir(srcDir, destDir);
+        DirectoryScanner ds = getDirectoryScanner(srcDir);
+
+        String[] files = ds.getIncludedFiles();
+
+        scanDir(srcDir, destDir, files);
+
+        // copy the source and support files
+        copyFilesToDestination();
+
+        // compile the source files
+        if (compileList.size() > 0) {
+            log("Compiling " + compileList.size() + " source file"
+                 + (compileList.size() == 1 ? "" : "s")
+                 + " to " + destDir);
+            doNetRexxCompile();
+        }
+    }
+
+
+    /**
+     * Scans the directory looking for source files to be compiled and support
+     * files to be copied.
+     */
+    private void scanDir(File srcDir, File destDir, String[] files) {
+        for (int i = 0; i < files.length; i++) {
+            File srcFile = new File(srcDir, files[i]);
+            File destFile = new File(destDir, files[i]);
+            String filename = files[i];
+            // if it's a non source file, copy it if a later date than the
+            // dest
+            // if it's a source file, see if the destination class file
+            // needs to be recreated via compilation
+            if (filename.toLowerCase().endsWith(".nrx")) {
+                File classFile =
+                    new File(destDir,
+                    filename.substring(0, filename.lastIndexOf('.')) + ".class");
+
+                if (!compile || srcFile.lastModified() > classFile.lastModified()) {
+                    filecopyList.put(srcFile.getAbsolutePath(), destFile.getAbsolutePath());
+                    compileList.addElement(destFile.getAbsolutePath());
+                }
+            } else {
+                if (srcFile.lastModified() > destFile.lastModified()) {
+                    filecopyList.put(srcFile.getAbsolutePath(), destFile.getAbsolutePath());
+                }
+            }
+        }
+    }
+
+
+    /** Copy eligible files from the srcDir to destDir  */
+    private void copyFilesToDestination() {
+        if (filecopyList.size() > 0) {
+            log("Copying " + filecopyList.size() + " file"
+                 + (filecopyList.size() == 1 ? "" : "s")
+                 + " to " + destDir.getAbsolutePath());
+
+            Enumeration e = filecopyList.keys();
+
+            while (e.hasMoreElements()) {
+                String fromFile = (String) e.nextElement();
+                String toFile = (String) filecopyList.get(fromFile);
+
+                try {
+                    getProject().copyFile(fromFile, toFile);
+                } catch (IOException ioe) {
+                    String msg = "Failed to copy " + fromFile + " to " + toFile
+                         + " due to " + ioe.getMessage();
+
+                    throw new BuildException(msg, ioe);
+                }
+            }
+        }
+    }
+
+
+    /** Performs a compile using the NetRexx 1.1.x compiler  */
+    private void doNetRexxCompile() throws BuildException {
+        log("Using NetRexx compiler", Project.MSG_VERBOSE);
+
+        String classpath = getCompileClasspath();
+        StringBuffer compileOptions = new StringBuffer();
+
+        // create an array of strings for input to the compiler: one array
+        // comes from the compile options, the other from the compileList
+        String[] compileOptionsArray = getCompileOptionsAsArray();
+        String[] fileListArray = new String[compileList.size()];
+        Enumeration e = compileList.elements();
+        int j = 0;
+
+        while (e.hasMoreElements()) {
+            fileListArray[j] = (String) e.nextElement();
+            j++;
+        }
+        // create a single array of arguments for the compiler
+        String[] compileArgs = new String[compileOptionsArray.length + fileListArray.length];
+
+        for (int i = 0; i < compileOptionsArray.length; i++) {
+            compileArgs[i] = compileOptionsArray[i];
+        }
+        for (int i = 0; i < fileListArray.length; i++) {
+            compileArgs[i + compileOptionsArray.length] = fileListArray[i];
+        }
+
+        // print nice output about what we are doing for the log
+        compileOptions.append("Compilation args: ");
+        for (int i = 0; i < compileOptionsArray.length; i++) {
+            compileOptions.append(compileOptionsArray[i]);
+            compileOptions.append(" ");
+        }
+        log(compileOptions.toString(), Project.MSG_VERBOSE);
+
+        String eol = System.getProperty("line.separator");
+        StringBuffer niceSourceList = new StringBuffer("Files to be compiled:" + eol);
+
+        for (int i = 0; i < compileList.size(); i++) {
+            niceSourceList.append("    ");
+            niceSourceList.append(compileList.elementAt(i).toString());
+            niceSourceList.append(eol);
+        }
+
+        log(niceSourceList.toString(), Project.MSG_VERBOSE);
+
+        // need to set java.class.path property and restore it later
+        // since the NetRexx compiler has no option for the classpath
+        String currentClassPath = System.getProperty("java.class.path");
+        Properties currentProperties = System.getProperties();
+
+        currentProperties.put("java.class.path", classpath);
+
+        try {
+            StringWriter out = new StringWriter();
+            int rc =
+                COM.ibm.netrexx.process.NetRexxC.main(new Rexx(compileArgs), new PrintWriter(out));
+            String sdir = srcDir.getAbsolutePath();
+            String ddir = destDir.getAbsolutePath();
+            boolean doReplace = !(sdir.equals(ddir));
+            int dlen = ddir.length();
+            String l;
+            BufferedReader in = new BufferedReader(new StringReader(out.toString()));
+
+            log("replacing destdir '" + ddir + "' through sourcedir '"
+                + sdir + "'", Project.MSG_VERBOSE);
+            while ((l = in.readLine()) != null) {
+                int idx;
+
+                while (doReplace && ((idx = l.indexOf(ddir)) != -1)) {
+                    // path is mentioned in the message
+                    l = (new StringBuffer(l)).replace(idx, idx + dlen, sdir).toString();
+                }
+                // verbose level logging for suppressed messages
+                if (suppressMethodArgumentNotUsed
+                    && l.indexOf(MSG_METHOD_ARGUMENT_NOT_USED) != -1) {
+                    log(l, Project.MSG_VERBOSE);
+                } else if (suppressPrivatePropertyNotUsed
+                    && l.indexOf(MSG_PRIVATE_PROPERTY_NOT_USED) != -1) {
+                    log(l, Project.MSG_VERBOSE);
+                } else if (suppressVariableNotUsed
+                    && l.indexOf(MSG_VARIABLE_NOT_USED) != -1) {
+                    log(l, Project.MSG_VERBOSE);
+                } else if (suppressExceptionNotSignalled
+                    && l.indexOf(MSG_EXCEPTION_NOT_SIGNALLED) != -1) {
+                    log(l, Project.MSG_VERBOSE);
+                } else if (suppressDeprecation
+                    && l.indexOf(MSG_DEPRECATION) != -1) {
+                    log(l, Project.MSG_VERBOSE);
+                } else if (l.indexOf("Error:") != -1) {
+                    // error level logging for compiler errors
+                    log(l, Project.MSG_ERR);
+                } else if (l.indexOf("Warning:") != -1) {
+                    // warning for all warning messages
+                    log(l, Project.MSG_WARN);
+                } else {
+                    log(l, Project.MSG_INFO); // info level for the rest.
+                }
+            }
+            if (rc > 1) {
+                throw new BuildException("Compile failed, messages should "
+                    + "have been provided.");
+            }
+        } catch (IOException ioe) {
+            throw new BuildException("Unexpected IOException while "
+                + "playing with Strings", ioe);
+        } finally {
+            // need to reset java.class.path property
+            // since the NetRexx compiler has no option for the classpath
+            currentProperties = System.getProperties();
+            currentProperties.put("java.class.path", currentClassPath);
+        }
+
+    }
+
+
+    /** Builds the compilation classpath.  */
+    private String getCompileClasspath() {
+        StringBuffer classpath = new StringBuffer();
+
+        // add dest dir to classpath so that previously compiled and
+        // untouched classes are on classpath
+        classpath.append(destDir.getAbsolutePath());
+
+        // add our classpath to the mix
+        if (this.classpath != null) {
+            addExistingToClasspath(classpath, this.classpath);
+        }
+
+        // add the system classpath
+        // addExistingToClasspath(classpath,System.getProperty("java.class.path"));
+        return classpath.toString();
+    }
+
+
+    /** This  */
+    private String[] getCompileOptionsAsArray() {
+        Vector options = new Vector();
+
+        options.addElement(binary ? "-binary" : "-nobinary");
+        options.addElement(comments ? "-comments" : "-nocomments");
+        options.addElement(compile ? "-compile" : "-nocompile");
+        options.addElement(compact ? "-compact" : "-nocompact");
+        options.addElement(console ? "-console" : "-noconsole");
+        options.addElement(crossref ? "-crossref" : "-nocrossref");
+        options.addElement(decimal ? "-decimal" : "-nodecimal");
+        options.addElement(diag ? "-diag" : "-nodiag");
+        options.addElement(explicit ? "-explicit" : "-noexplicit");
+        options.addElement(format ? "-format" : "-noformat");
+        options.addElement(keep ? "-keep" : "-nokeep");
+        options.addElement(logo ? "-logo" : "-nologo");
+        options.addElement(replace ? "-replace" : "-noreplace");
+        options.addElement(savelog ? "-savelog" : "-nosavelog");
+        options.addElement(sourcedir ? "-sourcedir" : "-nosourcedir");
+        options.addElement(strictargs ? "-strictargs" : "-nostrictargs");
+        options.addElement(strictassign ? "-strictassign" : "-nostrictassign");
+        options.addElement(strictcase ? "-strictcase" : "-nostrictcase");
+        options.addElement(strictimport ? "-strictimport" : "-nostrictimport");
+        options.addElement(strictprops ? "-strictprops" : "-nostrictprops");
+        options.addElement(strictsignal ? "-strictsignal" : "-nostrictsignal");
+        options.addElement(symbols ? "-symbols" : "-nosymbols");
+        options.addElement(time ? "-time" : "-notime");
+        options.addElement("-" + trace);
+        options.addElement(utf8 ? "-utf8" : "-noutf8");
+        options.addElement("-" + verbose);
+
+        String[] results = new String[options.size()];
+
+        options.copyInto(results);
+        return results;
+    }
+
+
+    /**
+     * Takes a classpath-like string, and adds each element of this string to
+     * a new classpath, if the components exist. Components that don't exist,
+     * aren't added. We do this, because jikes issues warnings for
+     * non-existant files/dirs in his classpath, and these warnings are pretty
+     * annoying.
+     *
+     * @param target - target classpath
+     * @param source - source classpath to get file objects.
+     */
+    private void addExistingToClasspath(StringBuffer target, String source) {
+        StringTokenizer tok = new StringTokenizer(source,
+            System.getProperty("path.separator"), false);
+
+        while (tok.hasMoreTokens()) {
+            File f = getProject().resolveFile(tok.nextToken());
+
+            if (f.exists()) {
+                target.append(File.pathSeparator);
+                target.append(f.getAbsolutePath());
+            } else {
+                log("Dropping from classpath: "
+                    + f.getAbsolutePath(), Project.MSG_VERBOSE);
+            }
+        }
+
+    }
+
+
+    public static class TraceAttr extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[]{"trace", "trace1", "trace2", "notrace"};
+        }
+    }
+
+
+    public static class VerboseAttr extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[]{"verbose", "verbose0", "verbose1",
+                "verbose2", "verbose3", "verbose4",
+                "verbose5", "noverbose"};
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/PropertyFile.java	(revision 16139)
@@ -0,0 +1,624 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.DecimalFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+/**
+ *Modifies settings in a property file.
+ *
+ * <p>
+ *The following is an example of its usage:
+ *    <ul>&lt;target name="setState"&gt;<br>
+ *    <ul>&lt;property<br>
+ *        <ul>name="header"<br>
+ *        value="##Generated file - do not modify!"/&gt;<br>
+ *      &lt;propertyfile file="apropfile.properties" comment="${header}"&gt;<br>
+ *        &lt;entry key="product.version.major" type="int"  value="5"/&gt;<br>
+ *        &lt;entry key="product.version.minor" type="int"  value="0"/&gt;<br>
+ *        &lt;entry key="product.build.major"   type="int"  value="0" /&gt;<br>
+ *        &lt;entry key="product.build.minor"   type="int"  operation="+" /&gt;<br>
+ *        &lt;entry key="product.build.date"    type="date" value="now" /&gt;<br>
+ *        &lt;entry key="intSet" type="int" operation="=" value="681"/&gt;<br>
+ *        &lt;entry key="intDec" type="int" operation="-"/&gt;<br>
+ *        &lt;entry key="StringEquals" type="string" value="testValue"/&gt;<br>
+ *     &lt;/propertyfile&gt;<br></ul>
+ *   &lt;/target&gt;</ul><p>
+ *
+ *The &lt;propertyfile&gt; task must have:<br>
+ *    <ul><li>file</li></ul>
+ *Other parameters are:<br>
+ *    <ul><li>comment, key, operation, type and value (the final four being
+ *            eliminated shortly)</li></ul>
+ *
+ *The &lt;entry&gt; task must have:<br>
+ *    <ul><li>key</li></ul>
+ *Other parameters are:<br>
+ *    <ul><li>operation</li>
+ *        <li>type</li>
+ *        <li>value</li>
+ *        <li>default</li>
+ *        <li>unit</li>
+ *    </ul>
+ *
+ *If type is unspecified, it defaults to string
+ *
+ *Parameter values:<br>
+ *    <ul><li>operation:</li>
+ *        <ul><li>"=" (set -- default)</li>
+ *        <li>"-" (dec)</li>
+ *        <li>"+" (inc)</li>
+ *
+ *    <li>type:</li>
+ *        <ul><li>"int"</li>
+ *        <li>"date"</li>
+ *        <li>"string"</li></ul></ul>
+ *
+ *    <li>value:</li>
+ *      <ul><li>holds the default value, if the property
+ *              was not found in property file</li>
+ *          <li>"now" In case of type "date", the
+ *              value "now" will be replaced by the current
+ *              date/time and used even if a valid date was
+ *              found in the property file.</li></ul>
+ *
+ *
+ *String property types can only use the "=" operation.
+ *Int property types can only use the "=", "-" or "+" operations.<p>
+ *
+ *The message property is used for the property file header, with "\\" being
+ *a newline delimiter character.
+ *
+ */
+public class PropertyFile extends Task {
+
+    /* ========================================================================
+    *
+    * Instance variables.
+    */
+
+    // Use this to prepend a message to the properties file
+    private String              comment;
+
+    private Properties          properties;
+    private File                propertyfile;
+
+    private Vector entries = new Vector();
+
+    /* ========================================================================
+    *
+    * Constructors
+    */
+
+    /* ========================================================================
+    *
+    * Methods
+    */
+
+    public void execute() throws BuildException {
+        checkParameters();
+        readFile();
+        executeOperation();
+        writeFile();
+    }
+
+    public Entry createEntry() {
+        Entry e = new Entry();
+        entries.addElement(e);
+        return e;
+    }
+
+    private void executeOperation() throws BuildException {
+        for (Enumeration e = entries.elements(); e.hasMoreElements();) {
+            Entry entry = (Entry) e.nextElement();
+            entry.executeOn(properties);
+        }
+    }
+
+    private void readFile() throws BuildException {
+        // Create the PropertyFile
+        properties = new Properties();
+        try {
+            if (propertyfile.exists()) {
+                log("Updating property file: "
+                    + propertyfile.getAbsolutePath());
+                FileInputStream fis = null;
+                try {
+                    fis = new FileInputStream(propertyfile);
+                    BufferedInputStream bis = new BufferedInputStream(fis);
+                    properties.load(bis);
+                } finally {
+                    if (fis != null) {
+                        fis.close();
+                    }
+                }
+            } else {
+                log("Creating new property file: "
+                    + propertyfile.getAbsolutePath());
+                FileOutputStream out = null;
+                try {
+                    out = new FileOutputStream(propertyfile.getAbsolutePath());
+                    out.flush();
+                } finally {
+                    if (out != null) {
+                        out.close();
+                    }
+                }
+            }
+        } catch (IOException ioe) {
+            throw new BuildException(ioe.toString());
+        }
+    }
+
+    private void checkParameters() throws BuildException {
+        if (!checkParam(propertyfile)) {
+            throw new BuildException("file token must not be null.", getLocation());
+        }
+    }
+
+    /**
+     * Location of the property file to be edited; required.
+     */
+    public void setFile(File file) {
+        propertyfile = file;
+    }
+
+    /**
+     * optional header comment for the file
+     */
+    public void setComment(String hdr) {
+        comment = hdr;
+    }
+
+    private void writeFile() throws BuildException {
+        BufferedOutputStream bos = null;
+        try {
+            bos = new BufferedOutputStream(new FileOutputStream(propertyfile));
+            properties.store(bos, comment);
+        } catch (IOException ioe) {
+            throw new BuildException(ioe, getLocation());
+        } finally {
+            if (bos != null) {
+                try {
+                    bos.close();
+                } catch (IOException ioex) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    private boolean checkParam(File param) {
+        return !(param == null);
+    }
+
+    /**
+     * Instance of this class represents nested elements of
+     * a task propertyfile.
+     */
+    public static class Entry {
+        private static final int DEFAULT_INT_VALUE = 0;
+        private static final String DEFAULT_DATE_VALUE = "now";
+        private static final String DEFAULT_STRING_VALUE = "";
+
+        private String              key = null;
+        private int                 type = Type.STRING_TYPE;
+        private int                 operation = Operation.EQUALS_OPER;
+        private String              value = null;
+        private String              defaultValue = null;
+        private String              newValue = null;
+        private String              pattern = null;
+        private int                 field = Calendar.DATE;
+
+        /**
+         * Name of the property name/value pair
+         */
+        public void setKey(String value) {
+            this.key = value;
+        }
+
+        /**
+         * Value to set (=), to add (+) or subtract (-)
+         */
+        public void setValue(String value) {
+            this.value = value;
+        }
+
+        /**
+         * operation to apply.
+         * &quot;+&quot; or &quot;=&quot;
+         *(default) for all datatypes; &quot;-&quot; for date and int only)\.
+         */
+        public void setOperation(Operation value) {
+            this.operation = Operation.toOperation(value.getValue());
+        }
+
+        /**
+         * Regard the value as : int, date or string (default)
+         */
+        public void setType(Type value) {
+            this.type = Type.toType(value.getValue());
+        }
+
+        /**
+         * Initial value to set for a property if it is not
+         * already defined in the property file.
+         * For type date, an additional keyword is allowed: &quot;now&quot;
+         */
+
+        public void setDefault(String value) {
+            this.defaultValue = value;
+        }
+
+        /**
+         * For int and date type only. If present, Values will
+         * be parsed and formatted accordingly.
+         */
+        public void setPattern(String value) {
+            this.pattern = value;
+        }
+
+        /**
+         * The unit of the value to be applied to date +/- operations.
+         *            Valid Values are:
+         *            <ul>
+         *               <li>millisecond</li>
+         *               <li>second</li>
+         *               <li>minute</li>
+         *               <li>hour</li>
+         *               <li>day (default)</li>
+         *               <li>week</li>
+         *               <li>month</li>
+         *               <li>year</li>
+         *            </ul>
+         *            This only applies to date types using a +/- operation.
+         * @since Ant 1.5
+         */
+        public void setUnit(PropertyFile.Unit unit) {
+            field = unit.getCalendarField();
+        }
+
+        protected void executeOn(Properties props) throws BuildException {
+            checkParameters();
+
+            // type may be null because it wasn't set
+            String oldValue = (String) props.get(key);
+            try {
+                if (type == Type.INTEGER_TYPE) {
+                    executeInteger(oldValue);
+                } else if (type == Type.DATE_TYPE) {
+                    executeDate(oldValue);
+                } else if (type == Type.STRING_TYPE) {
+                    executeString(oldValue);
+                } else {
+                    throw new BuildException("Unknown operation type: "
+                        + type);
+                }
+            } catch (NullPointerException npe) {
+                // Default to string type
+                // which means do nothing
+                npe.printStackTrace();
+            }
+
+            if (newValue == null) {
+                newValue = "";
+            }
+
+            // Insert as a string by default
+            props.put(key, newValue);
+        }
+
+        /**
+        * Handle operations for type <code>date</code>.
+        *
+        * @param oldValue the current value read from the property file or
+        *                 <code>null</code> if the <code>key</code> was
+        *                 not contained in the property file.
+        */
+        private void executeDate(String oldValue) throws BuildException {
+            Calendar currentValue = Calendar.getInstance();
+
+            if (pattern == null) {
+              pattern = "yyyy/MM/dd HH:mm";
+            }
+            DateFormat fmt = new SimpleDateFormat(pattern);
+
+            String currentStringValue = getCurrentValue(oldValue);
+            if (currentStringValue == null) {
+                currentStringValue = DEFAULT_DATE_VALUE;
+            }
+
+            if ("now".equals(currentStringValue)) {
+                currentValue.setTime(new Date());
+            } else {
+                try {
+                    currentValue.setTime(fmt.parse(currentStringValue));
+                } catch (ParseException pe)  {
+                    // swallow
+                }
+            }
+
+            if (operation != Operation.EQUALS_OPER) {
+                int offset = 0;
+                try {
+                    offset = Integer.parseInt(value);
+                    if (operation == Operation.DECREMENT_OPER) {
+                        offset = -1 * offset;
+                    }
+                } catch (NumberFormatException e) {
+                    throw new BuildException("Value not an integer on " + key);
+                }
+                currentValue.add(field, offset);
+            }
+
+            newValue = fmt.format(currentValue.getTime());
+        }
+
+
+        /**
+        * Handle operations for type <code>int</code>.
+        *
+        * @param oldValue the current value read from the property file or
+        *                 <code>null</code> if the <code>key</code> was
+        *                 not contained in the property file.
+        */
+        private void executeInteger(String oldValue) throws BuildException {
+            int currentValue = DEFAULT_INT_VALUE;
+            int newValue  = DEFAULT_INT_VALUE;
+
+
+            DecimalFormat fmt = (pattern != null) ? new DecimalFormat(pattern)
+                                                    : new DecimalFormat();
+            try {
+                String curval = getCurrentValue(oldValue);
+                if (curval != null) {
+                    currentValue = fmt.parse(curval).intValue();
+                } else {
+                    currentValue = 0;
+                }
+            } catch (NumberFormatException nfe) {
+                // swallow
+            } catch (ParseException pe)  {
+                // swallow
+            }
+
+            if (operation == Operation.EQUALS_OPER) {
+                newValue = currentValue;
+            } else {
+                int operationValue = 1;
+                if (value != null) {
+                    try {
+                        operationValue = fmt.parse(value).intValue();
+                    } catch (NumberFormatException nfe) {
+                        // swallow
+                    } catch (ParseException pe)  {
+                        // swallow
+                    }
+                }
+
+                if (operation == Operation.INCREMENT_OPER) {
+                    newValue = currentValue + operationValue;
+                } else if (operation == Operation.DECREMENT_OPER) {
+                    newValue = currentValue - operationValue;
+                }
+            }
+
+            this.newValue = fmt.format(newValue);
+        }
+
+        /**
+        * Handle operations for type <code>string</code>.
+        *
+        * @param oldValue the current value read from the property file or
+        *                 <code>null</code> if the <code>key</code> was
+        *                 not contained in the property file.
+        */
+        private void executeString(String oldValue) throws BuildException {
+            String newValue  = DEFAULT_STRING_VALUE;
+
+            String currentValue = getCurrentValue(oldValue);
+
+            if (currentValue == null) {
+                currentValue = DEFAULT_STRING_VALUE;
+            }
+
+            if (operation == Operation.EQUALS_OPER) {
+                newValue = currentValue;
+            } else if (operation == Operation.INCREMENT_OPER) {
+                newValue = currentValue + value;
+            }
+            this.newValue = newValue;
+        }
+
+        /**
+         * Check if parameter combinations can be supported
+         * @todo make sure the 'unit' attribute is only specified on date
+         *      fields
+         */
+        private void checkParameters() throws BuildException {
+            if (type == Type.STRING_TYPE
+                && operation == Operation.DECREMENT_OPER) {
+                throw new BuildException("- is not supported for string "
+                    + "properties (key:" + key + ")");
+            }
+            if (value == null && defaultValue == null) {
+                throw new BuildException("\"value\" and/or \"default\" "
+                    + "attribute must be specified (key:" + key + ")");
+            }
+            if (key == null) {
+                throw new BuildException("key is mandatory");
+            }
+            if (type == Type.STRING_TYPE && pattern != null) {
+                throw new BuildException("pattern is not supported for string "
+                    + "properties (key:" + key + ")");
+            }
+        }
+
+        private String getCurrentValue(String oldValue) {
+            String ret = null;
+            if (operation == Operation.EQUALS_OPER) {
+                // If only value is specified, the property is set to it
+                // regardless of its previous value.
+                if (value != null && defaultValue == null) {
+                    ret = value;
+                }
+
+                // If only default is specified and the property previously
+                // existed in the property file, it is unchanged.
+                if (value == null && defaultValue != null && oldValue != null) {
+                    ret = oldValue;
+                }
+
+                // If only default is specified and the property did not
+                // exist in the property file, the property is set to default.
+                if (value == null && defaultValue != null && oldValue == null) {
+                    ret = defaultValue;
+                }
+
+                // If value and default are both specified and the property
+                // previously existed in the property file, the property
+                // is set to value.
+                if (value != null && defaultValue != null && oldValue != null) {
+                    ret = value;
+                }
+
+                // If value and default are both specified and the property
+                // did not exist in the property file, the property is set
+                // to default.
+                if (value != null && defaultValue != null && oldValue == null) {
+                    ret = defaultValue;
+                }
+            } else {
+                ret = (oldValue == null) ? defaultValue : oldValue;
+            }
+
+            return ret;
+        }
+
+        /**
+         * Enumerated attribute with the values "+", "-", "="
+         */
+        public static class Operation extends EnumeratedAttribute {
+
+            // Property type operations
+            public static final int INCREMENT_OPER =   0;
+            public static final int DECREMENT_OPER =   1;
+            public static final int EQUALS_OPER =      2;
+
+            public String[] getValues() {
+                return new String[] {"+", "-", "="};
+            }
+
+            public static int toOperation(String oper) {
+                if ("+".equals(oper)) {
+                    return INCREMENT_OPER;
+                } else if ("-".equals(oper)) {
+                    return DECREMENT_OPER;
+                }
+                return EQUALS_OPER;
+            }
+        }
+
+        /**
+         * Enumerated attribute with the values "int", "date" and "string".
+         */
+        public static class Type extends EnumeratedAttribute {
+
+            // Property types
+            public static final int INTEGER_TYPE =     0;
+            public static final int DATE_TYPE =        1;
+            public static final int STRING_TYPE =      2;
+
+            public String[] getValues() {
+                return new String[] {"int", "date", "string"};
+            }
+
+            public static int toType(String type) {
+                if ("int".equals(type)) {
+                    return INTEGER_TYPE;
+                } else if ("date".equals(type)) {
+                    return DATE_TYPE;
+                }
+                return STRING_TYPE;
+            }
+        }
+    }
+
+    /**
+     * Borrowed from Tstamp
+     * @todo share all this time stuff across many tasks as a datetime datatype
+     * @since Ant 1.5
+     */
+    public static class Unit extends EnumeratedAttribute {
+
+        private static final String MILLISECOND = "millisecond";
+        private static final String SECOND = "second";
+        private static final String MINUTE = "minute";
+        private static final String HOUR = "hour";
+        private static final String DAY = "day";
+        private static final String WEEK = "week";
+        private static final String MONTH = "month";
+        private static final String YEAR = "year";
+
+        private static final String[] UNITS
+            = {MILLISECOND, SECOND, MINUTE, HOUR,
+               DAY, WEEK, MONTH, YEAR };
+
+        private Hashtable calendarFields = new Hashtable();
+
+        public Unit() {
+            calendarFields.put(MILLISECOND,
+                                    new Integer(Calendar.MILLISECOND));
+            calendarFields.put(SECOND, new Integer(Calendar.SECOND));
+            calendarFields.put(MINUTE, new Integer(Calendar.MINUTE));
+            calendarFields.put(HOUR, new Integer(Calendar.HOUR_OF_DAY));
+            calendarFields.put(DAY, new Integer(Calendar.DATE));
+            calendarFields.put(WEEK, new Integer(Calendar.WEEK_OF_YEAR));
+            calendarFields.put(MONTH, new Integer(Calendar.MONTH));
+            calendarFields.put(YEAR, new Integer(Calendar.YEAR));
+        }
+
+        public int getCalendarField() {
+            String key = getValue().toLowerCase();
+            Integer i = (Integer) calendarFields.get(key);
+            return i.intValue();
+        }
+
+        public String[] getValues() {
+            return UNITS;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/RenameExtensions.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/RenameExtensions.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/RenameExtensions.java	(revision 16139)
@@ -0,0 +1,142 @@
+/*
+ * Copyright  2000,2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ * Task to rename files based on extension. This task has the following
+ * properties which can be set:
+ * <ul>
+ * <li>fromExtension: </li>
+ * <li>toExtension: </li>
+ * <li>srcDir: </li>
+ * <li>replace: </li>
+ * </ul>
+ */
+
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.taskdefs.Move;
+import org.apache.tools.ant.types.Mapper;
+
+/**
+ *
+ * @version 1.2
+ *
+ * @deprecated Use &lt;move&gt; instead
+ */
+public class RenameExtensions extends MatchingTask {
+
+    private String fromExtension = "";
+    private String toExtension = "";
+    private boolean replace = false;
+    private File srcDir;
+
+    private Mapper.MapperType globType;
+
+
+    /** Creates new RenameExtensions */
+    public RenameExtensions() {
+        super();
+        globType = new Mapper.MapperType();
+        globType.setValue("glob");
+    }
+
+    /**
+     * The string that files must end in to be renamed
+     *
+     * @param from the extension of files being renamed.
+     */
+    public void setFromExtension(String from) {
+        fromExtension = from;
+    }
+
+    /**
+     * The string that renamed files will end with on
+     * completion
+     *
+     * @param to the extension of the renamed files.
+     */
+    public void setToExtension(String to) {
+        toExtension = to;
+    }
+
+    /**
+     * store replace attribute - this determines whether the target file
+     * should be overwritten if present
+     *
+     * @param replace if true overwrite any target files that exist.
+     */
+    public void setReplace(boolean replace) {
+        this.replace = replace;
+    }
+
+    /**
+     * Set the source dir to find the files to be renamed.
+     *
+     * @param srcDir the source directory.
+     */
+    public void setSrcDir(File srcDir) {
+        this.srcDir = srcDir;
+    }
+
+    /**
+     * Executes the task.
+     *
+     * @throws BuildException is there is a problem in the task execution.
+     */
+    public void execute() throws BuildException {
+
+        // first off, make sure that we've got a from and to extension
+        if (fromExtension == null || toExtension == null || srcDir == null) {
+            throw new BuildException("srcDir, fromExtension and toExtension "
+                + "attributes must be set!");
+        }
+
+        log("DEPRECATED - The renameext task is deprecated.  Use move instead.",
+            Project.MSG_WARN);
+        log("Replace this with:", Project.MSG_INFO);
+        log("<move todir=\"" + srcDir + "\" overwrite=\"" + replace + "\">",
+            Project.MSG_INFO);
+        log("  <fileset dir=\"" + srcDir + "\" />", Project.MSG_INFO);
+        log("  <mapper type=\"glob\"", Project.MSG_INFO);
+        log("          from=\"*" + fromExtension + "\"", Project.MSG_INFO);
+        log("          to=\"*" + toExtension + "\" />", Project.MSG_INFO);
+        log("</move>", Project.MSG_INFO);
+        log("using the same patterns on <fileset> as you\'ve used here",
+            Project.MSG_INFO);
+
+        Move move = (Move) getProject().createTask("move");
+        move.setOwningTarget(getOwningTarget());
+        move.setTaskName(getTaskName());
+        move.setLocation(getLocation());
+        move.setTodir(srcDir);
+        move.setOverwrite(replace);
+
+        fileset.setDir(srcDir);
+        move.addFileset(fileset);
+
+        Mapper me = move.createMapper();
+        me.setType(globType);
+        me.setFrom("*" + fromExtension);
+        me.setTo("*" + toExtension);
+
+        move.execute();
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ReplaceRegExp.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ReplaceRegExp.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ReplaceRegExp.java	(revision 16139)
@@ -0,0 +1,558 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.RegularExpression;
+import org.apache.tools.ant.types.Substitution;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.regexp.Regexp;
+
+/**
+ * Performs regular expression string replacements in a text
+ * file.  The input file(s) must be able to be properly processed by
+ * a Reader instance.  That is, they must be text only, no binary.
+ *
+ * The syntax of the regular expression depends on the implementation that
+ * you choose to use. The system property <code>ant.regexp.regexpimpl</code>
+ * will be the classname of the implementation that will be used (the default
+ * is <code>org.apache.tools.ant.util.regexp.JakartaOroRegexp</code> and
+ * requires the Jakarta Oro Package).
+ *
+ * <pre>
+ * For jdk  &lt;= 1.3, there are two available implementations:
+ *   org.apache.tools.ant.util.regexp.JakartaOroRegexp (the default)
+ *        Requires  the jakarta-oro package
+ *
+ *   org.apache.tools.ant.util.regexp.JakartaRegexpRegexp
+ *        Requires the jakarta-regexp package
+ *
+ * For jdk &gt;= 1.4 an additional implementation is available:
+ *   org.apache.tools.ant.util.regexp.Jdk14RegexpRegexp
+ *        Requires the jdk 1.4 built in regular expression package.
+ *
+ * Usage:
+ *
+ *   Call Syntax:
+ *
+ *     &lt;replaceregexp file="file"
+ *                    match="pattern"
+ *                    replace="pattern"
+ *                    flags="options"?
+ *                    byline="true|false"? &gt;
+ *       regexp?
+ *       substitution?
+ *       fileset*
+ *     &lt;/replaceregexp&gt;
+ *
+ *    NOTE: You must have either the file attribute specified, or at least one fileset subelement
+ *    to operation on.  You may not have the file attribute specified if you nest fileset elements
+ *    inside this task.  Also, you cannot specify both match and a regular expression subelement at
+ *    the same time, nor can you specify the replace attribute and the substitution subelement at
+ *    the same time.
+ *
+ *   Attributes:
+ *
+ *     file    --&gt; A single file to operation on (mutually exclusive
+ *                    with the fileset subelements)
+ *     match   --&gt; The Regular expression to match
+ *     replace --&gt; The Expression replacement string
+ *     flags   --&gt; The options to give to the replacement
+ *                 g = Substitute all occurrences. default is to replace only the first one
+ *                 i = Case insensitive match
+ *
+ *     byline  --&gt; Should this file be processed a single line at a time (default is false)
+ *                 "true" indicates to perform replacement on a line by line basis
+ *                 "false" indicates to perform replacement on the whole file at once.
+ *
+ *  Example:
+ *
+ *     The following call could be used to replace an old property name in a ".properties"
+ *     file with a new name.  In the replace attribute, you can refer to any part of the
+ *     match expression in parenthesis using backslash followed by a number like '\1'.
+ *
+ *     &lt;replaceregexp file="test.properties"
+ *                    match="MyProperty=(.*)"
+ *                    replace="NewProperty=\1"
+ *                    byline="true" /&gt;
+ *
+ * </pre>
+ *
+ */
+public class ReplaceRegExp extends Task {
+
+    private File file;
+    private String flags;
+    private boolean byline;
+    private Vector filesets;// Keep jdk 1.1 compliant so others can use this
+    private RegularExpression regex;
+    private Substitution subs;
+
+    private FileUtils fileUtils = FileUtils.newFileUtils();
+
+    /**
+     * Encoding to assume for the files
+     */
+    private String encoding = null;
+
+    /** Default Constructor  */
+    public ReplaceRegExp() {
+        super();
+        this.file = null;
+        this.filesets = new Vector();
+        this.flags = "";
+        this.byline = false;
+
+        this.regex = null;
+        this.subs = null;
+    }
+
+
+    /**
+     * file for which the regular expression should be replaced;
+     * required unless a nested fileset is supplied.
+     * @param file The file for which the reg exp should be replaced.
+     */
+    public void setFile(File file) {
+        this.file = file;
+    }
+
+
+    /**
+     * the regular expression pattern to match in the file(s);
+     * required if no nested &lt;regexp&gt; is used
+     * @param match the match attribute.
+     */
+    public void setMatch(String match) {
+        if (regex != null) {
+            throw new BuildException("Only one regular expression is allowed");
+        }
+
+        regex = new RegularExpression();
+        regex.setPattern(match);
+    }
+
+
+    /**
+     * The substitution pattern to place in the file(s) in place
+     * of the regular expression.
+     * Required if no nested &lt;substitution&gt; is used
+     * @param replace the replace attribute
+     */
+
+    public void setReplace(String replace) {
+        if (subs != null) {
+            throw new BuildException("Only one substitution expression is "
+                                     + "allowed");
+        }
+
+        subs = new Substitution();
+        subs.setExpression(replace);
+    }
+
+    /**
+     * The flags to use when matching the regular expression.  For more
+     * information, consult the Perl5 syntax.
+     * <ul>
+     *  <li>g : Global replacement.  Replace all occurrences found
+     *  <li>i : Case Insensitive.  Do not consider case in the match
+     *  <li>m : Multiline.  Treat the string as multiple lines of input,
+     *         using "^" and "$" as the start or end of any line, respectively,
+     *         rather than start or end of string.
+     *  <li> s : Singleline.  Treat the string as a single line of input, using
+     *        "." to match any character, including a newline, which normally,
+     *        it would not match.
+     *</ul>
+     * @param flags the flags attribute
+     */
+    public void setFlags(String flags) {
+        this.flags = flags;
+    }
+
+
+    /**
+     * Process the file(s) one line at a time, executing the replacement
+     * on one line at a time.  This is useful if you
+     * want to only replace the first occurrence of a regular expression on
+     * each line, which is not easy to do when processing the file as a whole.
+     * Defaults to <i>false</i>.</td>
+     * @param byline the byline attribute as a string
+     * @deprecated - use setByLine(boolean)
+     */
+    public void setByLine(String byline) {
+        Boolean res = Boolean.valueOf(byline);
+
+        if (res == null) {
+            res = Boolean.FALSE;
+        }
+        this.byline = res.booleanValue();
+    }
+
+    /**
+     * Process the file(s) one line at a time, executing the replacement
+     * on one line at a time.  This is useful if you
+     * want to only replace the first occurrence of a regular expression on
+     * each line, which is not easy to do when processing the file as a whole.
+     * Defaults to <i>false</i>.</td>
+     * @param byline the byline attribute
+     */
+    public void setByLine(boolean byline) {
+        this.byline = byline;
+    }
+
+
+    /**
+     * Specifies the encoding Ant expects the files to be in -
+     * defaults to the platforms default encoding.
+     * @param encoding the encoding attribute
+     *
+     * @since Ant 1.6
+     */
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * list files to apply the replacement to
+     * @param set the fileset element
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+
+    /**
+     * A regular expression.
+     * You can use this element to refer to a previously
+     * defined regular expression datatype instance
+     * @return the regular expression object to be configured as an element
+     */
+    public RegularExpression createRegexp() {
+        if (regex != null) {
+            throw new BuildException("Only one regular expression is allowed.");
+        }
+
+        regex = new RegularExpression();
+        return regex;
+    }
+
+
+    /**
+     * A substitution pattern.  You can use this element to refer to a previously
+     * defined substitution pattern datatype instance.
+     * @return the substitution pattern object to be configured as an element
+     */
+    public Substitution createSubstitution() {
+        if (subs != null) {
+            throw new BuildException("Only one substitution expression is "
+                                     + "allowed");
+        }
+
+        subs = new Substitution();
+        return subs;
+    }
+
+
+    /**
+     * Invoke a regular expression (r) on a string (input) using
+     * substitutions (s) for a matching regex.
+     *
+     * @param r a regular expression
+     * @param s a Substitution
+     * @param input the string to do the replacement on
+     * @param options The options for the regular expression
+     * @return the replacement result
+     */
+    protected String doReplace(RegularExpression r,
+                               Substitution s,
+                               String input,
+                               int options) {
+        String res = input;
+        Regexp regexp = r.getRegexp(getProject());
+
+        if (regexp.matches(input, options)) {
+            log("Found match; substituting", Project.MSG_DEBUG);
+            res = regexp.substitute(input, s.getExpression(getProject()),
+                                    options);
+        }
+
+        return res;
+    }
+
+
+    /**
+     *  Perform the replacement on a file
+     *
+     * @param f the file to perform the relacement on
+     * @param options the regular expressions options
+     * @exception IOException if an error occurs
+     */
+    protected void doReplace(File f, int options)
+         throws IOException {
+        File temp = fileUtils.createTempFile("replace", ".txt", null);
+        temp.deleteOnExit();
+
+        Reader r = null;
+        Writer w = null;
+
+        try {
+            if (encoding == null) {
+                r = new FileReader(f);
+                w = new FileWriter(temp);
+            } else {
+                r = new InputStreamReader(new FileInputStream(f), encoding);
+                w = new OutputStreamWriter(new FileOutputStream(temp),
+                                           encoding);
+            }
+
+            BufferedReader br = new BufferedReader(r);
+            BufferedWriter bw = new BufferedWriter(w);
+            PrintWriter pw = new PrintWriter(bw);
+
+            boolean changes = false;
+
+            log("Replacing pattern '" + regex.getPattern(getProject())
+                + "' with '" + subs.getExpression(getProject())
+                + "' in '" + f.getPath() + "'" + (byline ? " by line" : "")
+                + (flags.length() > 0 ? " with flags: '" + flags + "'" : "")
+                + ".", Project.MSG_VERBOSE);
+
+            if (byline) {
+                StringBuffer linebuf = new StringBuffer();
+                String line = null;
+                String res = null;
+                int c;
+                boolean hasCR = false;
+
+                do {
+                    c = br.read();
+
+                    if (c == '\r') {
+                        if (hasCR) {
+                            // second CR -> EOL + possibly empty line
+                            line = linebuf.toString();
+                            res  = doReplace(regex, subs, line, options);
+
+                            if (!res.equals(line)) {
+                                changes = true;
+                            }
+
+                            pw.print(res);
+                            pw.print('\r');
+
+                            linebuf = new StringBuffer();
+                            // hasCR is still true (for the second one)
+                        } else {
+                            // first CR in this line
+                            hasCR = true;
+                        }
+                    } else if (c == '\n') {
+                        // LF -> EOL
+                        line = linebuf.toString();
+                        res  = doReplace(regex, subs, line, options);
+
+                        if (!res.equals(line)) {
+                            changes = true;
+                        }
+
+                        pw.print(res);
+                        if (hasCR) {
+                            pw.print('\r');
+                            hasCR = false;
+                        }
+                        pw.print('\n');
+
+                        linebuf = new StringBuffer();
+                    } else { // any other char
+                        if ((hasCR) || (c < 0)) {
+                            // Mac-style linebreak or EOF (or both)
+                            line = linebuf.toString();
+                            res  = doReplace(regex, subs, line, options);
+
+                            if (!res.equals(line)) {
+                                changes = true;
+                            }
+
+                            pw.print(res);
+                            if (hasCR) {
+                                pw.print('\r');
+                                hasCR = false;
+                            }
+
+                            linebuf = new StringBuffer();
+                        }
+
+                        if (c >= 0) {
+                            linebuf.append((char) c);
+                        }
+                    }
+                } while (c >= 0);
+
+                pw.flush();
+            } else {
+                String buf = fileUtils.readFully(br);
+                if (buf == null) {
+                    buf = "";
+                }
+
+                String res = doReplace(regex, subs, buf, options);
+
+                if (!res.equals(buf)) {
+                    changes = true;
+                }
+
+                pw.print(res);
+                pw.flush();
+            }
+
+            r.close();
+            r = null;
+            w.close();
+            w = null;
+
+            if (changes) {
+                log("File has changed; saving the updated file", Project.MSG_VERBOSE);
+                try {
+                    fileUtils.rename(temp, f);
+                    temp = null;
+                } catch (IOException e) {
+                    throw new BuildException("Couldn't rename temporary file "
+                                             + temp, getLocation());
+                }
+            } else {
+                log("No change made", Project.MSG_DEBUG);
+            }
+        } finally {
+            try {
+                if (r != null) {
+                    r.close();
+                }
+            } catch (Exception e) {
+                // ignore any secondary exceptions
+            }
+
+            try {
+                if (w != null) {
+                    w.close();
+                }
+            } catch (Exception e) {
+                // ignore any secondary exceptions
+            }
+            if (temp != null) {
+                temp.delete();
+            }
+        }
+    }
+
+
+    /**
+     * Execute the task
+     *
+     * @throws BuildException is there is a problem in the task execution.
+     */
+    public void execute() throws BuildException {
+        if (regex == null) {
+            throw new BuildException("No expression to match.");
+        }
+        if (subs == null) {
+            throw new BuildException("Nothing to replace expression with.");
+        }
+
+        if (file != null && filesets.size() > 0) {
+            throw new BuildException("You cannot supply the 'file' attribute "
+                                     + "and filesets at the same time.");
+        }
+
+        int options = 0;
+
+        if (flags.indexOf('g') != -1) {
+            options |= Regexp.REPLACE_ALL;
+        }
+
+        if (flags.indexOf('i') != -1) {
+            options |= Regexp.MATCH_CASE_INSENSITIVE;
+        }
+
+        if (flags.indexOf('m') != -1) {
+            options |= Regexp.MATCH_MULTILINE;
+        }
+
+        if (flags.indexOf('s') != -1) {
+            options |= Regexp.MATCH_SINGLELINE;
+        }
+
+        if (file != null && file.exists()) {
+            try {
+                doReplace(file, options);
+            } catch (IOException e) {
+                log("An error occurred processing file: '"
+                    + file.getAbsolutePath() + "': " + e.toString(),
+                    Project.MSG_ERR);
+            }
+        } else if (file != null) {
+            log("The following file is missing: '"
+                + file.getAbsolutePath() + "'", Project.MSG_ERR);
+        }
+
+        int sz = filesets.size();
+
+        for (int i = 0; i < sz; i++) {
+            FileSet fs = (FileSet) (filesets.elementAt(i));
+            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+
+            String[] files = ds.getIncludedFiles();
+
+            for (int j = 0; j < files.length; j++) {
+                File f = new File(fs.getDir(getProject()), files[j]);
+
+                if (f.exists()) {
+                    try {
+                        doReplace(f, options);
+                    } catch (Exception e) {
+                        log("An error occurred processing file: '"
+                            + f.getAbsolutePath() + "': " + e.toString(),
+                            Project.MSG_ERR);
+                    }
+                } else {
+                    log("The following file is missing: '"
+                        + f.getAbsolutePath() + "'", Project.MSG_ERR);
+                }
+            }
+        }
+    }
+
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Rpm.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Rpm.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Rpm.java	(revision 16139)
@@ -0,0 +1,359 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.taskdefs.PumpStreamHandler;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Invokes the rpm tool to build a Linux installation file.
+ *
+ */
+public class Rpm extends Task {
+
+    /**
+     * the spec file
+     */
+    private String specFile;
+
+    /**
+     * the rpm top dir
+     */
+    private File topDir;
+
+    /**
+     * the rpm command to use
+     */
+    private String command = "-bb";
+
+    /**
+     * The executable to use for building the packages.
+     * @since Ant 1.6
+     */
+    private String rpmBuildCommand = null;
+
+    /**
+     * clean BUILD directory
+     */
+    private boolean cleanBuildDir = false;
+
+    /**
+     * remove spec file
+     */
+    private boolean removeSpec = false;
+
+    /**
+     * remove sources
+     */
+    private boolean removeSource = false;
+
+    /**
+     * the file to direct standard output from the command
+     */
+    private File output;
+
+    /**
+     * the file to direct standard error from the command
+     */
+    private File error;
+
+    /**
+     * Halt on error return value from rpm build.
+     */
+    private boolean failOnError = false;
+
+    /**
+     * Don't show output of RPM build command on console. This does not affect
+     * the printing of output and error messages to files.
+     */
+    private boolean quiet = false;
+
+    /**
+     * Execute the task
+     *
+     * @throws BuildException is there is a problem in the task execution.
+     */
+    public void execute() throws BuildException {
+
+        Commandline toExecute = new Commandline();
+
+        toExecute.setExecutable(rpmBuildCommand == null
+                                ? guessRpmBuildCommand()
+                                : rpmBuildCommand);
+        if (topDir != null) {
+            toExecute.createArgument().setValue("--define");
+            toExecute.createArgument().setValue("_topdir" + topDir);
+        }
+
+        toExecute.createArgument().setLine(command);
+
+        if (cleanBuildDir) {
+            toExecute.createArgument().setValue("--clean");
+        }
+        if (removeSpec) {
+            toExecute.createArgument().setValue("--rmspec");
+        }
+        if (removeSource) {
+            toExecute.createArgument().setValue("--rmsource");
+        }
+
+        toExecute.createArgument().setValue("SPECS/" + specFile);
+
+        ExecuteStreamHandler streamhandler = null;
+        OutputStream outputstream = null;
+        OutputStream errorstream = null;
+        if (error == null && output == null) {
+            if (!quiet) {
+                streamhandler = new LogStreamHandler(this, Project.MSG_INFO,
+                                                     Project.MSG_WARN);
+            } else {
+                streamhandler = new LogStreamHandler(this, Project.MSG_DEBUG,
+                                                     Project.MSG_DEBUG);
+            }
+        } else {
+            if (output != null) {
+                try {
+                    BufferedOutputStream bos
+                        = new BufferedOutputStream(new FileOutputStream(output));
+                    outputstream = new PrintStream(bos);
+                } catch (IOException e) {
+                    throw new BuildException(e, getLocation());
+                }
+            } else if (!quiet) {
+                outputstream = new LogOutputStream(this, Project.MSG_INFO);
+            } else {
+                outputstream = new LogOutputStream(this, Project.MSG_DEBUG);
+            }
+            if (error != null) {
+                try {
+                    BufferedOutputStream bos
+                        = new BufferedOutputStream(new FileOutputStream(error));
+                    errorstream = new PrintStream(bos);
+                }  catch (IOException e) {
+                    throw new BuildException(e, getLocation());
+                }
+            } else if (!quiet) {
+                errorstream = new LogOutputStream(this, Project.MSG_WARN);
+            } else {
+                errorstream = new LogOutputStream(this, Project.MSG_DEBUG);
+            }
+            streamhandler = new PumpStreamHandler(outputstream, errorstream);
+        }
+
+        Execute exe = getExecute(toExecute, streamhandler);
+        try {
+            log("Building the RPM based on the " + specFile + " file");
+            int returncode = exe.execute();
+            if (Execute.isFailure(returncode)) {
+                String msg = "'" + toExecute.getExecutable() 
+                    + "' failed with exit code " + returncode;
+                if (failOnError) {
+                    throw new BuildException(msg);
+                } else {
+                    log(msg, Project.MSG_ERR);
+                }
+            }
+        } catch (IOException e) {
+            throw new BuildException(e, getLocation());
+        } finally {
+            if (output != null) {
+                try {
+                    outputstream.close();
+                } catch (IOException e) {
+                    // ignore any secondary error
+                }
+            }
+            if (error != null) {
+                try {
+                    errorstream.close();
+                } catch (IOException e) {
+                    // ignore any secondary error
+                }
+            }
+        }
+    }
+
+    /**
+     * The directory which will have the expected
+     * subdirectories, SPECS, SOURCES, BUILD, SRPMS ; optional.
+     * If this isn't specified,
+     * the <tt>baseDir</tt> value is used
+     *
+     * @param td the directory containing the normal RPM directories.
+     */
+    public void setTopDir(File td) {
+        this.topDir = td;
+    }
+
+    /**
+     * What command to issue to the rpm build tool; optional.
+     * The default is "-bb"
+     */
+    public void setCommand(String c) {
+        this.command = c;
+    }
+
+    /**
+     * The name of the spec File to use; required.
+     */
+    public void setSpecFile(String sf) {
+        if ((sf == null) || (sf.trim().equals(""))) {
+            throw new BuildException("You must specify a spec file", getLocation());
+        }
+        this.specFile = sf;
+    }
+
+    /**
+     * Flag (optional, default=false) to remove
+     * the generated files in the BUILD directory
+     */
+    public void setCleanBuildDir(boolean cbd) {
+        cleanBuildDir = cbd;
+    }
+
+    /**
+     * Flag (optional, default=false) to remove the spec file from SPECS
+     */
+    public void setRemoveSpec(boolean rs) {
+        removeSpec = rs;
+    }
+
+    /**
+     * Flag (optional, default=false)
+     * to remove the sources after the build.
+     * See the <tt>--rmsource</tt>  option of rpmbuild.
+     */
+    public void setRemoveSource(boolean rs) {
+        removeSource = rs;
+    }
+
+    /**
+     * Optional file to save stdout to.
+     */
+    public void setOutput(File output) {
+        this.output = output;
+    }
+
+    /**
+     * Optional file to save stderr to
+     */
+    public void setError(File error) {
+        this.error = error;
+    }
+
+    /**
+     * The executable to run when building; optional.
+     * The default is <code>rpmbuild</code>.
+     *
+     * @since Ant 1.6
+     * @param c the rpm build executable
+     */
+    public void setRpmBuildCommand(String c) {
+        this.rpmBuildCommand = c;
+    }
+
+    /**
+     * If <code>true</code>, stop the build process when the rpmbuild command 
+     * exits with an error status. 
+     * @param value <code>true</code> if it should halt, otherwise
+     * <code>false</code>. The default is <code>false</code>.
+     *
+     * @since Ant 1.6.3
+     */
+    public void setFailOnError(boolean value) {
+        failOnError = value;
+    }
+
+    /**
+     * If true, output from the RPM build command will only be logged to DEBUG. 
+     * @param value <code>false</code> if output should be logged, otherwise
+     * <code>true</code>. The default is <code>false</code>.
+     *
+     * @since Ant 1.6.3
+     */
+    public void setQuiet(boolean value) {
+        quiet = value;
+    }
+
+    /**
+     * Checks whether <code>rpmbuild</code> is on the PATH and returns
+     * the absolute path to it - falls back to <code>rpm</code>
+     * otherwise.
+     *
+     * @since 1.6
+     */
+    protected String guessRpmBuildCommand() {
+        Vector env = Execute.getProcEnvironment();
+        String path = null;
+        for (Enumeration e = env.elements(); e.hasMoreElements();) {
+            String var = (String) e.nextElement();
+            if (var.startsWith("PATH=") || var.startsWith("Path=")) {
+                path = var.substring(6 /* "PATH=".length() + 1 */);
+                break;
+            }
+        }
+
+        if (path != null) {
+            Path p = new Path(getProject(), path);
+            String[] pElements = p.list();
+            for (int i = 0; i < pElements.length; i++) {
+                File f = new File(pElements[i],
+                                  "rpmbuild"
+                                  + (Os.isFamily("dos") ? ".exe" : ""));
+                if (f.canRead()) {
+                    return f.getAbsolutePath();
+                }
+            }
+        }
+
+        return "rpm";
+    }
+
+    /**
+     * @since Ant 1.6.3
+     */
+    protected Execute getExecute(Commandline toExecute,
+                                 ExecuteStreamHandler streamhandler) {
+        Execute exe = new Execute(streamhandler, null);
+
+        exe.setAntRun(getProject());
+        if (topDir == null) {
+            topDir = getProject().getBaseDir();
+        }
+        exe.setWorkingDirectory(topDir);
+
+        exe.setCommandline(toExecute.getCommandline());
+        return exe;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Script.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Script.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Script.java	(revision 16139)
@@ -0,0 +1,89 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.ScriptRunner;
+
+/**
+ * Executes a script.
+ *
+ * @ant.task name="script"
+ */
+public class Script extends Task {
+
+    private String language;
+    private File   src;
+    private String text;
+
+    /**
+     * Do the work.
+     *
+     * @exception BuildException if something goes wrong with the build
+     */
+    public void execute() throws BuildException {
+        ScriptRunner runner = new ScriptRunner();
+        if (language != null) {
+            runner.setLanguage(language);
+        }
+        if (src != null) {
+            runner.setSrc(src);
+        }
+        if (text != null) {
+            runner.addText(text);
+        }
+
+        runner.addBeans(getProject().getProperties());
+        runner.addBeans(getProject().getUserProperties());
+        runner.addBeans(getProject().getTargets());
+        runner.addBeans(getProject().getReferences());
+
+        runner.addBean("project", getProject());
+        runner.addBean("self", this);
+
+        runner.executeScript("ANT");
+    }
+
+    /**
+     * Defines the language (required).
+     *
+     * @param language the scripting language name for the script.
+     */
+    public void setLanguage(String language) {
+        this.language = language;
+    }
+
+    /**
+     * Load the script from an external file ; optional.
+     *
+     * @param fileName the name of the file containing the script source.
+     */
+    public void setSrc(String fileName) {
+        this.src = new File(fileName);
+    }
+
+    /**
+     * Set the script text.
+     *
+     * @param text a component of the script text to be added.
+     */
+    public void addText(String text) {
+        this.text = text;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/StyleBook.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/StyleBook.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/StyleBook.java	(revision 16139)
@@ -0,0 +1,109 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Java;
+
+/**
+ * Executes the Apache Stylebook documentation generator.
+ * Unlike the commandline version of this tool, all three arguments
+ * are required to run stylebook.
+ * <p>
+ * Being extended from &lt;Java&gt;, all the parent's attributes
+ * and options are available. Do not set any apart from the <tt>classpath</tt>
+ * as they are not guaranteed to be there in future.
+ * @todo stop extending from Java.
+ */
+public class StyleBook extends Java {
+    protected File m_targetDirectory;
+    protected File m_skinDirectory;
+    protected String m_loaderConfig;
+    protected File m_book;
+
+
+    public StyleBook() {
+        setClassname("org.apache.stylebook.StyleBook");
+        setFork(true);
+        setFailonerror(true);
+    }
+
+    /**
+     * The book xml file that the documentation generation starts from;
+     * required.
+     */
+
+    public void setBook(final File book) {
+        m_book = book;
+    }
+
+
+    /**
+     * the directory that contains the stylebook skin;
+     * required.
+     */
+    public void setSkinDirectory(final File skinDirectory) {
+        m_skinDirectory = skinDirectory;
+    }
+
+
+    /**
+     * the destination directory where the documentation is generated;
+     * required.
+     */
+    public void setTargetDirectory(final File targetDirectory) {
+        m_targetDirectory = targetDirectory;
+    }
+
+    /**
+     * A loader configuration to send to stylebook; optional.
+     */
+    public void setLoaderConfig(final String loaderConfig) {
+        m_loaderConfig = loaderConfig;
+    }
+
+
+    /**
+     * call the program
+     */
+    public void execute()
+         throws BuildException {
+
+        if (null == m_targetDirectory) {
+            throw new BuildException("TargetDirectory attribute not set.");
+        }
+
+        if (null == m_skinDirectory) {
+            throw new BuildException("SkinDirectory attribute not set.");
+        }
+
+        if (null == m_book) {
+            throw new BuildException("book attribute not set.");
+        }
+
+        createArg().setValue("targetDirectory=" + m_targetDirectory);
+        createArg().setValue(m_book.toString());
+        createArg().setValue(m_skinDirectory.toString());
+        if (null != m_loaderConfig) {
+            createArg().setValue("loaderConfig=" + m_loaderConfig);
+        }
+
+        super.execute();
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Test.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Test.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/Test.java	(revision 16139)
@@ -0,0 +1,119 @@
+/*
+ * Copyright  2000,2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Java;
+
+/**
+ * This is a primitive task to execute a unit test in the
+ * org.apache.testlet framework.
+ *
+ * @deprecated testlet has been abandoned in favor of JUnit by the
+ * Avalon community
+ *
+ * @ant.task ignore="true"
+ */
+public class Test extends Java {
+
+    protected Vector m_tests = new Vector();
+
+
+    /**
+     * testlet to run
+     */
+    protected static final class TestletEntry {
+
+        protected String m_testname = "";
+
+
+        /** name of test. No property expansion takes place here */
+        public void addText(final String testname) {
+            m_testname += testname;
+        }
+
+
+        public String toString() {
+            return m_testname;
+        }
+    }
+
+
+    public Test() {
+        setClassname("org.apache.testlet.engine.TextTestEngine");
+    }
+
+
+    /**
+     * add a declaration of a testlet to run
+     */
+    public TestletEntry createTestlet() {
+        final TestletEntry entry = new TestletEntry();
+
+        m_tests.addElement(entry);
+        return entry;
+    }
+
+
+    /**
+     * a boolean value indicating whether tests should display a
+     * message on success; optional
+     */
+
+    public void setShowSuccess(final boolean showSuccess) {
+        createArg().setValue("-s=" + showSuccess);
+    }
+
+
+    /**
+     * a boolean value indicating whether a banner should be displayed
+     * when starting testlet engine; optional.
+     */
+    public void setShowBanner(final String showBanner) {
+        createArg().setValue("-b=" + showBanner);
+    }
+
+
+    /**
+     * a boolean indicating that a stack trace is displayed on
+     * error (but not normal failure); optional.
+     */
+    public void setShowTrace(final boolean showTrace) {
+        createArg().setValue("-t=" + showTrace);
+    }
+
+
+    public void setForceShowTrace(final boolean forceShowTrace) {
+        createArg().setValue("-f=" + forceShowTrace);
+    }
+
+
+    public void execute()
+         throws BuildException {
+
+        final int size = m_tests.size();
+
+        for (int i = 0; i < size; i++) {
+            createArg().setValue(m_tests.elementAt(i).toString());
+        }
+
+        super.execute();
+    }
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/TraXLiaison.java	(revision 16139)
@@ -0,0 +1,433 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Vector;
+import java.util.Enumeration;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.ErrorListener;
+import javax.xml.transform.Source;
+import javax.xml.transform.SourceLocator;
+import javax.xml.transform.Templates;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+import javax.xml.transform.TransformerConfigurationException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.XSLTLiaison2;
+import org.apache.tools.ant.taskdefs.XSLTProcess;
+import org.apache.tools.ant.taskdefs.XSLTLogger;
+import org.apache.tools.ant.taskdefs.XSLTLoggerAware;
+import org.apache.tools.ant.types.XMLCatalog;
+import org.apache.tools.ant.util.JAXPUtils;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+/**
+ * Concrete liaison for XSLT processor implementing TraX. (ie JAXP 1.1)
+ *
+ * @since Ant 1.3
+ */
+public class TraXLiaison implements XSLTLiaison2, ErrorListener, XSLTLoggerAware {
+
+    /**
+     * the name of the factory implementation class to use
+     * or null for default JAXP lookup.
+     */
+    private String factoryName = null;
+
+    /** The trax TransformerFactory */
+    private TransformerFactory tfactory = null;
+
+    /** stylesheet to use for transformation */
+    private File stylesheet;
+
+    private XSLTLogger logger;
+
+    /** possible resolver for publicIds */
+    private EntityResolver entityResolver;
+
+    /** transformer to use for processing files */
+    private Transformer transformer;
+
+    /** The In memory version of the stylesheet */
+    private Templates templates;
+
+    /**
+     * The modification time of the stylesheet from which the templates
+     * are read
+     */
+    private long templatesModTime;
+
+    /** possible resolver for URIs */
+    private URIResolver uriResolver;
+
+    /** transformer output properties */
+    private Vector outputProperties = new Vector();
+
+    /** stylesheet parameters */
+    private Vector params = new Vector();
+
+    /** factory attributes */
+    private Vector attributes = new Vector();
+
+    public TraXLiaison() throws Exception {
+    }
+
+    public void setStylesheet(File stylesheet) throws Exception {
+        if (this.stylesheet != null) {
+            // resetting the stylesheet - reset transformer
+            transformer = null;
+
+            // do we need to reset templates as well
+            if (!this.stylesheet.equals(stylesheet)
+                || (stylesheet.lastModified() != templatesModTime)) {
+                templates = null;
+            }
+        }
+        this.stylesheet = stylesheet;
+    }
+
+    public void transform(File infile, File outfile) throws Exception {
+        if (transformer == null) {
+            createTransformer();
+        }
+
+        InputStream fis = null;
+        OutputStream fos = null;
+        try {
+            fis = new BufferedInputStream(new FileInputStream(infile));
+            fos = new BufferedOutputStream(new FileOutputStream(outfile));
+            StreamResult res = new StreamResult(fos);
+            // not sure what could be the need of this...
+            res.setSystemId(JAXPUtils.getSystemId(outfile));
+            Source src = getSource(fis, infile);
+            transformer.transform(src, res);
+        } finally {
+            // make sure to close all handles, otherwise the garbage
+            // collector will close them...whenever possible and
+            // Windows may complain about not being able to delete files.
+            try {
+                if (fis != null) {
+                    fis.close();
+                }
+            } catch (IOException ignored) {
+                // ignore
+            }
+            try {
+                if (fos != null) {
+                    fos.close();
+                }
+            } catch (IOException ignored) {
+                // ignore
+            }
+        }
+    }
+
+    /**
+     * Get the source instance from the stream and id of the file.
+     * @param is the stream containing the stylesheet data.
+     * @param infile the file that will be used for the systemid.
+     * @return the configured source instance matching the stylesheet.
+     * @throws Exception if there is a problem creating the source.
+     */
+    private Source getSource(InputStream is, File infile)
+        throws ParserConfigurationException, SAXException {
+        // todo: is this comment still relevant ??
+        // FIXME: need to use a SAXSource as the source for the transform
+        // so we can plug in our own entity resolver
+        Source src = null;
+        if (entityResolver != null) {
+            if (getFactory().getFeature(SAXSource.FEATURE)) {
+                SAXParserFactory spFactory = SAXParserFactory.newInstance();
+                spFactory.setNamespaceAware(true);
+                XMLReader reader = spFactory.newSAXParser().getXMLReader();
+                reader.setEntityResolver(entityResolver);
+                src = new SAXSource(reader, new InputSource(is));
+            } else {
+                throw new IllegalStateException("xcatalog specified, but "
+                    + "parser doesn't support SAX");
+            }
+        } else {
+            // WARN: Don't use the StreamSource(File) ctor. It won't work with
+            // xalan prior to 2.2 because of systemid bugs.
+            src = new StreamSource(is);
+        }
+        src.setSystemId(JAXPUtils.getSystemId(infile));
+        return src;
+    }
+
+    /**
+     * Read in templates from the stylesheet
+     */
+    private void readTemplates()
+        throws IOException, TransformerConfigurationException,
+               ParserConfigurationException, SAXException {
+
+        // Use a stream so that you can close it yourself quickly
+        // and avoid keeping the handle until the object is garbaged.
+        // (always keep control), otherwise you won't be able to delete
+        // the file quickly on windows.
+        InputStream xslStream = null;
+        try {
+            xslStream
+                = new BufferedInputStream(new FileInputStream(stylesheet));
+            templatesModTime = stylesheet.lastModified();
+            Source src = getSource(xslStream, stylesheet);
+            templates = getFactory().newTemplates(src);
+        } finally {
+            if (xslStream != null) {
+                xslStream.close();
+            }
+        }
+    }
+
+    /**
+     * Create a new transformer based on the liaison settings
+     * @return the newly created and configured transformer.
+     * @throws Exception thrown if there is an error during creation.
+     * @see #setStylesheet(java.io.File)
+     * @see #addParam(java.lang.String, java.lang.String)
+     * @see #setOutputProperty(java.lang.String, java.lang.String)
+     */
+    private void createTransformer() throws Exception {
+        if (templates == null) {
+            readTemplates();
+        }
+
+        transformer = templates.newTransformer();
+
+        // configure the transformer...
+        transformer.setErrorListener(this);
+        if (uriResolver != null) {
+            transformer.setURIResolver(uriResolver);
+        }
+        for (int i = 0; i < params.size(); i++) {
+            final String[] pair = (String[]) params.elementAt(i);
+            transformer.setParameter(pair[0], pair[1]);
+        }
+        for (int i = 0; i < outputProperties.size(); i++) {
+            final String[] pair = (String[]) outputProperties.elementAt(i);
+            transformer.setOutputProperty(pair[0], pair[1]);
+        }
+    }
+
+    /**
+     * return the Transformer factory associated to this liaison.
+     * @return the Transformer factory associated to this liaison.
+     * @throws BuildException thrown if there is a problem creating
+     * the factory.
+     * @see #setFactory(String)
+     * @since Ant 1.5.2
+     */
+    private TransformerFactory getFactory() throws BuildException {
+        if (tfactory != null) {
+            return tfactory;
+        }
+        // not initialized yet, so create the factory
+        if (factoryName == null) {
+            tfactory = TransformerFactory.newInstance();
+        } else {
+            try {
+                Class clazz = Class.forName(factoryName);
+                tfactory = (TransformerFactory) clazz.newInstance();
+            } catch (Exception e) {
+                throw new BuildException(e);
+            }
+        }
+        tfactory.setErrorListener(this);
+
+        // specific attributes for the transformer
+        for (int i = 0; i < attributes.size(); i++) {
+            final Object[] pair = (Object[]) attributes.elementAt(i);
+            tfactory.setAttribute((String) pair[0], pair[1]);
+        }
+
+        if (uriResolver != null) {
+            tfactory.setURIResolver(uriResolver);
+        }
+        return tfactory;
+    }
+
+
+    /**
+     * Set the factory name to use instead of JAXP default lookup.
+     * @param name the fully qualified class name of the factory to use
+     * or null for the default JAXP look up mechanism.
+     * @since Ant 1.6
+     */
+    public void setFactory(String name) {
+        factoryName = name;
+    }
+
+    /**
+     * Set a custom attribute for the JAXP factory implementation.
+     * @param name the attribute name.
+     * @param value the value of the attribute, usually a boolean
+     * string or object.
+     * @since Ant 1.6
+     */
+    public void setAttribute(String name, Object value) {
+        final Object[] pair = new Object[]{name, value};
+        attributes.addElement(pair);
+    }
+
+    /**
+     * Set the output property for the current transformer.
+     * Note that the stylesheet must be set prior to calling
+     * this method.
+     * @param name the output property name.
+     * @param value the output property value.
+     * @since Ant 1.5
+     * @since Ant 1.5
+     */
+    public void setOutputProperty(String name, String value) {
+        final String[] pair = new String[]{name, value};
+        outputProperties.addElement(pair);
+    }
+
+    /** Set the class to resolve entities during the transformation
+     */
+    public void setEntityResolver(EntityResolver aResolver) {
+        entityResolver = aResolver;
+    }
+
+    /** Set the class to resolve URIs during the transformation
+     */
+    public void setURIResolver(URIResolver aResolver) {
+        uriResolver = aResolver;
+    }
+
+    public void addParam(String name, String value) {
+        final String[] pair = new String[]{name, value};
+        params.addElement(pair);
+    }
+
+    public void setLogger(XSLTLogger l) {
+        logger = l;
+    }
+
+    public void error(TransformerException e) {
+        logError(e, "Error");
+    }
+
+    public void fatalError(TransformerException e) {
+        logError(e, "Fatal Error");
+        throw new BuildException("Fatal error during transformation", e);
+    }
+
+    public void warning(TransformerException e) {
+        logError(e, "Warning");
+    }
+
+    private void logError(TransformerException e, String type) {
+        if (logger == null) {
+            return;
+        }
+
+        StringBuffer msg = new StringBuffer();
+        SourceLocator locator = e.getLocator();
+        if (locator != null) {
+            String systemid = locator.getSystemId();
+            if (systemid != null) {
+                String url = systemid;
+                if (url.startsWith("file:///")) {
+                    url = url.substring(8);
+                }
+                msg.append(url);
+            } else {
+                msg.append("Unknown file");
+            }
+            int line = locator.getLineNumber();
+            if (line != -1) {
+                msg.append(":" + line);
+                int column = locator.getColumnNumber();
+                if (column != -1) {
+                    msg.append(":" + column);
+                }
+            }
+        }
+        msg.append(": " + type + "! ");
+        msg.append(e.getMessage());
+        if (e.getCause() != null) {
+            msg.append(" Cause: " + e.getCause());
+        }
+
+        logger.log(msg.toString());
+    }
+
+    // kept for backwards compatibility
+    /**
+     * @deprecated use org.apache.tools.ant.util.JAXPUtils#getSystemId instead
+     */
+    protected String getSystemId(File file) {
+        return JAXPUtils.getSystemId(file);
+    }
+
+
+    /**
+     * Specific configuration for the TRaX liaison.
+     * @param xsltTask the XSLTProcess task instance from which this liasion
+     *        is to be configured.
+     */
+    public void configure(XSLTProcess xsltTask) {
+        XSLTProcess.Factory factory = xsltTask.getFactory();
+        if (factory != null) {
+            setFactory(factory.getName());
+
+            // configure factory attributes
+            for (Enumeration attrs = factory.getAttributes();
+                    attrs.hasMoreElements();) {
+                XSLTProcess.Factory.Attribute attr =
+                        (XSLTProcess.Factory.Attribute) attrs.nextElement();
+                setAttribute(attr.getName(), attr.getValue());
+            }
+        }
+
+        XMLCatalog xmlCatalog = xsltTask.getXMLCatalog();
+        // use XMLCatalog as the entity resolver and URI resolver
+        if (xmlCatalog != null) {
+            setEntityResolver(xmlCatalog);
+            setURIResolver(xmlCatalog);
+        }
+
+
+        // configure output properties
+        for (Enumeration props = xsltTask.getOutputProperties();
+                props.hasMoreElements();) {
+            XSLTProcess.OutputProperty prop
+                = (XSLTProcess.OutputProperty) props.nextElement();
+            setOutputProperty(prop.getName(), prop.getValue());
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/XMLValidateTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/XMLValidateTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/XMLValidateTask.java	(revision 16139)
@@ -0,0 +1,657 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Vector;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.DTDLocation;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.types.XMLCatalog;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JAXPUtils;
+
+import org.xml.sax.EntityResolver;
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.InputSource;
+import org.xml.sax.Parser;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXNotRecognizedException;
+import org.xml.sax.SAXNotSupportedException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.ParserAdapter;
+
+/**
+ * Checks XML files are valid (or only well formed). The
+ * task uses the SAX2 parser implementation provided by JAXP by default
+ * (probably the one that is used by Ant itself), but one can specify any
+ * SAX1/2 parser if needed.
+ *
+ */
+public class XMLValidateTask extends Task {
+
+    /**
+     * helper for path -> URI and URI -> path conversions.
+     */
+    private static final FileUtils FILE_UTILS = FileUtils.newFileUtils();
+
+    protected static final String INIT_FAILED_MSG =
+        "Could not start xml validation: ";
+
+    // ant task properties
+    // defaults
+    protected boolean failOnError = true;
+    protected boolean warn = true;
+    protected boolean lenient = false;
+    protected String readerClassName = null;
+
+    /** file to be validated */
+    protected File file = null;
+    /** sets of file to be validated */
+    protected Vector filesets = new Vector();
+    protected Path classpath;
+
+    /**
+     * the parser is viewed as a SAX2 XMLReader. If a SAX1 parser is specified,
+     * it's wrapped in an adapter that make it behave as a XMLReader.
+     * a more 'standard' way of doing this would be to use the JAXP1.1 SAXParser
+     * interface.
+     */
+    protected XMLReader xmlReader = null;
+    // XMLReader used to validation process
+    protected ValidatorErrorHandler errorHandler = new ValidatorErrorHandler();
+    // to report sax parsing errors
+
+    /** The vector to store all attributes (features) to be set on the parser. **/
+    private Vector attributeList = new Vector();
+
+    /**
+     * List of properties.
+     */
+    private final Vector propertyList = new Vector();
+
+    private XMLCatalog xmlCatalog = new XMLCatalog();
+
+    /**
+     * Specify how parser error are to be handled.
+     * Optional, default is <code>true</code>.
+     * <p>
+     * If set to <code>true</code> (default), throw a buildException if the
+     * parser yields an error.
+     * @param fail if set to <code>false</code> do not fail on error
+     */
+    public void setFailOnError(boolean fail) {
+        failOnError = fail;
+    }
+
+    /**
+     * Specify how parser error are to be handled.
+     * <p>
+     * If set to <code>true</true> (default), log a warn message for each SAX warn event.
+     * @param bool if set to <code>false</code> do not send warnings
+     */
+    public void setWarn(boolean bool) {
+        warn = bool;
+    }
+
+    /**
+     * Specify whether the parser should be validating. Default
+     * is <code>true</code>.
+     * <p>
+     * If set to false, the validation will fail only if the parsed document
+     * is not well formed XML.
+     * <p>
+     * this option is ignored if the specified class
+     * with {@link #setClassName(String)} is not a SAX2 XMLReader.
+     * @param bool if set to <code>false</code> only fail on malformed XML
+     */
+    public void setLenient(boolean bool) {
+        lenient = bool;
+    }
+
+    /**
+     * Specify the class name of the SAX parser to be used. (optional)
+     * @param className should be an implementation of SAX2
+     * <code>org.xml.sax.XMLReader</code> or SAX2 <code>org.xml.sax.Parser</code>.
+     * <p> if className is an implementation of
+     * <code>org.xml.sax.Parser</code>, {@link #setLenient(boolean)},
+     * will be ignored.
+     * <p> if not set, the default will be used.
+     * @see org.xml.sax.XMLReader
+     * @see org.xml.sax.Parser
+     */
+    public void setClassName(String className) {
+        readerClassName = className;
+    }
+
+    /**
+     * Specify the classpath to be searched to load the parser (optional)
+     * @param classpath the classpath to load the parser
+     */
+    public void setClasspath(Path classpath) {
+        if (this.classpath == null) {
+            this.classpath = classpath;
+        } else {
+            this.classpath.append(classpath);
+        }
+    }
+
+    /**
+     * @see #setClasspath
+     * @return the classpath created
+     */
+    public Path createClasspath() {
+        if (this.classpath == null) {
+            this.classpath = new Path(getProject());
+        }
+        return this.classpath.createPath();
+    }
+
+    /**
+     * Where to find the parser class; optional.
+     * @see #setClasspath
+     * @param r reference to a classpath defined elsewhere
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+
+    /**
+     * specify the file to be checked; optional.
+     * @param file the file to be checked
+     */
+    public void setFile(File file) {
+        this.file = file;
+    }
+
+    /**
+     * add an XMLCatalog as a nested element; optional.
+     * @param catalog XMLCatalog to use
+     */
+    public void addConfiguredXMLCatalog(XMLCatalog catalog) {
+        xmlCatalog.addConfiguredXMLCatalog(catalog);
+    }
+
+    /**
+     * specify a set of file to be checked
+     * @param set the fileset to check
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * Add an attribute nested element. This is used for setting arbitrary
+     * features of the SAX parser.
+     * Valid attributes
+     * <a href="http://www.saxproject.org/apidoc/org/xml/sax/package-summary.html#package_description">include</a>
+     * @return attribute created
+     * @since ant1.6
+     */
+    public Attribute createAttribute() {
+        final Attribute feature = new Attribute();
+        attributeList.addElement(feature);
+        return feature;
+    }
+
+    /**
+     * Creates a property.
+     *
+     * @return a property.
+     * @since ant 1.6.2
+     */
+    public Property createProperty() {
+        final Property prop = new Property();
+        propertyList.addElement(prop);
+        return prop;
+    }
+
+    /**
+     * Called by the project to let the task initialize properly.
+     *
+     * @exception BuildException if something goes wrong with the build
+     */
+    public void init() throws BuildException {
+        super.init();
+        xmlCatalog.setProject(getProject());
+    }
+
+    /**
+     * Create a DTD location record; optional.
+     * This stores the location of a DTD. The DTD is identified
+     * by its public Id.
+     * @return created DTD location
+     */
+    public DTDLocation createDTD() {
+        DTDLocation dtdLocation = new DTDLocation();
+        xmlCatalog.addDTD(dtdLocation);
+        return dtdLocation;
+    }
+    /**
+     * accessor to the xmlCatalog used in the task
+     * @return xmlCatalog reference
+     */
+    protected EntityResolver getEntityResolver() {
+        return xmlCatalog;
+    }
+    /**
+     * execute the task
+     * @throws BuildException if <code>failonerror</code> is true and an error happens
+     */
+    public void execute() throws BuildException {
+
+        int fileProcessed = 0;
+        if (file == null && (filesets.size() == 0)) {
+            throw new BuildException(
+                "Specify at least one source - " + "a file or a fileset.");
+        }
+
+        initValidator();
+
+        if (file != null) {
+            if (file.exists() && file.canRead() && file.isFile()) {
+                doValidate(file);
+                fileProcessed++;
+            } else {
+                String errorMsg = "File " + file + " cannot be read";
+                if (failOnError) {
+                    throw new BuildException(errorMsg);
+                } else {
+                    log(errorMsg, Project.MSG_ERR);
+                }
+            }
+        }
+
+        for (int i = 0; i < filesets.size(); i++) {
+
+            FileSet fs = (FileSet) filesets.elementAt(i);
+            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+            String[] files = ds.getIncludedFiles();
+
+            for (int j = 0; j < files.length; j++) {
+                File srcFile = new File(fs.getDir(getProject()), files[j]);
+                doValidate(srcFile);
+                fileProcessed++;
+            }
+        }
+        log(fileProcessed + " file(s) have been successfully validated.");
+    }
+
+    /**
+     * init the parser :
+     * load the parser class, and set features if necessary
+     */
+    private void initValidator() {
+
+        Object reader = null;
+        if (readerClassName == null) {
+            try {
+                reader = JAXPUtils.getXMLReader();
+            } catch (BuildException exc) {
+                reader = JAXPUtils.getParser();
+            }
+        } else {
+
+            Class readerClass = null;
+            try {
+                // load the parser class
+                if (classpath != null) {
+                    AntClassLoader loader =
+                        getProject().createClassLoader(classpath);
+                    readerClass = Class.forName(readerClassName, true, loader);
+                } else {
+                    readerClass = Class.forName(readerClassName);
+                }
+
+                reader = readerClass.newInstance();
+            } catch (ClassNotFoundException e) {
+                throw new BuildException(INIT_FAILED_MSG + readerClassName, e);
+            } catch (InstantiationException e) {
+                throw new BuildException(INIT_FAILED_MSG + readerClassName, e);
+            } catch (IllegalAccessException e) {
+                throw new BuildException(INIT_FAILED_MSG + readerClassName, e);
+            }
+        }
+
+        // then check it implements XMLReader
+        if (reader instanceof XMLReader) {
+            xmlReader = (XMLReader) reader;
+            log(
+                "Using SAX2 reader " + reader.getClass().getName(),
+                Project.MSG_VERBOSE);
+        } else {
+
+            // see if it is a SAX1 Parser
+            if (reader instanceof Parser) {
+                xmlReader = new ParserAdapter((Parser) reader);
+                log(
+                    "Using SAX1 parser " + reader.getClass().getName(),
+                    Project.MSG_VERBOSE);
+            } else {
+                throw new BuildException(
+                    INIT_FAILED_MSG
+                        + reader.getClass().getName()
+                        + " implements nor SAX1 Parser nor SAX2 XMLReader.");
+            }
+        }
+
+        xmlReader.setEntityResolver(getEntityResolver());
+        xmlReader.setErrorHandler(errorHandler);
+
+        if (!(xmlReader instanceof ParserAdapter)) {
+            // turn validation on
+            if (!lenient) {
+                setFeature("http://xml.org/sax/features/validation", true);
+            }
+            // set the feature from the attribute list
+            for (int i = 0; i < attributeList.size(); i++) {
+                Attribute feature = (Attribute) attributeList.elementAt(i);
+                setFeature(feature.getName(), feature.getValue());
+
+            }
+
+            // Sets properties
+            for (int i = 0; i < propertyList.size(); i++) {
+                final Property prop = (Property) propertyList.elementAt(i);
+                setProperty(prop.getName(), prop.getValue());
+            }
+        }
+    }
+
+    /**
+     * Set a feature on the parser.
+     * @param feature the name of the feature to set
+     * @param value the value of the feature
+     */
+    private void setFeature(String feature, boolean value)
+        throws BuildException {
+
+        try {
+            xmlReader.setFeature(feature, value);
+        } catch (SAXNotRecognizedException e) {
+            throw new BuildException(
+                "Parser "
+                    + xmlReader.getClass().getName()
+                    + " doesn't recognize feature "
+                    + feature,
+                e,
+                getLocation());
+        } catch (SAXNotSupportedException e) {
+            throw new BuildException(
+                "Parser "
+                    + xmlReader.getClass().getName()
+                    + " doesn't support feature "
+                    + feature,
+                e,
+                getLocation());
+        }
+    }
+
+    /**
+     * Sets a property.
+     *
+     * @param name a property name
+     * @param value a property value.
+     * @throws BuildException if an error occurs.
+     */
+    private void setProperty(String name, String value) throws BuildException {
+        // Validates property
+        if (name == null || value == null) {
+            throw new BuildException("Property name and value must be specified.");
+        }
+
+        try {
+            xmlReader.setProperty(name, value);
+        } catch (SAXNotRecognizedException e) {
+            throw new BuildException(
+                "Parser "
+                    + xmlReader.getClass().getName()
+                    + " doesn't recognize property "
+                    + name,
+                e,
+                getLocation());
+        } catch (SAXNotSupportedException e) {
+            throw new BuildException(
+                "Parser "
+                    + xmlReader.getClass().getName()
+                    + " doesn't support property "
+                    + name,
+                e,
+                getLocation());
+        }
+    }
+
+    /**
+     * parse the file
+     */
+    private void doValidate(File afile) {
+        try {
+            log("Validating " + afile.getName() + "... ", Project.MSG_VERBOSE);
+            errorHandler.init(afile);
+            InputSource is = new InputSource(new FileInputStream(afile));
+            String uri = FILE_UTILS.toURI(afile.getAbsolutePath());
+            is.setSystemId(uri);
+            xmlReader.parse(is);
+        } catch (SAXException ex) {
+            if (failOnError) {
+                throw new BuildException(
+                    "Could not validate document " + afile);
+            } else {
+                log("Could not validate document " + afile + ": " + ex.toString());
+            }
+        } catch (IOException ex) {
+            throw new BuildException(
+                "Could not validate document " + afile,
+                ex);
+        }
+
+        if (errorHandler.getFailure()) {
+            if (failOnError) {
+                throw new BuildException(
+                    afile + " is not a valid XML document.");
+            } else {
+                log(afile + " is not a valid XML document", Project.MSG_ERR);
+            }
+        }
+    }
+
+    /**
+     * ValidatorErrorHandler role :
+     * <ul>
+     * <li> log SAX parse exceptions,
+     * <li> remember if an error occurred
+     * </ul>
+     */
+    protected class ValidatorErrorHandler implements ErrorHandler {
+
+        protected File currentFile = null;
+        protected String lastErrorMessage = null;
+        protected boolean failed = false;
+        /**
+         * initialises the class
+         * @param file file used
+         */
+        public void init(File file) {
+            currentFile = file;
+            failed = false;
+        }
+        /**
+         * did an error happen during last parsing ?
+         * @return did an error happen during last parsing ?
+         */
+        public boolean getFailure() {
+            return failed;
+        }
+
+        /**
+         * record a fatal error
+         * @param exception the fatal error
+         */
+        public void fatalError(SAXParseException exception) {
+            failed = true;
+            doLog(exception, Project.MSG_ERR);
+        }
+        /**
+         * receive notification of a recoverable error
+         * @param exception the error
+         */
+        public void error(SAXParseException exception) {
+            failed = true;
+            doLog(exception, Project.MSG_ERR);
+        }
+        /**
+         * receive notification of a warning
+         * @param exception the warning
+         */
+        public void warning(SAXParseException exception) {
+            // depending on implementation, XMLReader can yield hips of warning,
+            // only output then if user explicitly asked for it
+            if (warn) {
+                doLog(exception, Project.MSG_WARN);
+            }
+        }
+
+        private void doLog(SAXParseException e, int logLevel) {
+
+            log(getMessage(e), logLevel);
+        }
+
+        private String getMessage(SAXParseException e) {
+            String sysID = e.getSystemId();
+            if (sysID != null) {
+                String name = sysID;
+                if (sysID.startsWith("file:")) {
+                    try {
+                        name = FILE_UTILS.fromURI(sysID);
+                    } catch (Exception ex) {
+                        // if this is not a valid file: just use the uri
+                    }
+                }
+                int line = e.getLineNumber();
+                int col = e.getColumnNumber();
+                return  name
+                    + (line == -1
+                       ? ""
+                       : (":" + line + (col == -1 ? "" : (":" + col))))
+                    + ": "
+                    + e.getMessage();
+            }
+            return e.getMessage();
+        }
+    }
+
+    /**
+     * The class to create to set a feature of the parser.
+     * @since ant1.6
+     */
+    public class Attribute {
+        /** The name of the attribute to set.
+         *
+         * Valid attributes <a href="http://www.saxproject.org/apidoc/org/xml/sax/package-summary.html#package_description">include.</a>
+         */
+        private String attributeName = null;
+
+        /**
+         * The value of the feature.
+         **/
+        private boolean attributeValue;
+
+        /**
+         * Set the feature name.
+         * @param name the name to set
+         */
+        public void setName(String name) {
+            attributeName = name;
+        }
+        /**
+         * Set the feature value to true or false.
+         * @param value feature value
+         */
+        public void setValue(boolean value) {
+            attributeValue = value;
+        }
+
+        /**
+         * Gets the attribute name.
+         * @return the feature name
+         */
+        public String getName() {
+            return attributeName;
+        }
+
+        /**
+         * Gets the attribute value.
+         * @return the feature value
+         */
+        public boolean getValue() {
+            return attributeValue;
+        }
+    }
+
+    /**
+     * A Parser property.
+     * See <a href="http://xml.apache.org/xerces-j/properties.html">
+     * XML parser properties</a> for usable properties
+     * @since ant 1.6.2
+     */
+    public final class Property {
+
+        private String name;
+        private String value;
+        /**
+         * accessor to the name of the property
+         * @return name of the property
+         */
+        public String getName() {
+            return name;
+        }
+        /**
+         * setter for the name of the property
+         * @param name name of the property
+         */
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        /**
+         * getter for the value of the property
+         * @return value of the property
+         */
+        public String getValue() {
+            return value;
+        }
+        /**
+         * sets the value of the property
+         * @param value value of the property
+         */
+        public void setValue(String value) {
+            this.value = value;
+        }
+
+    } // Property
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/XalanLiaison.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/XalanLiaison.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/XalanLiaison.java	(revision 16139)
@@ -0,0 +1,96 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import org.apache.tools.ant.taskdefs.XSLTLiaison;
+import org.apache.xalan.xslt.XSLTInputSource;
+import org.apache.xalan.xslt.XSLTProcessor;
+import org.apache.xalan.xslt.XSLTProcessorFactory;
+import org.apache.xalan.xslt.XSLTResultTarget;
+
+/**
+ * Concrete liaison for Xalan 1.x API.
+ *
+ * @since Ant 1.1
+ */
+public class XalanLiaison implements XSLTLiaison {
+
+    protected XSLTProcessor processor;
+    protected File stylesheet;
+
+    public XalanLiaison() throws Exception {
+        processor = XSLTProcessorFactory.getProcessor();
+    }
+
+    public void setStylesheet(File stylesheet) throws Exception {
+        this.stylesheet = stylesheet;
+    }
+
+    public void transform(File infile, File outfile) throws Exception {
+        FileInputStream fis = null;
+        FileOutputStream fos = null;
+        FileInputStream xslStream = null;
+        try {
+            xslStream = new FileInputStream(stylesheet);
+            fis = new FileInputStream(infile);
+            fos = new FileOutputStream(outfile);
+            // systemid such as file:/// + getAbsolutePath() are considered
+            // invalid here...
+            XSLTInputSource xslSheet = new XSLTInputSource(xslStream);
+            xslSheet.setSystemId(stylesheet.getAbsolutePath());
+            XSLTInputSource src = new XSLTInputSource(fis);
+            src.setSystemId(infile.getAbsolutePath());
+            XSLTResultTarget res = new XSLTResultTarget(fos);
+            processor.process(src, xslSheet, res);
+        } finally {
+            // make sure to close all handles, otherwise the garbage
+            // collector will close them...whenever possible and
+            // Windows may complain about not being able to delete files.
+            try {
+                if (xslStream != null) {
+                    xslStream.close();
+                }
+            } catch (IOException ignored) {
+                //ignore
+            }
+            try {
+                if (fis != null) {
+                    fis.close();
+                }
+            } catch (IOException ignored) {
+                //ignore
+           }
+            try {
+                if (fos != null) {
+                    fos.close();
+                }
+            } catch (IOException ignored) {
+                //ignore
+            }
+        }
+    }
+
+    public void addParam(String name, String value) {
+        processor.setStylesheetParam(name, value);
+    }
+
+} //-- XalanLiaison
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/XslpLiaison.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/XslpLiaison.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/XslpLiaison.java	(revision 16139)
@@ -0,0 +1,65 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional;
+
+import com.kvisco.xsl.XSLProcessor;
+import com.kvisco.xsl.XSLReader;
+import com.kvisco.xsl.XSLStylesheet;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import org.apache.tools.ant.taskdefs.XSLTLiaison;
+
+/**
+ * Concrete liaison for XSLP
+ *
+ * @since Ant 1.1
+ */
+public class XslpLiaison implements XSLTLiaison {
+
+    protected XSLProcessor processor;
+    protected XSLStylesheet xslSheet;
+
+    public XslpLiaison() {
+        processor = new XSLProcessor();
+        // uh ?! I'm forced to do that otherwise a setProperty crashes
+        // with NPE !  I don't understand why the property map is static
+        // though...  how can we do multithreading w/ multiple identical
+        // parameters ?
+        processor.getProperty("dummy-to-init-properties-map");
+    }
+
+    public void setStylesheet(File fileName) throws Exception {
+        XSLReader xslReader = new XSLReader();
+        // a file:/// + getAbsolutePath() does not work here
+        // it is really the pathname
+        xslSheet = xslReader.read(fileName.getAbsolutePath());
+    }
+
+    public void transform(File infile, File outfile) throws Exception {
+        FileOutputStream fos = new FileOutputStream(outfile);
+        // XSLP does not support encoding...we're in hot water.
+        OutputStreamWriter out = new OutputStreamWriter(fos, "UTF8");
+        processor.process(infile.getAbsolutePath(), xslSheet, out);
+    }
+
+    public void addParam(String name, String expression) {
+        processor.setProperty(name, expression);
+    }
+
+} //-- XSLPLiaison
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheck.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheck.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheck.java	(revision 16139)
@@ -0,0 +1,199 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ccm;
+
+
+import java.io.File;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.FileSet;
+
+
+/**
+ * Class common to all check commands (checkout, checkin,checkin default task);
+ * @ant.task ignore="true"
+ */
+public class CCMCheck extends Continuus {
+
+    private File file = null;
+    private String comment = null;
+    private String task = null;
+
+    protected Vector filesets = new Vector();
+
+    public CCMCheck() {
+        super();
+    }
+
+    /**
+     * Get the value of file.
+     * @return value of file.
+     */
+    public File getFile() {
+        return file;
+    }
+
+    /**
+     * Sets the path to the file that the command will operate on.
+     * @param v  Value to assign to file.
+     */
+    public void setFile(File v) {
+        log("working file " + v, Project.MSG_VERBOSE);
+        this.file = v;
+    }
+
+    /**
+     * Get the value of comment.
+     * @return value of comment.
+     */
+    public String getComment() {
+        return comment;
+    }
+
+    /**
+     * Specifies a comment.
+     * @param v  Value to assign to comment.
+     */
+    public void setComment(String v) {
+        this.comment = v;
+    }
+
+
+    /**
+     * Get the value of task.
+     * @return value of task.
+     */
+    public String getTask() {
+        return task;
+    }
+
+    /**
+     * Specifies the task number used to check
+     * in the file (may use 'default').
+     * @param v  Value to assign to task.
+     */
+    public void setTask(String v) {
+        this.task = v;
+    }
+
+
+    /**
+     * Adds a set of files to copy.
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+
+    /**
+     * Executes the task.
+     * <p>
+     * Builds a command line to execute ccm and then calls Exec's run method
+     * to execute the command line.
+     * </p>
+     */
+    public void execute() throws BuildException {
+
+        if (file == null && filesets.size() == 0) {
+            throw new BuildException(
+                "Specify at least one source - a file or a fileset.");
+        }
+
+        if (file != null && file.exists() && file.isDirectory()) {
+            throw new BuildException("CCMCheck cannot be generated for directories");
+        }
+
+        if (file != null  && filesets.size() > 0) {
+            throw new BuildException("Choose between file and fileset !");
+        }
+
+        if (getFile() != null) {
+            doit();
+            return;
+        }
+
+        int sizeofFileSet = filesets.size();
+        for (int i = 0; i < sizeofFileSet; i++) {
+            FileSet fs = (FileSet) filesets.elementAt(i);
+            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+            String[] srcFiles = ds.getIncludedFiles();
+            for (int j = 0; j < srcFiles.length; j++) {
+                File src = new File(fs.getDir(getProject()), srcFiles[j]);
+                setFile(src);
+                doit();
+            }
+        }
+    }
+
+    /**
+     * check the file given by getFile().
+     */
+    private void doit() {
+        Commandline commandLine = new Commandline();
+
+        // build the command line from what we got the format is
+        // ccm co /t .. files
+        // as specified in the CCM.EXE help
+
+        commandLine.setExecutable(getCcmCommand());
+        commandLine.createArgument().setValue(getCcmAction());
+
+        checkOptions(commandLine);
+
+        int result = run(commandLine);
+        if (Execute.isFailure(result)) {
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+
+    /**
+     * Check the command line options.
+     */
+    private void checkOptions(Commandline cmd) {
+        if (getComment() != null) {
+            cmd.createArgument().setValue(FLAG_COMMENT);
+            cmd.createArgument().setValue(getComment());
+        }
+
+        if (getTask() != null) {
+            cmd.createArgument().setValue(FLAG_TASK);
+            cmd.createArgument().setValue(getTask());
+        }
+
+        if (getFile() != null) {
+            cmd.createArgument().setValue(file.getAbsolutePath());
+        }
+    }
+
+    /**
+     * -comment flag -- comment to attach to the file
+     */
+    public static final String FLAG_COMMENT = "/comment";
+
+    /**
+     *  -task flag -- associate checkout task with task
+     */
+    public static final String FLAG_TASK = "/task";
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckin.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckin.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckin.java	(revision 16139)
@@ -0,0 +1,38 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ccm;
+
+import java.util.Date;
+
+/**
+ * Performs Continuus checkin command.
+ *
+ */
+public class CCMCheckin extends CCMCheck {
+
+    /**
+     * Default constructor - setup checkin command
+     */
+    public CCMCheckin() {
+        super();
+        setCcmAction(COMMAND_CHECKIN);
+        setComment("Checkin " + new Date());
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckinDefault.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckinDefault.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckinDefault.java	(revision 16139)
@@ -0,0 +1,36 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ccm;
+
+/**
+ * Performs Continuus Checkin Default task command.
+ *
+ *
+ * @ant.task name="ccmcheckintask" category="scm"
+ */
+public class CCMCheckinDefault extends CCMCheck {
+
+    public CCMCheckinDefault() {
+        super();
+        setCcmAction(COMMAND_CHECKIN);
+        setTask(DEFAULT_TASK);
+    }
+
+    public static final String DEFAULT_TASK = "default";
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckout.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckout.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCheckout.java	(revision 16139)
@@ -0,0 +1,34 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ccm;
+
+/**
+ * Performs Continuus checkout command.
+ *
+ */
+public class CCMCheckout extends CCMCheck {
+
+    /**
+     * default constructor
+     */
+    public CCMCheckout() {
+        super();
+        setCcmAction(COMMAND_CHECKOUT);
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCreateTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCreateTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMCreateTask.java	(revision 16139)
@@ -0,0 +1,330 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ccm;
+
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+
+
+/**
+ * Creates new Continuus ccm task and sets it as the default.
+ *
+ *
+ * @ant.task name="ccmcreatetask" category="scm"
+ */
+public class CCMCreateTask extends Continuus implements ExecuteStreamHandler {
+
+    private String comment = null;
+    private String platform = null;
+    private String resolver = null;
+    private String release = null;
+    private String subSystem = null;
+    private String task = null;
+
+    public CCMCreateTask() {
+        super();
+        setCcmAction(COMMAND_CREATE_TASK);
+    }
+
+
+    /**
+     * Executes the task.
+     * <p>
+     * Builds a command line to execute ccm and then calls Exec's run method
+     * to execute the command line.
+     * </p>
+     */
+    public void execute() throws BuildException {
+        Commandline commandLine = new Commandline();
+        int result = 0;
+
+        // build the command line from what we got the format
+        // as specified in the CCM.EXE help
+        commandLine.setExecutable(getCcmCommand());
+        commandLine.createArgument().setValue(getCcmAction());
+
+        checkOptions(commandLine);
+
+        result = run(commandLine, this);
+        if (Execute.isFailure(result)) {
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+
+        //create task ok, set this task as the default one
+        Commandline commandLine2 = new Commandline();
+        commandLine2.setExecutable(getCcmCommand());
+        commandLine2.createArgument().setValue(COMMAND_DEFAULT_TASK);
+        commandLine2.createArgument().setValue(getTask());
+
+        log(commandLine.describeCommand(), Project.MSG_DEBUG);
+
+        result = run(commandLine2);
+        if (result != 0) {
+            String msg = "Failed executing: " + commandLine2.toString();
+            throw new BuildException(msg, getLocation());
+        }
+
+    }
+
+
+    /**
+     * Check the command line options.
+     */
+    private void checkOptions(Commandline cmd) {
+        if (getComment() != null) {
+            cmd.createArgument().setValue(FLAG_COMMENT);
+            cmd.createArgument().setValue("\"" + getComment() + "\"");
+        }
+
+        if (getPlatform() != null) {
+            cmd.createArgument().setValue(FLAG_PLATFORM);
+            cmd.createArgument().setValue(getPlatform());
+        } // end of if ()
+
+        if (getResolver() != null) {
+            cmd.createArgument().setValue(FLAG_RESOLVER);
+            cmd.createArgument().setValue(getResolver());
+        } // end of if ()
+
+        if (getSubSystem() != null) {
+            cmd.createArgument().setValue(FLAG_SUBSYSTEM);
+            cmd.createArgument().setValue("\"" + getSubSystem() + "\"");
+        } // end of if ()
+
+        if (getRelease() != null) {
+            cmd.createArgument().setValue(FLAG_RELEASE);
+            cmd.createArgument().setValue(getRelease());
+        } // end of if ()
+    }
+
+
+    /**
+     * Get the value of comment.
+     * @return value of comment.
+     */
+    public String getComment() {
+        return comment;
+    }
+
+    /**
+     * Specifies a comment.
+     *
+     * @param v  Value to assign to comment.
+     */
+    public void setComment(String v) {
+        this.comment = v;
+    }
+
+
+    /**
+     * Get the value of platform.
+     * @return value of platform.
+     */
+    public String getPlatform() {
+        return platform;
+    }
+
+    /**
+     * Specifies the target platform.
+     *
+     * @param v  Value to assign to platform.
+     */
+    public void setPlatform(String v) {
+        this.platform = v;
+    }
+
+
+    /**
+     * Get the value of resolver.
+     * @return value of resolver.
+     */
+    public String getResolver() {
+        return resolver;
+    }
+
+    /**
+     * Specifies the resolver.
+     *
+     * @param v  Value to assign to resolver.
+     */
+    public void setResolver(String v) {
+        this.resolver = v;
+    }
+
+
+    /**
+     * Get the value of release.
+     * @return value of release.
+     */
+    public String getRelease() {
+        return release;
+    }
+
+    /**
+     * Specify the CCM release.
+     *
+     * @param v  Value to assign to release.
+     */
+    public void setRelease(String v) {
+        this.release = v;
+    }
+
+    /**
+     * Get the value of subSystem.
+     * @return value of subSystem.
+     */
+    public String getSubSystem() {
+        return subSystem;
+    }
+
+    /**
+     * Specifies the subsystem.
+     *
+     * @param v  Value to assign to subSystem.
+     */
+    public void setSubSystem(String v) {
+        this.subSystem = v;
+    }
+
+
+    /**
+     * Get the value of task.
+     * @return value of task.
+     */
+    public String getTask() {
+        return task;
+    }
+
+    /**
+     * Specifies the task number used to checkin
+     * the file (may use 'default').
+     *
+     * @param v  Value to assign to task.
+     */
+    public void setTask(String v) {
+        this.task = v;
+    }
+
+    /**
+     * /comment -- comments associated to the task
+     */
+    public static final String FLAG_COMMENT = "/synopsis";
+
+    /**
+     *  /platform flag -- target platform
+     */
+    public static final String FLAG_PLATFORM = "/plat";
+
+    /**
+     * /resolver flag
+     */
+    public static final String FLAG_RESOLVER = "/resolver";
+
+    /**
+     * /release flag
+     */
+    public static final String FLAG_RELEASE = "/release";
+
+    /**
+     * /release flag
+     */
+    public static final String FLAG_SUBSYSTEM = "/subsystem";
+
+    /**
+     *  -task flag -- associate checkout task with task
+     */
+    public static final String FLAG_TASK = "/task";
+
+
+    // implementation of org.apache.tools.ant.taskdefs.ExecuteStreamHandler interface
+
+    /**
+     *
+     * @exception java.io.IOException
+     */
+    public void start() throws IOException {
+    }
+
+    /**
+     *
+     */
+    public void stop() {
+    }
+
+    /**
+     *
+     * @param param1
+     * @exception java.io.IOException
+     */
+    public void setProcessInputStream(OutputStream param1) throws IOException {
+    }
+
+    /**
+     *
+     * @param is
+     * @exception java.io.IOException
+     */
+    public void setProcessErrorStream(InputStream is) throws IOException {
+        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+        String s = reader.readLine();
+        if (s != null) {
+            log("err " + s, Project.MSG_DEBUG);
+        } // end of if ()
+    }
+
+    /**
+     * read the output stream to retrieve the new task number.
+     * @param is InputStream
+     * @exception java.io.IOException
+     */
+    public void setProcessOutputStream(InputStream is) throws IOException {
+
+        String buffer = "";
+        try {
+            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+            buffer = reader.readLine();
+            if (buffer != null) {
+                log("buffer:" + buffer, Project.MSG_DEBUG);
+                String taskstring = buffer.substring(buffer.indexOf(' ')).trim();
+                taskstring = taskstring.substring(0, taskstring.lastIndexOf(' ')).trim();
+                setTask(taskstring);
+                log("task is " + getTask(), Project.MSG_DEBUG);
+            } // end of if ()
+        } catch (NullPointerException npe) {
+            log("error procession stream , null pointer exception", Project.MSG_ERR);
+            npe.printStackTrace();
+            throw new BuildException(npe.getClass().getName());
+        } catch (Exception e) {
+            log("error procession stream " + e.getMessage(), Project.MSG_ERR);
+            throw new BuildException(e.getMessage());
+        } // end of try-catch
+
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMReconfigure.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMReconfigure.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/CCMReconfigure.java	(revision 16139)
@@ -0,0 +1,156 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ccm;
+
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+
+/**
+ * Task allows to reconfigure a project, recursively or not
+ */
+public class CCMReconfigure extends Continuus {
+
+    private String project = null;
+    private boolean recurse = false;
+    private boolean verbose = false;
+
+    public CCMReconfigure() {
+        super();
+        setCcmAction(COMMAND_RECONFIGURE);
+    }
+
+
+    /**
+     * Executes the task.
+     * <p>
+     * Builds a command line to execute ccm and then calls Exec's run method
+     * to execute the command line.
+     * </p>
+     */
+    public void execute() throws BuildException {
+        Commandline commandLine = new Commandline();
+        int result = 0;
+
+        // build the command line from what we got the format
+        // as specified in the CCM.EXE help
+        commandLine.setExecutable(getCcmCommand());
+        commandLine.createArgument().setValue(getCcmAction());
+
+        checkOptions(commandLine);
+
+        result = run(commandLine);
+        if (Execute.isFailure(result)) {
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+
+    /**
+     * Check the command line options.
+     */
+    private void checkOptions(Commandline cmd) {
+
+        if (isRecurse() == true) {
+            cmd.createArgument().setValue(FLAG_RECURSE);
+        } // end of if ()
+
+        if (isVerbose() == true) {
+            cmd.createArgument().setValue(FLAG_VERBOSE);
+        } // end of if ()
+
+        if (getCcmProject() != null) {
+            cmd.createArgument().setValue(FLAG_PROJECT);
+            cmd.createArgument().setValue(getCcmProject());
+        }
+
+    }
+
+    /**
+     * Get the value of project.
+     * @return value of project.
+     */
+    public String getCcmProject() {
+        return project;
+    }
+
+    /**
+     * Sets the ccm project on which the operation is applied.
+     * @param v  Value to assign to project.
+     */
+    public void setCcmProject(String v) {
+        this.project = v;
+    }
+
+
+    /**
+     * Get the value of recurse.
+     * @return value of recurse.
+     */
+    public boolean isRecurse() {
+        return recurse;
+    }
+
+    /**
+     * If true, recurse on subproject (default false).
+     *
+     * @param v  Value to assign to recurse.
+     */
+    public void setRecurse(boolean v) {
+        this.recurse = v;
+    }
+
+
+    /**
+     * Get the value of verbose.
+     * @return value of verbose.
+     */
+    public boolean isVerbose() {
+        return verbose;
+    }
+
+    /**
+     * If true, do a verbose reconfigure operation (default false).
+     * @param v  Value to assign to verbose.
+     */
+    public void setVerbose(boolean v) {
+        this.verbose = v;
+    }
+
+
+    /**
+     * /recurse --
+     */
+    public static final String FLAG_RECURSE = "/recurse";
+
+    /**
+     * /recurse --
+     */
+    public static final String FLAG_VERBOSE = "/verbose";
+
+
+    /**
+     *  /project flag -- target project
+     */
+    public static final String FLAG_PROJECT = "/project";
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/Continuus.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/Continuus.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ccm/Continuus.java	(revision 16139)
@@ -0,0 +1,131 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ccm;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+
+
+/**
+ * A base class for creating tasks for executing commands on Continuus 5.1.
+ * <p>
+ * The class extends the  task as it operates by executing the ccm.exe program
+ * supplied with Continuus/Synergy. By default the task expects the ccm executable to be
+ * in the path,
+ * you can override this be specifying the ccmdir attribute.
+ * </p>
+ *
+ */
+public abstract class Continuus extends Task {
+
+    private String ccmDir = "";
+    private String ccmAction = "";
+
+    /**
+     * Get the value of ccmAction.
+     * @return value of ccmAction.
+     */
+    public String getCcmAction() {
+        return ccmAction;
+    }
+
+    /**
+     * Set the value of ccmAction.
+     * @param v  Value to assign to ccmAction.
+     * @ant.attribute ignore="true"
+     */
+    public void setCcmAction(String v) {
+        this.ccmAction = v;
+    }
+
+
+    /**
+     * Set the directory where the ccm executable is located.
+     *
+     * @param dir the directory containing the ccm executable
+     */
+    public final void setCcmDir(String dir) {
+        ccmDir = Project.translatePath(dir);
+    }
+
+    /**
+     * Builds and returns the command string to execute ccm
+     * @return String containing path to the executable
+     */
+    protected final String getCcmCommand() {
+        String toReturn = ccmDir;
+        if (!toReturn.equals("") && !toReturn.endsWith("/")) {
+            toReturn += "/";
+        }
+
+        toReturn += CCM_EXE;
+
+        return toReturn;
+    }
+
+
+    protected int run(Commandline cmd, ExecuteStreamHandler handler) {
+        try {
+            Execute exe = new Execute(handler);
+            exe.setAntRun(getProject());
+            exe.setWorkingDirectory(getProject().getBaseDir());
+            exe.setCommandline(cmd.getCommandline());
+            return exe.execute();
+        } catch (java.io.IOException e) {
+            throw new BuildException(e, getLocation());
+        }
+    }
+
+    protected int run(Commandline cmd) {
+        return run(cmd, new LogStreamHandler(this, Project.MSG_VERBOSE, Project.MSG_WARN));
+    }
+
+    /**
+     * Constant for the thing to execute
+     */
+    private static final String CCM_EXE = "ccm";
+
+    /**
+     * The 'CreateTask' command
+     */
+    public static final String COMMAND_CREATE_TASK = "create_task";
+    /**
+     * The 'Checkout' command
+     */
+    public static final String COMMAND_CHECKOUT = "co";
+    /**
+     * The 'Checkin' command
+     */
+    public static final String COMMAND_CHECKIN = "ci";
+    /**
+     * The 'Reconfigure' command
+     */
+    public static final String COMMAND_RECONFIGURE = "reconfigure";
+
+    /**
+     * The 'Reconfigure' command
+     */
+    public static final String COMMAND_DEFAULT_TASK = "default_task";
+
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckin.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckin.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckin.java	(revision 16139)
@@ -0,0 +1,342 @@
+/*
+ * Copyright  2000,2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Performs ClearCase checkin.
+ *
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ *   <tr>
+ *     <th>Attribute</th>
+ *     <th>Values</th>
+ *     <th>Required</th>
+ *   </tr>
+ *   <tr>
+ *      <td>viewpath</td>
+ *      <td>Path to the ClearCase view file or directory that the command will operate on</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>comment</td>
+ *      <td>Specify a comment. Only one of comment or cfile may be used.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>commentfile</td>
+ *      <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>nowarn</td>
+ *      <td>Suppress warning messages</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>preservetime</td>
+ *      <td>Preserve the modification time</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>keepcopy</td>
+ *      <td>Keeps a copy of the file with a .keep extension</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>identical</td>
+ *      <td>Allows the file to be checked in even if it is identical to the original</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>failonerr</td>
+ *      <td>Throw an exception if the command fails. Default is true</td>
+ *      <td>No</td>
+ *   <tr>
+ * </table>
+ *
+ */
+public class CCCheckin extends ClearCase {
+    private String mComment = null;
+    private String mCfile = null;
+    private boolean mNwarn = false;
+    private boolean mPtime = false;
+    private boolean mKeep = false;
+    private boolean mIdentical = true;
+
+    /**
+     * Executes the task.
+     * <p>
+     * Builds a command line to execute cleartool and then calls Exec's run method
+     * to execute the command line.
+     * @throws BuildException if the command fails and failonerr is set to true
+     */
+    public void execute() throws BuildException {
+        Commandline commandLine = new Commandline();
+        Project aProj = getProject();
+        int result = 0;
+
+        // Default the viewpath to basedir if it is not specified
+        if (getViewPath() == null) {
+            setViewPath(aProj.getBaseDir().getPath());
+        }
+
+        // build the command line from what we got. the format is
+        // cleartool checkin [options...] [viewpath ...]
+        // as specified in the CLEARTOOL.EXE help
+        commandLine.setExecutable(getClearToolCommand());
+        commandLine.createArgument().setValue(COMMAND_CHECKIN);
+
+        checkOptions(commandLine);
+
+        if (!getFailOnErr()) {
+            getProject().log("Ignoring any errors that occur for: "
+                    + getViewPathBasename(), Project.MSG_VERBOSE);
+        }
+        result = run(commandLine);
+        if (Execute.isFailure(result) && getFailOnErr()) {
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+
+    /**
+     * Check the command line options.
+     */
+    private void checkOptions(Commandline cmd) {
+        if (getComment() != null) {
+            // -c
+            getCommentCommand(cmd);
+        } else {
+            if (getCommentFile() != null) {
+                // -cfile
+                getCommentFileCommand(cmd);
+            } else {
+                cmd.createArgument().setValue(FLAG_NOCOMMENT);
+            }
+        }
+
+        if (getNoWarn()) {
+            // -nwarn
+            cmd.createArgument().setValue(FLAG_NOWARN);
+        }
+
+        if (getPreserveTime()) {
+            // -ptime
+            cmd.createArgument().setValue(FLAG_PRESERVETIME);
+        }
+
+        if (getKeepCopy()) {
+            // -keep
+            cmd.createArgument().setValue(FLAG_KEEPCOPY);
+        }
+
+        if (getIdentical()) {
+            // -identical
+            cmd.createArgument().setValue(FLAG_IDENTICAL);
+        }
+
+        // viewpath
+        cmd.createArgument().setValue(getViewPath());
+    }
+
+
+    /**
+     * Sets the comment string.
+     *
+     * @param comment the comment string
+     */
+    public void setComment(String comment) {
+        mComment = comment;
+    }
+
+    /**
+     * Get comment string
+     *
+     * @return String containing the comment
+     */
+    public String getComment() {
+        return mComment;
+    }
+
+    /**
+     * Specifies a file containing a comment.
+     *
+     * @param cfile the path to the comment file
+     */
+    public void setCommentFile(String cfile) {
+        mCfile = cfile;
+    }
+
+    /**
+     * Get comment file
+     *
+     * @return String containing the path to the comment file
+     */
+    public String getCommentFile() {
+        return mCfile;
+    }
+
+    /**
+     * If true, suppress warning messages.
+     *
+     * @param nwarn the status to set the flag to
+     */
+    public void setNoWarn(boolean nwarn) {
+        mNwarn = nwarn;
+    }
+
+    /**
+     * Get nowarn flag status
+     *
+     * @return boolean containing status of nwarn flag
+     */
+    public boolean getNoWarn() {
+        return mNwarn;
+    }
+
+    /**
+     * If true, preserve the modification time.
+     *
+     * @param ptime the status to set the flag to
+     */
+    public void setPreserveTime(boolean ptime) {
+        mPtime = ptime;
+    }
+
+    /**
+     * Get preservetime flag status
+     *
+     * @return boolean containing status of preservetime flag
+     */
+    public boolean getPreserveTime() {
+        return mPtime;
+    }
+
+    /**
+     * If true, keeps a copy of the file with a .keep extension.
+     *
+     * @param keep the status to set the flag to
+     */
+    public void setKeepCopy(boolean keep) {
+        mKeep = keep;
+    }
+
+    /**
+     * Get keepcopy flag status
+     *
+     * @return boolean containing status of keepcopy flag
+     */
+    public boolean getKeepCopy() {
+        return mKeep;
+    }
+
+    /**
+     * If true, allows the file to be checked in even
+     * if it is identical to the original.
+     *
+     * @param identical the status to set the flag to
+     */
+    public void setIdentical(boolean identical) {
+        mIdentical = identical;
+    }
+
+    /**
+     * Get identical flag status
+     *
+     * @return boolean containing status of identical flag
+     */
+    public boolean getIdentical() {
+        return mIdentical;
+    }
+
+
+    /**
+     * Get the 'comment' command
+     *
+     * @param cmd containing the command line string with or
+     *            without the comment flag and string appended
+     */
+    private void getCommentCommand(Commandline cmd) {
+        if (getComment() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENT);
+            cmd.createArgument().setValue(getComment());
+        }
+    }
+
+    /**
+     * Get the 'commentfile' command
+     *
+     * @param cmd containing the command line string with or
+     *            without the commentfile flag and file appended
+     */
+    private void getCommentFileCommand(Commandline cmd) {
+        if (getCommentFile() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENTFILE);
+            cmd.createArgument().setValue(getCommentFile());
+        }
+    }
+
+
+        /**
+     * -c flag -- comment to attach to the file
+     */
+    public static final String FLAG_COMMENT = "-c";
+        /**
+     * -cfile flag -- file containing a comment to attach to the file
+     */
+    public static final String FLAG_COMMENTFILE = "-cfile";
+        /**
+     * -nc flag -- no comment is specified
+     */
+    public static final String FLAG_NOCOMMENT = "-nc";
+        /**
+     * -nwarn flag -- suppresses warning messages
+     */
+    public static final String FLAG_NOWARN = "-nwarn";
+        /**
+     * -ptime flag -- preserves the modification time
+     */
+    public static final String FLAG_PRESERVETIME = "-ptime";
+        /**
+     * -keep flag -- keeps a copy of the file with a .keep extension
+     */
+    public static final String FLAG_KEEPCOPY = "-keep";
+        /**
+     * -identical flag -- allows the file to be checked in even if it is identical to the original
+     */
+    public static final String FLAG_IDENTICAL = "-identical";
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckout.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckout.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCCheckout.java	(revision 16139)
@@ -0,0 +1,515 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+
+/**
+ * Performs ClearCase checkout.
+ *
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ *   <tr>
+ *     <th>Attribute</th>
+ *     <th>Values</th>
+ *     <th>Required</th>
+ *   </tr>
+ *   <tr>
+ *      <td>viewpath</td>
+ *      <td>Path to the ClearCase view file or directory that the command will operate on</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>reserved</td>
+ *      <td>Specifies whether to check out the file as reserved or not</td>
+ *      <td>Yes</td>
+ *   <tr>
+ *   <tr>
+ *      <td>out</td>
+ *      <td>Creates a writable file under a different filename</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>nodata</td>
+ *      <td>Checks out the file but does not create an editable file containing its data</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>branch</td>
+ *      <td>Specify a branch to check out the file to</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>version</td>
+ *      <td>Allows checkout of a version other than main latest</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>nowarn</td>
+ *      <td>Suppress warning messages</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>comment</td>
+ *      <td>Specify a comment. Only one of comment or cfile may be used.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>commentfile</td>
+ *      <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>notco</td>
+ *      <td>Fail if it's already checked out to the current view. Set to false to ignore it.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>failonerr</td>
+ *      <td>Throw an exception if the command fails. Default is true</td>
+ *      <td>No</td>
+ *   <tr>
+ * </table>
+ *
+ */
+public class CCCheckout extends ClearCase {
+    private boolean mReserved = true;
+    private String mOut = null;
+    private boolean mNdata = false;
+    private String mBranch = null;
+    private boolean mVersion = false;
+    private boolean mNwarn = false;
+    private String mComment = null;
+    private String mCfile = null;
+    private boolean mNotco = true;
+
+    /**
+     * Executes the task.
+     * <p>
+     * Builds a command line to execute cleartool and then calls Exec's run method
+     * to execute the command line.
+     * @throws BuildException if the command fails and failonerr is set to true
+     */
+    public void execute() throws BuildException {
+        Commandline commandLine = new Commandline();
+        Project aProj = getProject();
+        int result = 0;
+
+        // Default the viewpath to basedir if it is not specified
+        if (getViewPath() == null) {
+            setViewPath(aProj.getBaseDir().getPath());
+        }
+
+        // build the command line from what we got the format is
+        // cleartool checkout [options...] [viewpath ...]
+        // as specified in the CLEARTOOL.EXE help
+        commandLine.setExecutable(getClearToolCommand());
+        commandLine.createArgument().setValue(COMMAND_CHECKOUT);
+
+        checkOptions(commandLine);
+        /*
+         * If configured to not care about whether the element is
+         * already checked out to the current view.
+         * Then check to see if it is checked out.
+         */
+        if (!getNotco() && lsCheckout()) {
+            getProject().log("Already checked out in this view: "
+                    + getViewPathBasename(), Project.MSG_VERBOSE);
+            return;
+        }
+        if (!getFailOnErr()) {
+            getProject().log("Ignoring any errors that occur for: "
+                    + getViewPathBasename(), Project.MSG_VERBOSE);
+        }
+        result = run(commandLine);
+        if (Execute.isFailure(result) && getFailOnErr()) {
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+    /**
+     * Check to see if the element is checked out in the current view.
+     */
+    private boolean lsCheckout() {
+        Commandline cmdl = new Commandline();
+        String result;
+
+        // build the command line from what we got the format is
+        // cleartool lsco [options...] [viewpath ...]
+        // as specified in the CLEARTOOL.EXE help
+        cmdl.setExecutable(getClearToolCommand());
+        cmdl.createArgument().setValue(COMMAND_LSCO);
+        cmdl.createArgument().setValue("-cview");
+        cmdl.createArgument().setValue("-short");
+        cmdl.createArgument().setValue("-d");
+        // viewpath
+        cmdl.createArgument().setValue(getViewPath());
+
+        result = runS(cmdl);
+
+        // System.out.println( "lsCheckout: " + result );
+
+        return (result != null && result.length() > 0) ? true : false;
+    }
+    /**
+     * Check the command line options.
+     */
+    private void checkOptions(Commandline cmd) {
+        // ClearCase items
+        if (getReserved()) {
+            // -reserved
+            cmd.createArgument().setValue(FLAG_RESERVED);
+        } else {
+            // -unreserved
+            cmd.createArgument().setValue(FLAG_UNRESERVED);
+        }
+
+        if (getOut() != null) {
+            // -out
+            getOutCommand(cmd);
+        } else {
+            if (getNoData()) {
+                // -ndata
+                cmd.createArgument().setValue(FLAG_NODATA);
+            }
+
+        }
+
+        if (getBranch() != null) {
+            // -branch
+            getBranchCommand(cmd);
+        } else {
+            if (getVersion()) {
+                // -version
+                cmd.createArgument().setValue(FLAG_VERSION);
+            }
+
+        }
+
+        if (getNoWarn()) {
+            // -nwarn
+            cmd.createArgument().setValue(FLAG_NOWARN);
+        }
+
+        if (getComment() != null) {
+            // -c
+            getCommentCommand(cmd);
+        } else {
+            if (getCommentFile() != null) {
+                // -cfile
+                getCommentFileCommand(cmd);
+            } else {
+                cmd.createArgument().setValue(FLAG_NOCOMMENT);
+            }
+        }
+
+        // viewpath
+        cmd.createArgument().setValue(getViewPath());
+
+        // Print out info about the notco option
+        // System.out.println( "Notco: " + (getNotco() ? "yes" : "no") );
+    }
+
+    /**
+     * If true, checks out the file as reserved.
+     *
+     * @param reserved the status to set the flag to
+     */
+    public void setReserved(boolean reserved) {
+        mReserved = reserved;
+    }
+
+    /**
+     * Get reserved flag status
+     *
+     * @return boolean containing status of reserved flag
+     */
+    public boolean getReserved() {
+        return mReserved;
+    }
+
+    /**
+     * If true, checkout fails if the element is already checked out to the current view.
+     *
+     * @param notco the status to set the flag to
+     * @since ant 1.6.1
+     */
+    public void setNotco(boolean notco) {
+        mNotco = notco;
+    }
+
+    /**
+     * Get notco flag status
+     *
+     * @return boolean containing status of notco flag
+     * @since ant 1.6.1
+     */
+    public boolean getNotco() {
+        return mNotco;
+    }
+
+
+    /**
+     * Creates a writable file under a different filename.
+     *
+     * @param outf the path to the out file
+     */
+    public void setOut(String outf) {
+        mOut = outf;
+    }
+
+    /**
+     * Get out file
+     *
+     * @return String containing the path to the out file
+     */
+    public String getOut() {
+        return mOut;
+    }
+
+    /**
+     * If true, checks out the file but does not create an
+     * editable file containing its data.
+     *
+     * @param ndata the status to set the flag to
+     */
+    public void setNoData(boolean ndata) {
+        mNdata = ndata;
+    }
+
+    /**
+     * Get nodata flag status
+     *
+     * @return boolean containing status of ndata flag
+     */
+    public boolean getNoData() {
+        return mNdata;
+    }
+
+    /**
+     * Specify a branch to check out the file to.
+     *
+     * @param branch the name of the branch
+     */
+    public void setBranch(String branch) {
+        mBranch = branch;
+    }
+
+    /**
+     * Get branch name
+     *
+     * @return String containing the name of the branch
+     */
+    public String getBranch() {
+        return mBranch;
+    }
+
+    /**
+     * If true, allows checkout of a version other than main latest.
+     *
+     * @param version the status to set the flag to
+     */
+    public void setVersion(boolean version) {
+        mVersion = version;
+    }
+
+    /**
+     * Get version flag status
+     *
+     * @return boolean containing status of version flag
+     */
+    public boolean getVersion() {
+        return mVersion;
+    }
+
+    /**
+     * If true, warning messages are suppressed.
+     *
+     * @param nwarn the status to set the flag to
+     */
+    public void setNoWarn(boolean nwarn) {
+        mNwarn = nwarn;
+    }
+
+    /**
+     * Get nowarn flag status
+     *
+     * @return boolean containing status of nwarn flag
+     */
+    public boolean getNoWarn() {
+        return mNwarn;
+    }
+
+    /**
+     * Sets the comment string.
+     *
+     * @param comment the comment string
+     */
+    public void setComment(String comment) {
+        mComment = comment;
+    }
+
+    /**
+     * Get comment string
+     *
+     * @return String containing the comment
+     */
+    public String getComment() {
+        return mComment;
+    }
+
+    /**
+     * Specifies a file containing a comment.
+     *
+     * @param cfile the path to the comment file
+     */
+    public void setCommentFile(String cfile) {
+        mCfile = cfile;
+    }
+
+    /**
+     * Get comment file
+     *
+     * @return String containing the path to the comment file
+     */
+    public String getCommentFile() {
+        return mCfile;
+    }
+
+    /**
+     * Get the 'out' command
+     *
+     * @param cmd containing the command line string with or
+     *                    without the out flag and path appended
+     */
+    private void getOutCommand(Commandline cmd) {
+        if (getOut() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_OUT);
+            cmd.createArgument().setValue(getOut());
+        }
+    }
+
+    /**
+     * Get the 'branch' command
+     *
+     * @param cmd containing the command line string with or
+                          without the branch flag and name appended
+     */
+    private void getBranchCommand(Commandline cmd) {
+        if (getBranch() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_BRANCH);
+            cmd.createArgument().setValue(getBranch());
+        }
+    }
+
+
+    /**
+     * Get the 'comment' command
+     *
+     * @param cmd containing the command line string with or
+     *                    without the comment flag and string appended
+     */
+    private void getCommentCommand(Commandline cmd) {
+        if (getComment() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENT);
+            cmd.createArgument().setValue(getComment());
+        }
+    }
+
+    /**
+     * Get the 'cfile' command
+     *
+     * @param cmd containing the command line string with or
+     *                    without the cfile flag and file appended
+     */
+    private void getCommentFileCommand(Commandline cmd) {
+        if (getCommentFile() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENTFILE);
+            cmd.createArgument().setValue(getCommentFile());
+        }
+    }
+
+        /**
+     *  -reserved flag -- check out the file as reserved
+     */
+    public static final String FLAG_RESERVED = "-reserved";
+        /**
+     *  -reserved flag -- check out the file as unreserved
+     */
+    public static final String FLAG_UNRESERVED = "-unreserved";
+        /**
+     * -out flag -- create a writable file under a different filename
+     */
+    public static final String FLAG_OUT = "-out";
+        /**
+     * -ndata flag -- checks out the file but does not create an editable file containing its data
+     */
+    public static final String FLAG_NODATA = "-ndata";
+        /**
+     * -branch flag -- checks out the file on a specified branch
+     */
+    public static final String FLAG_BRANCH = "-branch";
+        /**
+     * -version flag -- allows checkout of a version that is not main latest
+     */
+    public static final String FLAG_VERSION = "-version";
+        /**
+     * -nwarn flag -- suppresses warning messages
+     */
+    public static final String FLAG_NOWARN = "-nwarn";
+        /**
+     * -c flag -- comment to attach to the file
+     */
+    public static final String FLAG_COMMENT = "-c";
+        /**
+     * -cfile flag -- file containing a comment to attach to the file
+     */
+    public static final String FLAG_COMMENTFILE = "-cfile";
+        /**
+     * -nc flag -- no comment is specified
+     */
+    public static final String FLAG_NOCOMMENT = "-nc";
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCLock.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCLock.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCLock.java	(revision 16139)
@@ -0,0 +1,377 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+
+/**
+ * TODO:
+ * comment field doesn't include all options yet
+ */
+
+
+
+/**
+ * Performs a ClearCase Lock command.
+ *
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ *   <tr>
+ *     <th>Attribute</th>
+ *     <th>Values</th>
+ *     <th>Required</th>
+ *   </tr>
+ *   <tr>
+ *      <td>replace</td>
+ *      <td>Specifies replacing an existing lock</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>nusers</td>
+ *      <td>Specifies user(s) who can still modify the object/pname</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>obsolete</td>
+ *      <td>Specifies that the object/pname should be marked obsolete</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>comment</td>
+ *      <td>Specifies how to populate comments fields</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>pname</td>
+ *      <td>Specifies the pathname to be locked.</td>
+ *      <td>No</td>
+ *   <tr>
+ *      <td>objselect</td>
+ *      <td>This variable is obsolete. Should use <i>objsel</i> instead.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>objsel</td>
+ *      <td>Specifies the object(s) to be unlocked.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>failonerr</td>
+ *      <td>Throw an exception if the command fails. Default is true</td>
+ *      <td>No</td>
+ *   <tr>
+ * </table>
+ *
+ */
+public class CCLock extends ClearCase {
+    private boolean mReplace = false;
+    private boolean mObsolete = false;
+    private String mComment = null;
+    private String mNusers = null;
+    private String mPname = null;
+    private String mObjselect = null;
+
+    /**
+     * Executes the task.
+     * <p>
+     * Builds a command line to execute cleartool and then calls Exec's run method
+     * to execute the command line.
+     * @throws BuildException if the command fails and failonerr is set to true
+     */
+    public void execute() throws BuildException {
+        Commandline commandLine = new Commandline();
+        Project aProj = getProject();
+        int result = 0;
+
+        // Default the viewpath to basedir if it is not specified
+        if (getViewPath() == null) {
+            setViewPath(aProj.getBaseDir().getPath());
+        }
+
+        // build the command line from what we got the format is
+        // cleartool lock [options...]
+        // as specified in the CLEARTOOL.EXE help
+        commandLine.setExecutable(getClearToolCommand());
+        commandLine.createArgument().setValue(COMMAND_LOCK);
+
+        // Check the command line options
+        checkOptions(commandLine);
+
+        // For debugging
+        // System.out.println(commandLine.toString());
+
+        if (!getFailOnErr()) {
+            getProject().log("Ignoring any errors that occur for: "
+                    + getOpType(), Project.MSG_VERBOSE);
+        }
+        result = run(commandLine);
+        if (Execute.isFailure(result) && getFailOnErr()) {
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+    /**
+     * Check the command line options.
+     */
+private void checkOptions(Commandline cmd) {
+        // ClearCase items
+        if (getReplace()) {
+            // -replace
+            cmd.createArgument().setValue(FLAG_REPLACE);
+        }
+        if (getObsolete()) {
+            // -obsolete
+            cmd.createArgument().setValue(FLAG_OBSOLETE);
+        } else {
+            getNusersCommand(cmd);
+        }
+        getCommentCommand(cmd);
+
+        if (getObjselect() == null && getPname() == null) {
+            throw new BuildException("Should select either an element "
+            + "(pname) or an object (objselect)");
+        }
+        getPnameCommand(cmd);
+        // object selector
+        if (getObjselect() != null) {
+            cmd.createArgument().setValue(getObjselect());
+        }
+}
+
+    /**
+     * If true, replace an existing lock.
+     *
+     * @param replace the status to set the flag to
+     */
+    public void setReplace(boolean replace) {
+        mReplace = replace;
+    }
+
+    /**
+     * Get replace flag status
+     *
+     * @return boolean containing status of replace flag
+     */
+    public boolean getReplace() {
+        return mReplace;
+    }
+
+    /**
+     * If true, mark object as obsolete.
+     *
+     * @param obsolete the status to set the flag to
+     */
+    public void setObsolete(boolean obsolete) {
+        mObsolete = obsolete;
+    }
+
+    /**
+     * Get obsolete flag status
+     *
+     * @return boolean containing status of obsolete flag
+     */
+    public boolean getObsolete() {
+        return mObsolete;
+    }
+
+    /**
+     * Sets the users who may continue to
+     * edit the object while it is locked.
+     *
+     * @param nusers users excluded from lock
+     */
+    public void setNusers(String nusers) {
+        mNusers = nusers;
+    }
+
+    /**
+     * Get nusers list
+     *
+     * @return String containing the list of users excluded from lock
+     */
+    public String getNusers() {
+        return mNusers;
+    }
+
+    /**
+     * Sets how comments should be written
+     * for the event record(s)
+     *
+     * @param comment comment method to use
+     */
+    public void setComment(String comment) {
+        mComment = comment;
+    }
+
+    /**
+     * Get comment method
+     *
+     * @return String containing the desired comment method
+     */
+    public String getComment() {
+        return mComment;
+    }
+
+    /**
+     * Sets the pathname to be locked
+     *
+     * @param pname pathname to be locked
+     */
+    public void setPname(String pname) {
+        mPname = pname;
+    }
+
+    /**
+     * Get the pathname to be locked
+     *
+     * @return String containing the pathname to be locked
+     */
+    public String getPname() {
+        return mPname;
+    }
+
+    /**
+     * Sets the object(s) to be locked
+     *
+     * @param objsel objects to be locked
+     * @since ant 1.6.1
+     */
+    public void setObjSel(String objsel) {
+        mObjselect = objsel;
+    }
+
+    /**
+     * Sets the object(s) to be locked
+     *
+     * @param objselect objects to be locked
+     */
+    public void setObjselect(String objselect) {
+        mObjselect = objselect;
+    }
+
+    /**
+     * Get list of objects to be locked
+     *
+     * @return String containing the objects to be locked
+     */
+    public String getObjselect() {
+        return mObjselect;
+    }
+
+    /**
+     * Get the 'nusers' command
+     *
+     * @param cmd containing the command line string with or
+     *            without the nusers flag and value appended
+     */
+    private void getNusersCommand(Commandline cmd) {
+        if (getNusers() == null) {
+            return;
+        } else {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_NUSERS);
+            cmd.createArgument().setValue(getNusers());
+        }
+    }
+
+    /**
+     * Get the 'comment' command
+     *
+     * @param cmd containing the command line string with or without the
+     *            comment flag and value appended
+     */
+    private void getCommentCommand(Commandline cmd) {
+        if (getComment() == null) {
+            return;
+        } else {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENT);
+            cmd.createArgument().setValue(getComment());
+        }
+    }
+
+    /**
+     * Get the 'pname' command
+     *
+     * @param cmd containing the command line string with or
+     *            without the pname flag and value appended
+     */
+    private void getPnameCommand(Commandline cmd) {
+        if (getPname() == null) {
+            return;
+        } else {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_PNAME);
+            cmd.createArgument().setValue(getPname());
+        }
+    }
+
+    /**
+     * Return which object/pname is being operated on
+     *
+     * @return String containing the object/pname being worked on
+     */
+    private String getOpType() {
+
+        if (getPname() != null) {
+            return getPname();
+        } else {
+            return getObjselect();
+        }
+    }
+
+    /**
+     *  -replace flag -- replace existing lock on object(s)
+     */
+    public static final String FLAG_REPLACE = "-replace";
+    /**
+     * -nusers flag -- list of users to exclude from lock
+     */
+    public static final String FLAG_NUSERS = "-nusers";
+    /**
+     * -obsolete flag -- mark locked object as obsolete
+     */
+    public static final String FLAG_OBSOLETE = "-obsolete";
+    /**
+     * -comment flag -- method to use for commenting events
+     */
+    public static final String FLAG_COMMENT = "-comment";
+    /**
+     * -pname flag -- pathname to lock
+     */
+    public static final String FLAG_PNAME = "-pname";
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkattr.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkattr.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkattr.java	(revision 16139)
@@ -0,0 +1,424 @@
+/*
+ * Copyright  2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.taskdefs.condition.Os;
+
+/**
+ * Task to perform mkattr command to ClearCase.
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ *   <tr>
+ *     <th>Attribute</th>
+ *     <th>Values</th>
+ *     <th>Required</th>
+ *   </tr>
+ *   <tr>
+ *      <td>viewpath</td>
+ *      <td>Path to the ClearCase view file or directory that the command will operate on</td>
+ *      <td>Yes</td>
+ *   <tr>
+ *   <tr>
+ *      <td>replace</td>
+ *      <td>Replace the value of the attribute if it already exists</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>recurse</td>
+ *      <td>Process each subdirectory under viewpath</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>version</td>
+ *      <td>Identify a specific version to attach the attribute to</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>typename</td>
+ *      <td>Name of the attribute type</td>
+ *      <td>Yes</td>
+ *   <tr>
+ *   <tr>
+ *      <td>typevalue</td>
+ *      <td>Value to attach to the attribute type</td>
+ *      <td>Yes</td>
+ *   <tr>
+ *   <tr>
+ *      <td>comment</td>
+ *      <td>Specify a comment. Only one of comment or cfile may be used.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>commentfile</td>
+ *      <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>failonerr</td>
+ *      <td>Throw an exception if the command fails. Default is true</td>
+ *      <td>No</td>
+ *   <tr>
+ * </table>
+ *
+ */
+public class CCMkattr extends ClearCase {
+    private boolean mReplace = false;
+    private boolean mRecurse = false;
+    private String mVersion = null;
+    private String mTypeName = null;
+    private String mTypeValue = null;
+    private String mComment = null;
+    private String mCfile = null;
+
+    /**
+     * Executes the task.
+     * <p>
+     * Builds a command line to execute cleartool and then calls Exec's run method
+     * to execute the command line.
+     * @throws BuildException if the command fails and failonerr is set to true
+     */
+    public void execute() throws BuildException {
+        Commandline commandLine = new Commandline();
+        Project aProj = getProject();
+        int result = 0;
+
+        // Check for required attributes
+        if (getTypeName() == null) {
+            throw new BuildException("Required attribute TypeName not specified");
+        }
+        if (getTypeValue() == null) {
+            throw new BuildException("Required attribute TypeValue not specified");
+        }
+        // Default the viewpath to basedir if it is not specified
+        if (getViewPath() == null) {
+            setViewPath(aProj.getBaseDir().getPath());
+        }
+
+        // build the command line from what we got. the format is
+        // cleartool mkattr [options...] [viewpath ...]
+        // as specified in the CLEARTOOL help
+        commandLine.setExecutable(getClearToolCommand());
+        commandLine.createArgument().setValue(COMMAND_MKATTR);
+
+        checkOptions(commandLine);
+
+        if (!getFailOnErr()) {
+            getProject().log("Ignoring any errors that occur for: "
+                    + getViewPathBasename(), Project.MSG_VERBOSE);
+        }
+
+        // For debugging
+        // System.out.println(commandLine.toString());
+
+        result = run(commandLine);
+        if (Execute.isFailure(result) && getFailOnErr()) {
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+
+    /**
+     * Check the command line options.
+     */
+    private void checkOptions(Commandline cmd) {
+        if (getReplace()) {
+            // -replace
+            cmd.createArgument().setValue(FLAG_REPLACE);
+        }
+
+        if (getRecurse()) {
+            // -recurse
+            cmd.createArgument().setValue(FLAG_RECURSE);
+        }
+
+        if (getVersion() != null) {
+            // -version
+            getVersionCommand(cmd);
+        }
+
+        if (getComment() != null) {
+            // -c
+            getCommentCommand(cmd);
+        } else {
+            if (getCommentFile() != null) {
+                // -cfile
+                getCommentFileCommand(cmd);
+            } else {
+                cmd.createArgument().setValue(FLAG_NOCOMMENT);
+            }
+        }
+
+        if (getTypeName() != null) {
+            // type
+            getTypeCommand(cmd);
+        }
+        if (getTypeValue() != null) {
+            // type value
+            getTypeValueCommand(cmd);
+        }
+        // viewpath
+        cmd.createArgument().setValue(getViewPath());
+    }
+
+
+    /**
+     * Set the replace flag
+     *
+     * @param replace the status to set the flag to
+     */
+    public void setReplace(boolean replace) {
+        mReplace = replace;
+    }
+
+    /**
+     * Get replace flag status
+     *
+     * @return boolean containing status of replace flag
+     */
+    public boolean getReplace() {
+        return mReplace;
+    }
+
+    /**
+     * Set recurse flag
+     *
+     * @param recurse the status to set the flag to
+     */
+    public void setRecurse(boolean recurse) {
+        mRecurse = recurse;
+    }
+
+    /**
+     * Get recurse flag status
+     *
+     * @return boolean containing status of recurse flag
+     */
+    public boolean getRecurse() {
+        return mRecurse;
+    }
+
+    /**
+     * Set the version flag
+     *
+     * @param version the status to set the flag to
+     */
+    public void setVersion(String version) {
+        mVersion = version;
+    }
+
+    /**
+     * Get version flag status
+     *
+     * @return boolean containing status of version flag
+     */
+    public String getVersion() {
+        return mVersion;
+    }
+
+    /**
+     * Set comment string
+     *
+     * @param comment the comment string
+     */
+    public void setComment(String comment) {
+        mComment = comment;
+    }
+
+    /**
+     * Get comment string
+     *
+     * @return String containing the comment
+     */
+    public String getComment() {
+        return mComment;
+    }
+
+    /**
+     * Set comment file
+     *
+     * @param cfile the path to the comment file
+     */
+    public void setCommentFile(String cfile) {
+        mCfile = cfile;
+    }
+
+    /**
+     * Get comment file
+     *
+     * @return String containing the path to the comment file
+     */
+    public String getCommentFile() {
+        return mCfile;
+    }
+
+    /**
+     * Set the attribute type-name
+     *
+     * @param tn the type name
+     */
+    public void setTypeName(String tn) {
+        mTypeName = tn;
+    }
+
+    /**
+     * Get attribute type-name
+     *
+     * @return String containing type name
+     */
+    public String getTypeName() {
+        return mTypeName;
+    }
+
+    /**
+     * Set the attribute type-value
+     *
+     * @param tv the type value
+     */
+    public void setTypeValue(String tv) {
+        mTypeValue = tv;
+    }
+
+    /**
+     * Get the attribute type-value
+     *
+     * @return String containing type value
+     */
+    public String getTypeValue() {
+        return mTypeValue;
+    }
+
+
+    /**
+     * Get the 'version' command
+     *
+     * @param cmd CommandLine containing the command line string with or
+     *                    without the version flag and string appended
+     */
+    private void getVersionCommand(Commandline cmd) {
+        if (getVersion() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_VERSION);
+            cmd.createArgument().setValue(getVersion());
+        }
+    }
+
+    /**
+     * Get the 'comment' command
+     *
+     * @param cmd containing the command line string with or
+     *        without the comment flag and string appended
+     */
+    private void getCommentCommand(Commandline cmd) {
+        if (getComment() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENT);
+            cmd.createArgument().setValue(getComment());
+        }
+    }
+
+    /**
+     * Get the 'commentfile' command
+     *
+     * @param cmd         containing the command line string with or
+     *                    without the commentfile flag and file appended
+     */
+    private void getCommentFileCommand(Commandline cmd) {
+        if (getCommentFile() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENTFILE);
+            cmd.createArgument().setValue(getCommentFile());
+        }
+    }
+
+    /**
+     * Get the attribute type-name
+     *
+     * @param cmd containing the command line string with or
+     *        without the type-name
+     */
+    private void getTypeCommand(Commandline cmd) {
+        String typenm = getTypeName();
+
+        if (typenm != null) {
+            cmd.createArgument().setValue(typenm);
+        }
+    }
+
+    /**
+     * Get the attribute type-value
+     *
+     * @param cmd containing the command line string with or
+     *        without the type-value
+     */
+    private void getTypeValueCommand(Commandline cmd) {
+        String typevl = getTypeValue();
+
+        if (typevl != null) {
+            if (Os.isFamily("windows")) {
+                typevl = "\\\"" + typevl + "\\\""; // Windows quoting of the value
+            } else {
+                typevl = "\"" + typevl + "\"";
+            }
+            cmd.createArgument().setValue(typevl);
+        }
+    }
+
+    /**
+     * -replace flag -- replace the existing value of the attribute
+     */
+    public static final String FLAG_REPLACE = "-replace";
+    /**
+     * -recurse flag -- process all subdirectories
+     */
+    public static final String FLAG_RECURSE = "-recurse";
+    /**
+     * -version flag -- attach attribute to specified version
+     */
+    public static final String FLAG_VERSION = "-version";
+    /**
+     * -c flag -- comment to attach to the element
+     */
+    public static final String FLAG_COMMENT = "-c";
+    /**
+     * -cfile flag -- file containing a comment to attach to the file
+     */
+    public static final String FLAG_COMMENTFILE = "-cfile";
+    /**
+     * -nc flag -- no comment is specified
+     */
+    public static final String FLAG_NOCOMMENT = "-nc";
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkbl.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkbl.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkbl.java	(revision 16139)
@@ -0,0 +1,364 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Task to CreateBaseline command to ClearCase.
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ *   <tr>
+ *     <th>Attribute</th>
+ *     <th>Values</th>
+ *     <th>Required</th>
+ *   </tr>
+ *   <tr>
+ *      <td>comment</td>
+ *      <td>Specify a comment. Only one of comment or cfile may be
+used.</td>
+ *      <td>No</td>
+ *   </tr>
+ *   <tr>
+ *      <td>commentfile</td>
+ *      <td>Specify a file containing a comment. Only one of comment or
+cfile may be used.</td>
+ *      <td>No</td>
+ *   </tr>
+ *   <tr>
+ *      <td>baselinerootname</td>
+ *      <td>Specify the name to be associated with the baseline.</td>
+ *      <td>Yes</td>
+ *   </tr>
+ *   <tr>
+ *      <td>nowarn</td>
+ *      <td>Suppress warning messages</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>identical</td>
+ *      <td>Allows the baseline to be created even if it is identical to the
+previous baseline.</td>
+ *      <td>No</td>
+ *   </tr>
+ *   <tr>
+ *      <td>full</td>
+ *      <td>Creates a full baseline.</td>
+ *      <td>No</td>
+ *   </tr>
+ *   <tr>
+ *      <td>nlabel</td>
+ *      <td>Allows the baseline to be created without a label.</td>
+ *      <td>No</td>
+ *   </tr>
+ *   <tr>
+ *      <td>failonerr</td>
+ *      <td>Throw an exception if the command fails. Default is true</td>
+ *      <td>No</td>
+ *   <tr>
+ * </table>
+ *
+ */
+public class CCMkbl extends ClearCase {
+    private String mComment = null;
+    private String mCfile = null;
+    private String mBaselineRootName = null;
+    private boolean mNwarn = false;
+    private boolean mIdentical = true;
+    private boolean mFull = false;
+    private boolean mNlabel = false;
+
+
+    /**
+     * Executes the task.
+     * <p>
+     * Builds a command line to execute cleartool and then calls Exec's run method
+     * to execute the command line.
+     * @throws BuildException if the command fails and failonerr is set to true
+     */
+    public void execute() throws BuildException {
+        Commandline commandLine = new Commandline();
+        Project aProj = getProject();
+        int result = 0;
+
+        // Default the viewpath to basedir if it is not specified
+        if (getViewPath() == null) {
+            setViewPath(aProj.getBaseDir().getPath());
+        }
+
+        // build the command line from what we got. the format is
+        // cleartool checkin [options...] [viewpath ...]
+        // as specified in the CLEARTOOL.EXE help
+        commandLine.setExecutable(getClearToolCommand());
+        commandLine.createArgument().setValue(COMMAND_MKBL);
+
+        checkOptions(commandLine);
+
+        if (!getFailOnErr()) {
+            getProject().log("Ignoring any errors that occur for: "
+                    + getBaselineRootName(), Project.MSG_VERBOSE);
+        }
+        result = run(commandLine);
+        if (Execute.isFailure(result) && getFailOnErr()) {
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+
+    /**
+     * Check the command line options.
+     */
+    private void checkOptions(Commandline cmd) {
+        if (getComment() != null) {
+            // -c
+            getCommentCommand(cmd);
+        } else {
+            if (getCommentFile() != null) {
+                // -cfile
+                getCommentFileCommand(cmd);
+            } else {
+                cmd.createArgument().setValue(FLAG_NOCOMMENT);
+            }
+        }
+
+        if (getIdentical()) {
+            // -identical
+            cmd.createArgument().setValue(FLAG_IDENTICAL);
+        }
+
+       if (getFull()) {
+           // -full
+           cmd.createArgument().setValue(FLAG_FULL);
+       } else {
+           // -incremental
+           cmd.createArgument().setValue(FLAG_INCREMENTAL);
+       }
+
+       if (getNlabel()) {
+           // -nlabel
+           cmd.createArgument().setValue(FLAG_NLABEL);
+       }
+
+       // baseline_root_name
+        cmd.createArgument().setValue(getBaselineRootName());
+
+    }
+
+
+    /**
+     * Set comment string
+     *
+     * @param comment the comment string
+     */
+    public void setComment(String comment) {
+        mComment = comment;
+    }
+
+    /**
+     * Get comment string
+     *
+     * @return String containing the comment
+     */
+    public String getComment() {
+        return mComment;
+    }
+
+    /**
+     * Set comment file
+     *
+     * @param cfile the path to the comment file
+     */
+    public void setCommentFile(String cfile) {
+        mCfile = cfile;
+    }
+
+    /**
+     * Get comment file
+     *
+     * @return String containing the path to the comment file
+     */
+    public String getCommentFile() {
+        return mCfile;
+    }
+
+    /**
+     * Set baseline_root_name
+     *
+     * @param baselineRootName the name of the baseline
+     */
+    public void setBaselineRootName(String baselineRootName) {
+        mBaselineRootName = baselineRootName;
+    }
+
+    /**
+     * Get baseline_root_name
+     *
+     * @return String containing the name of the baseline
+     */
+    public String getBaselineRootName() {
+        return mBaselineRootName;
+    }
+
+    /**
+
+    /**
+     * Set the nowarn flag
+     *
+     * @param nwarn the status to set the flag to
+     */
+    public void setNoWarn(boolean nwarn) {
+        mNwarn = nwarn;
+    }
+
+    /**
+     * Get nowarn flag status
+     *
+     * @return boolean containing status of nwarn flag
+     */
+    public boolean getNoWarn() {
+        return mNwarn;
+    }
+
+    /**
+     * Set the identical flag
+     *
+     * @param identical the status to set the flag to
+     */
+    public void setIdentical(boolean identical) {
+        mIdentical = identical;
+    }
+
+    /**
+     * Get identical flag status
+     *
+     * @return boolean containing status of identical flag
+     */
+    public boolean getIdentical() {
+        return mIdentical;
+    }
+
+    /**
+     * Set the full flag
+     *
+     * @param full the status to set the flag to
+     */
+    public void setFull(boolean full) {
+        mFull = full;
+    }
+
+    /**
+     * Get full flag status
+     *
+     * @return boolean containing status of full flag
+     */
+    public boolean getFull() {
+        return mFull;
+    }
+
+    /**
+     * Set the nlabel flag
+     *
+     * @param nlabel the status to set the flag to
+     */
+    public void setNlabel(boolean nlabel) {
+        mNlabel = nlabel;
+    }
+
+    /**
+     * Get nlabel status
+     *
+     * @return boolean containing status of nlabel flag
+     */
+    public boolean getNlabel() {
+        return mNlabel;
+    }
+
+
+    /**
+     * Get the 'comment' command
+     *
+     * @param cmd containing the command line string with or
+     *                    without the comment flag and string appended
+     */
+    private void getCommentCommand(Commandline cmd) {
+        if (getComment() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENT);
+            cmd.createArgument().setValue(getComment());
+        }
+    }
+
+    /**
+     * Get the 'commentfile' command
+     *
+     * @param cmd CommandLine containing the command line string with or
+     *                    without the commentfile flag and file appended
+     */
+    private void getCommentFileCommand(Commandline cmd) {
+        if (getCommentFile() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENTFILE);
+            cmd.createArgument().setValue(getCommentFile());
+        }
+    }
+
+
+        /**
+     * -c flag -- comment to attach to the file
+     */
+    public static final String FLAG_COMMENT = "-c";
+        /**
+     * -cfile flag -- file containing a comment to attach to the file
+     */
+    public static final String FLAG_COMMENTFILE = "-cfile";
+        /**
+     * -nc flag -- no comment is specified
+     */
+    public static final String FLAG_NOCOMMENT = "-nc";
+        /**
+     * -identical flag -- allows the file to be checked in even if it is identical to the original
+     */
+    public static final String FLAG_IDENTICAL = "-identical";
+       /**
+     * -incremental flag -- baseline to be created is incremental
+     */
+    public static final String FLAG_INCREMENTAL = "-incremental";
+       /**
+     * -full flag -- baseline to be created is full
+     */
+    public static final String FLAG_FULL = "-full";
+       /**
+     * -nlabel -- baseline to be created without a label
+     */
+    public static final String FLAG_NLABEL = "-nlabel";
+
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkdir.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkdir.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkdir.java	(revision 16139)
@@ -0,0 +1,236 @@
+/*
+ * Copyright  2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Performs ClearCase mkdir.
+ *
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ *   <tr>
+ *     <th>Attribute</th>
+ *     <th>Values</th>
+ *     <th>Required</th>
+ *   </tr>
+ *   <tr>
+ *      <td>viewpath</td>
+ *      <td>Path to the ClearCase view directory that the command will operate on</td>
+ *      <td>Yes</td>
+ *   <tr>
+ *   <tr>
+ *      <td>comment</td>
+ *      <td>Specify a comment. Only one of comment or cfile may be used.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>commentfile</td>
+ *      <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>nocheckout</td>
+ *      <td>Do not checkout after element creation</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>failonerr</td>
+ *      <td>Throw an exception if the command fails. Default is true</td>
+ *      <td>No</td>
+ *   <tr>
+ * </table>
+ *
+ */
+public class CCMkdir extends ClearCase {
+    private String  mComment = null;
+    private String  mCfile   = null;
+    private boolean mNoco    = false;
+
+    /**
+     * Executes the task.
+     * <p>
+     * Builds a command line to execute cleartool and then calls Exec's run method
+     * to execute the command line.
+     * @throws BuildException if the command fails and failonerr is set to true
+     */
+    public void execute() throws BuildException {
+        Commandline commandLine = new Commandline();
+        Project aProj = getProject();
+        int result = 0;
+
+        // Default the viewpath to basedir if it is not specified
+        if (getViewPath() == null) {
+            setViewPath(aProj.getBaseDir().getPath());
+        }
+
+        // build the command line from what we got. the format is
+        // cleartool mkelem [options...] [viewpath ...]
+        // as specified in the CLEARTOOL.EXE help
+        commandLine.setExecutable(getClearToolCommand());
+        commandLine.createArgument().setValue(COMMAND_MKDIR);
+
+        checkOptions(commandLine);
+
+        if (!getFailOnErr()) {
+            getProject().log("Ignoring any errors that occur for: "
+                    + getViewPathBasename(), Project.MSG_VERBOSE);
+        }
+        result = run(commandLine);
+        if (Execute.isFailure(result) && getFailOnErr()) {
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+    /**
+     * Check the command line options.
+     */
+    private void checkOptions(Commandline cmd) {
+        if (getComment() != null) {
+            // -c
+            getCommentCommand(cmd);
+        } else {
+            if (getCommentFile() != null) {
+                // -cfile
+                getCommentFileCommand(cmd);
+            } else {
+                cmd.createArgument().setValue(FLAG_NOCOMMENT);
+            }
+        }
+        if (getNoCheckout()) {
+            // -nco
+            cmd.createArgument().setValue(FLAG_NOCHECKOUT);
+        }
+        // viewpath
+        cmd.createArgument().setValue(getViewPath());
+    }
+
+    /**
+     * Sets the comment string.
+     *
+     * @param comment the comment string
+     */
+    public void setComment(String comment) {
+        mComment = comment;
+    }
+
+    /**
+     * Get comment string
+     *
+     * @return String containing the comment
+     */
+    public String getComment() {
+        return mComment;
+    }
+
+    /**
+     * Specifies a file containing a comment.
+     *
+     * @param cfile the path to the comment file
+     */
+    public void setCommentFile(String cfile) {
+        mCfile = cfile;
+    }
+
+    /**
+     * Get comment file
+     *
+     * @return String containing the path to the comment file
+     */
+    public String getCommentFile() {
+        return mCfile;
+    }
+
+    /**
+     * If true, do not checkout element after creation.
+     *
+     * @param co the status to set the flag to
+     */
+    public void setNoCheckout(boolean co) {
+        mNoco = co;
+    }
+
+    /**
+     * Get no checkout flag status
+     *
+     * @return boolean containing status of noco flag
+     */
+    public boolean getNoCheckout() {
+        return mNoco;
+    }
+
+
+    /**
+     * Get the 'comment' command
+     *
+     * @param cmd containing the command line string with or
+     *            without the comment flag and string appended
+     */
+    private void getCommentCommand(Commandline cmd) {
+        if (getComment() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENT);
+            cmd.createArgument().setValue(getComment());
+        }
+    }
+
+    /**
+     * Get the 'commentfile' command
+     *
+     * @param cmd containing the command line string with or
+     *            without the commentfile flag and file appended
+     */
+    private void getCommentFileCommand(Commandline cmd) {
+        if (getCommentFile() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENTFILE);
+            cmd.createArgument().setValue(getCommentFile());
+        }
+    }
+
+    /**
+     * -c flag -- comment to attach to the directory
+     */
+    public static final String FLAG_COMMENT = "-c";
+    /**
+     * -cfile flag -- file containing a comment to attach to the directory
+     */
+    public static final String FLAG_COMMENTFILE = "-cfile";
+    /**
+     * -nc flag -- no comment is specified
+     */
+    public static final String FLAG_NOCOMMENT = "-nc";
+    /**
+     * -nco flag -- do not checkout element after creation
+     */
+    public static final String FLAG_NOCHECKOUT = "-nco";
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkelem.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkelem.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMkelem.java	(revision 16139)
@@ -0,0 +1,423 @@
+/*
+ * Copyright  2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Performs ClearCase mkelem.
+ *
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ *   <tr>
+ *     <th>Attribute</th>
+ *     <th>Values</th>
+ *     <th>Required</th>
+ *   </tr>
+ *   <tr>
+ *      <td>viewpath</td>
+ *      <td>Path to the ClearCase view file or directory that the command will operate on</td>
+ *      <td>Yes</td>
+ *   <tr>
+ *   <tr>
+ *      <td>comment</td>
+ *      <td>Specify a comment. Only one of comment or cfile may be used.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>commentfile</td>
+ *      <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>nowarn</td>
+ *      <td>Suppress warning messages</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>nocheckout</td>
+ *      <td>Do not checkout after element creation</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>checkin</td>
+ *      <td>Checkin element after creation</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>preservetime</td>
+ *      <td>Preserve the modification time (for checkin)</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>master</td>
+ *      <td>Assign mastership of the main branch to the current site</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>eltype</td>
+ *      <td>Element type to use during element creation</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>failonerr</td>
+ *      <td>Throw an exception if the command fails. Default is true</td>
+ *      <td>No</td>
+ *   <tr>
+ * </table>
+ *
+ */
+public class CCMkelem extends ClearCase {
+    private String  mComment = null;
+    private String  mCfile   = null;
+    private boolean mNwarn   = false;
+    private boolean mPtime   = false;
+    private boolean mNoco    = false;
+    private boolean mCheckin = false;
+    private boolean mMaster  = false;
+    private String  mEltype  = null;
+
+    /**
+     * Executes the task.
+     * <p>
+     * Builds a command line to execute cleartool and then calls Exec's run method
+     * to execute the command line.
+     * @throws BuildException if the command fails and failonerr is set to true
+     */
+    public void execute() throws BuildException {
+        Commandline commandLine = new Commandline();
+        Project aProj = getProject();
+        int result = 0;
+
+        // Default the viewpath to basedir if it is not specified
+        if (getViewPath() == null) {
+            setViewPath(aProj.getBaseDir().getPath());
+        }
+
+        // build the command line from what we got. the format is
+        // cleartool mkelem [options...] [viewpath ...]
+        // as specified in the CLEARTOOL.EXE help
+        commandLine.setExecutable(getClearToolCommand());
+        commandLine.createArgument().setValue(COMMAND_MKELEM);
+
+        checkOptions(commandLine);
+
+        if (!getFailOnErr()) {
+            getProject().log("Ignoring any errors that occur for: "
+                    + getViewPathBasename(), Project.MSG_VERBOSE);
+        }
+        result = run(commandLine);
+        if (Execute.isFailure(result) && getFailOnErr()) {
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+
+    /**
+     * Check the command line options.
+     */
+    private void checkOptions(Commandline cmd) {
+        if (getComment() != null) {
+            // -c
+            getCommentCommand(cmd);
+        } else {
+            if (getCommentFile() != null) {
+                // -cfile
+                getCommentFileCommand(cmd);
+            } else {
+                cmd.createArgument().setValue(FLAG_NOCOMMENT);
+            }
+        }
+
+        if (getNoWarn()) {
+            // -nwarn
+            cmd.createArgument().setValue(FLAG_NOWARN);
+        }
+        /*
+         * Should choose either -ci or -nco.
+         */
+        if (getNoCheckout() && getCheckin()) {
+            throw new BuildException("Should choose either [nocheckout | checkin]");
+        }
+        if (getNoCheckout()) {
+            // -nco
+            cmd.createArgument().setValue(FLAG_NOCHECKOUT);
+        }
+        if (getCheckin()) {
+            // -ci
+            cmd.createArgument().setValue(FLAG_CHECKIN);
+            if (getPreserveTime()) {
+                // -ptime
+                cmd.createArgument().setValue(FLAG_PRESERVETIME);
+            }
+        }
+        if (getMaster()) {
+            // -master
+            cmd.createArgument().setValue(FLAG_MASTER);
+        }
+        if (getEltype() != null) {
+            // -eltype
+            getEltypeCommand(cmd);
+        }
+        // viewpath
+        cmd.createArgument().setValue(getViewPath());
+    }
+
+    /**
+     * Sets the comment string.
+     *
+     * @param comment the comment string
+     */
+    public void setComment(String comment) {
+        mComment = comment;
+    }
+
+    /**
+     * Get comment string
+     *
+     * @return String containing the comment
+     */
+    public String getComment() {
+        return mComment;
+    }
+
+    /**
+     * Specifies a file containing a comment.
+     *
+     * @param cfile the path to the comment file
+     */
+    public void setCommentFile(String cfile) {
+        mCfile = cfile;
+    }
+
+    /**
+     * Get comment file
+     *
+     * @return String containing the path to the comment file
+     */
+    public String getCommentFile() {
+        return mCfile;
+    }
+
+    /**
+     * If true, suppress warning messages.
+     *
+     * @param nwarn the status to set the flag to
+     */
+    public void setNoWarn(boolean nwarn) {
+        mNwarn = nwarn;
+    }
+
+    /**
+     * Get nowarn flag status
+     *
+     * @return boolean containing status of nwarn flag
+     */
+    public boolean getNoWarn() {
+        return mNwarn;
+    }
+
+    /**
+     * If true, preserve the modification time.
+     *
+     * @param ptime the status to set the flag to
+     */
+    public void setPreserveTime(boolean ptime) {
+        mPtime = ptime;
+    }
+
+    /**
+     * Get preservetime flag status
+     *
+     * @return boolean containing status of preservetime flag
+     */
+    public boolean getPreserveTime() {
+        return mPtime;
+    }
+
+    /**
+     * If true, do not checkout element after creation.
+     *
+     * @param co the status to set the flag to
+     */
+    public void setNoCheckout(boolean co) {
+        mNoco = co;
+    }
+
+    /**
+     * Get no checkout flag status
+     *
+     * @return boolean containing status of noco flag
+     */
+    public boolean getNoCheckout() {
+        return mNoco;
+    }
+
+    /**
+     * If true, checkin the element after creation
+     *
+     * @param ci the status to set the flag to
+     */
+    public void setCheckin(boolean ci) {
+        mCheckin = ci;
+    }
+
+    /**
+     * Get ci flag status
+     *
+     * @return boolean containing status of ci flag
+     */
+    public boolean getCheckin() {
+        return mCheckin;
+    }
+
+    /**
+     * If true, changes mastership of the main branch
+     * to the current site
+     *
+     * @param master the status to set the flag to
+     */
+    public void setMaster(boolean master) {
+        mMaster = master;
+    }
+
+    /**
+     * Get master flag status
+     *
+     * @return boolean containing status of master flag
+     */
+    public boolean getMaster() {
+        return mMaster;
+    }
+
+    /**
+     * Specifies the element type to use.
+     *
+     * @param eltype to create element
+     */
+    public void setEltype(String eltype) {
+        mEltype = eltype;
+    }
+
+    /**
+     * Get element type
+     *
+     * @return String containing the element type
+     */
+    public String getEltype() {
+        return mEltype;
+    }
+
+
+    /**
+     * Get the 'comment' command
+     *
+     * @param cmd containing the command line string with or
+     *            without the comment flag and string appended
+     */
+    private void getCommentCommand(Commandline cmd) {
+        if (getComment() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENT);
+            cmd.createArgument().setValue(getComment());
+        }
+    }
+
+    /**
+     * Get the 'commentfile' command
+     *
+     * @param cmd containing the command line string with or
+     *            without the commentfile flag and file appended
+     */
+    private void getCommentFileCommand(Commandline cmd) {
+        if (getCommentFile() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENTFILE);
+            cmd.createArgument().setValue(getCommentFile());
+        }
+    }
+
+    /**
+     * Get the 'element type' command
+     *
+     * @param cmd containing the command line string with or
+     *            without the comment flag and string appended
+     */
+    private void getEltypeCommand(Commandline cmd) {
+        if (getEltype() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_ELTYPE);
+            cmd.createArgument().setValue(getEltype());
+        }
+    }
+
+    /**
+     * -c flag -- comment to attach to the file
+     */
+    public static final String FLAG_COMMENT = "-c";
+    /**
+     * -cfile flag -- file containing a comment to attach to the file
+     */
+    public static final String FLAG_COMMENTFILE = "-cfile";
+    /**
+     * -nc flag -- no comment is specified
+     */
+    public static final String FLAG_NOCOMMENT = "-nc";
+    /**
+     * -nwarn flag -- suppresses warning messages
+     */
+    public static final String FLAG_NOWARN = "-nwarn";
+    /**
+     * -ptime flag -- preserves the modification time on checkin
+     */
+    public static final String FLAG_PRESERVETIME = "-ptime";
+    /**
+     * -nco flag -- do not checkout element after creation
+     */
+    public static final String FLAG_NOCHECKOUT = "-nco";
+    /**
+     * -ci flag -- checkin element after creation
+     */
+    public static final String FLAG_CHECKIN = "-ci";
+    /**
+     * -master flag -- change mastership of main branch to current site
+     */
+    public static final String FLAG_MASTER = "-master";
+    /**
+     * -eltype flag -- element type to use during creation
+     */
+    public static final String FLAG_ELTYPE = "-eltype";
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklabel.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklabel.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklabel.java	(revision 16139)
@@ -0,0 +1,401 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Task to perform mklabel command to ClearCase.
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ *   <tr>
+ *     <th>Attribute</th>
+ *     <th>Values</th>
+ *     <th>Required</th>
+ *   </tr>
+ *   <tr>
+ *      <td>viewpath</td>
+ *      <td>Path to the ClearCase view file or directory that the command will operate on</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>replace</td>
+ *      <td>Replace a label of the same type on the same branch</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>recurse</td>
+ *      <td>Process each subdirectory under viewpath</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>version</td>
+ *      <td>Identify a specific version to attach the label to</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>typename</td>
+ *      <td>Name of the label type</td>
+ *      <td>Yes</td>
+ *   <tr>
+ *   <tr>
+ *      <td>vob</td>
+ *      <td>Name of the VOB</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>comment</td>
+ *      <td>Specify a comment. Only one of comment or cfile may be used.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>commentfile</td>
+ *      <td>Specify a file containing a comment. Only one of comment or cfile may be used.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>failonerr</td>
+ *      <td>Throw an exception if the command fails. Default is true</td>
+ *      <td>No</td>
+ *   <tr>
+ * </table>
+ *
+ */
+public class CCMklabel extends ClearCase {
+    private boolean mReplace = false;
+    private boolean mRecurse = false;
+    private String mVersion = null;
+    private String mTypeName = null;
+    private String mVOB = null;
+    private String mComment = null;
+    private String mCfile = null;
+
+    /**
+     * Executes the task.
+     * <p>
+     * Builds a command line to execute cleartool and then calls Exec's run method
+     * to execute the command line.
+     * @throws BuildException if the command fails and failonerr is set to true
+     */
+    public void execute() throws BuildException {
+        Commandline commandLine = new Commandline();
+        Project aProj = getProject();
+        int result = 0;
+
+        // Check for required attributes
+        if (getTypeName() == null) {
+            throw new BuildException("Required attribute TypeName not specified");
+        }
+
+        // Default the viewpath to basedir if it is not specified
+        if (getViewPath() == null) {
+            setViewPath(aProj.getBaseDir().getPath());
+        }
+
+        // build the command line from what we got. the format is
+        // cleartool mklabel [options...] [viewpath ...]
+        // as specified in the CLEARTOOL help
+        commandLine.setExecutable(getClearToolCommand());
+        commandLine.createArgument().setValue(COMMAND_MKLABEL);
+
+        checkOptions(commandLine);
+
+        if (!getFailOnErr()) {
+            getProject().log("Ignoring any errors that occur for: "
+                    + getViewPathBasename(), Project.MSG_VERBOSE);
+        }
+        result = run(commandLine);
+        if (Execute.isFailure(result) && getFailOnErr()) {
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+
+    /**
+     * Check the command line options.
+     */
+    private void checkOptions(Commandline cmd) {
+        if (getReplace()) {
+            // -replace
+            cmd.createArgument().setValue(FLAG_REPLACE);
+        }
+
+        if (getRecurse()) {
+            // -recurse
+            cmd.createArgument().setValue(FLAG_RECURSE);
+        }
+
+        if (getVersion() != null) {
+            // -version
+            getVersionCommand(cmd);
+        }
+
+        if (getComment() != null) {
+            // -c
+            getCommentCommand(cmd);
+        } else {
+            if (getCommentFile() != null) {
+                // -cfile
+                getCommentFileCommand(cmd);
+            } else {
+                cmd.createArgument().setValue(FLAG_NOCOMMENT);
+            }
+        }
+
+        if (getTypeName() != null) {
+            // type
+            getTypeCommand(cmd);
+        }
+
+        // viewpath
+        cmd.createArgument().setValue(getViewPath());
+    }
+
+
+    /**
+     * Set the replace flag
+     *
+     * @param replace the status to set the flag to
+     */
+    public void setReplace(boolean replace) {
+        mReplace = replace;
+    }
+
+    /**
+     * Get replace flag status
+     *
+     * @return boolean containing status of replace flag
+     */
+    public boolean getReplace() {
+        return mReplace;
+    }
+
+    /**
+     * Set recurse flag
+     *
+     * @param recurse the status to set the flag to
+     */
+    public void setRecurse(boolean recurse) {
+        mRecurse = recurse;
+    }
+
+    /**
+     * Get recurse flag status
+     *
+     * @return boolean containing status of recurse flag
+     */
+    public boolean getRecurse() {
+        return mRecurse;
+    }
+
+    /**
+     * Set the version flag
+     *
+     * @param version the status to set the flag to
+     */
+    public void setVersion(String version) {
+        mVersion = version;
+    }
+
+    /**
+     * Get version flag status
+     *
+     * @return boolean containing status of version flag
+     */
+    public String getVersion() {
+        return mVersion;
+    }
+
+    /**
+     * Set comment string
+     *
+     * @param comment the comment string
+     */
+    public void setComment(String comment) {
+        mComment = comment;
+    }
+
+    /**
+     * Get comment string
+     *
+     * @return String containing the comment
+     */
+    public String getComment() {
+        return mComment;
+    }
+
+    /**
+     * Set comment file
+     *
+     * @param cfile the path to the comment file
+     */
+    public void setCommentFile(String cfile) {
+        mCfile = cfile;
+    }
+
+    /**
+     * Get comment file
+     *
+     * @return String containing the path to the comment file
+     */
+    public String getCommentFile() {
+        return mCfile;
+    }
+
+    /**
+     * Set the type-name
+     *
+     * @param tn the type name
+     */
+    public void setTypeName(String tn) {
+        mTypeName = tn;
+    }
+
+    /**
+     * Get type-name
+     *
+     * @return String containing type name
+     */
+    public String getTypeName() {
+        return mTypeName;
+    }
+
+    /**
+     * Set the VOB name
+     *
+     * @param vob the VOB name
+     */
+    public void setVOB(String vob) {
+        mVOB = vob;
+    }
+
+    /**
+     * Get VOB name
+     *
+     * @return String containing VOB name
+     */
+    public String getVOB() {
+        return mVOB;
+    }
+
+
+    /**
+     * Get the 'version' command
+     *
+     * @param cmd CommandLine containing the command line string with or
+     *                    without the version flag and string appended
+     */
+    private void getVersionCommand(Commandline cmd) {
+        if (getVersion() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_VERSION);
+            cmd.createArgument().setValue(getVersion());
+        }
+    }
+
+    /**
+     * Get the 'comment' command
+     *
+     * @param cmd containing the command line string with or
+     *        without the comment flag and string appended
+     */
+    private void getCommentCommand(Commandline cmd) {
+        if (getComment() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENT);
+            cmd.createArgument().setValue(getComment());
+        }
+    }
+
+    /**
+     * Get the 'commentfile' command
+     *
+     * @param cmd         containing the command line string with or
+     *                    without the commentfile flag and file appended
+     */
+    private void getCommentFileCommand(Commandline cmd) {
+        if (getCommentFile() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENTFILE);
+            cmd.createArgument().setValue(getCommentFile());
+        }
+    }
+
+    /**
+     * Get the type-name
+     *
+     * @param cmd containing the command line string with or
+     *        without the type-name
+     */
+    private void getTypeCommand(Commandline cmd) {
+        String typenm = null;
+
+        if (getTypeName() != null) {
+            typenm = getTypeName();
+            if (getVOB() != null) {
+                typenm += "@" + getVOB();
+            }
+            cmd.createArgument().setValue(typenm);
+        }
+    }
+
+
+    /**
+     * -replace flag -- replace another label of the same type
+     */
+    public static final String FLAG_REPLACE = "-replace";
+    /**
+     * -recurse flag -- process all subdirectories
+     */
+    public static final String FLAG_RECURSE = "-recurse";
+    /**
+     * -version flag -- attach label to specified version
+     */
+    public static final String FLAG_VERSION = "-version";
+    /**
+     * -c flag -- comment to attach to the file
+     */
+    public static final String FLAG_COMMENT = "-c";
+    /**
+     * -cfile flag -- file containing a comment to attach to the file
+     */
+    public static final String FLAG_COMMENTFILE = "-cfile";
+    /**
+     * -nc flag -- no comment is specified
+     */
+    public static final String FLAG_NOCOMMENT = "-nc";
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklbtype.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklbtype.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCMklbtype.java	(revision 16139)
@@ -0,0 +1,440 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Task to perform mklbtype command to ClearCase.
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ *   <tr>
+ *     <th>Attribute</th>
+ *     <th>Values</th>
+ *     <th>Required</th>
+ *   </tr>
+ *   <tr>
+ *      <td>typename</td>
+ *      <td>Name of the label type to create</td>
+ *      <td>Yes</td>
+ *   <tr>
+ *   <tr>
+ *      <td>vob</td>
+ *      <td>Name of the VOB</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>replace</td>
+ *      <td>Replace an existing label definition of the same type</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>global</td>
+ *      <td>Either global or ordinary can be specified, not both.
+ *          Creates a label type that is global to the VOB or to
+ *          VOBs that use this VOB</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>ordinary</td>
+ *      <td>Either global or ordinary can be specified, not both.
+ *          Creates a label type that can be used only in the current
+ *          VOB. <B>Default</B></td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>pbranch</td>
+ *      <td>Allows the label type to be used once per branch in a given
+ *          element's version tree</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>shared</td>
+ *      <td>Sets the way mastership is checked by ClearCase. See ClearCase
+ *          documentation for details</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>comment</td>
+ *      <td>Specify a comment. Only one of comment or cfile may be used.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>commentfile</td>
+ *      <td>Specify a file containing a comment. Only one of comment or
+ *          cfile may be used.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>failonerr</td>
+ *      <td>Throw an exception if the command fails. Default is true</td>
+ *      <td>No</td>
+ *   <tr>
+ * </table>
+ *
+ */
+public class CCMklbtype extends ClearCase {
+    private String mTypeName = null;
+    private String mVOB = null;
+    private String mComment = null;
+    private String mCfile = null;
+    private boolean mReplace = false;
+    private boolean mGlobal = false;
+    private boolean mOrdinary = true;
+    private boolean mPbranch = false;
+    private boolean mShared = false;
+
+    /**
+     * Executes the task.
+     * <p>
+     * Builds a command line to execute cleartool and then calls Exec's run method
+     * to execute the command line.
+     * @throws BuildException if the command fails and failonerr is set to true
+     */
+    public void execute() throws BuildException {
+        Commandline commandLine = new Commandline();
+        Project aProj = getProject();
+        int result = 0;
+
+        // Check for required attributes
+        if (getTypeName() == null) {
+            throw new BuildException("Required attribute TypeName not specified");
+        }
+
+        // build the command line from what we got. the format is
+        // cleartool mklbtype [options...] type-selector...
+        // as specified in the CLEARTOOL help
+        commandLine.setExecutable(getClearToolCommand());
+        commandLine.createArgument().setValue(COMMAND_MKLBTYPE);
+
+        checkOptions(commandLine);
+
+        if (!getFailOnErr()) {
+            getProject().log("Ignoring any errors that occur for: "
+                    + getTypeSpecifier(), Project.MSG_VERBOSE);
+        }
+        result = run(commandLine);
+        if (Execute.isFailure(result) && getFailOnErr()) {
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+
+    /**
+     * Check the command line options.
+     */
+    private void checkOptions(Commandline cmd) {
+        if (getReplace()) {
+            // -replace
+            cmd.createArgument().setValue(FLAG_REPLACE);
+        }
+
+        if (getOrdinary()) {
+            // -ordinary
+            cmd.createArgument().setValue(FLAG_ORDINARY);
+        } else {
+            if (getGlobal()) {
+                // -global
+                cmd.createArgument().setValue(FLAG_GLOBAL);
+            }
+        }
+
+        if (getPbranch()) {
+            // -pbranch
+            cmd.createArgument().setValue(FLAG_PBRANCH);
+        }
+
+        if (getShared()) {
+            // -shared
+            cmd.createArgument().setValue(FLAG_SHARED);
+        }
+
+        if (getComment() != null) {
+            // -c
+            getCommentCommand(cmd);
+        } else {
+            if (getCommentFile() != null) {
+                // -cfile
+                getCommentFileCommand(cmd);
+            } else {
+                cmd.createArgument().setValue(FLAG_NOCOMMENT);
+            }
+        }
+
+        // type-name@vob
+        cmd.createArgument().setValue(getTypeSpecifier());
+    }
+
+
+    /**
+     * Set type-name string
+     *
+     * @param tn the type-name string
+     */
+    public void setTypeName(String tn) {
+        mTypeName = tn;
+    }
+
+    /**
+     * Get type-name string
+     *
+     * @return String containing the type-name
+     */
+    public String getTypeName() {
+        return mTypeName;
+    }
+
+    /**
+     * Set the VOB name
+     *
+     * @param vob the VOB name
+     */
+    public void setVOB(String vob) {
+        mVOB = vob;
+    }
+
+    /**
+     * Get VOB name
+     *
+     * @return String containing VOB name
+     */
+    public String getVOB() {
+        return mVOB;
+    }
+
+    /**
+     * Set the replace flag
+     *
+     * @param repl the status to set the flag to
+     */
+    public void setReplace(boolean repl) {
+        mReplace = repl;
+    }
+
+    /**
+     * Get replace flag status
+     *
+     * @return boolean containing status of replace flag
+     */
+    public boolean getReplace() {
+        return mReplace;
+    }
+
+    /**
+     * Set the global flag
+     *
+     * @param glob the status to set the flag to
+     */
+    public void setGlobal(boolean glob) {
+        mGlobal = glob;
+    }
+
+    /**
+     * Get global flag status
+     *
+     * @return boolean containing status of global flag
+     */
+    public boolean getGlobal() {
+        return mGlobal;
+    }
+
+    /**
+     * Set the ordinary flag
+     *
+     * @param ordinary the status to set the flag to
+     */
+    public void setOrdinary(boolean ordinary) {
+        mOrdinary = ordinary;
+    }
+
+    /**
+     * Get ordinary flag status
+     *
+     * @return boolean containing status of ordinary flag
+     */
+    public boolean getOrdinary() {
+        return mOrdinary;
+    }
+
+    /**
+     * Set the pbranch flag
+     *
+     * @param pbranch the status to set the flag to
+     */
+    public void setPbranch(boolean pbranch) {
+        mPbranch = pbranch;
+    }
+
+    /**
+     * Get pbranch flag status
+     *
+     * @return boolean containing status of pbranch flag
+     */
+    public boolean getPbranch() {
+        return mPbranch;
+    }
+
+    /**
+     * Set the shared flag
+     *
+     * @param shared the status to set the flag to
+     */
+    public void setShared(boolean shared) {
+        mShared = shared;
+    }
+
+    /**
+     * Get shared flag status
+     *
+     * @return boolean containing status of shared flag
+     */
+    public boolean getShared() {
+        return mShared;
+    }
+
+    /**
+     * Set comment string
+     *
+     * @param comment the comment string
+     */
+    public void setComment(String comment) {
+        mComment = comment;
+    }
+
+    /**
+     * Get comment string
+     *
+     * @return String containing the comment
+     */
+    public String getComment() {
+        return mComment;
+    }
+
+    /**
+     * Set comment file
+     *
+     * @param cfile the path to the comment file
+     */
+    public void setCommentFile(String cfile) {
+        mCfile = cfile;
+    }
+
+    /**
+     * Get comment file
+     *
+     * @return String containing the path to the comment file
+     */
+    public String getCommentFile() {
+        return mCfile;
+    }
+
+
+    /**
+     * Get the 'comment' command
+     *
+     * @param cmd containing the command line string with or
+     *        without the comment flag and string appended
+     */
+    private void getCommentCommand(Commandline cmd) {
+        if (getComment() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENT);
+            cmd.createArgument().setValue(getComment());
+        }
+    }
+
+    /**
+     * Get the 'commentfile' command
+     *
+     * @param cmd containing the command line string with or
+     *        without the commentfile flag and file appended
+     */
+    private void getCommentFileCommand(Commandline cmd) {
+        if (getCommentFile() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENTFILE);
+            cmd.createArgument().setValue(getCommentFile());
+        }
+    }
+
+    /**
+     * Get the type-name specifier
+     *
+     * @return the 'type-name-specifier' command if the attribute was
+     *         specified, otherwise an empty string
+     */
+    private String getTypeSpecifier() {
+        String typenm = null;
+
+        typenm = getTypeName();
+        if (getVOB() != null) {
+            typenm += "@" + getVOB();
+        }
+
+        return typenm;
+    }
+
+
+    /**
+     * -replace flag -- replace existing label definition of the same type
+     */
+    public static final String FLAG_REPLACE = "-replace";
+    /**
+     * -global flag -- creates a label type that is global to the VOB or to VOBs that use this VOB
+     */
+    public static final String FLAG_GLOBAL = "-global";
+    /**
+     * -ordinary flag -- creates a label type that can be used only in the current VOB
+     */
+    public static final String FLAG_ORDINARY = "-ordinary";
+    /**
+     * -pbranch flag -- allows label type to be used once per branch
+     */
+    public static final String FLAG_PBRANCH = "-pbranch";
+    /**
+     * -shared flag -- sets the way mastership is checked by ClearCase
+     */
+    public static final String FLAG_SHARED = "-shared";
+    /**
+     * -c flag -- comment to attach to the file
+     */
+    public static final String FLAG_COMMENT = "-c";
+    /**
+     * -cfile flag -- file containing a comment to attach to the file
+     */
+    public static final String FLAG_COMMENTFILE = "-cfile";
+    /**
+     * -nc flag -- no comment is specified
+     */
+    public static final String FLAG_NOCOMMENT = "-nc";
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCRmtype.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCRmtype.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCRmtype.java	(revision 16139)
@@ -0,0 +1,373 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Task to perform rmtype command to ClearCase.
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ *   <tr>
+ *     <th>Attribute</th>
+ *     <th>Values</th>
+ *     <th>Required</th>
+ *   </tr>
+ *   <tr>
+ *      <td>typekind</td>
+ *      <td>The kind of type to create. Valid types are:<br>
+ *              attype                         attribute type<br>
+ *              brtype                         branch type<br>
+ *              eltype                         element type<br>
+ *              hltype                         hyperlink type<br>
+ *              lbtype                         label type<br>
+ *              trtype                         trigger type<br>
+ *      </td>
+ *      <td>Yes</td>
+ *   <tr>
+ *   <tr>
+ *      <td>typename</td>
+ *      <td>The name of the type to remove</td>
+ *      <td>Yes</td>
+ *   <tr>
+ *   <tr>
+ *      <td>vob</td>
+ *      <td>Name of the VOB</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>ignore</td>
+ *      <td>Used with trigger types only. Forces removal of trigger type
+ *          even if a pre-operation trigger would prevent its removal</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>rmall</td>
+ *      <td>Removes all instances of a type and the type object itself</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>comment</td>
+ *      <td>Specify a comment. Only one of comment or cfile may be used.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>commentfile</td>
+ *      <td>Specify a file containing a comment. Only one of comment or cfile
+ *          may be used.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>failonerr</td>
+ *      <td>Throw an exception if the command fails. Default is true</td>
+ *      <td>No</td>
+ *   <tr>
+ * </table>
+ *
+ */
+public class CCRmtype extends ClearCase {
+    private String mTypeKind = null;
+    private String mTypeName = null;
+    private String mVOB = null;
+    private String mComment = null;
+    private String mCfile = null;
+    private boolean mRmall = false;
+    private boolean mIgnore = false;
+
+    /**
+     * Executes the task.
+     * <p>
+     * Builds a command line to execute cleartool and then calls Exec's run method
+     * to execute the command line.
+     * @throws BuildException if the command fails and failonerr is set to true
+     */
+    public void execute() throws BuildException {
+        Commandline commandLine = new Commandline();
+        Project aProj = getProject();
+        int result = 0;
+
+        // Check for required attributes
+        if (getTypeKind() == null) {
+            throw new BuildException("Required attribute TypeKind not specified");
+        }
+        if (getTypeName() == null) {
+            throw new BuildException("Required attribute TypeName not specified");
+        }
+
+        // build the command line from what we got. the format is
+        // cleartool rmtype [options...] type-selector...
+        // as specified in the CLEARTOOL help
+        commandLine.setExecutable(getClearToolCommand());
+        commandLine.createArgument().setValue(COMMAND_RMTYPE);
+
+        checkOptions(commandLine);
+
+        if (!getFailOnErr()) {
+            getProject().log("Ignoring any errors that occur for: "
+                    + getTypeSpecifier(), Project.MSG_VERBOSE);
+        }
+        result = run(commandLine);
+        if (Execute.isFailure(result) && getFailOnErr()) {
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+
+    /**
+     * Check the command line options.
+     */
+    private void checkOptions(Commandline cmd) {
+        if (getIgnore()) {
+            // -ignore
+            cmd.createArgument().setValue(FLAG_IGNORE);
+        }
+        if (getRmAll()) {
+            // -rmall -force
+            cmd.createArgument().setValue(FLAG_RMALL);
+            cmd.createArgument().setValue(FLAG_FORCE);
+        }
+        if (getComment() != null) {
+            // -c
+            getCommentCommand(cmd);
+        } else {
+            if (getCommentFile() != null) {
+                // -cfile
+                getCommentFileCommand(cmd);
+            } else {
+                cmd.createArgument().setValue(FLAG_NOCOMMENT);
+            }
+        }
+
+        // type-kind:type-name
+        cmd.createArgument().setValue(getTypeSpecifier());
+    }
+
+    /**
+     * Set the ignore flag
+     *
+     * @param ignore the status to set the flag to
+     */
+    public void setIgnore(boolean ignore) {
+        mIgnore = ignore;
+    }
+
+    /**
+     * Get ignore flag status
+     *
+     * @return boolean containing status of ignore flag
+     */
+    public boolean getIgnore() {
+        return mIgnore;
+    }
+
+    /**
+     * Set rmall flag
+     *
+     * @param rmall the status to set the flag to
+     */
+    public void setRmAll(boolean rmall) {
+        mRmall = rmall;
+    }
+
+    /**
+     * Get rmall flag status
+     *
+     * @return boolean containing status of rmall flag
+     */
+    public boolean getRmAll() {
+        return mRmall;
+    }
+
+    /**
+     * Set comment string
+     *
+     * @param comment the comment string
+     */
+    public void setComment(String comment) {
+        mComment = comment;
+    }
+
+    /**
+     * Get comment string
+     *
+     * @return String containing the comment
+     */
+    public String getComment() {
+        return mComment;
+    }
+
+    /**
+     * Set comment file
+     *
+     * @param cfile the path to the comment file
+     */
+    public void setCommentFile(String cfile) {
+        mCfile = cfile;
+    }
+
+    /**
+     * Get comment file
+     *
+     * @return String containing the path to the comment file
+     */
+    public String getCommentFile() {
+        return mCfile;
+    }
+
+    /**
+     * Set type-kind string
+     *
+     * @param tk the type-kind string
+     */
+    public void setTypeKind(String tk) {
+        mTypeKind = tk;
+    }
+
+    /**
+     * Get type-kind string
+     *
+     * @return String containing the type-kind
+     */
+    public String getTypeKind() {
+        return mTypeKind;
+    }
+
+    /**
+     * Set type-name string
+     *
+     * @param tn the type-name string
+     */
+    public void setTypeName(String tn) {
+        mTypeName = tn;
+    }
+
+    /**
+     * Get type-name string
+     *
+     * @return String containing the type-name
+     */
+    public String getTypeName() {
+        return mTypeName;
+    }
+
+    /**
+     * Set the VOB name
+     *
+     * @param vob the VOB name
+     */
+    public void setVOB(String vob) {
+        mVOB = vob;
+    }
+
+    /**
+     * Get VOB name
+     *
+     * @return String containing VOB name
+     */
+    public String getVOB() {
+        return mVOB;
+    }
+
+    /**
+     * Get the 'type-specifier' string
+     *
+     * @return the 'type-kind:type-name@vob' specifier
+     *
+     */
+    private String getTypeSpecifier() {
+        String tkind = getTypeKind();
+        String tname = getTypeName();
+        String typeSpec = null;
+
+        // Return the type-selector
+        typeSpec = tkind + ":" + tname;
+        if (getVOB() != null) {
+            typeSpec += "@" + getVOB();
+        }
+        return typeSpec;
+    }
+
+    /**
+     * Get the 'comment' command
+     *
+     * @param cmd containing the command line string with or
+     *        without the comment flag and string appended
+     */
+    private void getCommentCommand(Commandline cmd) {
+        if (getComment() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENT);
+            cmd.createArgument().setValue(getComment());
+        }
+    }
+
+    /**
+     * Get the 'commentfile' command
+     *
+     * @param cmd containing the command line string with or
+     *        without the commentfile flag and file appended
+     */
+    private void getCommentFileCommand(Commandline cmd) {
+        if (getCommentFile() != null) {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENTFILE);
+            cmd.createArgument().setValue(getCommentFile());
+        }
+    }
+
+
+    /**
+     * -ignore flag -- ignore pre-trigger operations when removing a trigger type
+     */
+    public static final String FLAG_IGNORE = "-ignore";
+    /**
+     * -rmall flag -- removes all instances of a type and the type object itself
+     */
+    public static final String FLAG_RMALL = "-rmall";
+    /**
+     * -force flag -- suppresses confirmation prompts
+     */
+    public static final String FLAG_FORCE = "-force";
+    /**
+     * -c flag -- comment to attach to the file
+     */
+    public static final String FLAG_COMMENT = "-c";
+    /**
+     * -cfile flag -- file containing a comment to attach to the file
+     */
+    public static final String FLAG_COMMENTFILE = "-cfile";
+    /**
+     * -nc flag -- no comment is specified
+     */
+    public static final String FLAG_NOCOMMENT = "-nc";
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnCheckout.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnCheckout.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnCheckout.java	(revision 16139)
@@ -0,0 +1,140 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Performs ClearCase UnCheckout command.
+ *
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ *   <tr>
+ *     <th>Attribute</th>
+ *     <th>Values</th>
+ *     <th>Required</th>
+ *   </tr>
+ *   <tr>
+ *      <td>viewpath</td>
+ *      <td>Path to the ClearCase view file or directory that the command will operate on</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>keepcopy</td>
+ *      <td>Specifies whether to keep a copy of the file with a .keep extension or not</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>failonerr</td>
+ *      <td>Throw an exception if the command fails. Default is true</td>
+ *      <td>No</td>
+ *   <tr>
+ * </table>
+ *
+ */
+public class CCUnCheckout extends ClearCase {
+    private boolean mKeep = false;
+
+    /**
+     * Executes the task.
+     * <p>
+     * Builds a command line to execute cleartool and then calls Exec's run method
+     * to execute the command line.
+     * @throws BuildException if the command fails and failonerr is set to true
+     */
+    public void execute() throws BuildException {
+        Commandline commandLine = new Commandline();
+        Project aProj = getProject();
+        int result = 0;
+
+        // Default the viewpath to basedir if it is not specified
+        if (getViewPath() == null) {
+            setViewPath(aProj.getBaseDir().getPath());
+        }
+
+        // build the command line from what we got the format is
+        // cleartool uncheckout [options...] [viewpath ...]
+        // as specified in the CLEARTOOL.EXE help
+        commandLine.setExecutable(getClearToolCommand());
+        commandLine.createArgument().setValue(COMMAND_UNCHECKOUT);
+
+        checkOptions(commandLine);
+
+        if (!getFailOnErr()) {
+            getProject().log("Ignoring any errors that occur for: "
+                    + getViewPathBasename(), Project.MSG_VERBOSE);
+        }
+        result = run(commandLine);
+        if (Execute.isFailure(result) && getFailOnErr()) {
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+
+    /**
+     * Check the command line options.
+     */
+    private void checkOptions(Commandline cmd) {
+        // ClearCase items
+        if (getKeepCopy()) {
+            // -keep
+            cmd.createArgument().setValue(FLAG_KEEPCOPY);
+        } else {
+            // -rm
+            cmd.createArgument().setValue(FLAG_RM);
+        }
+
+        // viewpath
+        cmd.createArgument().setValue(getViewPath());
+    }
+
+    /**
+     * If true, keep a copy of the file with a .keep extension.
+     *
+     * @param keep the status to set the flag to
+     */
+    public void setKeepCopy(boolean keep) {
+        mKeep = keep;
+    }
+
+    /**
+     * Get keepcopy flag status
+     *
+     * @return boolean containing status of keep flag
+     */
+    public boolean getKeepCopy() {
+        return mKeep;
+    }
+
+
+    /**
+     *  -keep flag -- keep a copy of the file with .keep extension
+     */
+    public static final String FLAG_KEEPCOPY = "-keep";
+    /**
+     *  -rm flag -- remove the copy of the file
+     */
+    public static final String FLAG_RM = "-rm";
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnlock.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnlock.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUnlock.java	(revision 16139)
@@ -0,0 +1,262 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * TODO:
+ * comment field doesn't include all options yet
+ */
+
+
+/**
+ * Performs a ClearCase Unlock command.
+ *
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ *   <tr>
+ *     <th>Attribute</th>
+ *     <th>Values</th>
+ *     <th>Required</th>
+ *   </tr>
+ *   <tr>
+ *      <td>comment</td>
+ *      <td>Specifies how to populate comments fields</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>pname</td>
+ *      <td>Specifies the object pathname to be unlocked.</td>
+ *      <td>No</td>
+ *   <tr>
+ *      <td>objselect</td>
+ *      <td>This variable is obsolete. Should use <i>objsel</i> instead.</td>
+ *      <td>no</td>
+ *   <tr>
+ *   <tr>
+ *      <td>objsel</td>
+ *      <td>Specifies the object(s) to be unlocked.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>failonerr</td>
+ *      <td>Throw an exception if the command fails. Default is true</td>
+ *      <td>No</td>
+ *   <tr>
+ *
+ * </table>
+ *
+ */
+public class CCUnlock extends ClearCase {
+    private String mComment = null;
+    private String mPname = null;
+    private String mObjselect = null;
+
+    /**
+     * Executes the task.
+     * <p>
+     * Builds a command line to execute cleartool and then calls Exec's run method
+     * to execute the command line.
+     * @throws BuildException if the command fails and failonerr is set to true
+     */
+    public void execute() throws BuildException {
+        Commandline commandLine = new Commandline();
+        Project aProj = getProject();
+        int result = 0;
+
+        // Default the viewpath to basedir if it is not specified
+        if (getViewPath() == null) {
+            setViewPath(aProj.getBaseDir().getPath());
+        }
+
+        // build the command line from what we got the format is
+        // cleartool lock [options...]
+        // as specified in the CLEARTOOL.EXE help
+        commandLine.setExecutable(getClearToolCommand());
+        commandLine.createArgument().setValue(COMMAND_UNLOCK);
+
+        // Check the command line options
+        checkOptions(commandLine);
+
+        // For debugging
+        // System.out.println(commandLine.toString());
+
+        if (!getFailOnErr()) {
+            getProject().log("Ignoring any errors that occur for: "
+                    + getOpType(), Project.MSG_VERBOSE);
+        }
+        result = run(commandLine);
+        if (Execute.isFailure(result) && getFailOnErr()) {
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+    /**
+     * Check the command line options.
+     */
+private void checkOptions(Commandline cmd) {
+        // ClearCase items
+        getCommentCommand(cmd);
+
+        if (getObjselect() == null && getPname() == null) {
+            throw new BuildException("Should select either an element "
+            + "(pname) or an object (objselect)");
+        }
+        getPnameCommand(cmd);
+        // object selector
+        if (getObjselect() != null) {
+            cmd.createArgument().setValue(getObjselect());
+        }
+}
+
+    /**
+     * Sets how comments should be written
+     * for the event record(s)
+     *
+     * @param comment comment method to use
+     */
+    public void setComment(String comment) {
+        mComment = comment;
+    }
+
+    /**
+     * Get comment method
+     *
+     * @return String containing the desired comment method
+     */
+    public String getComment() {
+        return mComment;
+    }
+
+    /**
+     * Sets the pathname to be locked
+     *
+     * @param pname pathname to be locked
+     */
+    public void setPname(String pname) {
+        mPname = pname;
+    }
+
+    /**
+     * Get the pathname to be locked
+     *
+     * @return String containing the pathname to be locked
+     */
+    public String getPname() {
+        return mPname;
+    }
+
+    /**
+     * Sets the object(s) to be locked
+     *
+     * @param objselect objects to be locked
+     */
+    public void setObjselect(String objselect) {
+        mObjselect = objselect;
+    }
+
+    /**
+     * Sets the object(s) to be locked
+     *
+     * @param objsel objects to be locked
+     * @since ant 1.6.1
+     */
+    public void setObjSel(String objsel) {
+        mObjselect = objsel;
+    }
+
+    /**
+     * Get list of objects to be locked
+     *
+     * @return String containing the objects to be locked
+     */
+    public String getObjselect() {
+        return mObjselect;
+    }
+
+
+    /**
+     * Get the 'comment' command
+     *
+     * @param cmd containing the command line string with or without the
+     *            comment flag and value appended
+     */
+    private void getCommentCommand(Commandline cmd) {
+        if (getComment() == null) {
+            return;
+        } else {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_COMMENT);
+            cmd.createArgument().setValue(getComment());
+        }
+    }
+
+    /**
+     * Get the 'pname' command
+     *
+     * @param cmd containing the command line string with or without the
+     *            pname flag and value appended
+     */
+    private void getPnameCommand(Commandline cmd) {
+        if (getPname() == null) {
+            return;
+        } else {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_PNAME);
+            cmd.createArgument().setValue(getPname());
+        }
+    }
+
+    /**
+     * Return which object/pname is being operated on
+     *
+     * @return String containing the object/pname being worked on
+     */
+    private String getOpType() {
+
+        if (getPname() != null) {
+            return getPname();
+        } else {
+            return getObjselect();
+        }
+    }
+
+    /**
+     * -comment flag -- method to use for commenting events
+     */
+    public static final String FLAG_COMMENT = "-comment";
+    /**
+     * -pname flag -- pathname to lock
+     */
+    public static final String FLAG_PNAME = "-pname";
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUpdate.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUpdate.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/CCUpdate.java	(revision 16139)
@@ -0,0 +1,330 @@
+/*
+ * Copyright  2000,2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.clearcase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Performs a ClearCase Update command.
+ *
+ * <p>
+ * The following attributes are interpreted:
+ * <table border="1">
+ *   <tr>
+ *     <th>Attribute</th>
+ *     <th>Values</th>
+ *     <th>Required</th>
+ *   </tr>
+ *   <tr>
+ *      <td>viewpath</td>
+ *      <td>Path to the ClearCase view file or directory that the command will operate on</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>graphical</td>
+ *      <td>Displays a graphical dialog during the update</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>log</td>
+ *      <td>Specifies a log file for ClearCase to write to</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>overwrite</td>
+ *      <td>Specifies whether to overwrite hijacked files or not</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>rename</td>
+ *      <td>Specifies that hijacked files should be renamed with a .keep extension</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>currenttime</td>
+ *      <td>Specifies that modification time should be written as the current
+ *          time. Either currenttime or preservetime can be specified.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>preservetime</td>
+ *      <td>Specifies that modification time should preserved from the VOB
+ *          time. Either currenttime or preservetime can be specified.</td>
+ *      <td>No</td>
+ *   <tr>
+ *   <tr>
+ *      <td>failonerr</td>
+ *      <td>Throw an exception if the command fails. Default is true</td>
+ *      <td>No</td>
+ *   <tr>
+ * </table>
+ *
+ */
+public class CCUpdate extends ClearCase {
+    private boolean mGraphical = false;
+    private boolean mOverwrite = false;
+    private boolean mRename = false;
+    private boolean mCtime = false;
+    private boolean mPtime = false;
+    private String mLog = null;
+
+    /**
+     * Executes the task.
+     * <p>
+     * Builds a command line to execute cleartool and then calls Exec's run method
+     * to execute the command line.
+     * @throws BuildException if the command fails and failonerr is set to true
+     */
+    public void execute() throws BuildException {
+        Commandline commandLine = new Commandline();
+        Project aProj = getProject();
+        int result = 0;
+
+        // Default the viewpath to basedir if it is not specified
+        if (getViewPath() == null) {
+            setViewPath(aProj.getBaseDir().getPath());
+        }
+
+        // build the command line from what we got the format is
+        // cleartool update [options...] [viewpath ...]
+        // as specified in the CLEARTOOL.EXE help
+        commandLine.setExecutable(getClearToolCommand());
+        commandLine.createArgument().setValue(COMMAND_UPDATE);
+
+        // Check the command line options
+        checkOptions(commandLine);
+
+        // For debugging
+        getProject().log(commandLine.toString(), Project.MSG_DEBUG);
+
+        if (!getFailOnErr()) {
+            getProject().log("Ignoring any errors that occur for: "
+                    + getViewPathBasename(), Project.MSG_VERBOSE);
+        }
+        result = run(commandLine);
+        if (Execute.isFailure(result) && getFailOnErr()) {
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+    /**
+     * Check the command line options.
+     */
+    private void checkOptions(Commandline cmd) {
+        // ClearCase items
+        if (getGraphical()) {
+            // -graphical
+            cmd.createArgument().setValue(FLAG_GRAPHICAL);
+        } else {
+            if (getOverwrite()) {
+                // -overwrite
+                cmd.createArgument().setValue(FLAG_OVERWRITE);
+            } else {
+                if (getRename()) {
+                    // -rename
+                    cmd.createArgument().setValue(FLAG_RENAME);
+                } else {
+                    // -noverwrite
+                    cmd.createArgument().setValue(FLAG_NOVERWRITE);
+                }
+            }
+
+            if (getCurrentTime()) {
+                // -ctime
+                cmd.createArgument().setValue(FLAG_CURRENTTIME);
+            } else {
+                if (getPreserveTime()) {
+                    // -ptime
+                    cmd.createArgument().setValue(FLAG_PRESERVETIME);
+                }
+            }
+
+            // -log logname
+            getLogCommand(cmd);
+        }
+
+        // viewpath
+        cmd.createArgument().setValue(getViewPath());
+    }
+
+    /**
+     * If true, displays a graphical dialog during the update.
+     *
+     * @param graphical the status to set the flag to
+     */
+    public void setGraphical(boolean graphical) {
+        mGraphical = graphical;
+    }
+
+    /**
+     * Get graphical flag status
+     *
+     * @return boolean containing status of graphical flag
+     */
+    public boolean getGraphical() {
+        return mGraphical;
+    }
+
+    /**
+     * If true, overwrite hijacked files.
+     *
+     * @param ow the status to set the flag to
+     */
+    public void setOverwrite(boolean ow) {
+        mOverwrite = ow;
+    }
+
+    /**
+     * Get overwrite hijacked files status
+     *
+     * @return boolean containing status of overwrite flag
+     */
+    public boolean getOverwrite() {
+        return mOverwrite;
+    }
+
+    /**
+     * If true, hijacked files are renamed with a .keep extension.
+     *
+     * @param ren the status to set the flag to
+     */
+    public void setRename(boolean ren) {
+        mRename = ren;
+    }
+
+    /**
+     * Get rename hijacked files status
+     *
+     * @return boolean containing status of rename flag
+     */
+    public boolean getRename() {
+        return mRename;
+    }
+
+    /**
+     * If true, modification time should be written as the current time.
+     * Either currenttime or preservetime can be specified.
+     *
+     * @param ct the status to set the flag to
+     */
+    public void setCurrentTime(boolean ct) {
+        mCtime = ct;
+    }
+
+    /**
+     * Get current time status
+     *
+     * @return boolean containing status of current time flag
+     */
+    public boolean getCurrentTime() {
+        return mCtime;
+    }
+
+    /**
+     * If true, modification time should be preserved from the VOB time.
+     * Either currenttime or preservetime can be specified.
+     *
+     * @param pt the status to set the flag to
+     */
+    public void setPreserveTime(boolean pt) {
+        mPtime = pt;
+    }
+
+    /**
+     * Get preserve time status
+     *
+     * @return boolean containing status of preserve time flag
+     */
+    public boolean getPreserveTime() {
+        return mPtime;
+    }
+
+    /**
+     * Sets the log file where cleartool records
+     * the status of the command.
+     *
+     * @param log the path to the log file
+     */
+    public void setLog(String log) {
+        mLog = log;
+    }
+
+    /**
+     * Get log file
+     *
+     * @return String containing the path to the log file
+     */
+    public String getLog() {
+        return mLog;
+    }
+
+
+    /**
+     * Get the 'log' command
+     *
+     * @param cmd containing the command line string with or without the log flag and path appended
+     */
+    private void getLogCommand(Commandline cmd) {
+        if (getLog() == null) {
+            return;
+        } else {
+            /* Had to make two separate commands here because if a space is
+               inserted between the flag and the value, it is treated as a
+               Windows filename with a space and it is enclosed in double
+               quotes ("). This breaks clearcase.
+            */
+            cmd.createArgument().setValue(FLAG_LOG);
+            cmd.createArgument().setValue(getLog());
+        }
+    }
+
+    /**
+     *  -graphical flag -- display graphical dialog during update operation
+     */
+    public static final String FLAG_GRAPHICAL = "-graphical";
+    /**
+     * -log flag -- file to log status to
+     */
+    public static final String FLAG_LOG = "-log";
+    /**
+     * -overwrite flag -- overwrite hijacked files
+     */
+    public static final String FLAG_OVERWRITE = "-overwrite";
+    /**
+     * -noverwrite flag -- do not overwrite hijacked files
+     */
+    public static final String FLAG_NOVERWRITE = "-noverwrite";
+    /**
+     * -rename flag -- rename hijacked files with .keep extension
+     */
+    public static final String FLAG_RENAME = "-rename";
+    /**
+     * -ctime flag -- modified time is written as the current time
+     */
+    public static final String FLAG_CURRENTTIME = "-ctime";
+    /**
+     * -ptime flag -- modified time is written as the VOB time
+     */
+    public static final String FLAG_PRESERVETIME = "-ptime";
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/ClearCase.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/ClearCase.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/clearcase/ClearCase.java	(revision 16139)
@@ -0,0 +1,241 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.clearcase;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.ExecTask;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+
+
+
+/**
+ * A base class for creating tasks for executing commands on ClearCase.
+ * <p>
+ * The class extends the 'exec' task as it operates by executing the cleartool program
+ * supplied with ClearCase. By default the task expects the cleartool executable to be
+ * in the path, * you can override this be specifying the cleartooldir attribute.
+ * </p>
+ * <p>
+ * This class provides set and get methods for the 'viewpath' and 'objselect'
+ * attribute. It also contains constants for the flags that can be passed to
+ * cleartool.
+ * </p>
+ *
+ */
+public abstract class ClearCase extends Task {
+    private String mClearToolDir = "";
+    private String mviewPath = null;
+    private String mobjSelect = null;
+    private static int pcnt = 0;
+    private boolean mFailonerr = true;
+    /**
+     * Set the directory where the cleartool executable is located.
+     *
+     * @param dir the directory containing the cleartool executable
+     */
+    public final void setClearToolDir(String dir) {
+        mClearToolDir = getProject().translatePath(dir);
+    }
+
+    /**
+     * Builds and returns the command string to execute cleartool
+     *
+     * @return String containing path to the executable
+     */
+    protected final String getClearToolCommand() {
+        String toReturn = mClearToolDir;
+        if (!toReturn.equals("") && !toReturn.endsWith("/")) {
+            toReturn += "/";
+        }
+
+        toReturn += CLEARTOOL_EXE;
+
+        return toReturn;
+    }
+
+    /**
+     * Set the path to the item in a ClearCase view to operate on.
+     *
+     * @param viewPath Path to the view directory or file
+     */
+    public final void setViewPath(String viewPath) {
+        mviewPath = viewPath;
+    }
+
+    /**
+     * Get the path to the item in a clearcase view
+     *
+     * @return mviewPath
+     */
+    public String getViewPath() {
+        return mviewPath;
+    }
+
+    /**
+     * Get the basename path of the item in a clearcase view
+     *
+     * @return basename
+     */
+    public String getViewPathBasename() {
+        return (new File(mviewPath)).getName();
+    }
+
+    /**
+     * Set the object to operate on.
+     *
+     * @param objSelect object to operate on
+     */
+    public final void setObjSelect(String objSelect) {
+        mobjSelect = objSelect;
+    }
+
+    /**
+     * Get the object to operate on
+     *
+     * @return mobjSelect
+     */
+    public String getObjSelect() {
+        return mobjSelect;
+    }
+
+    /**
+     * Execute the given command are return success or failure
+     * @param cmd command line to execute
+     * @return the exit status of the subprocess or <code>INVALID</code>
+     */
+    protected int run(Commandline cmd) {
+        try {
+            Project aProj = getProject();
+            Execute exe
+                = new Execute(new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_WARN));
+            exe.setAntRun(aProj);
+            exe.setWorkingDirectory(aProj.getBaseDir());
+            exe.setCommandline(cmd.getCommandline());
+            return exe.execute();
+        } catch (java.io.IOException e) {
+            throw new BuildException(e, getLocation());
+        }
+    }
+
+    /**
+     * Execute the given command, and return it's output
+     * @param cmdline command line to execute
+     * @return output of the command line
+     */
+    protected String runS(Commandline cmdline) {
+        String   outV  = "opts.cc.runS.output" + pcnt++;
+        Project  aProj = getProject();
+        ExecTask exe   = (ExecTask) aProj.createTask("exec");
+        Commandline.Argument arg = exe.createArg();
+
+        exe.setExecutable(cmdline.getExecutable());
+        arg.setLine(cmdline.toString(cmdline.getArguments()));
+        exe.setOutputproperty(outV);
+        exe.execute();
+        // System.out.println( "runS: " + outV + " : " + aProj.getProperty( outV ));
+
+        return aProj.getProperty(outV);
+    }
+    /**
+     * If true, command will throw an exception on failure.
+     *
+     * @param failonerr the status to set the flag to
+     * @since ant 1.6.1
+     */
+    public void setFailOnErr(boolean failonerr) {
+        mFailonerr = failonerr;
+    }
+
+    /**
+     * Get failonerr flag status
+     *
+     * @return boolean containing status of failonerr flag
+     * @since ant 1.6.1
+     */
+    public boolean getFailOnErr() {
+        return mFailonerr;
+    }
+
+    /**
+     * Constant for the thing to execute
+     */
+    private static final String CLEARTOOL_EXE = "cleartool";
+    /**
+     * The 'Update' command
+     */
+    public static final String COMMAND_UPDATE = "update";
+    /**
+     * The 'Checkout' command
+     */
+    public static final String COMMAND_CHECKOUT = "checkout";
+    /**
+     * The 'Checkin' command
+     */
+    public static final String COMMAND_CHECKIN = "checkin";
+    /**
+     * The 'UndoCheckout' command
+     */
+    public static final String COMMAND_UNCHECKOUT = "uncheckout";
+    /**
+     * The 'Lock' command
+     */
+    public static final String COMMAND_LOCK = "lock";
+    /**
+     * The 'Unlock' command
+     */
+    public static final String COMMAND_UNLOCK = "unlock";
+    /**
+     * The 'Mkbl' command
+     */
+    public static final String COMMAND_MKBL = "mkbl";
+    /**
+     * The 'Mklabel' command
+     */
+    public static final String COMMAND_MKLABEL = "mklabel";
+    /**
+     * The 'Mklbtype' command
+     */
+    public static final String COMMAND_MKLBTYPE = "mklbtype";
+    /**
+     * The 'Rmtype' command
+     */
+    public static final String COMMAND_RMTYPE = "rmtype";
+    /**
+     * The 'LsCheckout' command
+     */
+    public static final String COMMAND_LSCO = "lsco";
+    /**
+     * The 'Mkelem' command
+     */
+    public static final String COMMAND_MKELEM = "mkelem";
+    /**
+     * The 'Mkattr' command
+     */
+    public static final String COMMAND_MKATTR = "mkattr";
+    /**
+     * The 'Mkdir' command
+     */
+    public static final String COMMAND_MKDIR = "mkdir";
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/AntAnalyzer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/AntAnalyzer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/AntAnalyzer.java	(revision 16139)
@@ -0,0 +1,150 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.apache.tools.ant.util.depend.AbstractAnalyzer;
+
+/**
+ * An analyzer which uses the depend task's bytecode classes to analyze
+ * dependencies
+ *
+ */
+public class AntAnalyzer extends AbstractAnalyzer {
+    /**
+     * Default constructor
+     */
+    public AntAnalyzer() {
+    }
+
+    /**
+     * Determine the dependencies of the configured root classes.
+     *
+     * @param files a vector to be populated with the files which contain
+     *      the dependency classes
+     * @param classes a vector to be populated with the names of the
+     *      dependency classes.
+     */
+    protected void determineDependencies(Vector files, Vector classes) {
+        // we get the root classes and build up a set of
+        // classes upon which they depend
+        Hashtable dependencies = new Hashtable();
+        Hashtable containers = new Hashtable();
+        Hashtable toAnalyze = new Hashtable();
+        for (Enumeration e = getRootClasses(); e.hasMoreElements();) {
+            String classname = (String) e.nextElement();
+            toAnalyze.put(classname, classname);
+        }
+
+        int count = 0;
+        int maxCount = isClosureRequired() ? MAX_LOOPS : 1;
+        Hashtable analyzedDeps = null;
+        while (toAnalyze.size() != 0 && count++ < maxCount) {
+            analyzedDeps = new Hashtable();
+            for (Enumeration e = toAnalyze.keys(); e.hasMoreElements();) {
+                String classname = (String) e.nextElement();
+                dependencies.put(classname, classname);
+                try {
+                    File container = getClassContainer(classname);
+                    if (container == null) {
+                        continue;
+                    }
+                    containers.put(container, container);
+
+                    ZipFile zipFile = null;
+                    InputStream inStream = null;
+                    try {
+                        if (container.getName().endsWith(".class")) {
+                            inStream = new FileInputStream(container.getPath());
+                        } else {
+                            zipFile = new ZipFile(container.getPath());
+                            String entryName
+                                = classname.replace('.', '/') + ".class";
+                            ZipEntry entry = new ZipEntry(entryName);
+                            inStream
+                                = zipFile.getInputStream(entry);
+                        }
+                        ClassFile classFile = new ClassFile();
+                        classFile.read(inStream);
+                        Vector dependencyList = classFile.getClassRefs();
+                        Enumeration depEnum = dependencyList.elements();
+                        while (depEnum.hasMoreElements()) {
+                            String dependency = (String) depEnum.nextElement();
+                            analyzedDeps.put(dependency, dependency);
+                        }
+                    } finally {
+                        if (inStream != null) {
+                            inStream.close();
+                        }
+                        if (zipFile != null) {
+                            zipFile.close();
+                        }
+                    }
+                } catch (IOException ioe) {
+                    // ignore
+                }
+            }
+
+            toAnalyze.clear();
+
+            // now recover all the dependencies collected and add to the list.
+            Enumeration depsEnum = analyzedDeps.elements();
+            while (depsEnum.hasMoreElements()) {
+                String className = (String) depsEnum.nextElement();
+                if (!dependencies.containsKey(className)) {
+                    toAnalyze.put(className, className);
+                }
+            }
+        }
+
+        // pick up the last round of dependencies that were determined
+        Enumeration depsEnum = analyzedDeps.elements();
+        while (depsEnum.hasMoreElements()) {
+            String className = (String) depsEnum.nextElement();
+            dependencies.put(className, className);
+        }
+
+        files.removeAllElements();
+        for (Enumeration e = containers.keys(); e.hasMoreElements();) {
+            files.addElement((File) e.nextElement());
+        }
+
+        classes.removeAllElements();
+        for (Enumeration e = dependencies.keys(); e.hasMoreElements();) {
+            classes.addElement((String) e.nextElement());
+        }
+    }
+
+    /**
+     * Indicate if this analyzer can determine dependent files.
+     *
+     * @return true if the analyzer provides dependency file information.
+     */
+    protected boolean supportsFileDependencies() {
+        return true;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFile.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFile.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFile.java	(revision 16139)
@@ -0,0 +1,117 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Vector;
+import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ClassCPInfo;
+import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ConstantPool;
+import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ConstantPoolEntry;
+
+/**
+ * A ClassFile object stores information about a Java class. The class may
+ * be read from a DataInputStream.and written to a DataOutputStream. These
+ * are usually streams from a Java class file or a class file component of a
+ * Jar file.
+ *
+ */
+public class ClassFile {
+
+    /** The Magic Value that marks the start of a Java class file  */
+    private static final int CLASS_MAGIC = 0xCAFEBABE;
+
+    /** This class' constant pool.  */
+    private ConstantPool constantPool;
+
+    /** The class name for this class.  */
+    private String className;
+
+    /**
+     * Read the class from a data stream. This method takes an InputStream
+     * as input and parses the class from the stream. <p>
+     *
+     *
+     *
+     * @param stream an InputStream from which the class will be read
+     * @exception IOException if there is a problem reading from the given
+     *      stream.
+     * @exception ClassFormatError if the class cannot be parsed correctly
+     */
+    public void read(InputStream stream) throws IOException, ClassFormatError {
+        DataInputStream classStream = new DataInputStream(stream);
+
+        if (classStream.readInt() != CLASS_MAGIC) {
+            throw new ClassFormatError("No Magic Code Found "
+                + "- probably not a Java class file.");
+        }
+
+        // right we have a good looking class file.
+        /* int minorVersion = */ classStream.readUnsignedShort();
+        /* int majorVersion = */ classStream.readUnsignedShort();
+
+        // read the constant pool in and resolve it
+        constantPool = new ConstantPool();
+
+        constantPool.read(classStream);
+        constantPool.resolve();
+
+        /* int accessFlags = */ classStream.readUnsignedShort();
+        int thisClassIndex = classStream.readUnsignedShort();
+        /* int superClassIndex = */ classStream.readUnsignedShort();
+        ClassCPInfo classInfo
+            = (ClassCPInfo) constantPool.getEntry(thisClassIndex);
+        className  = classInfo.getClassName();
+    }
+
+
+    /**
+     * Get the classes which this class references.
+     *
+     * @return a vector of class names which this class references
+     */
+    public Vector getClassRefs() {
+
+        Vector classRefs = new Vector();
+
+        for (int i = 0; i < constantPool.size(); ++i) {
+            ConstantPoolEntry entry = constantPool.getEntry(i);
+
+            if (entry != null
+                && entry.getTag() == ConstantPoolEntry.CONSTANT_CLASS) {
+                ClassCPInfo classEntry = (ClassCPInfo) entry;
+
+                if (!classEntry.getClassName().equals(className)) {
+                    classRefs.addElement(ClassFileUtils.convertSlashName(classEntry.getClassName()));
+                }
+            }
+        }
+
+        return classRefs;
+    }
+
+    /**
+     * Get the class' fully qualified name in dot format.
+     *
+     * @return the class name in dot format (eg. java.lang.Object)
+     */
+    public String getFullClassName() {
+        return ClassFileUtils.convertSlashName(className);
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileIterator.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileIterator.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileIterator.java	(revision 16139)
@@ -0,0 +1,32 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend;
+
+/**
+ * Iterator interface for iterating over a set of class files
+ *
+ */
+public interface ClassFileIterator {
+
+    /**
+     * Get the next class file in the iteration
+     *
+     * @return the next class file in the iteration
+     */
+    ClassFile getNextClassFile();
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileUtils.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileUtils.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/ClassFileUtils.java	(revision 16139)
@@ -0,0 +1,50 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend;
+
+/**
+ * Utility class file routines. This class provides a number of static
+ * utility methods to convert between the formats used in the Java class
+ * file format and those commonly used in Java programming.
+ *
+ *
+ */
+public class ClassFileUtils {
+
+    /**
+     * Convert a class name from class file slash notation to java source
+     * file dot notation.
+     *
+     * @param name the class name in slash notation org/apache/ant
+     * @return the class name in dot notation (eg. java.lang.Object).
+     */
+    public static String convertSlashName(String name) {
+        return name.replace('\\', '.').replace('/', '.');
+    }
+
+    /**
+     * Convert a class name from java source file dot notation to class file
+     * slash notation..
+     *
+     * @param dotName the class name in dot notation (eg. java.lang.Object).
+     * @return the class name in slash notation (eg. java/lang/Object).
+     */
+    public static String convertDotName(String dotName) {
+        return dotName.replace('.', '/');
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java	(revision 16139)
@@ -0,0 +1,816 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.depend.DependencyAnalyzer;
+
+/**
+ * Generates a dependency file for a given set of classes.
+ *
+ */
+public class Depend extends MatchingTask {
+    /**
+     * A class (struct) user to manage information about a class
+     *
+     */
+    private static class ClassFileInfo {
+        /** The file where the class file is stored in the file system */
+        private File absoluteFile;
+
+        /** The Java class name of this class */
+        private String className;
+
+        /** The source File containing this class */
+        private File sourceFile;
+
+        /** if user has been warned about this file not having a source file */
+        private boolean isUserWarned = false;
+    }
+
+    /** The path where source files exist */
+    private Path srcPath;
+
+    /** The path where compiled class files exist. */
+    private Path destPath;
+
+    /** The directory which contains the dependency cache. */
+    private File cache;
+
+    /** The list of source paths derived from the srcPath field. */
+    private String[] srcPathList;
+
+    /**
+     * A map which gives for every class a list of the class which it
+     * affects.
+     */
+    private Hashtable affectedClassMap;
+
+    /** A map which gives information about a class */
+    private Hashtable classFileInfoMap;
+
+    /**
+     * A map which gives the list of jars and classes from the classpath
+     * that a class depends upon
+     */
+    private Hashtable classpathDependencies;
+
+    /** The list of classes which are out of date. */
+    private Hashtable outOfDateClasses;
+
+    /**
+     * indicates that the dependency relationships should be extended beyond
+     * direct dependencies to include all classes. So if A directly affects
+     * B and B directly affects C, then A indirectly affects C.
+     */
+    private boolean closure = false;
+
+    /**
+     * Flag which controls whether the reversed dependencies should be
+     * dumped to the log
+     */
+    private boolean dump = false;
+
+    /** The classpath to look for additional dependencies */
+    private Path dependClasspath;
+
+    /** constants used with the cache file */
+    private static final String CACHE_FILE_NAME = "dependencies.txt";
+    /** String Used to separate classnames in the dependency file */
+    private static final String CLASSNAME_PREPEND = "||:";
+
+    /**
+     * Set the classpath to be used for this dependency check.
+     *
+     * @param classpath the classpath to be used when checking for
+     *      dependencies on elements in the classpath
+     */
+    public void setClasspath(Path classpath) {
+        if (dependClasspath == null) {
+            dependClasspath = classpath;
+        } else {
+            dependClasspath.append(classpath);
+        }
+    }
+
+    /**
+     * Gets the classpath to be used for this dependency check.
+     *
+     * @return the current dependency classpath
+     */
+    public Path getClasspath() {
+        return dependClasspath;
+    }
+
+    /**
+     * Adds a classpath to be used for this dependency check.
+     *
+     * @return A path object to be configured by Ant
+     */
+    public Path createClasspath() {
+        if (dependClasspath == null) {
+            dependClasspath = new Path(getProject());
+        }
+        return dependClasspath.createPath();
+    }
+
+    /**
+     * Adds a reference to a classpath defined elsewhere.
+     *
+     * @param r a reference to a path object to be used as the depend
+     *      classpath
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+
+    /**
+     * Read the dependencies from cache file
+     *
+     * @return a collection of class dependencies
+     * @exception IOException if the dependency file cannot be read
+     */
+    private Hashtable readCachedDependencies(File depFile) throws IOException {
+        Hashtable dependencyMap = new Hashtable();
+
+        BufferedReader in = null;
+        try {
+            in = new BufferedReader(new FileReader(depFile));
+            String line = null;
+            Vector dependencyList = null;
+            String className = null;
+            int prependLength = CLASSNAME_PREPEND.length();
+            while ((line = in.readLine()) != null) {
+                if (line.startsWith(CLASSNAME_PREPEND)) {
+                    dependencyList = new Vector();
+                    className = line.substring(prependLength);
+                    dependencyMap.put(className, dependencyList);
+                } else {
+                    dependencyList.addElement(line);
+                }
+            }
+        } finally {
+            if (in != null) {
+                in.close();
+            }
+        }
+
+        return dependencyMap;
+    }
+
+    /**
+     * Write the dependencies to cache file
+     *
+     * @param dependencyMap the map of dependencies to be written out.
+     * @exception IOException if the dependency file cannot be written out.
+     */
+    private void writeCachedDependencies(Hashtable dependencyMap)
+         throws IOException {
+        if (cache != null) {
+            PrintWriter pw = null;
+            try {
+                cache.mkdirs();
+                File depFile = new File(cache, CACHE_FILE_NAME);
+
+                pw = new PrintWriter(new FileWriter(depFile));
+                Enumeration e = dependencyMap.keys();
+                while (e.hasMoreElements()) {
+                    String className = (String) e.nextElement();
+
+                    pw.println(CLASSNAME_PREPEND + className);
+
+                    Vector dependencyList
+                         = (Vector) dependencyMap.get(className);
+                    int size = dependencyList.size();
+                    for (int x = 0; x < size; x++) {
+                        pw.println(dependencyList.elementAt(x));
+                    }
+                }
+            } finally {
+                if (pw != null) {
+                    pw.close();
+                }
+            }
+        }
+    }
+
+    /**
+     * Get the classpath for dependency checking.
+     *
+     * This method removes the dest dirs if it is given from the dependency classpath
+     */
+    private Path getCheckClassPath() {
+        if (dependClasspath == null) {
+            return null;
+        }
+
+        String[] destPathElements = destPath.list();
+        String[] classpathElements = dependClasspath.list();
+        String checkPath = "";
+        for (int i = 0; i < classpathElements.length; ++i) {
+            String element = classpathElements[i];
+            boolean inDestPath = false;
+            for (int j = 0; j < destPathElements.length && !inDestPath; ++j) {
+                inDestPath = destPathElements[j].equals(element);
+            }
+            if (!inDestPath) {
+                if (checkPath.length() == 0) {
+                    checkPath = element;
+                } else {
+                    checkPath += ":" + element;
+                }
+            }
+        }
+
+        if (checkPath.length() == 0) {
+            return null;
+        }
+
+        return new Path(getProject(), checkPath);
+    }
+
+    /**
+     * Determine the dependencies between classes. Class dependencies are
+     * determined by examining the class references in a class file to other
+     * classes.
+     *
+     * This method sets up the following fields
+     * <ul>
+     *   <li>affectedClassMap - the list of classes each class affects</li>
+     *   <li>classFileInfoMap - information about each class</li>
+     *   <li>classpathDependencies - the list of jars and classes from the
+     *                             classpath that each class depends upon.</li>
+     * </ul>
+     *
+     * If required, the dependencies are written to the cache.
+     *
+     * @exception IOException if either the dependencies cache or the class
+     *      files cannot be read or written
+     */
+    private void determineDependencies() throws IOException {
+        affectedClassMap = new Hashtable();
+        classFileInfoMap = new Hashtable();
+        boolean cacheDirty = false;
+
+        Hashtable dependencyMap = new Hashtable();
+        File cacheFile = null;
+        boolean cacheFileExists = true;
+        long cacheLastModified = Long.MAX_VALUE;
+
+        // read the dependency cache from the disk
+        if (cache != null) {
+            cacheFile = new File(cache, CACHE_FILE_NAME);
+            cacheFileExists = cacheFile.exists();
+            cacheLastModified = cacheFile.lastModified();
+            if (cacheFileExists) {
+                dependencyMap = readCachedDependencies(cacheFile);
+            }
+        }
+        Enumeration classfileEnum = getClassFiles(destPath).elements();
+        while (classfileEnum.hasMoreElements()) {
+            ClassFileInfo info = (ClassFileInfo) classfileEnum.nextElement();
+            log("Adding class info for " + info.className, Project.MSG_DEBUG);
+            classFileInfoMap.put(info.className, info);
+
+            Vector dependencyList = null;
+
+            if (cache != null) {
+                // try to read the dependency info from the map if it is
+                // not out of date
+                if (cacheFileExists
+                    && cacheLastModified > info.absoluteFile.lastModified()) {
+                    // depFile exists and is newer than the class file
+                    // need to get dependency list from the map.
+                    dependencyList = (Vector) dependencyMap.get(info.className);
+                }
+            }
+
+            if (dependencyList == null) {
+                // not cached - so need to read directly from the class file
+                DependencyAnalyzer analyzer = new AntAnalyzer();
+                analyzer.addRootClass(info.className);
+                analyzer.addClassPath(destPath);
+                analyzer.setClosure(false);
+                dependencyList = new Vector();
+                Enumeration depEnum = analyzer.getClassDependencies();
+                while (depEnum.hasMoreElements()) {
+                    dependencyList.addElement(depEnum.nextElement());
+                }
+                if (dependencyList != null) {
+                    cacheDirty = true;
+                    dependencyMap.put(info.className, dependencyList);
+                }
+            }
+
+            // This class depends on each class in the dependency list. For each
+            // one of those, add this class into their affected classes list
+            Enumeration depEnum = dependencyList.elements();
+            while (depEnum.hasMoreElements()) {
+                String dependentClass = (String) depEnum.nextElement();
+
+                Hashtable affectedClasses
+                    = (Hashtable) affectedClassMap.get(dependentClass);
+                if (affectedClasses == null) {
+                    affectedClasses = new Hashtable();
+                    affectedClassMap.put(dependentClass, affectedClasses);
+                }
+
+                affectedClasses.put(info.className, info);
+            }
+        }
+
+        classpathDependencies = null;
+        Path checkPath = getCheckClassPath();
+        if (checkPath != null) {
+            // now determine which jars each class depends upon
+            classpathDependencies = new Hashtable();
+            AntClassLoader loader = getProject().createClassLoader(checkPath);
+
+            Hashtable classpathFileCache = new Hashtable();
+            Object nullFileMarker = new Object();
+            for (Enumeration e = dependencyMap.keys(); e.hasMoreElements();) {
+                String className = (String) e.nextElement();
+                Vector dependencyList = (Vector) dependencyMap.get(className);
+                Hashtable dependencies = new Hashtable();
+                classpathDependencies.put(className, dependencies);
+                Enumeration e2 = dependencyList.elements();
+                while (e2.hasMoreElements()) {
+                    String dependency = (String) e2.nextElement();
+                    Object classpathFileObject
+                        = classpathFileCache.get(dependency);
+                    if (classpathFileObject == null) {
+                        classpathFileObject = nullFileMarker;
+
+                        if (!dependency.startsWith("java.")
+                            && !dependency.startsWith("javax.")) {
+                            URL classURL
+                                = loader.getResource(dependency.replace('.', '/') + ".class");
+                            if (classURL != null) {
+                                if (classURL.getProtocol().equals("jar")) {
+                                    String jarFilePath = classURL.getFile();
+                                    if (jarFilePath.startsWith("file:")) {
+                                        int classMarker = jarFilePath.indexOf('!');
+                                        jarFilePath = jarFilePath.substring(5, classMarker);
+                                    }
+                                    classpathFileObject = new File(jarFilePath);
+                                } else if (classURL.getProtocol().equals("file")) {
+                                    String classFilePath = classURL.getFile();
+                                    classpathFileObject = new File(classFilePath);
+                                }
+                                log("Class " + className
+                                    + " depends on " + classpathFileObject
+                                    + " due to " + dependency, Project.MSG_DEBUG);
+                            }
+                        }
+                        classpathFileCache.put(dependency, classpathFileObject);
+                    }
+                    if (classpathFileObject != null && classpathFileObject != nullFileMarker) {
+                        // we need to add this jar to the list for this class.
+                        File jarFile = (File) classpathFileObject;
+                        dependencies.put(jarFile, jarFile);
+                    }
+                }
+            }
+        }
+
+        // write the dependency cache to the disk
+        if (cache != null && cacheDirty) {
+            writeCachedDependencies(dependencyMap);
+        }
+    }
+
+    /**
+     * Delete all the class files which are out of date, by way of their
+     * dependency on a class which is out of date
+     *
+     * @return the number of files deleted.
+     */
+    private int deleteAllAffectedFiles() {
+        int count = 0;
+        for (Enumeration e = outOfDateClasses.elements(); e.hasMoreElements();) {
+            String className = (String) e.nextElement();
+            count += deleteAffectedFiles(className);
+            ClassFileInfo classInfo
+                = (ClassFileInfo) classFileInfoMap.get(className);
+            if (classInfo != null && classInfo.absoluteFile.exists()) {
+                classInfo.absoluteFile.delete();
+                count++;
+            }
+        }
+        return count;
+    }
+
+    /**
+     * Delete all the class files of classes which depend on the given class
+     *
+     * @param className the name of the class whose dependent classes will be
+     *      deleted
+     * @return the number of class files removed
+     */
+    private int deleteAffectedFiles(String className) {
+        int count = 0;
+
+        Hashtable affectedClasses = (Hashtable) affectedClassMap.get(className);
+        if (affectedClasses == null) {
+            return count;
+        }
+        for (Enumeration e = affectedClasses.keys(); e.hasMoreElements();) {
+            String affectedClass = (String) e.nextElement();
+            ClassFileInfo affectedClassInfo
+                = (ClassFileInfo) affectedClasses.get(affectedClass);
+
+            if (!affectedClassInfo.absoluteFile.exists()) {
+                continue;
+            }
+
+            if (affectedClassInfo.sourceFile == null) {
+                if (!affectedClassInfo.isUserWarned) {
+                    log("The class " + affectedClass + " in file "
+                        + affectedClassInfo.absoluteFile.getPath()
+                        + " is out of date due to " + className
+                        + " but has not been deleted because its source file"
+                        + " could not be determined", Project.MSG_WARN);
+                    affectedClassInfo.isUserWarned = true;
+                }
+                continue;
+            }
+
+            log("Deleting file " + affectedClassInfo.absoluteFile.getPath()
+                + " since " + className + " out of date", Project.MSG_VERBOSE);
+
+            affectedClassInfo.absoluteFile.delete();
+            count++;
+            if (closure) {
+                count += deleteAffectedFiles(affectedClass);
+            } else {
+                // without closure we may delete an inner class but not the
+                // top level class which would not trigger a recompile.
+
+                if (affectedClass.indexOf("$") == -1) {
+                    continue;
+                }
+                // need to delete the main class
+                String topLevelClassName
+                     = affectedClass.substring(0, affectedClass.indexOf("$"));
+                log("Top level class = " + topLevelClassName,
+                    Project.MSG_VERBOSE);
+                ClassFileInfo topLevelClassInfo
+                     = (ClassFileInfo) classFileInfoMap.get(topLevelClassName);
+                if (topLevelClassInfo != null
+                    && topLevelClassInfo.absoluteFile.exists()) {
+                    log("Deleting file "
+                        + topLevelClassInfo.absoluteFile.getPath()
+                        + " since one of its inner classes was removed",
+                        Project.MSG_VERBOSE);
+                    topLevelClassInfo.absoluteFile.delete();
+                    count++;
+                    if (closure) {
+                        count += deleteAffectedFiles(topLevelClassName);
+                    }
+                }
+            }
+        }
+        return count;
+    }
+
+    /**
+     * Dump the dependency information loaded from the classes to the Ant log
+     */
+    private void dumpDependencies() {
+        log("Reverse Dependency Dump for " + affectedClassMap.size()
+            + " classes:", Project.MSG_DEBUG);
+
+        Enumeration classEnum = affectedClassMap.keys();
+        while (classEnum.hasMoreElements()) {
+            String className = (String) classEnum.nextElement();
+            log(" Class " + className + " affects:", Project.MSG_DEBUG);
+            Hashtable affectedClasses
+                = (Hashtable) affectedClassMap.get(className);
+            Enumeration affectedClassEnum = affectedClasses.keys();
+            while (affectedClassEnum.hasMoreElements()) {
+                String affectedClass = (String) affectedClassEnum.nextElement();
+                ClassFileInfo info
+                    = (ClassFileInfo) affectedClasses.get(affectedClass);
+                log("    " + affectedClass + " in "
+                    + info.absoluteFile.getPath(), Project.MSG_DEBUG);
+            }
+        }
+
+        if (classpathDependencies != null) {
+            log("Classpath file dependencies (Forward):", Project.MSG_DEBUG);
+
+            Enumeration classpathEnum = classpathDependencies.keys();
+            while (classpathEnum.hasMoreElements()) {
+                String className = (String) classpathEnum.nextElement();
+                log(" Class " + className + " depends on:", Project.MSG_DEBUG);
+                Hashtable dependencies
+                    = (Hashtable) classpathDependencies.get(className);
+
+                Enumeration classpathFileEnum = dependencies.elements();
+                while (classpathFileEnum.hasMoreElements()) {
+                    File classpathFile = (File) classpathFileEnum.nextElement();
+                    log("    " + classpathFile.getPath(), Project.MSG_DEBUG);
+                }
+            }
+        }
+    }
+
+    private void determineOutOfDateClasses() {
+        outOfDateClasses = new Hashtable();
+        for (int i = 0; i < srcPathList.length; i++) {
+            File srcDir = (File) getProject().resolveFile(srcPathList[i]);
+            if (srcDir.exists()) {
+                DirectoryScanner ds = this.getDirectoryScanner(srcDir);
+                String[] files = ds.getIncludedFiles();
+                scanDir(srcDir, files);
+            }
+        }
+
+        // now check classpath file dependencies
+        if (classpathDependencies == null) {
+            return;
+        }
+
+        Enumeration classpathDepsEnum = classpathDependencies.keys();
+        while (classpathDepsEnum.hasMoreElements()) {
+            String className = (String) classpathDepsEnum.nextElement();
+            if (outOfDateClasses.containsKey(className)) {
+                continue;
+            }
+            ClassFileInfo info
+                = (ClassFileInfo) classFileInfoMap.get(className);
+
+            // if we have no info about the class - it may have been deleted already and we
+            // are using cached info.
+            if (info != null) {
+                Hashtable dependencies
+                    = (Hashtable) classpathDependencies.get(className);
+                for (Enumeration e2 = dependencies.elements(); e2.hasMoreElements();) {
+                    File classpathFile = (File) e2.nextElement();
+                    if (classpathFile.lastModified()
+                        > info.absoluteFile.lastModified()) {
+                        log("Class " + className
+                            + " is out of date with respect to "
+                            + classpathFile, Project.MSG_DEBUG);
+                        outOfDateClasses.put(className, className);
+                        break;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Does the work.
+     *
+     * @exception BuildException Thrown in case of an unrecoverable error.
+     */
+    public void execute() throws BuildException {
+        try {
+            long start = System.currentTimeMillis();
+            if (srcPath == null) {
+                throw new BuildException("srcdir attribute must be set",
+                                         getLocation());
+            }
+
+            srcPathList = srcPath.list();
+            if (srcPathList.length == 0) {
+                throw new BuildException("srcdir attribute must be non-empty",
+                                         getLocation());
+            }
+
+            if (destPath == null) {
+                destPath = srcPath;
+            }
+
+            if (cache != null && cache.exists() && !cache.isDirectory()) {
+                throw new BuildException("The cache, if specified, must "
+                    + "point to a directory");
+            }
+
+            if (cache != null && !cache.exists()) {
+                cache.mkdirs();
+            }
+
+            determineDependencies();
+            if (dump) {
+                dumpDependencies();
+            }
+            determineOutOfDateClasses();
+            int count = deleteAllAffectedFiles();
+
+            long duration = (System.currentTimeMillis() - start) / 1000;
+            
+            final int summaryLogLevel;
+            if(count>0) {
+                summaryLogLevel = Project.MSG_INFO;
+            }  else {
+                summaryLogLevel = Project.MSG_DEBUG;
+            }
+            
+            log("Deleted " + count + " out of date files in "
+                + duration + " seconds", summaryLogLevel);
+        } catch (Exception e) {
+            throw new BuildException(e);
+        }
+    }
+
+    /**
+     * Scans the directory looking for source files that are newer than
+     * their class files. The results are returned in the class variable
+     * compileList
+     *
+     * @param srcDir the source directory
+     * @param files the names of the files in the source dir which are to be
+     *      checked.
+     */
+    protected void scanDir(File srcDir, String files[]) {
+
+        for (int i = 0; i < files.length; i++) {
+            File srcFile = new File(srcDir, files[i]);
+            if (files[i].endsWith(".java")) {
+                String filePath = srcFile.getPath();
+                String className
+                    = filePath.substring(srcDir.getPath().length() + 1,
+                        filePath.length() - ".java".length());
+                className = ClassFileUtils.convertSlashName(className);
+                ClassFileInfo info
+                    = (ClassFileInfo) classFileInfoMap.get(className);
+                if (info == null) {
+                    // there was no class file. add this class to the list
+                    outOfDateClasses.put(className, className);
+                } else {
+                    if (srcFile.lastModified()
+                        > info.absoluteFile.lastModified()) {
+                        outOfDateClasses.put(className, className);
+                    }
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Get the list of class files we are going to analyse.
+     *
+     * @param classLocations a path structure containing all the directories
+     *      where classes can be found.
+     * @return a vector containing the classes to analyse.
+     */
+    private Vector getClassFiles(Path classLocations) {
+        // break the classLocations into its components.
+        String[] classLocationsList = classLocations.list();
+
+        Vector classFileList = new Vector();
+
+        for (int i = 0; i < classLocationsList.length; ++i) {
+            File dir = new File(classLocationsList[i]);
+            if (dir.isDirectory()) {
+                addClassFiles(classFileList, dir, dir);
+            }
+        }
+
+        return classFileList;
+    }
+
+    /**
+     * Find the source file for a given class
+     *
+     * @param classname the classname in slash format.
+     */
+    private File findSourceFile(String classname) {
+        String sourceFilename = classname + ".java";
+        int innerIndex = classname.indexOf("$");
+        if (innerIndex != -1) {
+            sourceFilename = classname.substring(0, innerIndex) + ".java";
+        }
+
+        // search the various source path entries
+        for (int i = 0; i < srcPathList.length; ++i) {
+            File sourceFile = new File(srcPathList[i], sourceFilename);
+            if (sourceFile.exists()) {
+                return sourceFile;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Add the list of class files from the given directory to the class
+     * file vector, including any subdirectories.
+     *
+     * @param classFileList a list of ClassFileInfo objects for all the
+     *      files in the directory tree
+     * @param dir the directory tree to be searched, recursively, for class
+     *      files
+     * @param root the root of the source tree. This is used to determine
+     *      the absolute class name from the relative position in the
+     *      source tree
+     */
+    private void addClassFiles(Vector classFileList, File dir, File root) {
+        String[] filesInDir = dir.list();
+
+        if (filesInDir == null) {
+            return;
+        }
+        int length = filesInDir.length;
+
+        int rootLength = root.getPath().length();
+        for (int i = 0; i < length; ++i) {
+            File file = new File(dir, filesInDir[i]);
+            if (file.isDirectory()) {
+                addClassFiles(classFileList, file, root);
+            } else if (file.getName().endsWith(".class")) {
+                ClassFileInfo info = new ClassFileInfo();
+                info.absoluteFile = file;
+                String relativeName = file.getPath().substring(rootLength + 1,
+                    file.getPath().length() - 6);
+                info.className
+                    = ClassFileUtils.convertSlashName(relativeName);
+                info.sourceFile = findSourceFile(relativeName);
+                classFileList.addElement(info);
+            }
+        }
+    }
+
+
+    /**
+     * Set the directories path to find the Java source files.
+     *
+     * @param srcPath the source path
+     */
+    public void setSrcdir(Path srcPath) {
+        this.srcPath = srcPath;
+    }
+
+    /**
+     * Set the destination directory where the compiled Java files exist.
+     *
+     * @param destPath the destination areas where build files are written
+     */
+    public void setDestDir(Path destPath) {
+        this.destPath = destPath;
+    }
+
+    /**
+     * Sets the dependency cache file.
+     *
+     * @param cache the dependency cache file
+     */
+    public void setCache(File cache) {
+        this.cache = cache;
+    }
+
+    /**
+     * If true, transitive dependencies are followed until the
+     * closure of the dependency set if reached.
+     * When not set, the depend task will only follow
+     * direct dependencies between classes.
+     *
+     * @param closure indicate if dependency closure is required.
+     */
+    public void setClosure(boolean closure) {
+        this.closure = closure;
+    }
+
+    /**
+     * If true, the dependency information will be written
+     * to the debug level log.
+     *
+     * @param dump set to true to dump dependency information to the log
+     */
+    public void setDump(boolean dump) {
+        this.dump = dump;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/DirectoryIterator.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/DirectoryIterator.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/DirectoryIterator.java	(revision 16139)
@@ -0,0 +1,175 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Stack;
+import java.util.Vector;
+
+/**
+ * An iterator which iterates through the contents of a java directory. The
+ * iterator should be created with the directory at the root of the Java
+ * namespace.
+ *
+ */
+public class DirectoryIterator implements ClassFileIterator {
+
+    /**
+     * This is a stack of current iterators supporting the depth first
+     * traversal of the directory tree.
+     */
+    private Stack enumStack;
+
+    /**
+     * The current directory iterator. As directories encounter lower level
+     * directories, the current iterator is pushed onto the iterator stack
+     * and a new iterator over the sub directory becomes the current
+     * directory. This implements a depth first traversal of the directory
+     * namespace.
+     */
+    private Enumeration currentEnum;
+
+    /**
+     * The length of the root directory. This is used to remove the root
+     * directory from full paths.
+     */
+    private int rootLength;
+
+    /**
+     * Creates a directory iterator. The directory iterator is created to
+     * scan the root directory. If the changeInto flag is given, then the
+     * entries returned will be relative to this directory and not the
+     * current directory.
+     *
+     * @param rootDirectory the root if the directory namespace which is to
+     *      be iterated over
+     * @param changeInto if true then the returned entries will be relative
+     *      to the rootDirectory and not the current directory.
+     * @exception IOException if there is a problem reading the directory
+     *      information.
+     */
+    public DirectoryIterator(File rootDirectory, boolean changeInto)
+         throws IOException {
+        super();
+
+        enumStack = new Stack();
+
+        if (rootDirectory.isAbsolute() || changeInto) {
+            rootLength = rootDirectory.getPath().length() + 1;
+        } else {
+            rootLength = 0;
+        }
+
+        Vector filesInRoot = getDirectoryEntries(rootDirectory);
+
+        currentEnum = filesInRoot.elements();
+    }
+
+    /**
+     * Get a vector covering all the entries (files and subdirectories in a
+     * directory).
+     *
+     * @param directory the directory to be scanned.
+     * @return a vector containing File objects for each entry in the
+     *      directory.
+     */
+    private Vector getDirectoryEntries(File directory) {
+        Vector files = new Vector();
+
+        // File[] filesInDir = directory.listFiles();
+        String[] filesInDir = directory.list();
+
+        if (filesInDir != null) {
+            int length = filesInDir.length;
+
+            for (int i = 0; i < length; ++i) {
+                files.addElement(new File(directory, filesInDir[i]));
+            }
+        }
+
+        return files;
+    }
+
+    /**
+     * Template method to allow subclasses to supply elements for the
+     * iteration. The directory iterator maintains a stack of iterators
+     * covering each level in the directory hierarchy. The current iterator
+     * covers the current directory being scanned. If the next entry in that
+     * directory is a subdirectory, the current iterator is pushed onto the
+     * stack and a new iterator is created for the subdirectory. If the
+     * entry is a file, it is returned as the next element and the iterator
+     * remains valid. If there are no more entries in the current directory,
+     * the topmost iterator on the stack is popped off to become the
+     * current iterator.
+     *
+     * @return the next ClassFile in the iteration.
+     */
+    public ClassFile getNextClassFile() {
+        ClassFile nextElement = null;
+
+        try {
+            while (nextElement == null) {
+                if (currentEnum.hasMoreElements()) {
+                    File element = (File) currentEnum.nextElement();
+
+                    if (element.isDirectory()) {
+
+                        // push the current iterator onto the stack and then
+                        // iterate through this directory.
+                        enumStack.push(currentEnum);
+
+                        Vector files = getDirectoryEntries(element);
+
+                        currentEnum = files.elements();
+                    } else {
+
+                        // we have a file. create a stream for it
+                        FileInputStream inFileStream
+                            = new FileInputStream(element);
+
+                        if (element.getName().endsWith(".class")) {
+
+                            // create a data input stream from the jar
+                            // input stream
+                            ClassFile javaClass = new ClassFile();
+
+                            javaClass.read(inFileStream);
+
+                            nextElement = javaClass;
+                        }
+                    }
+                } else {
+                    // this iterator is exhausted. Can we pop one off the stack
+                    if (enumStack.empty()) {
+                        break;
+                    } else {
+                        currentEnum = (Enumeration) enumStack.pop();
+                    }
+                }
+            }
+        } catch (IOException e) {
+            nextElement = null;
+        }
+
+        return nextElement;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/JarFileIterator.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/JarFileIterator.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/JarFileIterator.java	(revision 16139)
@@ -0,0 +1,88 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+/**
+ * A class file iterator which iterates through the contents of a Java jar
+ * file.
+ *
+ */
+public class JarFileIterator implements ClassFileIterator {
+    /** The jar stream from the jar file being iterated over*/
+    private ZipInputStream jarStream;
+
+    /**
+     * Construct an iterator over a jar stream
+     *
+     * @param stream the basic input stream from which the Jar is received
+     * @exception IOException if the jar stream cannot be created
+     */
+    public JarFileIterator(InputStream stream) throws IOException {
+        super();
+
+        jarStream = new ZipInputStream(stream);
+    }
+
+    /**
+     * Get the next ClassFile object from the jar
+     *
+     * @return a ClassFile object describing the class from the jar
+     */
+    public ClassFile getNextClassFile() {
+        ZipEntry jarEntry;
+        ClassFile nextElement = null;
+
+        try {
+            jarEntry = jarStream.getNextEntry();
+
+            while (nextElement == null && jarEntry != null) {
+                String entryName = jarEntry.getName();
+
+                if (!jarEntry.isDirectory() && entryName.endsWith(".class")) {
+
+                    // create a data input stream from the jar input stream
+                    ClassFile javaClass = new ClassFile();
+
+                    javaClass.read(jarStream);
+
+                    nextElement = javaClass;
+                } else {
+
+                    jarEntry = jarStream.getNextEntry();
+                }
+            }
+        } catch (IOException e) {
+            String message = e.getMessage();
+            String text = e.getClass().getName();
+
+            if (message != null) {
+                text += ": " + message;
+            }
+
+            throw new RuntimeException("Problem reading JAR file: " + text);
+        }
+
+        return nextElement;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ClassCPInfo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ClassCPInfo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ClassCPInfo.java	(revision 16139)
@@ -0,0 +1,92 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * The constant pool entry which stores class information.
+ *
+ */
+public class ClassCPInfo extends ConstantPoolEntry {
+
+    /**
+     * The class' name. This will be only valid if the entry has been
+     * resolved against the constant pool.
+     */
+    private String className;
+
+    /**
+     * The index into the constant pool where this class' name is stored. If
+     * the class name is changed, this entry is invalid until this entry is
+     * connected to a constant pool.
+     */
+    private int index;
+
+    /**
+     * Constructor. Sets the tag value for this entry to type Class
+     */
+    public ClassCPInfo() {
+        super(CONSTANT_CLASS, 1);
+    }
+
+    /**
+     * Read the entry from a stream.
+     *
+     * @param cpStream the stream containing the constant pool entry to be
+     *      read.
+     * @exception IOException thrown if there is a problem reading the entry
+     *      from the stream.
+     */
+    public void read(DataInputStream cpStream) throws IOException {
+        index = cpStream.readUnsignedShort();
+        className = "unresolved";
+    }
+
+    /**
+     * Generate a string readable version of this entry
+     *
+     * @return string representation of this constant pool entry
+     */
+    public String toString() {
+        return "Class Constant Pool Entry for " + className + "[" + index + "]";
+    }
+
+    /**
+     * Resolve this class info against the given constant pool.
+     *
+     * @param constantPool the constant pool with which to resolve the
+     *      class.
+     */
+    public void resolve(ConstantPool constantPool) {
+        className = ((Utf8CPInfo) constantPool.getEntry(index)).getValue();
+
+        super.resolve(constantPool);
+    }
+
+    /**
+     * Get the class name of this entry.
+     *
+     * @return the class' name.
+     */
+    public String getClassName() {
+        return className;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantCPInfo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantCPInfo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantCPInfo.java	(revision 16139)
@@ -0,0 +1,62 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend.constantpool;
+
+/**
+ * A Constant Pool entry which represents a constant value.
+ *
+ */
+public abstract class ConstantCPInfo extends ConstantPoolEntry {
+
+    /**
+     * The entry's untyped value. Each subclass interprets the constant
+     * value based on the subclass's type. The value here must be
+     * compatible.
+     */
+    private Object value;
+
+    /**
+     * Initialise the constant entry.
+     *
+     * @param tagValue the constant pool entry type to be used.
+     * @param entries the number of constant pool entry slots occupied by
+     *      this entry.
+     */
+    protected ConstantCPInfo(int tagValue, int entries) {
+        super(tagValue, entries);
+    }
+
+    /**
+     * Get the value of the constant.
+     *
+     * @return the value of the constant (untyped).
+     */
+    public Object getValue() {
+        return value;
+    }
+
+    /**
+     * Set the constant value.
+     *
+     * @param newValue the new untyped value of this constant.
+     */
+    public void setValue(Object newValue) {
+        value = newValue;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPool.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPool.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPool.java	(revision 16139)
@@ -0,0 +1,353 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+/**
+ * The constant pool of a Java class. The constant pool is a collection of
+ * constants used in a Java class file. It stores strings, constant values,
+ * class names, method names, field names etc.
+ *
+ * @see <a href="http://java.sun.com/docs/books/vmspec/">The Java Virtual
+ *      Machine Specification</a>
+ */
+public class ConstantPool {
+
+    /** The entries in the constant pool. */
+    private Vector entries;
+
+    /**
+     * A Hashtable of UTF8 entries - used to get constant pool indexes of
+     * the UTF8 values quickly
+     */
+    private Hashtable utf8Indexes;
+
+    /** Initialise the constant pool. */
+    public ConstantPool() {
+        entries = new Vector();
+
+        // The zero index is never present in the constant pool itself so
+        // we add a null entry for it
+        entries.addElement(null);
+
+        utf8Indexes = new Hashtable();
+    }
+
+    /**
+     * Read the constant pool from a class input stream.
+     *
+     * @param classStream the DataInputStream of a class file.
+     * @exception IOException if there is a problem reading the constant pool
+     *      from the stream
+     */
+    public void read(DataInputStream classStream) throws IOException {
+        int numEntries = classStream.readUnsignedShort();
+
+        for (int i = 1; i < numEntries;) {
+            ConstantPoolEntry nextEntry
+                 = ConstantPoolEntry.readEntry(classStream);
+
+            i += nextEntry.getNumEntries();
+
+            addEntry(nextEntry);
+        }
+    }
+
+    /**
+     * Get the size of the constant pool.
+     *
+     * @return the size of the constant pool
+     */
+    public int size() {
+        return entries.size();
+    }
+
+    /**
+     * Add an entry to the constant pool.
+     *
+     * @param entry the new entry to be added to the constant pool.
+     * @return the index into the constant pool at which the entry is
+     *      stored.
+     */
+    public int addEntry(ConstantPoolEntry entry) {
+        int index = entries.size();
+
+        entries.addElement(entry);
+
+        int numSlots = entry.getNumEntries();
+
+        // add null entries for any additional slots required.
+        for (int j = 0; j < numSlots - 1; ++j) {
+            entries.addElement(null);
+        }
+
+        if (entry instanceof Utf8CPInfo) {
+            Utf8CPInfo utf8Info = (Utf8CPInfo) entry;
+
+            utf8Indexes.put(utf8Info.getValue(), new Integer(index));
+        }
+
+        return index;
+    }
+
+    /**
+     * Resolve the entries in the constant pool. Resolution of the constant
+     * pool involves transforming indexes to other constant pool entries
+     * into the actual data for that entry.
+     */
+    public void resolve() {
+        for (Enumeration i = entries.elements(); i.hasMoreElements();) {
+            ConstantPoolEntry poolInfo = (ConstantPoolEntry) i.nextElement();
+
+            if (poolInfo != null && !poolInfo.isResolved()) {
+                poolInfo.resolve(this);
+            }
+        }
+    }
+
+
+    /**
+     * Get an constant pool entry at a particular index.
+     *
+     * @param index the index into the constant pool.
+     * @return the constant pool entry at that index.
+     */
+    public ConstantPoolEntry getEntry(int index) {
+        return (ConstantPoolEntry) entries.elementAt(index);
+    }
+
+    /**
+     * Get the index of a given UTF8 constant pool entry.
+     *
+     * @param value the string value of the UTF8 entry.
+     * @return the index at which the given string occurs in the constant
+     *      pool or -1 if the value does not occur.
+     */
+    public int getUTF8Entry(String value) {
+        int index = -1;
+        Integer indexInteger = (Integer) utf8Indexes.get(value);
+
+        if (indexInteger != null) {
+            index = indexInteger.intValue();
+        }
+
+        return index;
+    }
+
+    /**
+     * Get the index of a given CONSTANT_CLASS entry in the constant pool.
+     *
+     * @param className the name of the class for which the class entry
+     *      index is required.
+     * @return the index at which the given class entry occurs in the
+     *      constant pool or -1 if the value does not occur.
+     */
+    public int getClassEntry(String className) {
+        int index = -1;
+
+        for (int i = 0; i < entries.size() && index == -1; ++i) {
+            Object element = entries.elementAt(i);
+
+            if (element instanceof ClassCPInfo) {
+                ClassCPInfo classinfo = (ClassCPInfo) element;
+
+                if (classinfo.getClassName().equals(className)) {
+                    index = i;
+                }
+            }
+        }
+
+        return index;
+    }
+
+    /**
+     * Get the index of a given constant value entry in the constant pool.
+     *
+     * @param constantValue the constant value for which the index is
+     *      required.
+     * @return the index at which the given value entry occurs in the
+     *      constant pool or -1 if the value does not occur.
+     */
+    public int getConstantEntry(Object constantValue) {
+        int index = -1;
+
+        for (int i = 0; i < entries.size() && index == -1; ++i) {
+            Object element = entries.elementAt(i);
+
+            if (element instanceof ConstantCPInfo) {
+                ConstantCPInfo constantEntry = (ConstantCPInfo) element;
+
+                if (constantEntry.getValue().equals(constantValue)) {
+                    index = i;
+                }
+            }
+        }
+
+        return index;
+    }
+
+    /**
+     * Get the index of a given CONSTANT_METHODREF entry in the constant
+     * pool.
+     *
+     * @param methodClassName the name of the class which contains the
+     *      method being referenced.
+     * @param methodName the name of the method being referenced.
+     * @param methodType the type descriptor of the method being referenced.
+     * @return the index at which the given method ref entry occurs in the
+     *      constant pool or -1 if the value does not occur.
+     */
+    public int getMethodRefEntry(String methodClassName, String methodName,
+                                 String methodType) {
+        int index = -1;
+
+        for (int i = 0; i < entries.size() && index == -1; ++i) {
+            Object element = entries.elementAt(i);
+
+            if (element instanceof MethodRefCPInfo) {
+                MethodRefCPInfo methodRefEntry = (MethodRefCPInfo) element;
+
+                if (methodRefEntry.getMethodClassName().equals(methodClassName)
+                     && methodRefEntry.getMethodName().equals(methodName)
+                     && methodRefEntry.getMethodType().equals(methodType)) {
+                    index = i;
+                }
+            }
+        }
+
+        return index;
+    }
+
+    /**
+     * Get the index of a given CONSTANT_INTERFACEMETHODREF entry in the
+     * constant pool.
+     *
+     * @param interfaceMethodClassName the name of the interface which
+     *      contains the method being referenced.
+     * @param interfaceMethodName the name of the method being referenced.
+     * @param interfaceMethodType the type descriptor of the method being
+     *      referenced.
+     * @return the index at which the given method ref entry occurs in the
+     *      constant pool or -1 if the value does not occur.
+     */
+    public int getInterfaceMethodRefEntry(String interfaceMethodClassName,
+                                          String interfaceMethodName,
+                                          String interfaceMethodType) {
+        int index = -1;
+
+        for (int i = 0; i < entries.size() && index == -1; ++i) {
+            Object element = entries.elementAt(i);
+
+            if (element instanceof InterfaceMethodRefCPInfo) {
+                InterfaceMethodRefCPInfo interfaceMethodRefEntry
+                     = (InterfaceMethodRefCPInfo) element;
+
+                if (interfaceMethodRefEntry.getInterfaceMethodClassName().equals(interfaceMethodClassName)
+                     && interfaceMethodRefEntry.getInterfaceMethodName().equals(interfaceMethodName)
+                     && interfaceMethodRefEntry.getInterfaceMethodType().equals(interfaceMethodType)) {
+                    index = i;
+                }
+            }
+        }
+
+        return index;
+    }
+
+    /**
+     * Get the index of a given CONSTANT_FIELDREF entry in the constant
+     * pool.
+     *
+     * @param fieldClassName the name of the class which contains the field
+     *      being referenced.
+     * @param fieldName the name of the field being referenced.
+     * @param fieldType the type descriptor of the field being referenced.
+     * @return the index at which the given field ref entry occurs in the
+     *      constant pool or -1 if the value does not occur.
+     */
+    public int getFieldRefEntry(String fieldClassName, String fieldName,
+                                String fieldType) {
+        int index = -1;
+
+        for (int i = 0; i < entries.size() && index == -1; ++i) {
+            Object element = entries.elementAt(i);
+
+            if (element instanceof FieldRefCPInfo) {
+                FieldRefCPInfo fieldRefEntry = (FieldRefCPInfo) element;
+
+                if (fieldRefEntry.getFieldClassName().equals(fieldClassName)
+                     && fieldRefEntry.getFieldName().equals(fieldName)
+                     && fieldRefEntry.getFieldType().equals(fieldType)) {
+                    index = i;
+                }
+            }
+        }
+
+        return index;
+    }
+
+    /**
+     * Get the index of a given CONSTANT_NAMEANDTYPE entry in the constant
+     * pool.
+     *
+     * @param name the name
+     * @param type the type
+     * @return the index at which the given NameAndType entry occurs in the
+     *      constant pool or -1 if the value does not occur.
+     */
+    public int getNameAndTypeEntry(String name, String type) {
+        int index = -1;
+
+        for (int i = 0; i < entries.size() && index == -1; ++i) {
+            Object element = entries.elementAt(i);
+
+            if (element instanceof NameAndTypeCPInfo) {
+                NameAndTypeCPInfo nameAndTypeEntry
+                    = (NameAndTypeCPInfo) element;
+
+                if (nameAndTypeEntry.getName().equals(name)
+                     && nameAndTypeEntry.getType().equals(type)) {
+                    index = i;
+                }
+            }
+        }
+
+        return index;
+    }
+
+    /**
+     * Dump the constant pool to a string.
+     *
+     * @return the constant pool entries as strings
+     */
+    public String toString() {
+        StringBuffer sb = new StringBuffer("\n");
+        int size = entries.size();
+
+        for (int i = 0; i < size; ++i) {
+            sb.append("[" + i + "] = " + getEntry(i) + "\n");
+        }
+
+        return sb.toString();
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPoolEntry.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPoolEntry.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/ConstantPoolEntry.java	(revision 16139)
@@ -0,0 +1,220 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * An entry in the constant pool. This class contains a representation of the
+ * constant pool entries. It is an abstract base class for all the different
+ * forms of constant pool entry.
+ *
+ * @see ConstantPool
+ */
+public abstract class ConstantPoolEntry {
+
+    /** Tag value for UTF8 entries. */
+    public static final int CONSTANT_UTF8 = 1;
+
+    /** Tag value for Integer entries. */
+    public static final int CONSTANT_INTEGER = 3;
+
+    /** Tag value for Float entries. */
+    public static final int CONSTANT_FLOAT = 4;
+
+    /** Tag value for Long entries. */
+    public static final int CONSTANT_LONG = 5;
+
+    /** Tag value for Double entries. */
+    public static final int CONSTANT_DOUBLE = 6;
+
+    /** Tag value for Class entries. */
+    public static final int CONSTANT_CLASS = 7;
+
+    /** Tag value for String entries. */
+    public static final int CONSTANT_STRING = 8;
+
+    /** Tag value for Field Reference entries. */
+    public static final int CONSTANT_FIELDREF = 9;
+
+    /** Tag value for Method Reference entries. */
+    public static final int CONSTANT_METHODREF = 10;
+
+    /** Tag value for Interface Method Reference entries. */
+    public static final int CONSTANT_INTERFACEMETHODREF = 11;
+
+    /** Tag value for Name and Type entries. */
+    public static final int CONSTANT_NAMEANDTYPE = 12;
+
+    /**
+     * This entry's tag which identifies the type of this constant pool
+     * entry.
+     */
+    private int tag;
+
+    /**
+     * The number of slots in the constant pool, occupied by this entry.
+     */
+    private int numEntries;
+
+    /**
+     * A flag which indicates if this entry has been resolved or not.
+     */
+    private boolean resolved;
+
+    /**
+     * Initialise the constant pool entry.
+     *
+     * @param tagValue the tag value which identifies which type of constant
+     *      pool entry this is.
+     * @param entries the number of constant pool entry slots this entry
+     *      occupies.
+     */
+    public ConstantPoolEntry(int tagValue, int entries) {
+        tag = tagValue;
+        numEntries = entries;
+        resolved = false;
+    }
+
+    /**
+     * Read a constant pool entry from a stream. This is a factory method
+     * which reads a constant pool entry form a stream and returns the
+     * appropriate subclass for the entry.
+     *
+     * @param cpStream the stream from which the constant pool entry is to
+     *      be read.
+     * @return the appropriate ConstantPoolEntry subclass representing the
+     *      constant pool entry from the stream.
+     * @exception IOException if the constant pool entry cannot be read
+     *      from the stream
+     */
+    public static ConstantPoolEntry readEntry(DataInputStream cpStream)
+         throws IOException {
+        ConstantPoolEntry cpInfo = null;
+        int cpTag = cpStream.readUnsignedByte();
+
+        switch (cpTag) {
+
+            case CONSTANT_UTF8:
+                cpInfo = new Utf8CPInfo();
+
+                break;
+            case CONSTANT_INTEGER:
+                cpInfo = new IntegerCPInfo();
+
+                break;
+            case CONSTANT_FLOAT:
+                cpInfo = new FloatCPInfo();
+
+                break;
+            case CONSTANT_LONG:
+                cpInfo = new LongCPInfo();
+
+                break;
+            case CONSTANT_DOUBLE:
+                cpInfo = new DoubleCPInfo();
+
+                break;
+            case CONSTANT_CLASS:
+                cpInfo = new ClassCPInfo();
+
+                break;
+            case CONSTANT_STRING:
+                cpInfo = new StringCPInfo();
+
+                break;
+            case CONSTANT_FIELDREF:
+                cpInfo = new FieldRefCPInfo();
+
+                break;
+            case CONSTANT_METHODREF:
+                cpInfo = new MethodRefCPInfo();
+
+                break;
+            case CONSTANT_INTERFACEMETHODREF:
+                cpInfo = new InterfaceMethodRefCPInfo();
+
+                break;
+            case CONSTANT_NAMEANDTYPE:
+                cpInfo = new NameAndTypeCPInfo();
+
+                break;
+            default:
+                throw new ClassFormatError("Invalid Constant Pool entry Type "
+                     + cpTag);
+        }
+
+        cpInfo.read(cpStream);
+
+        return cpInfo;
+    }
+
+    /**
+     * Indicates whether this entry has been resolved. In general a constant
+     * pool entry can reference another constant pool entry by its index
+     * value. Resolution involves replacing this index value with the
+     * constant pool entry at that index.
+     *
+     * @return true if this entry has been resolved.
+     */
+    public boolean isResolved() {
+        return resolved;
+    }
+
+    /**
+     * Resolve this constant pool entry with respect to its dependents in
+     * the constant pool.
+     *
+     * @param constantPool the constant pool of which this entry is a member
+     *      and against which this entry is to be resolved.
+     */
+    public void resolve(ConstantPool constantPool) {
+        resolved = true;
+    }
+
+    /**
+     * read a constant pool entry from a class stream.
+     *
+     * @param cpStream the DataInputStream which contains the constant pool
+     *      entry to be read.
+     * @exception IOException if there is a problem reading the entry from
+     *      the stream.
+     */
+    public abstract void read(DataInputStream cpStream) throws IOException;
+
+    /**
+     * Get the Entry's type tag.
+     *
+     * @return The Tag value of this entry
+     */
+    public int getTag() {
+        return tag;
+    }
+
+    /**
+     * Get the number of Constant Pool Entry slots within the constant pool
+     * occupied by this entry.
+     *
+     * @return the number of slots used.
+     */
+    public final int getNumEntries() {
+        return numEntries;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/DoubleCPInfo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/DoubleCPInfo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/DoubleCPInfo.java	(revision 16139)
@@ -0,0 +1,57 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * The constant pool entry subclass used to represent double constant
+ * values.
+ *
+ */
+public class DoubleCPInfo extends ConstantCPInfo {
+    /**
+     * Constructor
+     */
+    public DoubleCPInfo() {
+        super(CONSTANT_DOUBLE, 2);
+    }
+
+    /**
+     * read a constant pool entry from a class stream.
+     *
+     * @param cpStream the DataInputStream which contains the constant pool
+     *      entry to be read.
+     * @exception IOException if there is a problem reading the entry from the
+     *      stream.
+     */
+    public void read(DataInputStream cpStream) throws IOException {
+        setValue(new Double(cpStream.readDouble()));
+    }
+
+    /**
+     * Print a readable version of the constant pool entry.
+     *
+     * @return the string representation of this constant pool entry.
+     */
+    public String toString() {
+        return "Double Constant Pool Entry: " + getValue();
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FieldRefCPInfo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FieldRefCPInfo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FieldRefCPInfo.java	(revision 16139)
@@ -0,0 +1,129 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A FieldRef CP Info
+ *
+ */
+public class FieldRefCPInfo extends ConstantPoolEntry {
+    /** Name of the field's class */
+    private String fieldClassName;
+    /** name of the field in that class */
+    private String fieldName;
+    /** The type of the field */
+    private String fieldType;
+    /** Index into the constant pool for the class */
+    private int classIndex;
+    /** Index into the constant pool for the name and type entry */
+    private int nameAndTypeIndex;
+
+    /** Constructor.  */
+    public FieldRefCPInfo() {
+        super(CONSTANT_FIELDREF, 1);
+    }
+
+    /**
+     * read a constant pool entry from a class stream.
+     *
+     * @param cpStream the DataInputStream which contains the constant pool
+     *      entry to be read.
+     * @exception IOException if there is a problem reading the entry from
+     *      the stream.
+     */
+    public void read(DataInputStream cpStream) throws IOException {
+        classIndex = cpStream.readUnsignedShort();
+        nameAndTypeIndex = cpStream.readUnsignedShort();
+    }
+
+    /**
+     * Resolve this constant pool entry with respect to its dependents in
+     * the constant pool.
+     *
+     * @param constantPool the constant pool of which this entry is a member
+     *      and against which this entry is to be resolved.
+     */
+    public void resolve(ConstantPool constantPool) {
+        ClassCPInfo fieldClass
+            = (ClassCPInfo) constantPool.getEntry(classIndex);
+
+        fieldClass.resolve(constantPool);
+
+        fieldClassName = fieldClass.getClassName();
+
+        NameAndTypeCPInfo nt
+            = (NameAndTypeCPInfo) constantPool.getEntry(nameAndTypeIndex);
+
+        nt.resolve(constantPool);
+
+        fieldName = nt.getName();
+        fieldType = nt.getType();
+
+        super.resolve(constantPool);
+    }
+
+    /**
+     * Print a readable version of the constant pool entry.
+     *
+     * @return the string representation of this constant pool entry.
+     */
+    public String toString() {
+        String value;
+
+        if (isResolved()) {
+            value = "Field : Class = " + fieldClassName + ", name = "
+                + fieldName + ", type = " + fieldType;
+        } else {
+            value = "Field : Class index = " + classIndex
+                + ", name and type index = " + nameAndTypeIndex;
+        }
+
+        return value;
+    }
+
+    /**
+     * Gets the name of the class defining the field
+     *
+     * @return the name of the class defining the field
+     */
+    public String getFieldClassName() {
+        return fieldClassName;
+    }
+
+    /**
+     * Get the name of the field
+     *
+     * @return the field's name
+     */
+    public String getFieldName() {
+        return fieldName;
+    }
+
+    /**
+     * Get the type of the field
+     *
+     * @return the field's type in string format
+     */
+    public String getFieldType() {
+        return fieldType;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FloatCPInfo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FloatCPInfo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/FloatCPInfo.java	(revision 16139)
@@ -0,0 +1,55 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A Float CP Info
+ *
+ */
+public class FloatCPInfo extends ConstantCPInfo {
+
+    /** Constructor.  */
+    public FloatCPInfo() {
+        super(CONSTANT_FLOAT, 1);
+    }
+
+    /**
+     * read a constant pool entry from a class stream.
+     *
+     * @param cpStream the DataInputStream which contains the constant pool
+     *      entry to be read.
+     * @exception IOException if there is a problem reading the entry from
+     *      the stream.
+     */
+    public void read(DataInputStream cpStream) throws IOException {
+        setValue(new Float(cpStream.readFloat()));
+    }
+
+    /**
+     * Print a readable version of the constant pool entry.
+     *
+     * @return the string representation of this constant pool entry.
+     */
+    public String toString() {
+        return "Float Constant Pool Entry: " + getValue();
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/IntegerCPInfo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/IntegerCPInfo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/IntegerCPInfo.java	(revision 16139)
@@ -0,0 +1,55 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * An Integer CP Info
+ *
+ */
+public class IntegerCPInfo extends ConstantCPInfo {
+
+    /** Constructor.  */
+    public IntegerCPInfo() {
+        super(CONSTANT_INTEGER, 1);
+    }
+
+    /**
+     * read a constant pool entry from a class stream.
+     *
+     * @param cpStream the DataInputStream which contains the constant pool
+     *      entry to be read.
+     * @exception IOException if there is a problem reading the entry from
+     *      the stream.
+     */
+    public void read(DataInputStream cpStream) throws IOException {
+        setValue(new Integer(cpStream.readInt()));
+    }
+
+    /**
+     * Print a readable version of the constant pool entry.
+     *
+     * @return the string representation of this constant pool entry.
+     */
+    public String toString() {
+        return "Integer Constant Pool Entry: " + getValue();
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InterfaceMethodRefCPInfo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InterfaceMethodRefCPInfo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/InterfaceMethodRefCPInfo.java	(revision 16139)
@@ -0,0 +1,136 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A InterfaceMethodRef CP Info
+ *
+ */
+public class InterfaceMethodRefCPInfo extends ConstantPoolEntry {
+    /** the class name of the class defining the interface method */
+    private String interfaceMethodClassName;
+    /** the name of the interface nmethod */
+    private String interfaceMethodName;
+    /** the method signature of the interface method */
+    private String interfaceMethodType;
+    /**
+     * the index into the constant pool of the class entry for the interface
+     * class
+     */
+    private int classIndex;
+    /**
+     * the index into the constant pool of the name and type entry
+     * describing the method
+     */
+    private int nameAndTypeIndex;
+
+    /** Constructor. */
+    public InterfaceMethodRefCPInfo() {
+        super(CONSTANT_INTERFACEMETHODREF, 1);
+    }
+
+    /**
+     * read a constant pool entry from a class stream.
+     *
+     * @param cpStream the DataInputStream which contains the constant pool
+     *      entry to be read.
+     * @exception IOException if there is a problem reading the entry from
+     *      the stream.
+     */
+    public void read(DataInputStream cpStream) throws IOException {
+        classIndex = cpStream.readUnsignedShort();
+        nameAndTypeIndex = cpStream.readUnsignedShort();
+    }
+
+    /**
+     * Resolve this constant pool entry with respect to its dependents in
+     * the constant pool.
+     *
+     * @param constantPool the constant pool of which this entry is a member
+     *      and against which this entry is to be resolved.
+     */
+    public void resolve(ConstantPool constantPool) {
+        ClassCPInfo interfaceMethodClass
+             = (ClassCPInfo) constantPool.getEntry(classIndex);
+
+        interfaceMethodClass.resolve(constantPool);
+
+        interfaceMethodClassName = interfaceMethodClass.getClassName();
+
+        NameAndTypeCPInfo nt
+             = (NameAndTypeCPInfo) constantPool.getEntry(nameAndTypeIndex);
+
+        nt.resolve(constantPool);
+
+        interfaceMethodName = nt.getName();
+        interfaceMethodType = nt.getType();
+
+        super.resolve(constantPool);
+    }
+
+    /**
+     * Print a readable version of the constant pool entry.
+     *
+     * @return the string representation of this constant pool entry.
+     */
+    public String toString() {
+        String value;
+
+        if (isResolved()) {
+            value = "InterfaceMethod : Class = " + interfaceMethodClassName
+                 + ", name = " + interfaceMethodName + ", type = "
+                 + interfaceMethodType;
+        } else {
+            value = "InterfaceMethod : Class index = " + classIndex
+                 + ", name and type index = " + nameAndTypeIndex;
+        }
+
+        return value;
+    }
+
+    /**
+     * Gets the name of the class defining the interface method
+     *
+     * @return the name of the class defining the interface method
+     */
+    public String getInterfaceMethodClassName() {
+        return interfaceMethodClassName;
+    }
+
+    /**
+     * Get the name of the interface method
+     *
+     * @return the name of the interface method
+     */
+    public String getInterfaceMethodName() {
+        return interfaceMethodName;
+    }
+
+    /**
+     * Gets the type of the interface method
+     *
+     * @return the interface method's type signature
+     */
+    public String getInterfaceMethodType() {
+        return interfaceMethodType;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/LongCPInfo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/LongCPInfo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/LongCPInfo.java	(revision 16139)
@@ -0,0 +1,55 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A Long CP Info
+ *
+ */
+public class LongCPInfo extends ConstantCPInfo {
+
+    /** Constructor.  */
+    public LongCPInfo() {
+        super(CONSTANT_LONG, 2);
+    }
+
+    /**
+     * read a constant pool entry from a class stream.
+     *
+     * @param cpStream the DataInputStream which contains the constant pool
+     *      entry to be read.
+     * @exception IOException if there is a problem reading the entry from
+     *      the stream.
+     */
+    public void read(DataInputStream cpStream) throws IOException {
+        setValue(new Long(cpStream.readLong()));
+    }
+
+    /**
+     * Print a readable version of the constant pool entry.
+     *
+     * @return the string representation of this constant pool entry.
+     */
+    public String toString() {
+        return "Long Constant Pool Entry: " + getValue();
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodRefCPInfo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodRefCPInfo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/MethodRefCPInfo.java	(revision 16139)
@@ -0,0 +1,132 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A MethodRef CP Info
+ *
+ */
+public class MethodRefCPInfo extends ConstantPoolEntry {
+    /** the name of the class defining this method */
+    private String methodClassName;
+    /** the name of the method */
+    private String methodName;
+    /** the method's type descriptor */
+    private String methodType;
+    /** The index into the constant pool which defines the class of this method. */
+    private int classIndex;
+    /**
+     * the index into the constant pool which defined the name and type
+     * signature of the method
+     */
+    private int nameAndTypeIndex;
+
+    /** Constructor. */
+    public MethodRefCPInfo() {
+        super(CONSTANT_METHODREF, 1);
+    }
+
+    /**
+     * read a constant pool entry from a class stream.
+     *
+     * @param cpStream the DataInputStream which contains the constant pool
+     *      entry to be read.
+     * @exception IOException if there is a problem reading the entry from
+     *      the stream.
+     */
+    public void read(DataInputStream cpStream) throws IOException {
+        classIndex = cpStream.readUnsignedShort();
+        nameAndTypeIndex = cpStream.readUnsignedShort();
+    }
+
+    /**
+     * Print a readable version of the constant pool entry.
+     *
+     * @return the string representation of this constant pool entry.
+     */
+    public String toString() {
+        String value;
+
+        if (isResolved()) {
+            value = "Method : Class = " + methodClassName + ", name = "
+                 + methodName + ", type = " + methodType;
+        } else {
+            value = "Method : Class index = " + classIndex
+                 + ", name and type index = " + nameAndTypeIndex;
+        }
+
+        return value;
+    }
+
+    /**
+     * Resolve this constant pool entry with respect to its dependents in
+     * the constant pool.
+     *
+     * @param constantPool the constant pool of which this entry is a member
+     *      and against which this entry is to be resolved.
+     */
+    public void resolve(ConstantPool constantPool) {
+        ClassCPInfo methodClass
+             = (ClassCPInfo) constantPool.getEntry(classIndex);
+
+        methodClass.resolve(constantPool);
+
+        methodClassName = methodClass.getClassName();
+
+        NameAndTypeCPInfo nt
+             = (NameAndTypeCPInfo) constantPool.getEntry(nameAndTypeIndex);
+
+        nt.resolve(constantPool);
+
+        methodName = nt.getName();
+        methodType = nt.getType();
+
+        super.resolve(constantPool);
+    }
+
+    /**
+     * Get the name of the class defining the method
+     *
+     * @return the name of the class defining this method
+     */
+    public String getMethodClassName() {
+        return methodClassName;
+    }
+
+    /**
+     * Get the name of the method.
+     *
+     * @return the name of the method.
+     */
+    public String getMethodName() {
+        return methodName;
+    }
+
+    /**
+     * Get the type signature of the method.
+     *
+     * @return the type signature of the method.
+     */
+    public String getMethodType() {
+        return methodType;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/NameAndTypeCPInfo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/NameAndTypeCPInfo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/NameAndTypeCPInfo.java	(revision 16139)
@@ -0,0 +1,111 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A NameAndType CP Info
+ *
+ */
+public class NameAndTypeCPInfo extends ConstantPoolEntry {
+
+    /** Constructor. */
+    public NameAndTypeCPInfo() {
+        super(CONSTANT_NAMEANDTYPE, 1);
+    }
+
+    /**
+     * read a constant pool entry from a class stream.
+     *
+     * @param cpStream the DataInputStream which contains the constant pool
+     *      entry to be read.
+     * @exception IOException if there is a problem reading the entry from
+     *      the stream.
+     */
+    public void read(DataInputStream cpStream) throws IOException {
+        nameIndex = cpStream.readUnsignedShort();
+        descriptorIndex = cpStream.readUnsignedShort();
+    }
+
+    /**
+     * Print a readable version of the constant pool entry.
+     *
+     * @return the string representation of this constant pool entry.
+     */
+    public String toString() {
+        String value;
+
+        if (isResolved()) {
+            value = "Name = " + name + ", type = " + type;
+        } else {
+            value = "Name index = " + nameIndex
+                 + ", descriptor index = " + descriptorIndex;
+        }
+
+        return value;
+    }
+
+    /**
+     * Resolve this constant pool entry with respect to its dependents in
+     * the constant pool.
+     *
+     * @param constantPool the constant pool of which this entry is a member
+     *      and against which this entry is to be resolved.
+     */
+    public void resolve(ConstantPool constantPool) {
+        name = ((Utf8CPInfo) constantPool.getEntry(nameIndex)).getValue();
+        type = ((Utf8CPInfo) constantPool.getEntry(descriptorIndex)).getValue();
+
+        super.resolve(constantPool);
+    }
+
+    /**
+     * Get the name component of this entry
+     *
+     * @return the name of this name and type entry
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Get the type signature of this entry
+     *
+     * @return the type signature of this entry
+     */
+    public String getType() {
+        return type;
+    }
+
+    /** the name component of this entry */
+    private String name;
+    /** the type component of this entry */
+    private String type;
+    /**
+     * the index into the constant pool at which the name component's string
+     * value is stored
+     */
+    private int nameIndex;
+    /**
+     * the index into the constant pool where the type descriptor string is
+     * stored.
+     */
+    private int descriptorIndex;
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/StringCPInfo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/StringCPInfo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/StringCPInfo.java	(revision 16139)
@@ -0,0 +1,73 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A String Constant Pool Entry. The String info contains an index into the
+ * constant pool where a UTF8 string is stored.
+ *
+ */
+public class StringCPInfo extends ConstantCPInfo {
+
+    /** Constructor.  */
+    public StringCPInfo() {
+        super(CONSTANT_STRING, 1);
+    }
+
+    /**
+     * read a constant pool entry from a class stream.
+     *
+     * @param cpStream the DataInputStream which contains the constant pool
+     *      entry to be read.
+     * @exception IOException if there is a problem reading the entry from
+     *      the stream.
+     */
+    public void read(DataInputStream cpStream) throws IOException {
+        index = cpStream.readUnsignedShort();
+
+        setValue("unresolved");
+    }
+
+    /**
+     * Print a readable version of the constant pool entry.
+     *
+     * @return the string representation of this constant pool entry.
+     */
+    public String toString() {
+        return "String Constant Pool Entry for "
+            + getValue() + "[" + index + "]";
+    }
+
+    /**
+     * Resolve this constant pool entry with respect to its dependents in
+     * the constant pool.
+     *
+     * @param constantPool the constant pool of which this entry is a member
+     *      and against which this entry is to be resolved.
+     */
+    public void resolve(ConstantPool constantPool) {
+        setValue(((Utf8CPInfo) constantPool.getEntry(index)).getValue());
+        super.resolve(constantPool);
+    }
+
+    /** the index into the constant pool containing the string's content */
+    private int index;
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/Utf8CPInfo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/Utf8CPInfo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/depend/constantpool/Utf8CPInfo.java	(revision 16139)
@@ -0,0 +1,66 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.depend.constantpool;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+
+/**
+ * A UTF8 Constant Pool Entry.
+ *
+ */
+public class Utf8CPInfo extends ConstantPoolEntry {
+    /** The String value of the UTF-8 entry */
+    private String value;
+
+    /** Constructor.  */
+    public Utf8CPInfo() {
+        super(CONSTANT_UTF8, 1);
+    }
+
+    /**
+     * read a constant pool entry from a class stream.
+     *
+     * @param cpStream the DataInputStream which contains the constant pool
+     *      entry to be read.
+     * @exception IOException if there is a problem reading the entry from
+     *      the stream.
+     */
+    public void read(DataInputStream cpStream) throws IOException {
+        value = cpStream.readUTF();
+    }
+
+    /**
+     * Print a readable version of the constant pool entry.
+     *
+     * @return the string representation of this constant pool entry.
+     */
+    public String toString() {
+        return "UTF8 Value = " + value;
+    }
+
+    /**
+     * Get the string value of the UTF-8 entry
+     *
+     * @return the UTF-8 value as a Java string
+     */
+    public String getValue() {
+        return value;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/CSharp.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/CSharp.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/CSharp.java	(revision 16139)
@@ -0,0 +1,396 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ *  build notes
+ *  -The reference CD to listen to while editing this file is
+ *  nap: Underworld  - Everything, Everything
+ */
+// ====================================================================
+// place in the optional ant tasks package
+// but in its own dotnet group
+// ====================================================================
+
+package org.apache.tools.ant.taskdefs.optional.dotnet;
+
+// ====================================================================
+// imports
+// ====================================================================
+
+import java.io.File;
+
+import org.apache.tools.ant.taskdefs.condition.Os;
+
+// ====================================================================
+
+/**
+ *  Compiles C# source into executables or modules.
+ *
+ * csc.exe on Windows or mcs on other platforms must be on the execute path, unless another executable
+ * or the full path to that executable is specified in the <tt>executable</tt>
+ * parameter
+ * <p>
+ * All parameters are optional: &lt;csc/&gt; should suffice to produce a debug
+ * build of all *.cs files. However, naming an <tt>destFile</tt>stops the
+ * csc compiler from choosing an output name from random, and
+ * allows the dependency checker to determine if the file is out of date.
+ * <p>
+ *  The task is a directory based task, so attributes like <b>includes="*.cs"
+ *  </b> and <b>excludes="broken.cs"</b> can be used to control the files pulled
+ *  in. By default, all *.cs files from the project folder down are included in
+ *  the command. When this happens the output file -if not specified- is taken
+ *  as the first file in the list, which may be somewhat hard to control.
+ *  Specifying the output file with <tt>destFile</tt> seems prudent. <p>
+ *
+ * <p>
+ * For more complex source trees, nested <tt>src</tt> elemements can be
+ * supplied. When such an element is present, the implicit fileset is ignored.
+ * This makes sense, when you think about it :)
+ *
+ * <p>For historical reasons the pattern
+ * <code>**</code><code>/*.cs</code> is preset as includes list and
+ * you can not override it with an explicit includes attribute.  Use
+ * nested <code>&lt;src&gt;</code> elements instead of the basedir
+ * attribute if you need more control.</p>
+ *
+ * <p>
+ * References to external files can be made through the references attribute,
+ * or (since Ant1.6), via nested &lt;reference&gt; filesets. With the latter,
+ * the timestamps of the references are also used in the dependency
+ * checking algorithm.
+ * <p>
+ *
+ * Example
+ *
+ * <pre>&lt;csc
+ * 	optimize=&quot;true&quot;
+ * 	debug=&quot;false&quot;
+ * 	docFile=&quot;documentation.xml&quot;
+ * 	warnLevel=&quot;4&quot;
+ * 	unsafe=&quot;false&quot;
+ * 	targetType=&quot;exe&quot;
+ * 	incremental=&quot;false&quot;
+ * 	mainClass = &quot;MainApp&quot;
+ * 	destFile=&quot;NetApp.exe&quot;
+ * 	&gt;
+ * 	     &lt;src dir="src" includes="*.cs" /&gt;
+ *       &lt;reference file="${testCSC.dll}" /&gt;
+ *       &lt;define name="RELEASE" /&gt;
+ *       &lt;define name="DEBUG" if="debug.property"/&gt;
+ *       &lt;define name="def3" unless="def3.property"/&gt;
+ *    &lt;/csc&gt;
+ * </pre>
+ *
+ *
+ * @ant.task    name="csc" category="dotnet"
+ * @since Ant 1.3
+ */
+
+public class CSharp extends DotnetCompile {
+
+    /**
+     *  defines list: RELEASE;WIN32;NO_SANITY_CHECKS;;SOMETHING_ELSE'
+     */
+    String definitions;
+
+
+    /**
+     *  output XML documentation flag
+     */
+    private File docFile;
+
+    /**
+     *  file alignment; 0 means let the compiler decide
+     */
+    private int fileAlign = 0;
+
+    /**
+     *  use full paths to things
+     */
+    private boolean fullpaths = false;
+
+    /**
+     *  incremental build flag
+     */
+    private boolean incremental;
+
+    /**
+     *  enable unsafe code flag. Clearly set to false by default
+     */
+    protected boolean unsafe;
+
+    /**
+     * A flag that tells the compiler not to read in the compiler
+     * settings files 'csc.rsp' in its bin directory and then the local directory
+     */
+    private boolean noconfig = false;
+
+
+    /**
+     *  constructor inits everything and set up the search pattern
+     */
+
+    public CSharp() {
+        clear();
+    }
+
+    /**
+     * full cleanup
+     */
+    public void clear() {
+        super.clear();
+        docFile = null;
+        fileAlign = 0;
+        fullpaths = true;
+        incremental = false;
+        unsafe = false;
+        noconfig = false;
+        definitions = null;
+        setExecutable(Os.isFamily("windows") ? "csc" : "mcs");
+    }
+
+
+
+    /**
+     *  file for generated XML documentation
+     *
+     *@param  f  output file
+     */
+    public void setDocFile(File f) {
+        docFile = f;
+    }
+
+
+    /**
+     *  get the argument or null for no argument needed
+     *
+     *@return    The DocFile Parameter to CSC
+     */
+    protected String getDocFileParameter() {
+        if (docFile != null) {
+            return "/doc:" + docFile.toString();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Set the file alignment.
+     * Valid values are 0,512, 1024, 2048, 4096, 8192,
+     * and 16384, 0 means 'leave to the compiler'
+     */
+    public void setFileAlign(int fileAlign) {
+        this.fileAlign = fileAlign;
+    }
+
+    /**
+     *  get the argument or null for no argument needed
+     *
+     *@return    The OutputFile Parameter to CSC
+     */
+    protected String getFileAlignParameter() {
+        if (fileAlign != 0) {
+            return "/filealign:" + fileAlign;
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * If true, print the full path of files on errors.
+     *
+     *@param  enabled  The new fullPaths value
+     */
+    public void setFullPaths(boolean enabled) {
+        fullpaths = enabled;
+    }
+
+
+    /**
+     *  Gets the fullPathsParameter attribute of the CSharp object
+     *
+     *@return    The fullPathsParameter value or null if unset
+     */
+    protected String getFullPathsParameter() {
+        return fullpaths ? "/fullpaths" : null;
+    }
+
+
+    /**
+     *  set the incremental compilation flag on or off.
+     *
+     *@param  incremental  on/off flag
+     */
+    public void setIncremental(boolean incremental) {
+        this.incremental = incremental;
+    }
+
+
+    /**
+     *  query the incrementalflag
+     *
+     *@return    true if incremental compilation is turned on
+     */
+    public boolean getIncremental() {
+        return incremental;
+    }
+
+
+    /**
+     *  get the incremental build argument
+     *
+     *@return    The Incremental Parameter to CSC
+     */
+    protected String getIncrementalParameter() {
+        return "/incremental" + (incremental ? "+" : "-");
+    }
+
+    /**
+     *  The output file. This is identical to the destFile attribute.
+     *
+     *@param  params  The new outputFile value
+     */
+    public void setOutputFile(File params) {
+        setDestFile(params);
+    }
+
+
+    /**
+     * If true, enables the unsafe keyword.
+     *
+     *@param  unsafe  The new Unsafe value
+     */
+    public void setUnsafe(boolean unsafe) {
+        this.unsafe = unsafe;
+    }
+
+
+    /**
+     *  query the Unsafe attribute
+     *
+     *@return    The Unsafe value
+     */
+    public boolean getUnsafe() {
+        return this.unsafe;
+    }
+
+
+    /**
+     *  get the argument or null for no argument needed
+     *
+     *@return    The Unsafe Parameter to CSC
+     */
+    protected String getUnsafeParameter() {
+        return unsafe ? "/unsafe" : null;
+    }
+
+
+    /**
+     * A flag that tells the compiler not to read in the compiler
+     * settings files 'csc.rsp' in its bin directory and then the local directory
+     *
+     *@param  enabled  The new noConfig value
+     */
+    public void setNoConfig(boolean enabled) {
+        noconfig = enabled;
+    }
+
+
+    /**
+     *  Gets the noConfigParameter attribute of the CSharp object
+     *
+     *@return    The noConfigParameter value
+     */
+    protected String getNoConfigParameter() {
+        return noconfig ? "/noconfig" : null;
+    }
+
+
+    /**
+     *  Semicolon separated list of defined constants.
+     *
+     *@param  params  The new definitions value
+     */
+    public void setDefinitions(String params) {
+        definitions = params;
+    }
+
+    /**
+     * override the superclasses version of this method (which we call)
+     * with a check for a definitions attribute, the contents of which
+     * are appended to the list.
+     *@return    The Definitions Parameter to CSC
+     */
+    protected String getDefinitionsParameter() {
+        String predecessors = super.getDefinitionsParameter();
+        if (notEmpty(definitions)) {
+            if (predecessors == null) {
+                predecessors = "/define:";
+            }
+            return  predecessors + definitions;
+        } else {
+            return predecessors;
+        }
+    }
+
+
+    /**
+     * add Commands unique to C#.
+     * @param command ongoing command
+     */
+    public void addCompilerSpecificOptions(NetCommand command) {
+        command.addArgument(getIncludeDefaultReferencesParameter());
+        command.addArgument(getWarnLevelParameter());
+        command.addArgument(getDocFileParameter());
+        command.addArgument(getFullPathsParameter());
+        command.addArgument(getFileAlignParameter());
+        command.addArgument(getIncrementalParameter());
+        command.addArgument(getNoConfigParameter());
+        command.addArgument(getUnsafeParameter());
+    }
+
+    // end execute
+
+    /**
+     * Returns the delimiter which C# uses to separate references, i.e., a semi colon.
+     */
+    public String getReferenceDelimiter() {
+        return ";";
+    }
+
+
+    /**
+     * This method indicates the filename extension for C# files.
+     * @return the file extension for C#, i.e., "cs" (without the dot).
+     */
+    public String getFileExtension() {
+        return "cs";
+    }
+
+    /**
+     * from a resource, get the resource param string
+     * @param resource
+     * @return a string containing the resource param, or a null string
+     * to conditionally exclude a resource.
+     */
+    protected String createResourceParameter(DotnetResource resource) {
+        return resource.getCSharpStyleParameter();
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/DotnetBaseMatchingTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/DotnetBaseMatchingTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/DotnetBaseMatchingTask.java	(revision 16139)
@@ -0,0 +1,178 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.dotnet;
+
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.DirectoryScanner;
+
+import java.io.File;
+import java.util.Vector;
+import java.util.Hashtable;
+import java.util.Enumeration;
+
+/**
+ * refactoring of some stuff so that different things (like ILASM)
+ * can use shared code.
+ */
+public class DotnetBaseMatchingTask extends MatchingTask {
+    /**
+     *  output file. If not supplied this is derived from the source file
+     */
+    protected File outputFile;
+    /**
+     * filesets of file to compile
+     */
+    protected Vector filesets = new Vector();
+
+    /**
+     *  source directory upon which the search pattern is applied
+     */
+    protected File srcDir;
+
+    /**
+    * Overridden because we need to be able to set the srcDir.
+    */
+    public File getSrcDir() {
+        return this.srcDir;
+    }
+
+    /**
+     *  Set the source directory of the files to be compiled.
+     *
+     *@param  srcDirName  The new SrcDir value
+     */
+    public void setSrcDir(File srcDirName) {
+        this.srcDir = srcDirName;
+    }
+
+    /**
+     *  Set the name of exe/library to create.
+     *
+     *@param  file  The new outputFile value
+     */
+    public void setDestFile(File file) {
+        outputFile = file;
+    }
+
+    /**
+     * add a new source directory to the compile
+     * @param src
+     */
+    public void addSrc(FileSet src) {
+        filesets.add(src);
+    }
+
+    /**
+     * get the destination file
+     * @return the dest file or null for not assigned
+     */
+    public File getDestFile() {
+        return outputFile;
+    }
+
+    /**
+     * create the list of files
+     * @param filesToBuild vector to add files to
+     * @param outputTimestamp timestamp to compare against
+     * @return number of files out of date
+     */
+    protected int buildFileList(NetCommand command, Hashtable filesToBuild, long outputTimestamp) {
+        int filesOutOfDate = 0;
+        boolean scanImplicitFileset
+            = getSrcDir() != null || filesets.size() == 0;
+        if (scanImplicitFileset) {
+            //scan for an implicit fileset if there was a srcdir set
+            //or there was no srcDir set but there was no contained classes
+            if (getSrcDir() == null) {
+                //if there is no src dir here, set it
+                setSrcDir(getProject().resolveFile("."));
+            }
+            log("working from source directory " + getSrcDir(),
+                    Project.MSG_VERBOSE);
+            //get dependencies list.
+            DirectoryScanner scanner = getDirectoryScanner(getSrcDir());
+            filesOutOfDate = command.scanOneFileset(scanner,
+                    filesToBuild, outputTimestamp);
+        }
+        //get any included source directories
+        for (int i = 0; i < filesets.size(); i++) {
+            FileSet fs = (FileSet) filesets.elementAt(i);
+            filesOutOfDate += command.scanOneFileset(
+                    fs.getDirectoryScanner(getProject()),
+                    filesToBuild,
+                    outputTimestamp);
+        }
+
+        return filesOutOfDate;
+    }
+
+    /**
+     * add the list of files to a command
+     * @param filesToBuild vector of files
+     * @param command the command to append to
+     */
+    protected void addFilesToCommand(Hashtable filesToBuild, NetCommand command) {
+        int count = filesToBuild.size();
+        log("compiling " + count + " file" + ((count == 1) ? "" : "s"));
+        Enumeration files = filesToBuild.elements();
+        while (files.hasMoreElements()) {
+            File file = (File) files.nextElement();
+            command.addArgument(file.toString());
+        }
+    }
+
+    /**
+     * determine the timestamp of the output file
+     * @return a timestamp or 0 for no output file known/exists
+     */
+    protected long getOutputFileTimestamp() {
+        long outputTimestamp;
+        if (getDestFile() != null && getDestFile().exists()) {
+            outputTimestamp = getDestFile().lastModified();
+        } else {
+            outputTimestamp = 0;
+        }
+        return outputTimestamp;
+    }
+
+    /**
+     * finish off the command by adding all dependent files, execute
+     * @param command
+     */
+    protected void addFilesAndExecute(NetCommand command, boolean ignoreTimestamps) {
+        long outputTimestamp = getOutputFileTimestamp();
+        Hashtable filesToBuild = new Hashtable();
+        int filesOutOfDate = buildFileList(command, filesToBuild, outputTimestamp);
+
+        //add the files to the command
+        addFilesToCommand(filesToBuild, command);
+
+
+        //now run the command of exe + settings + files
+        if (filesOutOfDate > 0) {
+            command.runCommand();
+        } else {
+            log("output file is up to date", Project.MSG_VERBOSE);
+        }
+    }
+
+
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/DotnetCompile.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/DotnetCompile.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/DotnetCompile.java	(revision 16139)
@@ -0,0 +1,974 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ *  build notes
+ *  -The reference CD to listen to while editing this file is
+ *  nap:Cream+Live+2001+CD+2
+ */
+
+// place in the optional ant tasks package
+// but in its own dotnet group
+
+package org.apache.tools.ant.taskdefs.optional.dotnet;
+
+// imports
+
+import java.io.File;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+
+/**
+ *  Abstract superclass for dotnet compiler tasks.
+ *
+ *  History
+ *  <table>
+ *    <tr>
+ *      <td>
+ *        0.1
+ *      </td>
+ *      <td>
+ *        First creation
+ *      </td>
+ *      <td>
+ *        Most of the code here was copied verbatim from v0.3 of
+ *        Steve Loughran's CSharp optional task. Abstracted functionality
+ *        to allow subclassing of other dotnet compiler types.
+ *      </td>
+ *    </tr>
+ *
+ *  </table>
+ *
+ *
+ * @version     0.1
+ */
+
+public abstract class DotnetCompile
+         extends DotnetBaseMatchingTask {
+
+    /**
+     *  list of reference classes. (pretty much a classpath equivalent)
+     */
+    private String references;
+
+    /**
+     *  flag to enable automatic reference inclusion
+     */
+    private boolean includeDefaultReferences = true;
+
+    /**
+     *  icon for incorporation into apps
+     */
+    private File win32icon;
+
+    /**
+     *  icon for incorporation into apps
+     */
+    private File win32res;
+
+    /**
+     *  flag to control action on execution trouble
+     */
+    private boolean failOnError;
+
+    /**
+     *  using the path approach didn't work as it could not handle the implicit
+     *  execution path. Perhaps that could be extracted from the runtime and
+     *  then the path approach would be viable
+     */
+    private Path referenceFiles;
+
+    /**
+     *  optimise flag
+     */
+    private boolean optimize;
+
+    /**
+     * a list of definitions to support;
+     */
+    protected Vector definitionList = new Vector();
+
+    /**
+     * our resources
+     */
+    protected Vector resources = new Vector();
+
+    /**
+     *  executable
+     */
+
+    protected String executable;
+
+    protected static final String REFERENCE_OPTION = "/reference:";
+
+    /**
+     *  debug flag. Controls generation of debug information.
+     */
+    protected boolean debug;
+
+    /**
+     *  warning level: 0-4, with 4 being most verbose
+     */
+    private int warnLevel;
+
+    /**
+     *  main class (or null for automatic choice)
+     */
+    protected String mainClass;
+
+    /**
+     *  any extra command options?
+     */
+    protected String extraOptions;
+
+    /**
+     *  type of target. Should be one of exe|library|module|winexe|(null)
+     *  default is exe; the actual value (if not null) is fed to the command
+     *  line. <br>
+     *  See /target
+     */
+    protected String targetType;
+
+    /**
+     *  utf out flag
+     */
+
+    protected boolean utf8output = false;
+
+    /**
+     *  list of extra modules to refer to
+     */
+    protected String additionalModules;
+    /**
+     * filesets of references
+     */
+    protected Vector referenceFilesets = new Vector();
+
+    /**
+     * flag to set to to use @file based command cache
+     */
+    private boolean useResponseFile = false;
+    private static final int AUTOMATIC_RESPONSE_FILE_THRESHOLD = 64;
+
+    /**
+     *  constructor inits everything and set up the search pattern
+     */
+
+    public DotnetCompile() {
+        clear();
+        setIncludes(getFilePattern());
+    }
+
+    /**
+     *  reset all contents.
+     */
+    public void clear() {
+        targetType = null;
+        win32icon = null;
+        srcDir = null;
+        mainClass = null;
+        warnLevel = 3;
+        optimize = false;
+        debug = true;
+        references = null;
+        failOnError = true;
+        additionalModules = null;
+        includeDefaultReferences = true;
+        extraOptions = null;
+    }
+
+
+    /**
+     * Semicolon separated list of DLLs to refer to.
+     *
+     *@param  s  The new References value
+     */
+    public void setReferences(String s) {
+        references = s;
+    }
+
+
+    /**
+     *  get the reference string or null for no argument needed
+     *
+     *@return    The References Parameter to CSC
+     */
+    protected String getReferencesParameter() {
+        //bail on no references
+        if (notEmpty(references)) {
+            return REFERENCE_OPTION + references;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Path of references to include.
+     * Wildcards should work.
+     *
+     *@param  path  another path to append
+     */
+    public void setReferenceFiles(Path path) {
+        //demand create pathlist
+        if (referenceFiles == null) {
+            referenceFiles = new Path(this.getProject());
+        }
+        referenceFiles.append(path);
+    }
+
+    /**
+     * add a new reference fileset to the compilation
+     * @param reference
+     */
+    public void addReference(FileSet reference) {
+        referenceFilesets.add(reference);
+    }
+
+
+
+    /**
+     *  turn the path list into a list of files and a /references argument
+     *
+     *@return    null or a string of references.
+     */
+    protected String getReferenceFilesParameter() {
+        //bail on no references
+        if (references == null) {
+            return null;
+        }
+        //iterate through the ref list & generate an entry for each
+        //or just rely on the fact that the toString operator does this, but
+        //noting that the separator is ';' on windows, ':' on unix
+        String refpath = references.toString();
+
+        //bail on no references listed
+        if (refpath.length() == 0) {
+            return null;
+        }
+
+        StringBuffer s = new StringBuffer(REFERENCE_OPTION);
+        s.append(refpath);
+        return new String(s);
+    }
+
+
+    /**
+     * If true, automatically includes the common assemblies
+     * in dotnet, and tells the compiler to link in mscore.dll.
+     *
+     *  set the automatic reference inclusion flag on or off this flag controls
+     *  the /nostdlib option in CSC
+     *
+     *@param  f  on/off flag
+     */
+    public void setIncludeDefaultReferences(boolean f) {
+        includeDefaultReferences = f;
+    }
+
+
+    /**
+     *  query automatic reference inclusion flag
+     *
+     *@return    true if flag is turned on
+     */
+    public boolean getIncludeDefaultReferences() {
+        return includeDefaultReferences;
+    }
+
+
+    /**
+     *  get the include default references flag or null for no argument needed
+     *
+     *@return    The Parameter to CSC
+     */
+    protected String getIncludeDefaultReferencesParameter() {
+        return "/nostdlib" + (includeDefaultReferences ? "-" : "+");
+    }
+
+
+
+    /**
+     * If true, enables optimization flag.
+     *
+     *@param  f  on/off flag
+     */
+    public void setOptimize(boolean f) {
+        optimize = f;
+    }
+
+
+    /**
+     *  query the optimise flag
+     *
+     *@return    true if optimise is turned on
+     */
+    public boolean getOptimize() {
+        return optimize;
+    }
+
+
+    /**
+     *  get the optimise flag or null for no argument needed
+     *
+     *@return    The Optimize Parameter to CSC
+     */
+    protected String getOptimizeParameter() {
+        return "/optimize" + (optimize ? "+" : "-");
+    }
+
+
+    /**
+     *  set the debug flag on or off.
+     *
+     *@param  f  on/off flag
+     */
+    public void setDebug(boolean f) {
+        debug = f;
+    }
+
+
+    /**
+     *  query the debug flag
+     *
+     *@return    true if debug is turned on
+     */
+    public boolean getDebug() {
+        return debug;
+    }
+
+
+    /**
+     *  get the debug switch argument
+     *
+     *@return    The Debug Parameter to CSC
+     */
+    protected String getDebugParameter() {
+        return "/debug" + (debug ? "+" : "-");
+    }
+
+
+    /**
+     * Level of warning currently between 1 and 4
+     * with 4 being the strictest.
+     *
+     *@param  warnLevel  warn level -see .net docs for valid range (probably
+     *      0-4)
+     */
+    public void setWarnLevel(int warnLevel) {
+        this.warnLevel = warnLevel;
+    }
+
+
+    /**
+     *  query warn level
+     *
+     *@return    current value
+     */
+    public int getWarnLevel() {
+        return warnLevel;
+    }
+
+
+    /**
+     *  get the warn level switch
+     *
+     *@return    The WarnLevel Parameter to CSC
+     */
+    protected String getWarnLevelParameter() {
+        return "/warn:" + warnLevel;
+    }
+
+
+    /**
+     *  Sets the name of main class for executables.
+     *
+     *@param  mainClass  The new MainClass value
+     */
+    public void setMainClass(String mainClass) {
+        this.mainClass = mainClass;
+    }
+
+
+    /**
+     *  Gets the MainClass attribute
+     *
+     *@return    The MainClass value
+     */
+    public String getMainClass() {
+        return this.mainClass;
+    }
+
+
+    /**
+     *  get the /main argument or null for no argument needed
+     *
+     *@return    The MainClass Parameter to CSC
+     */
+    protected String getMainClassParameter() {
+        if (mainClass != null && mainClass.length() != 0) {
+            return "/main:" + mainClass;
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Any extra options which are not explicitly supported
+     * by this task.
+     *
+     *@param  extraOptions  The new ExtraOptions value
+     */
+    public void setExtraOptions(String extraOptions) {
+        this.extraOptions = extraOptions;
+    }
+
+
+    /**
+     *  Gets the ExtraOptions attribute
+     *
+     *@return    The ExtraOptions value
+     */
+    public String getExtraOptions() {
+        return this.extraOptions;
+    }
+
+
+    /**
+     *  get any extra options or null for no argument needed
+     *
+     *@return    The ExtraOptions Parameter to CSC
+     */
+    protected String getExtraOptionsParameter() {
+        if (extraOptions != null && extraOptions.length() != 0) {
+            return extraOptions;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     *  get any extra options or null for no argument needed, split
+     *  them if they represent multiple options.
+     *
+     * @return    The ExtraOptions Parameter to CSC
+     */
+    protected String[] getExtraOptionsParameters() {
+        String extra = getExtraOptionsParameter();
+        return extra == null ? null : Commandline.translateCommandline(extra);
+    }
+
+    /**
+     * Set the destination directory of files to be compiled.
+     *
+     *@param  dirName  The new DestDir value
+     */
+    public void setDestDir(File dirName) {
+        log("DestDir currently unused", Project.MSG_WARN);
+    }
+
+
+    /**
+     * set the target type to one of exe|library|module|winexe
+     * @param targetType
+     */
+    public void setTargetType(TargetTypes targetType) {
+        this.targetType = targetType.getValue();
+    }
+    /**
+     * Set the type of target.
+     *
+     *@param  ttype          The new TargetType value
+     *@exception  BuildException  if target is not one of
+     *      exe|library|module|winexe
+     */
+    public void setTargetType(String ttype)
+             throws BuildException {
+        ttype = ttype.toLowerCase();
+        if (ttype.equals("exe") || ttype.equals("library")
+            || ttype.equals("module") || ttype.equals("winexe")) {
+            targetType = ttype;
+        } else {
+            throw new BuildException("targetType " + ttype
+                    + " is not one of 'exe', 'module', 'winexe' or 'library'");
+        }
+    }
+
+
+    /**
+     *  Gets the TargetType attribute
+     *
+     *@return    The TargetType value
+     */
+    public String getTargetType() {
+        return targetType;
+    }
+
+
+    /**
+     *  get the argument or null for no argument needed
+     *
+     *@return    The TargetType Parameter to CSC
+     */
+    protected String getTargetTypeParameter() {
+        if (notEmpty(targetType)) {
+            return "/target:" + targetType;
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     *  Set the filename of icon to include.
+     *
+     *@param  fileName  path to the file. Can be relative, absolute, whatever.
+     */
+    public void setWin32Icon(File fileName) {
+        win32icon = fileName;
+    }
+
+
+    /**
+     *  get the argument or null for no argument needed
+     *
+     *@return    The Win32Icon Parameter to CSC
+     */
+    protected String getWin32IconParameter() {
+        if (win32icon != null) {
+            return "/win32icon:" + win32icon.toString();
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Sets the filename of a win32 resource (.RES) file to include.
+     * This is not a .NET resource, but what Windows is used to.
+     *
+     *@param  fileName  path to the file. Can be relative, absolute, whatever.
+     */
+    public void setWin32Res(File fileName) {
+        win32res = fileName;
+    }
+
+    /**
+     * Gets the file of the win32 .res file to include.
+     * @return path to the file.
+     */
+    public File getWin32Res() {
+        return win32res;
+    }
+
+
+    /**
+     *  get the argument or null for no argument needed
+     *
+     *@return    The Win32Res Parameter to CSC
+     */
+    protected String getWin32ResParameter() {
+        if (win32res != null) {
+            return "/win32res:" + win32res.toString();
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * If true, require all compiler output to be in UTF8 format.
+     *
+     *@param  enabled  The new utf8Output value
+     */
+    public void setUtf8Output(boolean enabled) {
+        utf8output = enabled;
+    }
+
+
+    /**
+     *  Gets the utf8OutpuParameter attribute of the CSharp object
+     *
+     *@return    The utf8OutpuParameter value
+     */
+    protected String getUtf8OutputParameter() {
+        return utf8output ? "/utf8output" : null;
+    }
+
+
+    /**
+     * add a define to the list of definitions
+     * @param define
+     */
+    public void addDefine(DotnetDefine define) {
+        definitionList.addElement(define);
+    }
+
+
+    /**
+     * get a list of definitions or null
+     * @return a string beginning /D: or null for no definitions
+     */
+    protected String getDefinitionsParameter() throws BuildException {
+        StringBuffer defines = new StringBuffer();
+        Enumeration defEnum = definitionList.elements();
+        boolean firstDefinition = true;
+        while (defEnum.hasMoreElements()) {
+            //loop through all definitions
+            DotnetDefine define = (DotnetDefine) defEnum.nextElement();
+            if (define.isSet(this)) {
+                //add those that are set, and a delimiter
+                if (!firstDefinition) {
+                    defines.append(getDefinitionsDelimiter());
+                }
+                defines.append(define.getValue(this));
+                firstDefinition = false;
+            }
+        }
+        if (defines.length() == 0) {
+            return null;
+        } else {
+            return "/d:" + defines;
+        }
+    }
+
+
+    /**
+     * Semicolon separated list of modules to refer to.
+     *
+     *@param  params  The new additionalModules value
+     */
+    public void setAdditionalModules(String params) {
+        additionalModules = params;
+    }
+
+
+    /**
+     *  get the argument or null for no argument needed
+     *
+     *@return    The AdditionalModules Parameter to CSC
+     */
+    protected String getAdditionalModulesParameter() {
+        if (notEmpty(additionalModules)) {
+            return "/addmodule:" + additionalModules;
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     *  get the argument or null for no argument needed
+     *
+     *@return    The OutputFile Parameter to CSC
+     */
+    protected String getDestFileParameter() {
+        if (outputFile != null) {
+            return "/out:" + outputFile.toString();
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * If true, fail on compilation errors.
+     *
+     *@param  b  The new FailOnError value
+     */
+    public void setFailOnError(boolean b) {
+        failOnError = b;
+    }
+
+
+    /**
+     *  query fail on error flag
+     *
+     *@return    The FailFailOnError value
+     */
+    public boolean getFailOnError() {
+        return failOnError;
+    }
+
+    /**
+     * link or embed a resource
+     * @param resource
+     */
+    public void addResource(DotnetResource resource) {
+        resources.add(resource);
+    }
+
+    /**
+     * This method gets the name of the executable.
+     * @return the name of the executable
+     */
+    protected String getExecutable() {
+        return executable;
+    }
+
+    /**
+     * set the name of the program, overriding the defaults.
+     * Can be used to set the full path to a program, or to switch
+     * to an alternate implementation of the command, such as the Mono or Rotor
+     * versions -provided they use the same command line arguments as the
+     * .NET framework edition
+     * @param executable
+     */
+    public void setExecutable(String executable) {
+        this.executable = executable;
+    }
+
+    /**
+     *  test for a string containing something useful
+     *
+     *@param  s  string in
+     *@return    true if the argument is not null or empty
+     */
+    protected boolean notEmpty(String s) {
+        return s != null && s.length() != 0;
+    }
+
+    /**
+     * validation code
+     * @throws  BuildException  if validation failed
+     */
+    protected void validate()
+            throws BuildException {
+        if (outputFile != null && outputFile.isDirectory()) {
+            throw new BuildException("destFile cannot be a directory");
+        }
+        if (getExecutable() == null) {
+            throw new BuildException("There is no executable defined for this task");
+        }
+    }
+
+    /**
+     * Get the pattern for files to compile.
+     * @return The compilation file pattern.
+     */
+    public String getFilePattern() {
+        return "**/*." + getFileExtension();
+    }
+
+    /**
+     * getter for flag
+     * @return The flag indicating whether the compilation is using a response file.
+     */
+    public boolean isUseResponseFile() {
+        return useResponseFile;
+    }
+
+    /**
+     * Flag to turn on response file use; default=false.
+     * When set the command params are saved to a file and
+     * this is passed in with @file. The task automatically switches
+     * to this mode with big commands; this option is here for
+     * testing and emergencies
+     * @param useResponseFile
+     */
+    public void setUseResponseFile(boolean useResponseFile) {
+        this.useResponseFile = useResponseFile;
+    }
+
+    /**
+     *  do the work by building the command line and then calling it
+     *
+     *@throws  BuildException  if validation or execution failed
+     */
+    public void execute()
+             throws BuildException {
+        validate();
+        NetCommand command = createNetCommand();
+        //set up response file options
+        command.setAutomaticResponseFileThreshold(AUTOMATIC_RESPONSE_FILE_THRESHOLD);
+        command.setUseResponseFile(useResponseFile);
+        //fill in args
+        fillInSharedParameters(command);
+        addResources(command);
+        addCompilerSpecificOptions(command);
+        int referencesOutOfDate
+            = addReferenceFilesets(command, getOutputFileTimestamp());
+        //if the refs are out of date, force a build.
+        boolean forceBuild = referencesOutOfDate > 0;
+        addFilesAndExecute(command, forceBuild);
+
+    }
+
+    /**
+     * Get the delimiter that the compiler uses between references.
+     * For example, c# will return ";"; VB.NET will return ","
+     * @return The string delimiter for the reference string.
+     */
+    public abstract String getReferenceDelimiter();
+
+    /**
+     * Get the extension of filenames to compile.
+     * @return The string extension of files to compile.
+     */
+    public abstract String getFileExtension();
+
+
+    /**
+     * fill in the common information
+     * @param command
+     */
+    protected void fillInSharedParameters(NetCommand command) {
+        command.setFailOnError(getFailOnError());
+        //fill in args
+        command.addArgument("/nologo");
+        command.addArgument(getAdditionalModulesParameter());
+        command.addArgument(getDebugParameter());
+        command.addArgument(getDefinitionsParameter());
+        command.addArguments(getExtraOptionsParameters());
+        command.addArgument(getMainClassParameter());
+        command.addArgument(getOptimizeParameter());
+        command.addArgument(getDestFileParameter());
+        command.addArgument(getReferencesParameter());
+        command.addArgument(getTargetTypeParameter());
+        command.addArgument(getUtf8OutputParameter());
+        command.addArgument(getWin32IconParameter());
+        command.addArgument(getWin32ResParameter());
+    }
+
+    /**
+     * for every resource declared, we get the (language specific)
+     * resource setting
+     */
+    protected void addResources(NetCommand command) {
+        Enumeration e = resources.elements();
+        while (e.hasMoreElements()) {
+            DotnetResource resource = (DotnetResource) e.nextElement();
+            command.addArgument(createResourceParameter(resource));
+        }
+    }
+
+    /**
+     * from a resource, get the
+     * @param resource
+     * @return a string containing the resource param, or a null string
+     * to conditionally exclude a resource.
+     */
+    protected abstract String createResourceParameter(DotnetResource resource);
+
+
+    /**
+     * run through the list of reference files and add them to the command
+     * @param outputTimestamp timestamp to compare against
+     * @return number of files out of date
+     */
+
+    protected int addReferenceFilesets(NetCommand command, long outputTimestamp) {
+        int filesOutOfDate = 0;
+        Hashtable filesToBuild = new Hashtable();
+        for (int i = 0; i < referenceFilesets.size(); i++) {
+            FileSet fs = (FileSet) referenceFilesets.elementAt(i);
+            filesOutOfDate += command.scanOneFileset(
+                    fs.getDirectoryScanner(getProject()),
+                    filesToBuild,
+                    outputTimestamp);
+        }
+        //bail out early if there were no files
+        if (filesToBuild.size() == 0) {
+            return 0;
+        }
+        StringBuffer referenceList = new StringBuffer(REFERENCE_OPTION);
+        //now scan the hashtable and add the files
+        Enumeration files = filesToBuild.elements();
+        boolean firstEntry = true;
+        while (files.hasMoreElements()) {
+            File file = (File) files.nextElement();
+            if (isFileManagedBinary(file)) {
+                if (!firstEntry) {
+                    referenceList.append(getReferenceDelimiter());
+                }
+                referenceList.append(file.toString());
+                firstEntry = false;
+            } else {
+                log("ignoring " + file + " as it is not a managed executable",
+                        Project.MSG_VERBOSE);
+            }
+
+        }
+        //add it all to an argument
+        command.addArgument(referenceList.toString());
+        return filesOutOfDate;
+    }
+
+    /**
+     * create our helper command
+     * @return a command prefilled with the exe name and task name
+     */
+    protected NetCommand createNetCommand() {
+        NetCommand command = new NetCommand(this, getTaskName(), getExecutable());
+        return command;
+    }
+
+    /**
+     * add any compiler specifics
+     * @param command
+     */
+    protected abstract void addCompilerSpecificOptions(NetCommand command);
+
+    /**
+     * override point for delimiting definitions.
+     * @return The definitions limiter, i.e., ";"
+     */
+    public String getDefinitionsDelimiter() {
+        return ";";
+    }
+
+
+    /**
+     * test for a file being managed or not
+     * @return true if we think this is a managed executable, and thus OK
+     * for linking
+     * @todo look at the PE header of the exe and see if it is managed or not.
+     */
+    protected static boolean isFileManagedBinary(File file) {
+        String filename = file.toString().toLowerCase();
+        return filename.endsWith(".exe") || filename.endsWith(".dll")
+                || filename.endsWith(".netmodule");
+    }
+
+    /**
+     * Target types to build.
+     * valid build types are exe|library|module|winexe
+     */
+    public static class TargetTypes extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[] {
+                "exe",
+                "library",
+                "module",
+                "winexe"
+            };
+        }
+    }
+
+
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/DotnetDefine.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/DotnetDefine.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/DotnetDefine.java	(revision 16139)
@@ -0,0 +1,97 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.dotnet;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.Project;
+
+/**
+ * definitions can be conditional. What .NET conditions can not be
+ * is in any state other than defined and undefined; you cannot give
+ * a definition a value.
+ */
+public class DotnetDefine {
+    private String name;
+    private String ifCond;
+    private String unlessCond;
+
+
+    /**
+     * the name of a property which must be defined for
+     * the definition to be set. Optional.
+     * @param condition the name of the property
+     */
+    public void setIf(String condition) {
+        this.ifCond = condition;
+    }
+
+    /**
+     * the name of a property which must be undefined for
+     * the definition to be set. Optional.
+     * @param condition the name of the property
+     */
+    public void setUnless(String condition) {
+        this.unlessCond = condition;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * the name of the definition. Required.
+     * @param name
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * This method gets the value of this definition. Will be null if a condition
+     * was declared and not met
+     * @param owner owning task
+     * @return The value of the definition.
+     * @throws BuildException
+     */
+    public String getValue(Task owner) throws BuildException {
+        if (name == null) {
+            throw new BuildException("No name provided for the define element",
+                owner.getLocation());
+        }
+        if (!isSet(owner)) {
+            return null;
+        }
+        return name;
+    }
+
+
+    /**
+     * logic taken from patternset
+     * @param owner
+     * @return true if the condition is valid
+     */
+    public boolean isSet(Task owner) {
+        Project p = owner.getProject();
+        if (ifCond != null && p.getProperty(ifCond) == null) {
+            return false;
+        } else if (unlessCond != null && p.getProperty(unlessCond) != null) {
+            return false;
+        }
+        return true;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/DotnetResource.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/DotnetResource.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/DotnetResource.java	(revision 16139)
@@ -0,0 +1,143 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.dotnet;
+
+import org.apache.tools.ant.BuildException;
+
+import java.io.File;
+
+/**
+ * class used by DotnetCompile to name resources, could be upgraded to a datatype
+ * in the distant future.
+ * a resource maps to /res:file,name
+ */
+public class DotnetResource {
+
+
+    /**
+     * name of resource
+     */
+    private File file;
+
+    /**
+     * embed (default) or link the resource
+     */
+    private boolean embed = true;
+
+    /**
+     * this is used in VBC and JSC
+     */
+    private Boolean isPublic = null;
+
+    /**
+     * name of the object
+     */
+    private String name = null;
+
+    public boolean isEmbed() {
+        return embed;
+    }
+
+    /**
+     * embed the resource in the assembly (default, true) or just link to it.
+     * @param embed
+     */
+    public void setEmbed(boolean embed) {
+        this.embed = embed;
+    }
+
+    public File getFile() {
+        return file;
+    }
+
+    /**
+     * name the resource
+     * @param file
+     */
+    public void setFile(File file) {
+        this.file = file;
+    }
+
+    public Boolean getPublic() {
+        return isPublic;
+    }
+
+    /**
+     * VB and J# only: is a resource public or not?
+     * @param aPublic
+     */
+    public void setPublic(Boolean aPublic) {
+        isPublic = aPublic;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * should the resource have a name?
+     * @param name
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * build the C# style parameter (which has no public/private option)
+     * @return the built C# style parameter
+     */
+    public String getCSharpStyleParameter() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(isEmbed() ? "/resource" : "/linkresource");
+        buffer.append(':');
+        buffer.append(getFile().toString());
+        if (getName() != null) {
+            buffer.append(',');
+            buffer.append(getName());
+        }
+        if (getPublic() != null) {
+            throw new BuildException("This compiler does not support the "
+                    + "public/private option.");
+        }
+        return buffer.toString();
+    }
+
+    /**
+     * This method gets the style of param used by VB and javascript
+     * @return The style VB parameter being used.
+     */
+    public String getVbStyleParameter() {
+        StringBuffer buffer = new StringBuffer();
+        buffer.append(isEmbed() ? "/resource" : "/linkresource");
+        buffer.append(':');
+        buffer.append(getFile().toString());
+        if (getName() != null) {
+            buffer.append(',');
+            buffer.append(getName());
+            if (getPublic() != null) {
+                buffer.append(',');
+                buffer.append(getPublic().booleanValue()
+                        ? "public" : "private");
+
+            }
+        } else if (getPublic() != null) {
+            throw new BuildException("You cannot have a public or private "
+                    + "option without naming the resource");
+        }
+        return buffer.toString();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ilasm.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ilasm.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ilasm.java	(revision 16139)
@@ -0,0 +1,508 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ *  build notes
+ *  -The reference CD to listen to while editing this file is
+ *  nap: Underworld  - Everything, Everything
+ */
+
+package org.apache.tools.ant.taskdefs.optional.dotnet;
+
+
+import java.io.File;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * Assembles .NET Intermediate Language files.
+ * ilasm.exe must be on the execute path, unless another executable
+ * or the full path to that executable is specified in the <tt>executable</tt>
+ * parameter
+ *  <p>
+ *
+ *  <p>
+ *
+ *  All parameters are optional: &lt;il/&gt; should suffice to produce a debug
+ *  build of all *.il files. The option set is roughly compatible with the
+ *  CSharp class; even though the command line options are only vaguely
+ *  equivalent. [The low level commands take things like /OUT=file, csc wants
+ *  /out:file ... /verbose is used some places; /quiet here in ildasm... etc.]
+ *  It would be nice if someone made all the command line tools consistent (and
+ *  not as brittle as the java cmdline tools) <p>
+ *  <p>
+ *  The task is a directory based task, so attributes like <b>includes="*.il"
+ *  </b> and <b>excludes="broken.il"</b> can be used to control the files pulled
+ *  in. You can also use nested &lt;src&gt filesets to refer to source.
+ * <p>
+ *
+ * @ant.task    name="ilasm" category="dotnet"
+ */
+
+public class Ilasm
+         extends DotnetBaseMatchingTask {
+
+    /**
+     *  Name of the executable. The .exe suffix is deliberately not included in
+     *  anticipation of the unix version
+     */
+    protected static final String exe_name = "ilasm";
+
+    /**
+     *  what is the file extension we search on?
+     */
+    protected static final String file_ext = "il";
+
+    /**
+     *  and now derive the search pattern from the extension
+     */
+    protected static final String file_pattern = "**/*." + file_ext;
+
+    /**
+     *  title of task for external presentation
+     */
+    protected static final String exe_title = "ilasm";
+
+    /**
+     *  type of target. Should be one of exe|library|module|winexe|(null)
+     *  default is exe; the actual value (if not null) is fed to the command
+     *  line. <br>
+     *  See /target
+     */
+    protected String targetType;
+
+    /**
+     *  verbose flag
+     */
+    protected boolean verbose;
+
+    /**
+     *  listing flag
+     */
+
+    protected boolean listing;
+
+    /**
+     *  resource file (.res format) to include in the app.
+     */
+    protected File resourceFile;
+
+    /**
+     *  flag to control action on execution trouble
+     */
+    protected boolean failOnError;
+
+    /**
+     *  debug flag. Controls generation of debug information.
+     */
+    protected boolean debug;
+
+    /**
+     *  file containing private key
+     */
+
+    private File keyfile;
+
+    /**
+     *  any extra command options?
+     */
+    protected String extraOptions;
+
+    /**
+     * filesets of references
+     */
+    protected Vector referenceFilesets = new Vector();
+
+
+    /**
+     *  constructor inits everything and set up the search pattern
+     */
+    public Ilasm() {
+        Clear();
+        setIncludes(file_pattern);
+    }
+
+
+    /**
+     *  reset all contents.
+     */
+    public void Clear() {
+        targetType = null;
+        srcDir = null;
+        listing = false;
+        verbose = false;
+        debug = true;
+        outputFile = null;
+        failOnError = true;
+        resourceFile = null;
+        extraOptions = null;
+    }
+
+
+
+    /**
+     * Sets the type of target, either "exe" or "library".
+     *
+     *@param  targetType          one of exe|library|
+     *@exception  BuildException  if target is not one of
+     *      exe|library
+     */
+    public void setTargetType(String targetType)
+             throws BuildException {
+        this.targetType = targetType.toLowerCase();
+        if (!targetType.equals("exe") && !targetType.equals("library")) {
+            throw new BuildException("targetType " + targetType + " is not a valid type");
+        }
+    }
+
+
+    /**
+     *  accessor method for target type
+     *
+     *@return    the current target option
+     */
+    public String getTargetType() {
+        return targetType;
+    }
+
+
+    /**
+     *  g get the target type or null for no argument needed
+     *
+     *@return    The TargetTypeParameter value
+     */
+
+    protected String getTargetTypeParameter() {
+        if (!notEmpty(targetType)) {
+            return null;
+        }
+        if (targetType.equals("exe")) {
+            return "/exe";
+        } else if (targetType.equals("library")) {
+            return "/dll";
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Sets the Owner attribute.
+     *
+     * @param  s  The new Owner value
+     * @ant.attribute ignore="true"
+     */
+    public void setOwner(String s) {
+        log("This option is not supported by ILASM as of Beta-2, "
+            + "and will be ignored", Project.MSG_WARN);
+    }
+
+
+    /**
+     *  test for a string containing something useful
+     *
+     *@param  s       any string
+     *@return         true if the argument is not null or empty
+     */
+    protected boolean notEmpty(String s) {
+        return s != null && s.length() != 0;
+    }
+
+
+    /**
+     *  If true, enable verbose ILASM output.
+     *
+     *@param  b  flag set to true for verbose on
+     */
+    public void setVerbose(boolean b) {
+        verbose = b;
+    }
+
+
+    /**
+     *  turn the verbose flag into a parameter for ILASM
+     *
+     *@return    null or the appropriate command line string
+     */
+    protected String getVerboseParameter() {
+        return verbose ? null : "/quiet";
+    }
+
+
+    /**
+     * If true, produce a listing (off by default).
+     *
+     *@param  b  flag set to true for listing on
+     */
+    public void setListing(boolean b) {
+        listing = b;
+    }
+
+
+    /**
+     *  turn the listing flag into a parameter for ILASM
+     *
+     *@return    the appropriate string from the state of the listing flag
+     */
+    protected String getListingParameter() {
+        return listing ? "/listing" : "/nolisting";
+    }
+
+
+    /**
+     * Set the output file; identical to setDestFile
+     * @see DotnetBaseMatchingTask#setDestFile
+     *@param  params  The new outputFile value
+     */
+    public void setOutputFile(File params) {
+        outputFile = params;
+    }
+
+
+    /**
+     *  get the output file
+     *
+     *@return    the argument string or null for no argument
+     */
+    protected String getOutputFileParameter() {
+        if (outputFile == null) {
+            return null;
+        }
+        return "/output=" + outputFile.toString();
+    }
+
+
+    /**
+     * name of resource file to include.
+     *
+     * @param  fileName  path to the file. Can be relative, absolute, whatever.
+     */
+    public void setResourceFile(File fileName) {
+        resourceFile = fileName;
+    }
+
+
+    /**
+     *  Gets the resourceFileParameter attribute of the Ilasm task
+     *
+     *@return    The resourceFileParameter value
+     */
+    protected String getResourceFileParameter() {
+        if (resourceFile != null) {
+            return "/resource=" + resourceFile.toString();
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * If true, fails if ilasm tool fails.
+     *
+     *@param  b  The new failOnError value
+     */
+    public void setFailOnError(boolean b) {
+        failOnError = b;
+    }
+
+
+    /**
+     *  query fail on error flag
+     *
+     *@return    The failFailOnError value
+     */
+    public boolean getFailOnError() {
+        return failOnError;
+    }
+
+
+    /**
+     *  set the debug flag on or off.
+     *
+     *@param  f  on/off flag
+     */
+    public void setDebug(boolean f) {
+        debug = f;
+    }
+
+
+    /**
+     *  query the debug flag
+     *
+     *@return    true if debug is turned on
+     */
+    public boolean getDebug() {
+        return debug;
+    }
+
+
+    /**
+     *  get the argument or null for no argument needed
+     *
+     *@return    The debugParameter value
+     */
+    protected String getDebugParameter() {
+        return debug ? "/debug" : null;
+    }
+
+
+    /**
+     * the name of a file containing a private key.
+     *
+     *@param  keyfile  The new keyfile value
+     */
+    public void setKeyfile(File keyfile) {
+        this.keyfile = keyfile;
+    }
+
+
+    /**
+     *  get the argument or null for no argument needed
+     *
+     *@return    The keyfileParameter value
+     */
+    protected String getKeyfileParameter() {
+        if (keyfile != null) {
+            return "/keyfile:" + keyfile.toString();
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Any extra options which are not explicitly
+     * supported by this task.
+     *
+     *@param  extraOptions  The new ExtraOptions value
+     */
+    public void setExtraOptions(String extraOptions) {
+        this.extraOptions = extraOptions;
+    }
+
+
+    /**
+     *  Gets the ExtraOptions attribute
+     *
+     *@return    The ExtraOptions value
+     */
+    public String getExtraOptions() {
+        return this.extraOptions;
+    }
+
+
+    /**
+     *  get any extra options or null for no argument needed
+     *
+     *@return    The ExtraOptions Parameter to CSC
+     */
+    protected String getExtraOptionsParameter() {
+        if (extraOptions != null && extraOptions.length() != 0) {
+            return extraOptions;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * set the target type to one of exe|library
+     * @param targetType
+     */
+    public void setTargetType(TargetTypes targetType) {
+        this.targetType = targetType.getValue();
+    }
+
+    /**
+     *  This is the execution entry point. Build a list of files and call ilasm
+     *  on each of them.
+     *
+     *@throws  BuildException  if the assembly failed and FailOnError is true
+     */
+    public void execute()
+             throws BuildException {
+        NetCommand command = buildIlasmCommand();
+
+        addFilesAndExecute(command, false);
+
+    }
+    // end execute
+
+
+    /**
+     * build up our ilasm command
+     * @return
+     */
+    private NetCommand buildIlasmCommand() {
+        NetCommand command = new NetCommand(this, exe_title, exe_name);
+        command.setFailOnError(getFailOnError());
+        //fill in args
+        command.addArgument(getDebugParameter());
+        command.addArgument(getTargetTypeParameter());
+        command.addArgument(getListingParameter());
+        command.addArgument(getOutputFileParameter());
+        command.addArgument(getResourceFileParameter());
+        command.addArgument(getVerboseParameter());
+        command.addArgument(getKeyfileParameter());
+        command.addArgument(getExtraOptionsParameter());
+
+        /*
+         *  space for more argumentativeness
+         *  command.addArgument();
+         *  command.addArgument();
+         */
+        return command;
+    }
+
+    /**
+     * add a new reference fileset to the compilation
+     * @param reference
+     */
+    public void addReference(FileSet reference) {
+        referenceFilesets.add(reference);
+    }
+
+    /**
+     * test for a file being managed or not
+     * @return true if we think this is a managed executable, and thus OK
+     * for linking
+     * @todo look at the PE header of the exe and see if it is managed or not.
+     */
+    protected static boolean isFileManagedBinary(File file) {
+        String filename = file.toString().toLowerCase();
+        return filename.endsWith(".exe") || filename.endsWith(".dll")
+                || filename.endsWith(".netmodule");
+    }
+
+
+    /**
+     * Target types to build.
+     * valid build types are exe|library|module|winexe
+     */
+    public static class TargetTypes extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[]{
+                "exe",
+                "library",
+            };
+        }
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ildasm.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ildasm.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/Ildasm.java	(revision 16139)
@@ -0,0 +1,460 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.dotnet;
+
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileUtils;
+
+import java.io.File;
+
+/**
+ * Task to take a .NET or Mono -generated managed executable and turn it
+ * into ILASM assembly code. Useful when converting imported typelibs into
+ * assembler before patching and recompiling, as one has to do when doing
+ * advanced typelib work.
+ * <p>
+ * As well as generating the named output file, the ildasm program
+ * will also generate resource files <code>Icons.resources</code>
+ * <code>Message.resources</code> and a .res file whose filename stub is derived
+ * from the source in ways to obscure to determine.
+ * There is no way to control whether or not these files are created, or where they are created
+ * (they are created in the current directory; their names come from inside the
+ * executable and may be those used by the original developer). This task
+ * creates the resources in the directory specified by <code>resourceDir</code> if
+ * set, else in the same directory as the <code>destFile</code>.
+ *
+ * <p>
+ * This task requires the .NET SDK installed and ildasm on the path.
+ * To disassemble using alternate CLR systems, set the executable attribute
+ * to the name/path of the alternate implementation -one that must
+ * support all the classic ildasm commands.
+ *
+ * <p>
+ * Dependency logic: the task executes the command if the output file is missing
+ * or older than the source file. It does not take into account changes
+ * in the options of the task, or timestamp differences in resource files.
+ * When the underlying ildasm executable fails for some reason, it leaves the
+ * .il file in place with some error message. To prevent this from confusing
+ * the dependency logic, the file specified by the <code>dest</code>
+ * attribute is <i>always</i> deleted after an unsuccessful build.
+ * @ant.task category="dotnet"
+ */
+public class Ildasm extends Task {
+
+    /**
+     * source file (mandatory)
+     */
+    private File sourceFile;
+
+    /**
+     * dest file (mandatory)
+     */
+    private File destFile;
+    /**
+     * progress bar switch
+     */
+    private boolean progressBar = false;
+
+    /**
+     * what is our encoding
+     */
+    private String encoding;
+
+    /**
+     * /bytes flag for byte markup
+     */
+
+    private boolean bytes = false;
+
+    /**
+     * line numbers? /linenum
+     */
+    private boolean linenumbers = false;
+
+    /**
+     * /raweh flag for raw exception handling
+     */
+    private boolean rawExceptionHandling = false;
+
+    /**
+     * show the source; /source
+     */
+    private boolean showSource = false;
+
+    /**
+     * /quoteallnames to quote all names
+     */
+    private boolean quoteallnames = false;
+
+    /**
+     * /header for header information
+     */
+    private boolean header = false;
+
+    /**
+     * when false, sets the /noil attribute
+     * to suppress assembly info
+     */
+    private boolean assembler = true;
+
+    /**
+     * include metadata
+     * /tokens
+     */
+
+    private boolean metadata = false;
+
+    /**
+     * what visibility do we want.
+     *
+     */
+    private String visibility;
+
+    /**
+     * specific item to disassemble
+     */
+
+    private String item;
+
+    /**
+     * override for the executable
+     */
+    private String executable = "ildasm";
+
+    /**
+     *  name of the directory for resources to be created. We cannot control
+     * their names, but we can say where they get created. If not set, the
+     * directory of the dest file is used
+     */
+    private File resourceDir;
+
+
+    /**
+     * Set the name of the directory for resources to be created. We cannot control
+     * their names, but we can say where they get created. If not set, the
+     * directory of the dest file is used
+     */
+    public void setResourceDir(File resourceDir) {
+        this.resourceDir = resourceDir;
+    }
+
+    /**
+     * override the name of the executable (normally ildasm) or set
+     * its full path. Do not set a relative path, as the ugly hacks
+     * needed to create resource files in the dest directory
+     * force us to change to this directory before running the application.
+     * i.e use &lt;property location&gt to create an absolute path from a
+     * relative one before setting this value.
+     * @param executable
+     */
+    public void setExecutable(String executable) {
+        this.executable = executable;
+    }
+
+    /**
+     * Select the output encoding: ascii, utf8 or unicode
+     * @param encoding
+     */
+    public void setEncoding(EncodingTypes encoding) {
+        this.encoding = encoding.getValue();
+    }
+
+    /**
+     * enable (default) or disable assembly language in the output
+     * @param assembler
+     */
+    public void setAssembler(boolean assembler) {
+        this.assembler = assembler;
+    }
+
+    /**
+     * enable or disable (default) the original bytes as comments
+     * @param bytes
+     */
+    public void setBytes(boolean bytes) {
+        this.bytes = bytes;
+    }
+
+    /**
+     * the output file (required)
+     * @param destFile
+     */
+    public void setDestFile(File destFile) {
+        this.destFile = destFile;
+    }
+
+    /**
+     * include header information; default false.
+     * @param header
+     */
+    public void setHeader(boolean header) {
+        this.header = header;
+    }
+
+    /**
+     * name a single item to decode; a class or a method
+     * e.g item="Myclass::method" or item="namespace1::namespace2::Myclass:method(void(int32))
+     * @param item
+     */
+    public void setItem(String item) {
+        this.item = item;
+    }
+
+    /**
+     * include line number information; default=false
+     * @param linenumbers
+     */
+    public void setLinenumbers(boolean linenumbers) {
+        this.linenumbers = linenumbers;
+    }
+
+    /**
+     * include metadata information
+     * @param metadata
+     */
+    public void setMetadata(boolean metadata) {
+        this.metadata = metadata;
+    }
+
+    /**
+     * show a graphical progress bar in a window during the process; off by default
+     * @param progressBar
+     */
+    public void setProgressBar(boolean progressBar) {
+        this.progressBar = progressBar;
+    }
+
+    /**
+     * quote all names.
+     * @param quoteallnames
+     */
+    public void setQuoteallnames(boolean quoteallnames) {
+        this.quoteallnames = quoteallnames;
+    }
+
+    /**
+     * enable raw exception handling (default = false)
+     * @param rawExceptionHandling
+     */
+    public void setRawExceptionHandling(boolean rawExceptionHandling) {
+        this.rawExceptionHandling = rawExceptionHandling;
+    }
+
+    /**
+     * include the source as comments (default=false)
+     */
+    public void setShowSource(boolean showSource) {
+        this.showSource = showSource;
+    }
+
+    /**
+     * the file to disassemble -required
+     * @param sourceFile
+     */
+    public void setSourceFile(File sourceFile) {
+        this.sourceFile = sourceFile;
+    }
+
+    /**
+     * alternate name for sourceFile
+     * @param sourceFile
+     */
+    public void setSrcFile(File sourceFile) {
+        setSourceFile(sourceFile);
+    }
+    /**
+     * This method sets the visibility options. It chooses one or more of the following, with + signs to
+     * concatenate them:
+     * <pre>
+     * pub : Public
+     * pri : Private
+     * fam : Family
+     * asm : Assembly
+     * faa : Family and Assembly
+     * foa : Family or Assembly
+     * psc : Private Scope
+     *</pre>
+     * e.g. visibility="pub+pri".
+     * Family means <code>protected</code> in C#;
+     * @param visibility
+     */
+    public void setVisibility(String visibility) {
+        this.visibility = visibility;
+    }
+
+    /**
+     *  verify that source and dest are ok
+     */
+    private void validate() {
+        if (sourceFile == null || !sourceFile.exists() || !sourceFile.isFile()) {
+            throw new BuildException("invalid source");
+        }
+        if (destFile == null || destFile.isDirectory()) {
+            throw new BuildException("invalid dest");
+        }
+        if (resourceDir != null
+                && (!resourceDir.exists() || !resourceDir.isDirectory())) {
+            throw new BuildException("invalid resource directory");
+        }
+    }
+
+    /**
+     * Test for disassembly being needed; use existence and granularity
+     * correct date stamps
+     * @return true iff a rebuild is required.
+     */
+    private boolean isDisassemblyNeeded() {
+        if (!destFile.exists()) {
+            log("Destination file does not exist: a build is required",
+                    Project.MSG_VERBOSE);
+            return true;
+        }
+        long sourceTime = sourceFile.lastModified();
+        long destTime = destFile.lastModified();
+        if(sourceTime > (destTime + FileUtils.newFileUtils().getFileTimestampGranularity())) {
+            log("Source file is newer than the dest file: a rebuild is required",
+                    Project.MSG_VERBOSE);
+            return true;
+        } else {
+            log("The .il file is up to date", Project.MSG_VERBOSE);
+            return false;
+        }
+
+    }
+    /**
+     * do the work
+     * @throws BuildException
+     */
+    public void execute() throws BuildException {
+        validate();
+        if(!isDisassemblyNeeded()) {
+            return;
+        }
+        NetCommand command = new NetCommand(this, "ildasm", executable);
+        command.setFailOnError(true);
+        //fill in args
+        command.addArgument("/text");
+        command.addArgument("/out=" + destFile.toString());
+        if (!progressBar) {
+            command.addArgument("/nobar");
+        }
+        if (linenumbers) {
+            command.addArgument("/linenum");
+        }
+        if (showSource) {
+            command.addArgument("/source");
+        }
+        if (quoteallnames) {
+            command.addArgument("/quoteallnames");
+        }
+        if (header) {
+            command.addArgument("/header");
+        }
+        if (!assembler) {
+            command.addArgument("/noil");
+        }
+        if (metadata) {
+            command.addArgument("/tokens");
+        }
+        command.addArgument("/item:", item);
+        if (rawExceptionHandling) {
+            command.addArgument("/raweh");
+        }
+        command.addArgument(EncodingTypes.getEncodingOption(encoding));
+        if (bytes) {
+            command.addArgument("/bytes");
+        }
+        command.addArgument("/vis:", visibility);
+
+        //add the source file
+        command.addArgument(sourceFile.getAbsolutePath());
+
+        //determine directory: resourceDir if set,
+        //the dir of the destFile if not
+        File execDir = resourceDir;
+        if (execDir == null) {
+            execDir = destFile.getParentFile();
+        }
+        command.setDirectory(execDir);
+
+        //now run
+        try {
+            command.runCommand();
+        } catch (BuildException e) {
+            //forcibly delete the output file in case of trouble
+            if (destFile.exists()) {
+                log("Deleting destination file as it may be corrupt");
+                destFile.delete();
+            }
+            //then rethrow the exception
+            throw e;
+        }
+
+    }
+
+    /**
+     * encoding options; the default is ascii
+     */
+    public static class EncodingTypes extends EnumeratedAttribute {
+        public final static String UNICODE = "unicode";
+        public final static String UTF8 = "utf8";
+        public final static String ASCII = "ascii";
+        public String[] getValues() {
+            return new String[]{
+                ASCII,
+                UTF8,
+                UNICODE,
+            };
+        }
+
+        /**
+         * This method maps from an encoding enum to an encoding option.
+         * @param enumValue
+         * @return The encoding option indicated by the enum value.
+         */
+        public static String getEncodingOption(String enumValue) {
+            if (UNICODE.equals(enumValue)) {
+                return "/unicode";
+            }
+            if (UTF8.equals(enumValue)) {
+                return "/utf8";
+            }
+            return null;
+        }
+    }
+
+    /**
+     * visibility options for decoding
+     */
+    public static class VisibilityOptions extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[]{
+                "pub", //Public
+                "pri", //Private
+                "fam", //Family
+                "asm", //Assembly
+                "faa", //Family and Assembly
+                "foa", //Family or Assembly
+                "psc", //Private Scope
+            };
+        }
+
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/ImportTypelib.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/ImportTypelib.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/ImportTypelib.java	(revision 16139)
@@ -0,0 +1,211 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.dotnet;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileUtils;
+
+import java.io.File;
+
+/**
+ * Import a COM type library into the .NET framework.
+ * <p>
+ *
+ * This task is a wrapper to .NET's tlbimport; it imports a tlb file to a NET assembly
+ * by generating a binary assembly (.dll) that contains all the binding
+ * metadata. It uses date timestamps to minimise rebuilds.
+ * <p>
+ * Example
+ * <pre>
+ *     &lt;importtypelib
+ *       srcfile="xerces.tlb"
+ *       destfile="xerces.dll"
+ *       namespace="Apache.Xerces"/&gt;
+ * </pre>
+ * @since Ant 1.6
+ * @ant.task category="dotnet"
+ */
+public class ImportTypelib extends Task {
+
+
+    /**
+     * input file; precedes options
+     */
+    private File srcFile;
+
+    /**
+     * /out:file
+     */
+    private File destFile;
+
+    /**
+     *  /namespace:[string]
+     */
+    private String namespace;
+
+    /**
+     * /sysarray
+     */
+    private boolean useSysArray = false;
+
+    /**
+     * /unsafe
+     */
+    private boolean unsafe = false;
+
+    /**
+     * extra commands?
+     */
+    private String extraOptions = null;
+
+    /**
+     * This method names the output file.
+     * 
+     * This is an operation which is required to have been performed.
+     * @param destFile
+     */
+    public void setDestFile(File destFile) {
+        this.destFile = destFile;
+    }
+
+    /**
+     * This method sets what namespace the typelib is to be in.
+     * This is an operation which is required to have been performed.
+     * @param namespace
+     */
+    public void setNamespace(String namespace) {
+        this.namespace = namespace;
+    }
+
+    /**
+     * This method sets which is the source .tlb file. 
+     * This is an operation which is required to have been performed.
+     * @param srcFile
+     */
+    public void setSrcFile(File srcFile) {
+        this.srcFile = srcFile;
+    }
+
+    /**
+     * do you want unsafe code.
+     * @param unsafe
+     */
+    public void setUnsafe(boolean unsafe) {
+        this.unsafe = unsafe;
+    }
+
+    /**
+     * set this to map a COM SafeArray to the System.Array class
+     * @param useSysArray
+     */
+    public void setUseSysArray(boolean useSysArray) {
+        this.useSysArray = useSysArray;
+    }
+
+    /**
+     * set any extra options that are not yet supported by this task.
+     * @param extraOptions
+     */
+    public void setExtraOptions(String extraOptions) {
+        this.extraOptions = extraOptions;
+    }
+
+    /**
+     * validation code
+     * @throws  BuildException  if validation failed
+     */
+    protected void validate()
+            throws BuildException {
+        if (destFile == null) {
+            throw new BuildException("destination file must be specified");
+        }
+        if (destFile.isDirectory()) {
+            throw new BuildException(
+                    "destination file is a directory");
+        }
+        if (srcFile == null || !srcFile.exists()) {
+            throw new BuildException(
+                    "source file does not exist");
+        }
+        if (srcFile.isDirectory()) {
+            throw new BuildException(
+                    "source file is a directory");
+        }
+        if (namespace == null) {
+            throw new BuildException("No namespace");
+        }
+    }
+
+    /**
+     * Test for disassembly being needed; use existence and granularity
+     * correct date stamps
+     * @return true iff a rebuild is required.
+     */
+    private boolean isExecuteNeeded() {
+        if (!destFile.exists()) {
+            log("Destination file does not exist: a build is required",
+                    Project.MSG_VERBOSE);
+            return true;
+        }
+        long sourceTime = srcFile.lastModified();
+        long destTime = destFile.lastModified();
+        if (sourceTime > (destTime + FileUtils.newFileUtils().getFileTimestampGranularity())) {
+            log("Source file is newer than the dest file: a rebuild is required",
+                    Project.MSG_VERBOSE);
+            return true;
+        } else {
+            log("The output file is up to date", Project.MSG_VERBOSE);
+            return false;
+        }
+
+    }
+
+
+    /**
+     * Create a typelib command
+     * @exception BuildException if something goes wrong with the build
+     */
+    public void execute() throws BuildException {
+        validate();
+        log("Importing typelib " + srcFile
+            + " to assembly " + destFile
+            + " in namespace " + namespace, Project.MSG_VERBOSE);
+        //rebuild unless the dest file is newer than the source file
+        if(!isExecuteNeeded()) {
+            return;
+        }
+
+        NetCommand command = new NetCommand(this, "ImportTypelib", "tlbimp");
+        command.setFailOnError(true);
+        command.addArgument(srcFile.toString());
+        //fill in args
+        command.addArgument("/nologo");
+        command.addArgument("/out:" + destFile);
+        command.addArgument("/namespace:", namespace);
+        if (useSysArray) {
+            command.addArgument("/sysarray");
+        }
+        if (unsafe) {
+            command.addArgument("/unsafe");
+        }
+        command.addArgument(extraOptions);
+        command.runCommand();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/JSharp.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/JSharp.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/JSharp.java	(revision 16139)
@@ -0,0 +1,137 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.dotnet;
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Compile J# source down to a managed .NET application.
+ * <p>
+ * J# is not Java. But it is the language closest to Java in the .NET framework.
+ * This task compiles jsharp source (.java files), and
+ * generates a .NET managed exe or dll.
+ * <p>
+ *
+ * <p>For historical reasons the pattern
+ * <code>**</code><code>/*.java</code> is preset as includes list and
+ * you can not override it with an explicit includes attribute.  Use
+ * nested <code>&lt;src&gt;</code> elements instead of the basedir
+ * attribute if you need more control.</p>
+ *
+ * @see <A=ref="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_vjsharp/html/vjoriMicrosoftVisualJ.asp">
+ * Visual J++ online documentation</a>
+ *
+ * @since ant1.6
+ * @ant.task category="dotnet" name="jsharpc"
+ */
+public class JSharp extends DotnetCompile {
+
+    /**
+     * hex base address
+     */
+    String baseAddress;
+
+    /** /x option to disable J++ and J# lang extensions
+     *
+     */
+    boolean pureJava = true;
+
+    /**
+     * whether to make package scoped stuff public or assembly scoped
+     */
+    boolean secureScoping = false;
+
+    public JSharp() {
+        setExecutable("vjc");
+    }
+
+
+    public void setBaseAddress(String baseAddress) {
+        this.baseAddress = baseAddress;
+    }
+
+    /**
+     * do we want pure java (default, true) or corrupted J#?
+     * @param pureJava
+     */
+    public void setPureJava(boolean pureJava) {
+        this.pureJava = pureJava;
+    }
+
+    /**
+     * Make package scoped code visible to the current assembly only (default: false)
+     * .NET does not have package scoping. Instead it has assembly, private and public.
+     * By default, package content is public to all.
+     * @param secureScoping
+     */
+    public void setSecureScoping(boolean secureScoping) {
+        this.secureScoping = secureScoping;
+    }
+
+    /**
+     * Get the delimiter that the compiler uses between references.
+     * For example, c# will return ";"; VB.NET will return ","
+     * @return The string delimiter for the reference string.
+     */
+    public String getReferenceDelimiter() {
+        return ";";
+    }
+
+    /**
+     * Get the extension of filenames to compile.
+     * @return The string extension of files to compile.
+     */
+    public String getFileExtension() {
+        return ".java";
+    }
+
+    /**
+     * add jvc specific commands
+     * @param command
+     */
+    protected void addCompilerSpecificOptions(NetCommand command) {
+        if (pureJava) {
+            command.addArgument("/x:all");
+        }
+        if (secureScoping) {
+            command.addArgument("/securescoping");
+        }
+    }
+
+    /**
+     * from a resource, get the resource param
+     * @param resource
+     * @return a string containing the resource param, or a null string
+     * to conditionally exclude a resource.
+     */
+    protected String createResourceParameter(DotnetResource resource) {
+        return resource.getCSharpStyleParameter();
+    }
+
+    /**
+     * validation code
+     * @throws  org.apache.tools.ant.BuildException  if validation failed
+     */
+    protected void validate()
+            throws BuildException {
+        super.validate();
+        if (getDestFile() == null) {
+            throw new BuildException("DestFile was not specified");
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/NetCommand.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/NetCommand.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/NetCommand.java	(revision 16139)
@@ -0,0 +1,397 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ *  build notes
+ *  The reference CD to listen to while editing this file is
+ *  Underworld Everything, Everything
+ *  variable naming policy from Fowler's refactoring book.
+ */
+// place below the optional ant tasks package
+
+package org.apache.tools.ant.taskdefs.optional.dotnet;
+
+// imports
+
+import java.io.File;
+import java.io.IOException;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.io.BufferedOutputStream;
+import java.io.FileNotFoundException;
+import java.util.Hashtable;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ *  This is a helper class to spawn net commands out. In its initial form it
+ *  contains no .net specifics, just contains all the command line/exe
+ *  construction stuff. However, it may be handy in future to have a means of
+ *  setting the path to point to the dotnet bin directory; in which case the
+ *  shared code should go in here.
+ *
+ *@version    0.5
+ */
+
+public class NetCommand {
+
+    /**
+     *  owner project
+     */
+    protected Task owner;
+
+    /**
+     *  executable
+     */
+    protected Execute executable;
+
+    /**
+     *  what is the command line
+     */
+    protected Commandline commandLine;
+
+    /**
+     *  title of the command
+     */
+    protected String title;
+
+    /**
+     *  actual program to invoke
+     */
+    protected String program;
+
+    /**
+     *  trace flag
+     */
+    protected boolean traceCommandLine = false;
+
+    /**
+     *  flag to control action on execution trouble
+     */
+    protected boolean failOnError;
+
+    /**
+     * the directory to execute the command in. When null, the current
+     * directory is used.
+     */
+    private File directory;
+
+    /**
+     * flag to set to to use @file based command cache
+     */
+    private boolean useResponseFile=false;
+
+    /**
+     * name of a temp file; may be null
+     */
+    private File temporaryCommandFile;
+
+    /**
+     * internal threshold for auto-switch
+     */
+    private int automaticResponseFileThreshold = 64;
+
+    /**
+     *  constructor
+     *
+     *@param  title        (for logging/errors)
+     *@param  owner        owner task
+     *@param  program      app we are to run
+     */
+
+    public NetCommand(Task owner, String title, String program) {
+        this.owner = owner;
+        this.title = title;
+        this.program = program;
+        commandLine = new Commandline();
+        commandLine.setExecutable(program);
+        prepareExecutor();
+    }
+
+
+    /**
+     *  turn tracing on or off
+     *
+     *@param  b  trace flag
+     */
+    public void setTraceCommandLine(boolean b) {
+        traceCommandLine = b;
+    }
+
+
+    /**
+     *  set fail on error flag
+     *
+     *@param  b  fail flag -set to true to cause an exception to be raised if
+     *      the return value != 0
+     */
+    public void setFailOnError(boolean b) {
+        failOnError = b;
+    }
+
+
+    /**
+     *  query fail on error flag
+     *
+     *@return    The failFailOnError value
+     */
+    public boolean getFailFailOnError() {
+        return failOnError;
+    }
+
+
+    /**
+     * set the directory to run from, if the default is inadequate
+     * @param directory
+     */
+    public void setDirectory(File directory) {
+        this.directory = directory;
+    }
+
+    /**
+     *  verbose text log
+     *
+     *@param  msg  string to add to log if verbose is defined for the build
+     */
+    protected void logVerbose(String msg) {
+        owner.getProject().log(msg, Project.MSG_VERBOSE);
+    }
+
+
+    /**
+     *  error text log
+     *
+     *@param  msg  message to display as an error
+     */
+    protected void logError(String msg) {
+        owner.getProject().log(msg, Project.MSG_ERR);
+    }
+
+
+    /**
+     *  add an argument to a command line; do nothing if the arg is null or
+     *  empty string
+     *
+     *@param  argument  The feature to be added to the Argument attribute
+     */
+    public void addArgument(String argument) {
+        if (argument != null && argument.length() != 0) {
+            commandLine.createArgument().setValue(argument);
+        }
+    }
+
+    /**
+     *  add an argument to a command line; do nothing if the arg is null or
+     *  empty string
+     *
+     *@param  argument  The feature to be added to the Argument attribute
+     */
+    public void addArguments(String[] arguments) {
+        if (arguments != null && arguments.length != 0) {
+            for (int i = 0; i < arguments.length; i++) {
+                addArgument(arguments[i]);
+            }
+        }
+    }
+
+    /**
+     *  concatenate two strings together and add them as a single argument,
+     *  but only if argument2 is non-null and non-zero length
+     *
+     *@param  argument1  The first argument
+     *@param  argument2  The second argument
+     */   public void addArgument(String argument1, String argument2) {
+        if (argument2 != null && argument2.length() != 0) {
+            commandLine.createArgument().setValue(argument1 + argument2);
+        }
+    }
+
+    /**
+     * getter
+     * @return response file state
+     */
+    public boolean isUseResponseFile() {
+        return useResponseFile;
+    }
+
+    /**
+     * set this to true to always use the response file
+     * @param useResponseFile
+     */
+    public void setUseResponseFile(boolean useResponseFile) {
+        this.useResponseFile = useResponseFile;
+    }
+
+    /**
+     * getter for threshold
+     * @return 0 for disabled, or a threshold for enabling response files
+     */
+    public int getAutomaticResponseFileThreshold() {
+        return automaticResponseFileThreshold;
+    }
+
+    /**
+     * set threshold for automatically using response files -use 0 for off
+     * @param automaticResponseFileThreshold
+     */
+    public void setAutomaticResponseFileThreshold(int automaticResponseFileThreshold) {
+        this.automaticResponseFileThreshold = automaticResponseFileThreshold;
+    }
+
+    /**
+     *  set up the command sequence..
+     */
+    protected void prepareExecutor() {
+        // default directory to the project's base directory
+        if (owner == null) {
+            throw new RuntimeException("no owner");
+        }
+        if (owner.getProject() == null) {
+            throw new RuntimeException("Owner has no project");
+        }
+        File dir = owner.getProject().getBaseDir();
+        if (directory != null) {
+            dir = directory;
+        }
+
+        ExecuteStreamHandler handler = new LogStreamHandler(owner,
+                Project.MSG_INFO, Project.MSG_WARN);
+        executable = new Execute(handler, null);
+        executable.setAntRun(owner.getProject());
+        executable.setWorkingDirectory(dir);
+    }
+
+
+    /**
+     *  Run the command using the given Execute instance.
+     *
+     *@exception  BuildException  if something goes wrong and the
+     *      failOnError flag is true
+     */
+    public void runCommand()
+             throws BuildException {
+        int err = -1;
+        // assume the worst
+        try {
+            if (traceCommandLine) {
+                owner.log(commandLine.describeCommand());
+            } else {
+                //in verbose mode we always log stuff
+                logVerbose(commandLine.describeCommand());
+            }
+            setExecutableCommandLine();
+            err = executable.execute();
+            if (Execute.isFailure(err)) {
+                if (failOnError) {
+                    throw new BuildException(title + " returned: " + err, owner.getLocation());
+                } else {
+                    owner.log(title + "  Result: " + err, Project.MSG_ERR);
+                }
+            }
+        } catch (IOException e) {
+            throw new BuildException(title + " failed: " + e, e, owner.getLocation());
+        } finally {
+            if (temporaryCommandFile != null) {
+                temporaryCommandFile.delete();
+            }
+        }
+    }
+
+    /**
+     * set the executable command line
+     */
+    private void setExecutableCommandLine() {
+
+        String[] commands = commandLine.getCommandline();
+        //always trigger file mode if commands are big enough
+        if (automaticResponseFileThreshold>0 &&
+                commands.length > automaticResponseFileThreshold) {
+            useResponseFile = true;
+        }
+        if (!useResponseFile || commands.length <= 1) {
+            //the simple action is to send the command line in as is
+            executable.setCommandline(commands);
+        } else {
+            //but for big operations, we save all the params to a temp file
+            //and set @tmpfile as the command -then we remember to delete the tempfile
+            //afterwards
+            FileOutputStream fos = null;
+            FileUtils fileUtils = FileUtils.newFileUtils();
+
+            temporaryCommandFile = fileUtils.createTempFile("cmd", ".txt", null);
+            owner.log("Using response file"+temporaryCommandFile,Project.MSG_VERBOSE);
+
+            try {
+                fos = new FileOutputStream(temporaryCommandFile);
+                PrintWriter out = new PrintWriter(new BufferedOutputStream(fos));
+                //start at 1 because element 0 is the executable name
+                for (int i = 1; i < commands.length; ++i) {
+                    out.println(commands[i]);
+                }
+                out.flush();
+                out.close();
+            } catch (IOException ex) {
+                throw new BuildException("saving command stream to " + temporaryCommandFile, ex);
+            }
+
+            String newCommandLine[] = new String[2];
+            newCommandLine[0] = commands[0];
+            newCommandLine[1] = "@" + temporaryCommandFile.getAbsolutePath();
+            executable.setCommandline(newCommandLine);
+        }
+    }
+
+
+    /**
+     * scan through one fileset for files to include
+     * @param scanner
+     * @param filesToBuild
+     * @param outputTimestamp timestamp to compare against
+     * @return #of files out of date
+     * @todo: should FAT granularity be included here?
+     */
+    public int scanOneFileset(DirectoryScanner scanner, Hashtable filesToBuild,
+                                        long outputTimestamp) {
+        int filesOutOfDate = 0;
+        String[] dependencies = scanner.getIncludedFiles();
+        File base = scanner.getBasedir();
+        //add to the list
+        for (int i = 0; i < dependencies.length; i++) {
+            File targetFile = new File(base, dependencies[i]);
+            if (filesToBuild.get(targetFile) == null) {
+                filesToBuild.put(targetFile, targetFile);
+                if (targetFile.lastModified() > outputTimestamp) {
+                    filesOutOfDate++;
+                    owner.log(targetFile.toString() + " is out of date",
+                            Project.MSG_VERBOSE);
+                } else {
+                    owner.log(targetFile.toString(),
+                            Project.MSG_VERBOSE);
+                }
+            }
+        }
+        return filesOutOfDate;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/VisualBasicCompile.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/VisualBasicCompile.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/VisualBasicCompile.java	(revision 16139)
@@ -0,0 +1,370 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.dotnet;
+
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * This task compiles Visual Basic.NET source into executables or modules.
+ * The task requires vbc.exe on the execute path, unless it or an equivalent
+ * program is specified in the <tt>executable</tt> parameter
+ *
+ * <p>
+ * All parameters are optional: &lt;vbc/&gt; should suffice to produce a debug
+ * build of all *.vb files.
+ *
+ * <p>
+
+ * The task is a directory based task, so attributes like
+ * <tt>includes=&quot;**\/*.vb&quot;</tt> and
+ * <tt>excludes=&quot;broken.vb&quot;</tt> can be used to control
+ * the files pulled in. By default,
+ * all *.vb files from the project folder down are included in the command.
+ * When this happens the destFile -if not specified-
+ * is taken as the first file in the list, which may be somewhat hard to control.
+   Specifying the output file with <tt>destfile</tt> is prudent.
+ </p>
+ <p>
+ * Also, dependency checking only works if destfile is set.
+ *
+ * <p>For historical reasons the pattern
+ * <code>**</code><code>/*.vb</code> is preset as includes list and
+ * you can not override it with an explicit includes attribute.  Use
+ * nested <code>&lt;src&gt;</code> elements instead of the basedir
+ * attribute if you need more control.</p>
+ *
+ * As with &lt;csc&gt; nested <tt>src</tt> filesets of source,
+ * reference filesets, definitions and resources can be provided.
+ *
+ * <p>
+ * Example
+ * </p>
+ * <pre>&lt;vbc
+ *   optimize=&quot;true&quot;
+ *   debug=&quot;false&quot;
+ *   warnLevel=&quot;4&quot;
+ *   targetType=&quot;exe&quot;
+ *   definitions=&quot;RELEASE&quot;
+ *   excludes=&quot;src/unicode_class.vb&quot;
+ *   mainClass = &quot;MainApp&quot;
+ *   destFile=&quot;NetApp.exe&quot;
+ *   optionExplicit=&quot;true&quot;
+ *   optionCompare=&quot;text&quot;
+ *   references="System.Xml,System.Web.Xml"
+ *   &gt;
+ *          &lt;reference file="${testCSC.dll}" /&gt;
+ *          &lt;define name="RELEASE" /&gt;
+ *          &lt;define name="DEBUG" if="debug.property"/&gt;
+ *          &lt;define name="def3" unless="def2.property"/&gt;
+ *   &lt;/vbc&gt;
+ </pre>
+ * @ant.task    name="vbc" category="dotnet"
+ */
+
+public class VisualBasicCompile extends DotnetCompile {
+
+    /**
+     * Compiler option to remove integer checks. Default: false.
+     */
+    private boolean removeIntChecks = false;
+
+    /**
+     * Require explicit declaration of variables? Default: false.
+     */
+    private boolean optionExplicit = false;
+
+    /**
+     * Enforce strict language semantics? Default: false.
+     */
+    private boolean optionStrict = false;
+
+    /**
+     * Whether to compare strings as "text" or "binary". Default: "binary".
+     */
+    private String optionCompare;
+
+    /**
+     * Root namespace for all type declarations.
+     */
+    private String rootNamespace;
+
+    /**
+     * Declare global imports fornamespaces in referenced metadata files.
+     */
+    private String imports;
+
+    /**
+     * Constructor for VisualBasicCompile.
+     */
+    public VisualBasicCompile() {
+        clear();
+    }
+
+    /**
+     *  reset all contents.
+     */
+    public void clear() {
+        super.clear();
+        imports = null;
+        rootNamespace = null;
+        optionCompare = null;
+        optionExplicit = false;
+        optionStrict = false;
+        removeIntChecks = false;
+        setExecutable("vbc");
+    }
+
+    /**
+     *  get the argument or null for no argument needed
+     *  This is overridden from DotnetCompile.java because VBC uses
+     *  "/win32resource:" rather than "/win32res:"
+     *
+     *@return    The Win32Res Parameter to CSC
+     */
+    protected String getWin32ResParameter() {
+        if (getWin32Res() != null) {
+            return "/win32resource:" + getWin32Res().toString();
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Whether to remove integer checks. Default false.
+     * @param  flag  on/off flag
+     */
+    public void setRemoveIntChecks(boolean flag) {
+        removeIntChecks = flag;
+    }
+
+    /**
+     * Get the flag for removing integer checks.
+     * @return    true if flag is turned on
+     */
+    public boolean getRemoveIntChecks() {
+        return removeIntChecks;
+    }
+
+    /**
+     * Form the option string for removeIntChecks.
+     * @return The parameter string.
+     */
+    public String getRemoveIntChecksParameter() {
+        return "/removeintchecks" + (removeIntChecks ? "+" : "-");
+    }
+
+    /**
+     * Whether to require explicit declaration of variables.
+     * @param  flag  on/off flag
+     */
+    public void setOptionExplicit(boolean flag) {
+        optionExplicit = flag;
+    }
+
+    /**
+     * Get the flag for whether to require explicit declaration of variables.
+     *@return    true if flag is turned on
+     */
+    public boolean getOptionExplicit() {
+        return optionExplicit;
+    }
+
+    /**
+     * Form the option string for optionExplicit..
+     * @return The parameter string.
+     */
+    public String getOptionExplicitParameter() {
+        return "/optionexplicit" + (optionExplicit ? "+" : "-");
+    }
+
+    /**
+     * Enforce strict language semantics.
+     * @param  flag  on/off flag
+     */
+    public void setOptionStrict(boolean flag) {
+        optionStrict = flag;
+    }
+
+    /**
+     * Get the flag for whether to enforce strict language semantics.
+     * @return    true if flag is turned on
+     */
+    public boolean getOptionStrict() {
+        return optionStrict;
+    }
+
+    /**
+     * For the option string for optionStrict.
+     * @return The parameter string.
+     */
+    public String getOptionStrictParameter() {
+        return "/optionstrict" + (optionStrict ? "+" : "-");
+    }
+
+
+    /**
+     * Specifies the root namespace for all type declarations.
+     * @param rootNamespace a root namespace.
+     */
+    public void setRootNamespace(String rootNamespace) {
+        this.rootNamespace = rootNamespace;
+    }
+
+
+    /**
+     * Get the root namespace.
+     * @return  the root namespace.
+     */
+    public String getRootNamespace() {
+        return this.rootNamespace;
+    }
+
+
+    /**
+     * Form the option string for rootNamespace.
+     * @return  the root namespace option string.
+     */
+    protected String getRootNamespaceParameter() {
+        if (rootNamespace != null && rootNamespace.length() != 0) {
+            return "/rootnamespace:" + rootNamespace;
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Declare global imports for namespaces in referenced metadata files.
+     * @param imports the imports string
+     */
+    public void setImports(String imports) {
+        this.imports = imports;
+    }
+
+
+    /**
+     * Get global imports for namespaces in referenced metadata files.
+     * @return  the imports string.
+     */
+    public String getImports() {
+        return this.imports;
+    }
+
+
+    /**
+     * Format the option for imports.
+     * @return  the formatted import option.
+     */
+    protected String getImportsParameter() {
+        if (imports != null && imports.length() != 0) {
+            return "/imports:" + imports;
+        } else {
+            return null;
+        }
+    }
+
+
+    /**
+     * Specify binary- or text-style string comparisons. Defaults
+     * to "binary"
+     * @param optionCompare the option compare style. "text" | "binary".
+     */
+    public void setOptionCompare(String optionCompare) {
+        if ("text".equalsIgnoreCase(optionCompare)) {
+            this.optionCompare = "text";
+        } else {
+            this.optionCompare = "binary";
+        }
+    }
+
+
+    /**
+     * "binary" or "text" for the string-comparison style.
+     * @return  the option compare style.
+     */
+    public String getOptionCompare() {
+        return this.optionCompare;
+    }
+
+    /**
+     * Format the option for string comparison style.
+     * @return  The formatted option.
+     */
+    protected String getOptionCompareParameter() {
+        if (optionCompare != null && "text".equalsIgnoreCase(optionCompare)) {
+            return "/optioncompare:text";
+        } else {
+            return "/optioncompare:binary";
+        }
+    }
+
+    /**
+     * implement VBC commands
+     * @param command
+     */
+    protected void addCompilerSpecificOptions(NetCommand command) {
+        command.addArgument(getRemoveIntChecksParameter());
+        command.addArgument(getImportsParameter());
+        command.addArgument(getOptionExplicitParameter());
+        command.addArgument(getOptionStrictParameter());
+        command.addArgument(getRootNamespaceParameter());
+        command.addArgument(getOptionCompareParameter());
+    }
+
+    /**
+     * Get the delimiter that the compiler uses between references.
+     * For example, c# will return ";"; VB.NET will return ","
+     * @return The string delimiter for the reference string.
+     */
+    public String getReferenceDelimiter() {
+        return ",";
+    }
+
+
+
+    /**
+     * Get the extension of filenames to compile.
+     * @return The string extension of files to compile.
+     */
+    public String getFileExtension() {
+        return "vb";
+    }
+
+    /**
+     * from a resource, get the resource param
+     * @param resource
+     * @return a string containing the resource param, or a null string
+     * to conditionally exclude a resource.
+     */
+    protected String createResourceParameter(DotnetResource resource) {
+        return resource.getVbStyleParameter();
+    }
+
+    /**
+     * validation code
+     * @throws  BuildException  if validation failed
+     */
+    protected void validate()
+            throws BuildException {
+        super.validate();
+        if (getDestFile() == null) {
+            throw new BuildException("DestFile was not specified");
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/WsdlToDotnet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/WsdlToDotnet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/dotnet/WsdlToDotnet.java	(revision 16139)
@@ -0,0 +1,234 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.dotnet;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Converts a WSDL file or URL resource into a .NET language.
+ *
+ * Why add a wrapper to the MS WSDL tool?
+ * So that you can verify that your web services, be they written with Axis or
+ *anyone else's SOAP toolkit, work with .NET clients.
+ *
+ *This task is dependency aware when using a file as a source and destination;
+ *so if you &lt;get&gt; the file (with <code>usetimestamp="true"</code>) then
+ *you only rebuild stuff when the WSDL file is changed. Of course,
+ *if the server generates a new timestamp every time you ask for the WSDL,
+ *this is not enough...use the &lt;filesmatch&gt; &lt;condition&gt; to
+ *to byte for byte comparison against a cached WSDL file then make
+ *the target conditional on that test failing.
+
+ * See "Creating an XML Web Service Proxy", "wsdl.exe" docs in
+ * the framework SDK documentation
+ * @version     0.5
+ * @ant.task    category="dotnet"
+ * @since       Ant 1.5
+ */
+
+public class WsdlToDotnet extends Task  {
+
+    /**
+     * name of output file (required)
+     */
+    private File destFile = null;
+
+    /**
+     * url to retrieve
+     */
+    private String url = null;
+
+    /**
+     * name of source file
+     */
+    private File srcFile = null;
+
+    /**
+     * language; defaults to C#
+     */
+    private String language = "CS";
+
+    /**
+     * flag set to true to generate server side skeleton
+     */
+    private boolean server = false;
+
+    /**
+     * namespace
+     */
+    private String namespace = null;
+
+    /**
+     *  flag to control action on execution trouble
+     */
+    private boolean failOnError = true;
+
+    /**
+     *  any extra command options?
+     */
+    protected String extraOptions = null;
+
+    /**
+     * Name of the file to generate. Required
+     * @param destFile filename
+     */
+    public void setDestFile(File destFile) {
+        this.destFile = destFile;
+    }
+
+    /**
+     * Sets the URL to fetch. Fetching is by wsdl.exe; Ant proxy settings
+     * are ignored; either url or srcFile is required.
+     * @param url url to save
+     */
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    /**
+     * The local WSDL file to parse; either url or srcFile is required.
+     * @param srcFile name of WSDL file
+     */
+    public void setSrcFile(File srcFile) {
+        this.srcFile = srcFile;
+    }
+
+    /**
+     * set the language; one of "CS", "JS", or "VB"
+     * optional, default is CS for C# source
+     * @param language language to generate
+     */
+    public void setLanguage(String language) {
+        this.language = language;
+    }
+
+    /**
+     * flag to enable server side code generation;
+     * optional, default=false
+     * @param server server-side flag
+     */
+
+    public void setServer(boolean server) {
+        this.server = server;
+    }
+
+    /**
+     * namespace to place  the source in.
+     * optional; default ""
+     * @param namespace new namespace
+     */
+    public void setNamespace(String namespace) {
+        this.namespace = namespace;
+    }
+
+    /**
+     * Whether or not a failure should halt the build.
+     * Optional - default is <code>true</code>.
+     * @param failOnError new failure option
+     */
+    public void setFailOnError(boolean failOnError) {
+        this.failOnError = failOnError;
+    }
+
+    /**
+     *  Any extra WSDL.EXE options which aren't explicitly
+     *  supported by the ant wrapper task; optional
+     *
+     *@param  extraOptions  The new ExtraOptions value
+     */
+    public void setExtraOptions(String extraOptions) {
+        this.extraOptions = extraOptions;
+    }
+
+    /**
+     * validation code
+     * @throws  BuildException  if validation failed
+     */
+    protected void validate()
+            throws BuildException {
+        if (destFile == null) {
+            throw new BuildException("destination file must be specified");
+        }
+        if (destFile.isDirectory()) {
+            throw new BuildException(
+                "destination file is a directory");
+        }
+        if (url != null && srcFile != null) {
+            throw new BuildException(
+                    "you can not specify both a source file and a URL");
+        }
+        if (url == null && srcFile == null) {
+            throw new BuildException(
+                    "you must specify either a source file or a URL");
+        }
+        if (srcFile != null) {
+            if (!srcFile.exists()) {
+                throw new BuildException(
+                    "source file does not exist");
+            }
+            if (srcFile.isDirectory()) {
+                throw new BuildException(
+                    "source file is a directory");
+            }
+        }
+
+    }
+
+    /**
+     *  do the work by building the command line and then calling it
+     *
+     *@throws  BuildException  if validation or execution failed
+     */
+    public void execute()
+             throws BuildException {
+        validate();
+        NetCommand command = new NetCommand(this, "WSDL", "wsdl");
+        command.setFailOnError(failOnError);
+        //fill in args
+        command.addArgument("/nologo");
+        command.addArgument("/out:" + destFile);
+        command.addArgument("/language:", language);
+        if (server) {
+            command.addArgument("/server");
+        }
+        command.addArgument("/namespace:", namespace);
+        command.addArgument(extraOptions);
+
+        //set source and rebuild options
+        boolean rebuild = true;
+        if (srcFile != null) {
+            command.addArgument(srcFile.toString());
+            //rebuild unless the dest file is newer than the source file
+            if (srcFile.exists() && destFile.exists()
+                && srcFile.lastModified() <= destFile.lastModified()) {
+                rebuild = false;
+            }
+        } else {
+            //no source file? must be a url, which has no dependency
+            //handling
+            rebuild = true;
+            command.addArgument(url);
+        }
+        if (rebuild) {
+            command.runCommand();
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandDeploymentTool.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandDeploymentTool.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandDeploymentTool.java	(revision 16139)
@@ -0,0 +1,531 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.ExecTask;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+
+/**
+ * BorlandDeploymentTool is dedicated to the Borland Application Server 4.5 and 4.5.1
+ * This task generates and compiles the stubs and skeletons for all ejb described into the
+ * Deployment Descriptor, builds the jar file including the support files and verify
+ * whether the produced jar is valid or not.
+ * The supported options are:
+ * <ul>
+ * <li>debug  (boolean)    : turn on the debug mode for generation of
+ *                           stubs and skeletons (default:false)</li>
+ * <li>verify (boolean)    : turn on the verification at the end of the jar
+ *                           production  (default:true) </li>
+ * <li>verifyargs (String) : add optional argument to verify command
+ *                           (see vbj com.inprise.ejb.util.Verify)</li>
+ * <li>basdtd (String)     : location of the BAS DTD </li>
+ * <li>generateclient (boolean) : turn on the client jar file generation </li>
+ * <li>version (int)       : tell what is the Borland appserver version 4 or 5 </li>
+ * </ul>
+ *
+ *<PRE>
+ *
+ *      &lt;ejbjar srcdir=&quot;${build.classes}&quot;
+ *               basejarname=&quot;vsmp&quot;
+ *               descriptordir=&quot;${rsc.dir}/hrmanager&quot;&gt;
+ *        &lt;borland destdir=&quot;tstlib&quot;&gt;
+ *          &lt;classpath refid=&quot;classpath&quot; /&gt;
+ *        &lt;/borland&gt;
+ *        &lt;include name=&quot;**\ejb-jar.xml&quot;/&gt;
+ *        &lt;support dir=&quot;${build.classes}&quot;&gt;
+ *          &lt;include name=&quot;demo\smp\*.class&quot;/&gt;
+ *          &lt;include name=&quot;demo\helper\*.class&quot;/&gt;
+ *         &lt;/support&gt;
+ *     &lt;/ejbjar&gt;
+ *</PRE>
+ *
+ */
+public class BorlandDeploymentTool extends GenericDeploymentTool
+                                   implements ExecuteStreamHandler {
+    public static final String PUBLICID_BORLAND_EJB
+    = "-//Inprise Corporation//DTD Enterprise JavaBeans 1.1//EN";
+
+    protected static final String DEFAULT_BAS45_EJB11_DTD_LOCATION
+    = "/com/inprise/j2ee/xml/dtds/ejb-jar.dtd";
+
+    protected static final String DEFAULT_BAS_DTD_LOCATION
+    = "/com/inprise/j2ee/xml/dtds/ejb-inprise.dtd";
+
+    protected static final String BAS_DD = "ejb-inprise.xml";
+    protected static final String BES_DD = "ejb-borland.xml";
+
+
+    /** Java2iiop executable **/
+    protected static final String JAVA2IIOP = "java2iiop";
+
+    /** Verify class */
+    protected static final String VERIFY = "com.inprise.ejb.util.Verify";
+
+    /** Instance variable that stores the suffix for the borland jarfile. */
+    private String jarSuffix = "-ejb.jar";
+
+    /** Instance variable that stores the location of the borland DTD file. */
+    private String borlandDTD;
+
+    /** Instance variable that determines whether the debug mode is on */
+    private boolean java2iiopdebug = false;
+
+    /** store additional param for java2iiop command used to build EJB Stubs */
+    private String java2iioparams = null;
+
+    /** Instance variable that determines whether the client jar file is generated */
+    private boolean generateclient = false;
+
+    /** Borland Enterprise Server = version 5 */
+    static final int    BES       = 5;
+    /** Borland Application Server or Inprise Application Server  = version 4 */
+    static final int    BAS       = 4;
+
+    /** borland appserver version 4 or 5 */
+    private int version = BAS;
+
+
+    /**
+     * Instance variable that determines whether it is necessary to verify the
+     * produced jar
+     */
+    private boolean verify     = true;
+    private String  verifyArgs = "";
+
+    private Hashtable _genfiles = new Hashtable();
+
+    /**
+     * set the debug mode for java2iiop (default false)
+     **/
+    public void setDebug(boolean debug) {
+        this.java2iiopdebug = debug;
+    }
+
+    /**
+     * set the verify  mode for the produced jar (default true)
+     **/
+    public void setVerify(boolean verify) {
+        this.verify = verify;
+    }
+
+
+    /**
+     * Setter used to store the suffix for the generated borland jar file.
+     * @param inString the string to use as the suffix.
+     */
+    public void setSuffix(String inString) {
+        this.jarSuffix = inString;
+    }
+
+
+    /**
+     * sets some additional args to send to verify command
+     * @param args additional command line parameters
+     */
+    public void setVerifyArgs(String args) {
+        this.verifyArgs = args;
+    }
+
+    /**
+     * Setter used to store the location of the borland DTD. This can be a file on the system
+     * or a resource on the classpath.
+     * @param inString the string to use as the DTD location.
+     */
+    public void setBASdtd(String inString) {
+        this.borlandDTD = inString;
+    }
+
+
+    /**
+     * setter used to store whether the task will include the generate client task.
+     * (see : BorlandGenerateClient task)
+     */
+    public void setGenerateclient(boolean b) {
+        this.generateclient = b;
+    }
+
+    /**
+     * setter used to store the borland appserver version [4 or 5]
+     * @param version app server version 4 or 5
+     */
+    public void setVersion(int version) {
+        this.version = version;
+    }
+
+    /**
+     * If filled, the params are added to the java2iiop command.
+     * (ex: -no_warn_missing_define)
+     * @param params additional params for java2iiop
+     */
+    public void setJava2iiopParams(String params) {
+        this.java2iioparams = params;
+    }
+
+
+    protected DescriptorHandler getBorlandDescriptorHandler(final File srcDir) {
+        DescriptorHandler handler =
+            new DescriptorHandler(getTask(), srcDir) {
+                    protected void processElement() {
+                        if (currentElement.equals("type-storage")) {
+                            // Get the filename of vendor specific descriptor
+                            String fileNameWithMETA = currentText;
+                            //trim the META_INF\ off of the file name
+                            String fileName
+                                = fileNameWithMETA.substring(META_DIR.length(),
+                                    fileNameWithMETA.length());
+                            File descriptorFile = new File(srcDir, fileName);
+
+                            ejbFiles.put(fileNameWithMETA, descriptorFile);
+                        }
+                    }
+                };
+        handler.registerDTD(PUBLICID_BORLAND_EJB,
+                            borlandDTD == null ? DEFAULT_BAS_DTD_LOCATION : borlandDTD);
+
+        for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) {
+            EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next();
+            handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation());
+        }
+        return handler;
+    }
+
+    /**
+     * Add any vendor specific files which should be included in the
+     * EJB Jar.
+     */
+    protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
+
+        //choose the right vendor DD
+        if (!(version == BES || version == BAS)) {
+            throw new BuildException("version " + version + " is not supported");
+        }
+
+        String dd = (version == BES ? BES_DD : BAS_DD);
+
+        log("vendor file : " + ddPrefix + dd, Project.MSG_DEBUG);
+
+        File borlandDD = new File(getConfig().descriptorDir, ddPrefix + dd);
+        if (borlandDD.exists()) {
+            log("Borland specific file found " + borlandDD,  Project.MSG_VERBOSE);
+            ejbFiles.put(META_DIR + dd ,  borlandDD);
+        } else {
+            log("Unable to locate borland deployment descriptor. "
+                + "It was expected to be in "
+                + borlandDD.getPath(), Project.MSG_WARN);
+            return;
+        }
+    }
+
+    /**
+     * Get the vendor specific name of the Jar that will be output. The modification date
+     * of this jar will be checked against the dependent bean classes.
+     */
+    File getVendorOutputJarFile(String baseName) {
+        return new File(getDestDir(), baseName +  jarSuffix);
+    }
+
+    /**
+     * Verify the produced jar file by invoking the Borland verify tool
+     * @param sourceJar java.io.File representing the produced jar file
+     */
+    private void verifyBorlandJar(File sourceJar) {
+        if (version == BAS) {
+            verifyBorlandJarV4(sourceJar);
+            return;
+        }
+        if (version == BES) {
+            verifyBorlandJarV5(sourceJar);
+            return;
+        }
+        log("verify jar skipped because the version is invalid ["
+            + version + "]", Project.MSG_WARN);
+    }
+
+    /**
+     * Verify the produced jar file by invoking the Borland iastool tool
+     * @param sourceJar java.io.File representing the produced jar file
+     */
+    private void verifyBorlandJarV5(File sourceJar) {
+        log("verify BES " + sourceJar, Project.MSG_INFO);
+        try {
+            org.apache.tools.ant.taskdefs.ExecTask execTask = null;
+            execTask = (ExecTask) getTask().getProject().createTask("exec");
+            execTask.setDir(new File("."));
+            execTask.setExecutable("iastool");
+            //classpath
+            if (getCombinedClasspath() != null)  {
+                execTask.createArg().setValue("-VBJclasspath");
+                execTask.createArg().setValue(getCombinedClasspath().toString());
+            }
+
+            if (java2iiopdebug) {
+                execTask.createArg().setValue("-debug");
+            }
+            execTask.createArg().setValue("-verify");
+            execTask.createArg().setValue("-src");
+            // ejb jar file to verify
+            execTask.createArg().setValue(sourceJar.getPath());
+            log("Calling iastool", Project.MSG_VERBOSE);
+            execTask.execute();
+        } catch (Exception e) {
+            // Have to catch this because of the semantics of calling main()
+            String msg = "Exception while calling generateclient Details: "
+                + e.toString();
+            throw new BuildException(msg, e);
+        }
+    }
+
+    /**
+     * Verify the produced jar file by invoking the Borland verify tool
+     * @param sourceJar java.io.File representing the produced jar file
+     */
+    private void verifyBorlandJarV4(File sourceJar) {
+        org.apache.tools.ant.taskdefs.Java javaTask = null;
+        log("verify BAS " + sourceJar, Project.MSG_INFO);
+        try  {
+            String args = verifyArgs;
+            args += " " + sourceJar.getPath();
+
+            javaTask = (Java) getTask().getProject().createTask("java");
+            javaTask.setTaskName("verify");
+            javaTask.setClassname(VERIFY);
+            Commandline.Argument arguments = javaTask.createArg();
+            arguments.setLine(args);
+            Path classpath = getCombinedClasspath();
+            if (classpath != null)  {
+                javaTask.setClasspath(classpath);
+                javaTask.setFork(true);
+            }
+
+            log("Calling " + VERIFY + " for " + sourceJar.toString(),
+                Project.MSG_VERBOSE);
+            javaTask.execute();
+        } catch (Exception e) {
+            //TO DO : delete the file if it is not a valid file.
+            String msg = "Exception while calling " + VERIFY + " Details: "
+                + e.toString();
+            throw new BuildException(msg, e);
+        }
+    }
+
+
+    /**
+     * Generate the client jar corresponding to the jar file passed as parameter
+     * the method uses the BorlandGenerateClient task.
+     * @param sourceJar java.io.File representing the produced jar file
+     */
+    private void generateClient(File sourceJar) {
+        getTask().getProject().addTaskDefinition("internal_bas_generateclient",
+            org.apache.tools.ant.taskdefs.optional.ejb.BorlandGenerateClient.class);
+
+        org.apache.tools.ant.taskdefs.optional.ejb.BorlandGenerateClient gentask = null;
+        log("generate client for " + sourceJar, Project.MSG_INFO);
+        try {
+            Project project = getTask().getProject();
+            gentask
+                = (BorlandGenerateClient) project.createTask("internal_bas_generateclient");
+            gentask.setEjbjar(sourceJar);
+            gentask.setDebug(java2iiopdebug);
+            Path classpath = getCombinedClasspath();
+            if (classpath != null) {
+                gentask.setClasspath(classpath);
+            }
+            gentask.setVersion(version);
+            gentask.setTaskName("generate client");
+            gentask.execute();
+        } catch (Exception e) {
+            //TO DO : delete the file if it is not a valid file.
+            String msg = "Exception while calling " + VERIFY + " Details: "
+                + e.toString();
+            throw new BuildException(msg, e);
+        }
+    }
+
+    /**
+     * Generate stubs & skeleton for each home found into the DD
+     * Add all the generate class file into the ejb files
+     * @param ithomes : iterator on home class
+     */
+    private void buildBorlandStubs(Iterator ithomes) {
+        Execute execTask = null;
+
+        execTask = new Execute(this);
+        Project project = getTask().getProject();
+        execTask.setAntRun(project);
+        execTask.setWorkingDirectory(project.getBaseDir());
+
+        Commandline commandline = new Commandline();
+        commandline.setExecutable(JAVA2IIOP);
+        //debug ?
+        if (java2iiopdebug) {
+            commandline.createArgument().setValue("-VBJdebug");
+        }
+        //set the classpath
+        commandline.createArgument().setValue("-VBJclasspath");
+        commandline.createArgument().setPath(getCombinedClasspath());
+        //list file
+        commandline.createArgument().setValue("-list_files");
+        //no TIE classes
+        commandline.createArgument().setValue("-no_tie");
+
+        if (java2iioparams != null) {
+            log("additional  " + java2iioparams + " to java2iiop ", 0);
+            commandline.createArgument().setValue(java2iioparams);
+        }
+
+
+        //root dir
+        commandline.createArgument().setValue("-root_dir");
+        commandline.createArgument().setValue(getConfig().srcDir.getAbsolutePath());
+        //compiling order
+        commandline.createArgument().setValue("-compile");
+        //add the home class
+        while (ithomes.hasNext()) {
+            commandline.createArgument().setValue(ithomes.next().toString());
+        }
+
+        try {
+            log("Calling java2iiop", Project.MSG_VERBOSE);
+            log(commandline.describeCommand(), Project.MSG_DEBUG);
+            execTask.setCommandline(commandline.getCommandline());
+            int result = execTask.execute();
+            if (Execute.isFailure(result)) {
+                String msg = "Failed executing java2iiop (ret code is "
+                    + result + ")";
+                throw new BuildException(msg, getTask().getLocation());
+            }
+        } catch (java.io.IOException e) {
+            log("java2iiop exception :" + e.getMessage(), Project.MSG_ERR);
+            throw new BuildException(e, getTask().getLocation());
+        }
+    }
+
+    /**
+     * Method used to encapsulate the writing of the JAR file. Iterates over the
+     * filenames/java.io.Files in the Hashtable stored on the instance variable
+     * ejbFiles.
+     */
+    protected void writeJar(String baseName, File jarFile, Hashtable files, String publicId)
+        throws BuildException {
+        //build the home classes list.
+        Vector homes = new Vector();
+        Iterator it = files.keySet().iterator();
+        while (it.hasNext()) {
+            String clazz = (String) it.next();
+            if (clazz.endsWith("Home.class")) {
+                //remove .class extension
+                String home = toClass(clazz);
+                homes.add(home);
+                log(" Home " + home, Project.MSG_VERBOSE);
+            }
+        }
+
+        buildBorlandStubs(homes.iterator());
+
+        //add the gen files to the collection
+        files.putAll(_genfiles);
+
+        super.writeJar(baseName, jarFile, files, publicId);
+
+        if (verify) {
+            verifyBorlandJar(jarFile);
+        }
+
+        if (generateclient) {
+            generateClient(jarFile);
+        }
+    }
+
+    /**
+     * convert a class file name : A/B/C/toto.class
+     * into    a class name: A.B.C.toto
+     */
+    private String toClass(String filename) {
+        //remove the .class
+        String classname = filename.substring(0, filename.lastIndexOf(".class"));
+        classname = classname.replace('\\', '.');
+        return classname;
+    }
+
+    /**
+     * convert a file name : A/B/C/toto.java
+     * into    a class name: A/B/C/toto.class
+     */
+    private  String toClassFile(String filename) {
+        //remove the .class
+        String classfile = filename.substring(0, filename.lastIndexOf(".java"));
+        classfile = classfile + ".class";
+        return classfile;
+    }
+
+    // implementation of org.apache.tools.ant.taskdefs.ExecuteStreamHandler interface
+
+    public void start() throws IOException  { }
+    public void stop()  {  }
+    public void setProcessInputStream(OutputStream param1) throws IOException   { }
+
+    /**
+     *
+     * @param is
+     * @exception java.io.IOException
+     */
+    public void setProcessOutputStream(InputStream is) throws IOException {
+        try {
+            BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+            String javafile;
+            while ((javafile = reader.readLine()) != null) {
+                if (javafile.endsWith(".java")) {
+                    String classfile = toClassFile(javafile);
+                    String key = classfile.substring(getConfig().srcDir.getAbsolutePath().length() + 1);
+                    _genfiles.put(key, new File(classfile));
+                }
+            }
+            reader.close();
+        } catch (Exception e) {
+            String msg = "Exception while parsing  java2iiop output. Details: " + e.toString();
+            throw new BuildException(msg, e);
+        }
+    }
+
+    public void setProcessErrorStream(InputStream is) throws IOException {
+        BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+        String s = reader.readLine();
+        if (s != null) {
+            log("[java2iiop] " + s, Project.MSG_ERR);
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandGenerateClient.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandGenerateClient.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/BorlandGenerateClient.java	(revision 16139)
@@ -0,0 +1,288 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.ExecTask;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Generates a Borland Application Server 4.5 client JAR using as
+ * input the EJB JAR file.
+ *
+ * Two mode are available: java mode (default) and fork mode. With the fork mode,
+ * it is impossible to add classpath to the command line.
+ *
+ *
+ * @ant.task name="blgenclient" category="ejb"
+ */
+public class BorlandGenerateClient extends Task {
+    static final String JAVA_MODE = "java";
+    static final String FORK_MODE = "fork";
+
+    /** debug the generateclient task */
+    boolean debug = false;
+
+    /** hold the ejbjar file name */
+    File ejbjarfile = null;
+
+    /** hold the client jar file name */
+    File clientjarfile = null;
+
+    /** hold the classpath */
+    Path classpath;
+
+    /** hold the mode (java|fork) */
+    String mode = FORK_MODE;
+
+    /** hold the version */
+    int version = BorlandDeploymentTool.BAS;
+
+    public void setVersion(int version) {
+        this.version = version;
+    }
+
+    /**
+     * Command launching mode: java or fork.
+     */
+    public void setMode(String s) {
+        mode = s;
+    }
+
+    /**
+     * If true, turn on the debug mode for each of the Borland tools launched.
+     */
+    public void setDebug(boolean debug) {
+        this.debug = debug;
+    }
+
+    /**
+     * EJB JAR file.
+     */
+    public void setEjbjar(File ejbfile) {
+        ejbjarfile = ejbfile;
+    }
+
+    /**
+     * Client JAR file name.
+     */
+    public void setClientjar(File clientjar) {
+        clientjarfile = clientjar;
+    }
+
+    /**
+     * Path to use for classpath.
+     */
+    public void setClasspath(Path classpath) {
+        if (this.classpath == null) {
+            this.classpath = classpath;
+        } else {
+            this.classpath.append(classpath);
+        }
+    }
+
+    /**
+     * Adds path to the classpath.
+     */
+    public Path createClasspath() {
+        if (this.classpath == null) {
+            this.classpath = new Path(getProject());
+        }
+        return this.classpath.createPath();
+    }
+
+    /**
+     * Reference to existing path, to use as a classpath.
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+
+
+    /**
+     * Do the work.
+     *
+     * The work is actually done by creating a separate JVM to run a java task.
+     *
+     * @exception BuildException if something goes wrong with the build
+     */
+    public void execute() throws BuildException {
+        if (ejbjarfile == null || ejbjarfile.isDirectory()) {
+            throw new BuildException("invalid ejb jar file.");
+        }
+
+        if (clientjarfile == null || clientjarfile.isDirectory()) {
+            log("invalid or missing client jar file.", Project.MSG_VERBOSE);
+            String ejbjarname = ejbjarfile.getAbsolutePath();
+            //clientname = ejbjarfile+client.jar
+            String clientname = ejbjarname.substring(0, ejbjarname.lastIndexOf("."));
+            clientname = clientname + "client.jar";
+            clientjarfile = new File(clientname);
+        }
+
+        if (mode == null) {
+            log("mode is null default mode  is java");
+            setMode(JAVA_MODE);
+        }
+
+        if (!(version == BorlandDeploymentTool.BES
+            || version == BorlandDeploymentTool.BAS)) {
+            throw new BuildException("version " + version
+                                      + " is not supported");
+        }
+
+        log("client jar file is " + clientjarfile);
+
+        if (mode.equalsIgnoreCase(FORK_MODE)) {
+            executeFork();
+        } else {
+            executeJava();
+        } // end of else
+    }
+
+    /** launch the generate client using java api */
+    protected void executeJava() throws BuildException {
+        try {
+            if (version == BorlandDeploymentTool.BES)  {
+                throw new BuildException("java mode is supported only for "
+                    + "previous version <=" + BorlandDeploymentTool.BAS);
+            }
+
+            log("mode : java");
+
+            org.apache.tools.ant.taskdefs.Java execTask = null;
+            execTask = (Java) getProject().createTask("java");
+
+            execTask.setDir(new File("."));
+            execTask.setClassname("com.inprise.server.commandline.EJBUtilities");
+            //classpath
+            //add at the end of the classpath
+            //the system classpath in order to find the tools.jar file
+            execTask.setClasspath(classpath.concatSystemClasspath());
+
+            execTask.setFork(true);
+            execTask.createArg().setValue("generateclient");
+            if (debug) {
+                execTask.createArg().setValue("-trace");
+            }
+
+            execTask.createArg().setValue("-short");
+            execTask.createArg().setValue("-jarfile");
+            // ejb jar file
+            execTask.createArg().setValue(ejbjarfile.getAbsolutePath());
+            //client jar file
+            execTask.createArg().setValue("-single");
+            execTask.createArg().setValue("-clientjarfile");
+            execTask.createArg().setValue(clientjarfile.getAbsolutePath());
+
+            log("Calling EJBUtilities", Project.MSG_VERBOSE);
+            execTask.execute();
+
+        } catch (Exception e) {
+            // Have to catch this because of the semantics of calling main()
+            String msg = "Exception while calling generateclient Details: " + e.toString();
+            throw new BuildException(msg, e);
+        }
+    }
+
+    /** launch the generate client using system api */
+    protected  void executeFork() throws BuildException {
+        if (version == BorlandDeploymentTool.BAS) {
+            executeForkV4();
+        }
+        if (version == BorlandDeploymentTool.BES) {
+            executeForkV5();
+        }
+    }
+
+    /** launch the generate client using system api */
+    protected  void executeForkV4() throws BuildException {
+        try {
+
+            log("mode : fork " + BorlandDeploymentTool.BAS, Project.MSG_DEBUG);
+
+            org.apache.tools.ant.taskdefs.ExecTask execTask = null;
+            execTask = (ExecTask) getProject().createTask("exec");
+
+            execTask.setDir(new File("."));
+            execTask.setExecutable("iastool");
+            execTask.createArg().setValue("generateclient");
+            if (debug) {
+                execTask.createArg().setValue("-trace");
+            }
+
+            execTask.createArg().setValue("-short");
+            execTask.createArg().setValue("-jarfile");
+            // ejb jar file
+            execTask.createArg().setValue(ejbjarfile.getAbsolutePath());
+            //client jar file
+            execTask.createArg().setValue("-single");
+            execTask.createArg().setValue("-clientjarfile");
+            execTask.createArg().setValue(clientjarfile.getAbsolutePath());
+
+            log("Calling iastool", Project.MSG_VERBOSE);
+            execTask.execute();
+        } catch (Exception e) {
+            // Have to catch this because of the semantics of calling main()
+            String msg = "Exception while calling generateclient Details: "
+                + e.toString();
+            throw new BuildException(msg, e);
+        }
+
+    }
+    /** launch the generate client using system api */
+    protected  void executeForkV5() throws BuildException {
+        try {
+            log("mode : fork " + BorlandDeploymentTool.BES, Project.MSG_DEBUG);
+            org.apache.tools.ant.taskdefs.ExecTask execTask = null;
+            execTask = (ExecTask) getProject().createTask("exec");
+
+            execTask.setDir(new File("."));
+
+            execTask.setExecutable("iastool");
+            if (debug) {
+                execTask.createArg().setValue("-debug");
+            }
+            execTask.createArg().setValue("-genclient");
+            execTask.createArg().setValue("-jars");
+            // ejb jar file
+            execTask.createArg().setValue(ejbjarfile.getAbsolutePath());
+            //client jar file
+            execTask.createArg().setValue("-target");
+            execTask.createArg().setValue(clientjarfile.getAbsolutePath());
+            //classpath
+            execTask.createArg().setValue("-cp");
+            execTask.createArg().setValue(classpath.toString());
+            log("Calling iastool", Project.MSG_VERBOSE);
+            execTask.execute();
+        } catch (Exception e) {
+            // Have to catch this because of the semantics of calling main()
+            String msg = "Exception while calling generateclient Details: "
+                + e.toString();
+            throw new BuildException(msg, e);
+        }
+
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/DDCreator.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/DDCreator.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/DDCreator.java	(revision 16139)
@@ -0,0 +1,131 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Builds a serialized deployment descriptor given a text file description of the
+ * descriptor in the format supported by WebLogic.
+ *
+ * This ant task is a front end for the weblogic DDCreator tool.
+ *
+ */
+public class DDCreator extends MatchingTask {
+    /**
+     * The root directory of the tree containing the textual deployment descriptors. The actual
+     * deployment descriptor files are selected using include and exclude constructs
+     * on the EJBC task, as supported by the MatchingTask superclass.
+     */
+    private File descriptorDirectory;
+
+    /**
+     * The directory where generated serialised deployment descriptors are placed.
+     */
+    private File generatedFilesDirectory;
+
+    /**
+     * The classpath to be used in the weblogic ejbc calls. It must contain the weblogic
+     * classes necessary fro DDCreator <b>and</b> the implementation classes of the
+     * home and remote interfaces.
+     */
+    private String classpath;
+
+    /**
+     * Do the work.
+     *
+     * The work is actually done by creating a helper task. This approach allows
+     * the classpath of the helper task to be set. Since the weblogic tools require
+     * the class files of the project's home and remote interfaces to be available in
+     * the classpath, this also avoids having to start ant with the class path of the
+     * project it is building.
+     *
+     * @exception BuildException if something goes wrong with the build
+     */
+    public void execute() throws BuildException {
+        if (descriptorDirectory == null
+            || !descriptorDirectory.isDirectory()) {
+            throw new BuildException("descriptors directory "
+                + descriptorDirectory.getPath() + " is not valid");
+        }
+        if (generatedFilesDirectory == null
+            || !generatedFilesDirectory.isDirectory()) {
+            throw new BuildException("dest directory "
+                + generatedFilesDirectory.getPath() + " is not valid");
+        }
+
+        String args = descriptorDirectory + " " + generatedFilesDirectory;
+
+        // get all the files in the descriptor directory
+        DirectoryScanner ds = super.getDirectoryScanner(descriptorDirectory);
+
+        String[] files = ds.getIncludedFiles();
+
+        for (int i = 0; i < files.length; ++i) {
+            args += " " + files[i];
+        }
+
+        String systemClassPath = System.getProperty("java.class.path");
+        String execClassPath = getProject().translatePath(systemClassPath + ":" + classpath);
+        Java ddCreatorTask = (Java) getProject().createTask("java");
+        ddCreatorTask.setTaskName(getTaskName());
+        ddCreatorTask.setFork(true);
+        ddCreatorTask.setClassname("org.apache.tools.ant.taskdefs.optional.ejb.DDCreatorHelper");
+        Commandline.Argument arguments = ddCreatorTask.createArg();
+        arguments.setLine(args);
+        ddCreatorTask.setClasspath(new Path(getProject(), execClassPath));
+        if (ddCreatorTask.executeJava() != 0) {
+            throw new BuildException("Execution of ddcreator helper failed");
+        }
+    }
+
+    /**
+     * Set the directory from where the text descriptions of the deployment descriptors are
+     * to be read.
+     *
+     * @param dirName the name of the directory containing the text deployment descriptor files.
+     */
+    public void setDescriptors(String dirName) {
+        descriptorDirectory = new File(dirName);
+    }
+
+    /**
+     * Set the directory into which the serialized deployment descriptors are to
+     * be written.
+     *
+     * @param dirName the name of the directory into which the serialised deployment
+     *                descriptors are written.
+     */
+    public void setDest(String dirName) {
+        generatedFilesDirectory = new File(dirName);
+    }
+
+    /**
+     * Set the classpath to be used for this compilation.
+     *
+     * @param s the classpath to use for the ddcreator tool.
+     */
+    public void setClasspath(String s) {
+        this.classpath = getProject().translatePath(s);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/DDCreatorHelper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/DDCreatorHelper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/DDCreatorHelper.java	(revision 16139)
@@ -0,0 +1,140 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.ObjectInputStream;
+import javax.ejb.deployment.DeploymentDescriptor;
+
+/**
+ * A helper class which performs the actual work of the ddcreator task.
+ *
+ * This class is run with a classpath which includes the weblogic tools and the home and remote
+ * interface class files referenced in the deployment descriptors being built.
+ *
+ */
+public class DDCreatorHelper {
+    /**
+     * The root directory of the tree containing the textual deployment descriptors.
+     */
+    private File descriptorDirectory;
+
+    /**
+     * The directory where generated serialised deployment descriptors are written.
+     */
+    private File generatedFilesDirectory;
+
+    /**
+     * The descriptor text files for which a serialised descriptor is to be created.
+     */
+    String[] descriptors;
+
+    /**
+     * The main method.
+     *
+     * The main method creates an instance of the DDCreatorHelper, passing it the
+     * args which it then processes.
+     */
+    public static void main(String[] args) throws Exception {
+        DDCreatorHelper helper = new DDCreatorHelper(args);
+        helper.process();
+    }
+
+    /**
+     * Initialise the helper with the command arguments.
+     *
+     */
+    private DDCreatorHelper(String[] args) {
+        int index = 0;
+        descriptorDirectory = new File(args[index++]);
+        generatedFilesDirectory = new File(args[index++]);
+
+        descriptors = new String[args.length - index];
+        for (int i = 0; index < args.length; ++i) {
+            descriptors[i] = args[index++];
+        }
+    }
+
+    /**
+     * Do the actual work.
+     *
+     * The work proceeds by examining each descriptor given. If the serialised
+     * file does not exist or is older than the text description, the weblogic
+     * DDCreator tool is invoked directly to build the serialised descriptor.
+     */
+    private void process() throws Exception {
+        for (int i = 0; i < descriptors.length; ++i) {
+            String descriptorName = descriptors[i];
+            File descriptorFile = new File(descriptorDirectory, descriptorName);
+
+            int extIndex = descriptorName.lastIndexOf(".");
+            String serName = null;
+            if (extIndex != -1) {
+                serName = descriptorName.substring(0, extIndex) + ".ser";
+            } else {
+                serName = descriptorName + ".ser";
+            }
+            File serFile = new File(generatedFilesDirectory, serName);
+
+            // do we need to regenerate the file
+            if (!serFile.exists() || serFile.lastModified() < descriptorFile.lastModified()
+                || regenerateSerializedFile(serFile)) {
+
+                String[] args = {"-noexit",
+                                 "-d", serFile.getParent(),
+                                 "-outputfile", serFile.getName(),
+                                 descriptorFile.getPath()};
+                try {
+                    weblogic.ejb.utils.DDCreator.main(args);
+                } catch (Exception e) {
+                    // there was an exception - run with no exit to get proper error
+                    String[] newArgs = {"-d", generatedFilesDirectory.getPath(),
+                                 "-outputfile", serFile.getName(),
+                                 descriptorFile.getPath()};
+                    weblogic.ejb.utils.DDCreator.main(newArgs);
+                }
+            }
+        }
+    }
+
+    /**
+     * EJBC will fail if the serialized descriptor file does not match the bean classes.
+     * You can test for this by trying to load the deployment descriptor.  If it fails,
+     * the serialized file needs to be regenerated because the associated class files
+     * don't match.
+     */
+    private boolean regenerateSerializedFile(File serFile) {
+        try {
+
+            FileInputStream fis = new FileInputStream(serFile);
+            ObjectInputStream ois = new ObjectInputStream(fis);
+            DeploymentDescriptor dd = (DeploymentDescriptor) ois.readObject();
+            fis.close();
+
+            // Since the descriptor read properly, everything should be o.k.
+            return false;
+
+        } catch (Exception e) {
+
+            // Weblogic will throw an error if the deployment descriptor does
+            // not match the class files.
+            return true;
+
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/DescriptorHandler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/DescriptorHandler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/DescriptorHandler.java	(revision 16139)
@@ -0,0 +1,350 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Hashtable;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.xml.sax.AttributeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * Inner class used by EjbJar to facilitate the parsing of deployment
+ * descriptors and the capture of appropriate information. Extends
+ * HandlerBase so it only implements the methods needed. During parsing
+ * creates a hashtable consisting of entries mapping the name it should be
+ * inserted into an EJB jar as to a File representing the file on disk. This
+ * list can then be accessed through the getFiles() method.
+ */
+public class DescriptorHandler extends org.xml.sax.HandlerBase {
+    private static final int STATE_LOOKING_EJBJAR = 1;
+    private static final int STATE_IN_EJBJAR = 2;
+    private static final int STATE_IN_BEANS = 3;
+    private static final int STATE_IN_SESSION = 4;
+    private static final int STATE_IN_ENTITY = 5;
+    private static final int STATE_IN_MESSAGE = 6;
+
+    private Task owningTask;
+
+    private String publicId = null;
+
+    /**
+     * Bunch of constants used for storing entries in a hashtable, and for
+     * constructing the filenames of various parts of the ejb jar.
+     */
+    private static final String EJB_REF               = "ejb-ref";
+    private static final String EJB_LOCAL_REF         = "ejb-local-ref";
+    private static final String HOME_INTERFACE        = "home";
+    private static final String REMOTE_INTERFACE      = "remote";
+    private static final String LOCAL_HOME_INTERFACE  = "local-home";
+    private static final String LOCAL_INTERFACE       = "local";
+    private static final String BEAN_CLASS            = "ejb-class";
+    private static final String PK_CLASS              = "prim-key-class";
+    private static final String EJB_NAME              = "ejb-name";
+    private static final String EJB_JAR               = "ejb-jar";
+    private static final String ENTERPRISE_BEANS      = "enterprise-beans";
+    private static final String ENTITY_BEAN           = "entity";
+    private static final String SESSION_BEAN          = "session";
+    private static final String MESSAGE_BEAN          = "message-driven";
+
+    /**
+     * The state of the parsing
+     */
+    private int parseState = STATE_LOOKING_EJBJAR;
+
+    /**
+     * Instance variable used to store the name of the current element being
+     * processed by the SAX parser.  Accessed by the SAX parser call-back methods
+     * startElement() and endElement().
+     */
+    protected String currentElement = null;
+
+    /**
+     * The text of the current element
+     */
+    protected String currentText = null;
+
+    /**
+     * Instance variable that stores the names of the files as they will be
+     * put into the jar file, mapped to File objects  Accessed by the SAX
+     * parser call-back method characters().
+     */
+    protected Hashtable ejbFiles = null;
+
+    /**
+     * Instance variable that stores the value found in the &lt;ejb-name&gt; element
+     */
+    protected String ejbName = null;
+
+    private Hashtable fileDTDs = new Hashtable();
+
+    private Hashtable resourceDTDs = new Hashtable();
+
+    private boolean inEJBRef = false;
+
+    private Hashtable urlDTDs = new Hashtable();
+
+    /**
+     * The directory containing the bean classes and interfaces. This is
+     * used for performing dependency file lookups.
+     */
+    private File srcDir;
+
+    public DescriptorHandler(Task task, File srcDir) {
+        this.owningTask = task;
+        this.srcDir = srcDir;
+    }
+
+    public void registerDTD(String publicId, String location) {
+        if (location == null) {
+            return;
+        }
+
+        File fileDTD = new File(location);
+        if (!fileDTD.exists()) {
+            // resolve relative to project basedir
+            fileDTD = owningTask.getProject().resolveFile(location);
+        }
+
+        if (fileDTD.exists()) {
+            if (publicId != null) {
+                fileDTDs.put(publicId, fileDTD);
+                owningTask.log("Mapped publicId " + publicId + " to file "
+                    + fileDTD, Project.MSG_VERBOSE);
+            }
+            return;
+        }
+
+        if (getClass().getResource(location) != null) {
+            if (publicId != null) {
+                resourceDTDs.put(publicId, location);
+                owningTask.log("Mapped publicId " + publicId + " to resource "
+                    + location, Project.MSG_VERBOSE);
+            }
+        }
+
+        try {
+            if (publicId != null) {
+                URL urldtd = new URL(location);
+                urlDTDs.put(publicId, urldtd);
+            }
+        } catch (java.net.MalformedURLException e) {
+            //ignored
+        }
+
+    }
+
+    public InputSource resolveEntity(String publicId, String systemId)
+        throws SAXException {
+        this.publicId = publicId;
+
+        File dtdFile = (File) fileDTDs.get(publicId);
+        if (dtdFile != null) {
+            try {
+                owningTask.log("Resolved " + publicId + " to local file "
+                    + dtdFile, Project.MSG_VERBOSE);
+                return new InputSource(new FileInputStream(dtdFile));
+            } catch (FileNotFoundException ex) {
+                // ignore
+            }
+        }
+
+        String dtdResourceName = (String) resourceDTDs.get(publicId);
+        if (dtdResourceName != null) {
+            InputStream is = this.getClass().getResourceAsStream(dtdResourceName);
+            if (is != null) {
+                owningTask.log("Resolved " + publicId + " to local resource "
+                    + dtdResourceName, Project.MSG_VERBOSE);
+                return new InputSource(is);
+            }
+        }
+
+        URL dtdUrl = (URL) urlDTDs.get(publicId);
+        if (dtdUrl != null) {
+            try {
+                InputStream is = dtdUrl.openStream();
+                owningTask.log("Resolved " + publicId + " to url "
+                    + dtdUrl, Project.MSG_VERBOSE);
+                return new InputSource(is);
+            } catch (IOException ioe) {
+                //ignore
+            }
+        }
+
+        owningTask.log("Could not resolve ( publicId: " + publicId
+            + ", systemId: " + systemId + ") to a local entity", Project.MSG_INFO);
+
+        return null;
+    }
+
+    /**
+     * Getter method that returns the set of files to include in the EJB jar.
+     */
+    public Hashtable getFiles() {
+        return (ejbFiles == null) ? new Hashtable() : ejbFiles;
+    }
+
+    /**
+     * Get the publicId of the DTD
+     */
+    public String getPublicId() {
+        return publicId;
+    }
+
+     /**
+     * Getter method that returns the value of the &lt;ejb-name&gt; element.
+     */
+    public String getEjbName() {
+        return ejbName;
+    }
+
+    /**
+     * SAX parser call-back method that is used to initialize the values of some
+     * instance variables to ensure safe operation.
+     */
+    public void startDocument() throws SAXException {
+        this.ejbFiles = new Hashtable(10, 1);
+        this.currentElement = null;
+        inEJBRef = false;
+    }
+
+
+    /**
+     * SAX parser call-back method that is invoked when a new element is entered
+     * into.  Used to store the context (attribute name) in the currentAttribute
+     * instance variable.
+     * @param name The name of the element being entered.
+     * @param attrs Attributes associated to the element.
+     */
+    public void startElement(String name, AttributeList attrs)
+        throws SAXException {
+        this.currentElement = name;
+        currentText = "";
+        if (name.equals(EJB_REF) || name.equals(EJB_LOCAL_REF)) {
+            inEJBRef = true;
+        } else if (parseState == STATE_LOOKING_EJBJAR && name.equals(EJB_JAR)) {
+            parseState = STATE_IN_EJBJAR;
+        } else if (parseState == STATE_IN_EJBJAR && name.equals(ENTERPRISE_BEANS)) {
+            parseState = STATE_IN_BEANS;
+        } else if (parseState == STATE_IN_BEANS && name.equals(SESSION_BEAN)) {
+            parseState = STATE_IN_SESSION;
+        } else if (parseState == STATE_IN_BEANS && name.equals(ENTITY_BEAN)) {
+            parseState = STATE_IN_ENTITY;
+        } else if (parseState == STATE_IN_BEANS && name.equals(MESSAGE_BEAN)) {
+            parseState = STATE_IN_MESSAGE;
+        }
+    }
+
+
+    /**
+     * SAX parser call-back method that is invoked when an element is exited.
+     * Used to blank out (set to the empty string, not nullify) the name of
+     * the currentAttribute.  A better method would be to use a stack as an
+     * instance variable, however since we are only interested in leaf-node
+     * data this is a simpler and workable solution.
+     * @param name The name of the attribute being exited. Ignored
+     *        in this implementation.
+     */
+    public void endElement(String name) throws SAXException {
+        processElement();
+        currentText = "";
+        this.currentElement = "";
+        if (name.equals(EJB_REF) || name.equals(EJB_LOCAL_REF)) {
+            inEJBRef = false;
+        } else if (parseState == STATE_IN_ENTITY && name.equals(ENTITY_BEAN)) {
+            parseState = STATE_IN_BEANS;
+        } else if (parseState == STATE_IN_SESSION && name.equals(SESSION_BEAN)) {
+            parseState = STATE_IN_BEANS;
+        } else if (parseState == STATE_IN_MESSAGE && name.equals(MESSAGE_BEAN)) {
+            parseState = STATE_IN_BEANS;
+        } else if (parseState == STATE_IN_BEANS && name.equals(ENTERPRISE_BEANS)) {
+            parseState = STATE_IN_EJBJAR;
+        } else if (parseState == STATE_IN_EJBJAR && name.equals(EJB_JAR)) {
+            parseState = STATE_LOOKING_EJBJAR;
+        }
+    }
+
+    /**
+     * SAX parser call-back method invoked whenever characters are located within
+     * an element.  currentAttribute (modified by startElement and endElement)
+     * tells us whether we are in an interesting element (one of the up to four
+     * classes of an EJB).  If so then converts the classname from the format
+     * org.apache.tools.ant.Parser to the convention for storing such a class,
+     * org/apache/tools/ant/Parser.class.  This is then resolved into a file
+     * object under the srcdir which is stored in a Hashtable.
+     * @param ch A character array containing all the characters in
+     *        the element, and maybe others that should be ignored.
+     * @param start An integer marking the position in the char
+     *        array to start reading from.
+     * @param length An integer representing an offset into the
+     *        char array where the current data terminates.
+     */
+    public void characters(char[] ch, int start, int length)
+        throws SAXException {
+
+        currentText += new String(ch, start, length);
+    }
+
+
+    protected void processElement() {
+        if (inEJBRef
+            || (parseState != STATE_IN_ENTITY
+                && parseState != STATE_IN_SESSION
+                && parseState != STATE_IN_MESSAGE)) {
+            return;
+        }
+
+        if (currentElement.equals(HOME_INTERFACE)
+            || currentElement.equals(REMOTE_INTERFACE)
+            || currentElement.equals(LOCAL_INTERFACE)
+            || currentElement.equals(LOCAL_HOME_INTERFACE)
+            || currentElement.equals(BEAN_CLASS)
+            || currentElement.equals(PK_CLASS)) {
+
+            // Get the filename into a String object
+            File classFile = null;
+            String className = currentText.trim();
+
+            // If it's a primitive wrapper then we shouldn't try and put
+            // it into the jar, so ignore it.
+            if (!className.startsWith("java.")
+                && !className.startsWith("javax.")) {
+                // Translate periods into path separators, add .class to the
+                // name, create the File object and add it to the Hashtable.
+                className = className.replace('.', File.separatorChar);
+                className += ".class";
+                classFile = new File(srcDir, className);
+                ejbFiles.put(className, classFile);
+            }
+        }
+
+    // Get the value of the <ejb-name> tag.  Only the first occurrence.
+        if (currentElement.equals(EJB_NAME)) {
+            if (ejbName == null) {
+                ejbName = currentText.trim();
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EJBDeploymentTool.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EJBDeploymentTool.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EJBDeploymentTool.java	(revision 16139)
@@ -0,0 +1,53 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+
+
+import javax.xml.parsers.SAXParser;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+
+public interface EJBDeploymentTool {
+    /**
+     * Process a deployment descriptor, generating the necessary vendor specific
+     * deployment files.
+     *
+     * @param descriptorFilename the name of the deployment descriptor
+     * @param saxParser a SAX parser which can be used to parse the deployment descriptor.
+     */
+    void processDescriptor(String descriptorFilename, SAXParser saxParser)
+        throws BuildException;
+
+    /**
+     * Called to validate that the tool parameters have been configured.
+     *
+     */
+    void validateConfigured() throws BuildException;
+
+    /**
+     * Set the task which owns this tool
+     */
+    void setTask(Task task);
+
+    /**
+     * Configure this tool for use in the ejbjar task.
+     */
+    void configure(EjbJar.Config config);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java	(revision 16139)
@@ -0,0 +1,622 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+// Standard java imports
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.xml.sax.SAXException;
+
+/**
+ * Provides automated EJB JAR file creation.
+ * <p>
+ * Extends the
+ * MatchingTask class provided in the default ant distribution to provide a
+ * directory scanning EJB jarfile generator.
+ * </p>
+ *
+ * <p>
+ * The task works by taking the deployment descriptors one at a time and
+ * parsing them to locate the names of the classes which should be placed in
+ * the jar. The classnames are translated to java.io.Files by replacing
+ * periods with File.separatorChar and resolving the generated filename as a
+ * relative path under the srcDir attribute. All necessary files are then
+ * assembled into a jarfile. One jarfile is constructed for each deployment
+ * descriptor found.
+ * </p>
+ *
+ * */
+public class EjbJar extends MatchingTask {
+
+    /**
+     * Inner class used to record information about the location of a local DTD
+     */
+    public static class DTDLocation
+        extends org.apache.tools.ant.types.DTDLocation {
+    }
+
+    /**
+     * A class which contains the configuration state of the ejbjar task.
+     * This state is passed to the deployment tools for configuration
+     */
+    static class Config {
+        /**
+         * Stores a handle to the directory under which to search for class
+         * files
+         */
+        public File srcDir;
+
+        /**
+         * Stores a handle to the directory under which to search for
+         * deployment descriptors
+         */
+        public File descriptorDir;
+
+        /** Instance variable that marks the end of the 'basename' */
+        public String baseNameTerminator = "-";
+
+        /** Stores a handle to the destination EJB Jar file */
+        public String baseJarName;
+
+        /**
+         * Instance variable that determines whether to use a package structure
+         * of a flat directory as the destination for the jar files.
+         */
+        public boolean flatDestDir = false;
+
+        /**
+         * The classpath to use when loading classes
+         */
+        public Path classpath;
+
+        /**
+         * A Fileset of support classes
+         */
+        public List supportFileSets = new ArrayList();
+
+        /**
+         * The list of configured DTD locations
+         */
+        public ArrayList dtdLocations = new ArrayList();
+
+        /**
+         * The naming scheme used to determine the generated jar name
+         * from the descriptor information
+         */
+        public NamingScheme namingScheme;
+
+        /**
+         * The Manifest file
+         */
+        public File manifest;
+
+        /**
+         * The dependency analyzer to use to add additional classes to the jar
+         */
+        public String analyzer;
+    }
+
+    /**
+     * An EnumeratedAttribute class for handling different EJB jar naming
+     * schemes
+     */
+    public static class NamingScheme extends EnumeratedAttribute {
+        /**
+         * Naming scheme where generated jar is determined from the ejb-name in
+         * the deployment descripor
+         */
+        public static final String EJB_NAME = "ejb-name";
+
+        /**
+         * Naming scheme where the generated jar name is based on the
+         * name of the directory containing the deployment descriptor
+         */
+        public static final String DIRECTORY = "directory";
+
+        /**
+         * Naming scheme where the generated jar name is based on the name of
+         * the deployment descriptor file
+         */
+        public static final String DESCRIPTOR = "descriptor";
+
+        /**
+         * Naming scheme where the generated jar is named by the basejarname
+         * attribute
+         */
+        public static final String BASEJARNAME = "basejarname";
+
+        /**
+         * Gets the values of the NamingScheme
+         *
+         * @return an array of the values of this attribute class.
+         */
+        public String[] getValues() {
+            return new String[] {EJB_NAME, DIRECTORY, DESCRIPTOR, BASEJARNAME};
+        }
+    }
+
+    /**
+     * CMP versions supported
+     * valid CMP versions are 1.0 and 2.0
+     * @since ant 1.6
+     */
+    public static class CMPVersion extends EnumeratedAttribute {
+        public static final String CMP1_0 = "1.0";
+        public static final String CMP2_0 = "2.0";
+        public String[] getValues() {
+            return new String[]{
+                CMP1_0,
+                CMP2_0,
+            };
+        }
+    }
+    /**
+     * The config which is built by this task and used by the various deployment
+     * tools to access the configuration of the ejbjar task
+     */
+    private Config config = new Config();
+
+
+    /**
+     * Stores a handle to the directory to put the Jar files in. This is
+     * only used by the generic deployment descriptor tool which is created
+     * if no other deployment descriptor tools are provided. Normally each
+     * deployment tool will specify the desitination dir itself.
+     */
+    private File destDir;
+
+    /** Instance variable that stores the suffix for the generated jarfile. */
+    private String genericJarSuffix = "-generic.jar";
+
+    /** Instance variable that stores the CMP version for the jboss jarfile. */
+    private String cmpVersion = CMPVersion.CMP1_0;
+
+    /** The list of deployment tools we are going to run. */
+    private ArrayList deploymentTools = new ArrayList();
+
+    /**
+     * Add a deployment tool to the list of deployment tools that will be
+     * processed
+     *
+     * @param deploymentTool a deployment tool instance to which descriptors
+     *        will be passed for processing.
+     */
+    protected void addDeploymentTool(EJBDeploymentTool deploymentTool) {
+        deploymentTool.setTask(this);
+        deploymentTools.add(deploymentTool);
+    }
+
+    /**
+     * Adds a deployment tool for Weblogic server.
+     *
+     * @return the deployment tool instance to be configured.
+     */
+    public WeblogicDeploymentTool createWeblogic() {
+        WeblogicDeploymentTool tool = new WeblogicDeploymentTool();
+        addDeploymentTool(tool);
+        return tool;
+    }
+
+    /**
+     * Adds a deployment tool for Websphere 4.0 server.
+     *
+     * @return the deployment tool instance to be configured.
+     */
+    public WebsphereDeploymentTool createWebsphere() {
+        WebsphereDeploymentTool tool = new WebsphereDeploymentTool();
+        addDeploymentTool(tool);
+        return tool;
+    }
+
+    /**
+     * Adds a deployment tool for Borland server.
+     *
+     * @return the deployment tool instance to be configured.
+     */
+    public BorlandDeploymentTool createBorland() {
+        log("Borland deployment tools",  Project.MSG_VERBOSE);
+
+        BorlandDeploymentTool tool = new BorlandDeploymentTool();
+        tool.setTask(this);
+        deploymentTools.add(tool);
+        return tool;
+    }
+
+    /**
+     * Adds a deployment tool for iPlanet Application Server.
+     *
+     * @return the deployment tool instance to be configured.
+     */
+    public IPlanetDeploymentTool createIplanet() {
+        log("iPlanet Application Server deployment tools", Project.MSG_VERBOSE);
+
+        IPlanetDeploymentTool tool = new IPlanetDeploymentTool();
+        addDeploymentTool(tool);
+        return tool;
+    }
+
+    /**
+     * Adds a deployment tool for JBoss server.
+     *
+     * @return the deployment tool instance to be configured.
+     */
+    public JbossDeploymentTool createJboss() {
+        JbossDeploymentTool tool = new JbossDeploymentTool();
+        addDeploymentTool(tool);
+        return tool;
+    }
+
+    /**
+     * Adds a deployment tool for JOnAS server.
+     *
+     * @return the deployment tool instance to be configured.
+     */
+    public JonasDeploymentTool createJonas() {
+        log("JOnAS deployment tools",  Project.MSG_VERBOSE);
+
+        JonasDeploymentTool tool = new JonasDeploymentTool();
+        addDeploymentTool(tool);
+        return tool;
+    }
+
+    /**
+     * Adds a deployment tool for Weblogic when using the Toplink
+     * Object-Relational mapping.
+     *
+     * @return the deployment tool instance to be configured.
+     */
+    public WeblogicTOPLinkDeploymentTool createWeblogictoplink() {
+        log("The <weblogictoplink> element is no longer required. Please use "
+            + "the <weblogic> element and set newCMP=\"true\"",
+            Project.MSG_INFO);
+        WeblogicTOPLinkDeploymentTool tool
+            = new WeblogicTOPLinkDeploymentTool();
+        addDeploymentTool(tool);
+        return tool;
+    }
+
+    /**
+     * Adds to the classpath used to locate the super classes and
+     * interfaces of the classes that will make up the EJB JAR.
+     *
+     * @return the path to be configured.
+     */
+    public Path createClasspath() {
+        if (config.classpath == null) {
+            config.classpath = new Path(getProject());
+        }
+        return config.classpath.createPath();
+    }
+
+    /**
+     * Create a DTD location record. This stores the location of a DTD. The
+     * DTD is identified by its public Id. The location may either be a file
+     * location or a resource location.
+     *
+     * @return the DTD location object to be configured by Ant
+     */
+    public DTDLocation createDTD() {
+        DTDLocation dtdLocation = new DTDLocation();
+        config.dtdLocations.add(dtdLocation);
+
+        return dtdLocation;
+    }
+
+    /**
+     * Adds a fileset for support elements.
+     *
+     * @return a fileset which can be populated with support files.
+     */
+    public FileSet createSupport() {
+        FileSet supportFileSet = new FileSet();
+        config.supportFileSets.add(supportFileSet);
+        return supportFileSet;
+    }
+
+
+    /**
+     * Set the Manifest file to use when jarring. As of EJB 1.1, manifest
+     * files are no longer used to configure the EJB. However, they still
+     * have a vital importance if the EJB is intended to be packaged in an
+     * EAR file. By adding "Class-Path" settings to a Manifest file, the EJB
+     * can look for classes inside the EAR file itself, allowing for easier
+     * deployment. This is outlined in the J2EE specification, and all J2EE
+     * components are meant to support it.
+     *
+     * @param manifest the manifest to be used in the EJB jar
+     */
+     public void setManifest(File manifest) {
+         config.manifest = manifest;
+     }
+
+    /**
+     * Sets the source directory, which is the directory that
+     * contains the classes that will be added to the EJB jar. Typically
+     * this will include the home and remote interfaces and the bean class.
+     *
+     * @param inDir the source directory.
+     */
+    public void setSrcdir(File inDir) {
+        config.srcDir = inDir;
+    }
+
+    /**
+     * Set the descriptor directory. The descriptor directory contains the
+     * EJB deployment descriptors. These are XML files that declare the
+     * properties of a bean in a particular deployment scenario. Such
+     * properties include, for example, the transactional nature of the bean
+     * and the security access control to the bean's methods.
+     *
+     * @param inDir the directory containing the deployment descriptors.
+     */
+    public void setDescriptordir(File inDir) {
+        config.descriptorDir = inDir;
+    }
+
+    /**
+     * Set the analyzer to use when adding in dependencies to the JAR.
+     *
+     * @param analyzer the name of the dependency analyzer or a class.
+     */
+    public void setDependency(String analyzer) {
+        config.analyzer = analyzer;
+    }
+
+    /**
+     * Set the base name of the EJB JAR that is to be created if it is not
+     * to be determined from the name of the deployment descriptor files.
+     *
+     * @param inValue the basename that will be used when writing the jar
+     *      file containing the EJB
+     */
+    public void setBasejarname(String inValue) {
+        config.baseJarName = inValue;
+        if (config.namingScheme == null) {
+            config.namingScheme = new NamingScheme();
+            config.namingScheme.setValue(NamingScheme.BASEJARNAME);
+        } else if (!config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME)) {
+            throw new BuildException("The basejarname attribute is not "
+                + "compatible with the "
+                + config.namingScheme.getValue() + " naming scheme");
+        }
+    }
+
+    /**
+     * Set the naming scheme used to determine the name of the generated jars
+     * from the deployment descriptor
+     *
+     * @param namingScheme the naming scheme to be used
+     */
+    public void setNaming(NamingScheme namingScheme) {
+        config.namingScheme = namingScheme;
+        if (!config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME)
+            && config.baseJarName != null) {
+            throw new BuildException("The basejarname attribute is not "
+                + "compatible with the "
+                + config.namingScheme.getValue() + " naming scheme");
+        }
+    }
+
+    /**
+     * Gets the destination directory.
+     *
+     * @return destination directory
+     * @since ant 1.6
+     */
+    public File getDestdir() {
+        return this.destDir;
+    }
+
+    /**
+     * Set the destination directory. The EJB jar files will be written into
+     * this directory. The jar files that exist in this directory are also
+     * used when determining if the contents of the jar file have changed.
+     * Note that this parameter is only used if no deployment tools are
+     * specified. Typically each deployment tool will specify its own
+     * destination directory.
+     *
+     * @param inDir the destination directory in which to generate jars
+     */
+    public void setDestdir(File inDir) {
+        this.destDir = inDir;
+    }
+
+    /**
+     * Gets the CMP version.
+     *
+     * @return CMP version
+     * @since ant 1.6
+     */
+    public String getCmpversion() {
+        return this.cmpVersion;
+    }
+
+    /**
+     * Sets the CMP version.
+     *
+     * @param version CMP version.
+     * Must be either <code>1.0</code> or <code>2.0</code>.<br/>
+     * Default is <code>1.0</code>.<br/>
+     * Initially, only the JBoss implementation does something specific for CMP 2.0.<br/>
+     * @since ant 1.6
+     */
+    public void setCmpversion(CMPVersion version) {
+        this.cmpVersion = version.getValue();
+    }
+
+    /**
+     * Set the classpath to use when resolving classes for inclusion in the jar.
+     *
+     * @param classpath the classpath to use.
+     */
+    public void setClasspath(Path classpath) {
+        config.classpath = classpath;
+    }
+
+    /**
+     * Controls whether the
+     * destination JARs are written out in the destination directory with
+     * the same hierarchical structure from which the deployment descriptors
+     * have been read. If this is set to true the generated EJB jars are
+     * written into the root of the destination directory, otherwise they
+     * are written out in the same relative position as the deployment
+     * descriptors in the descriptor directory.
+     *
+     * @param inValue the new value of the flatdestdir flag.
+     */
+    public void setFlatdestdir(boolean inValue) {
+        config.flatDestDir = inValue;
+    }
+
+    /**
+     * Set the suffix for the generated jar file. When generic jars are
+     * generated, they have a suffix which is appended to the the bean name
+     * to create the name of the jar file. Note that this suffix includes
+     * the extension fo te jar file and should therefore end with an
+     * appropriate extension such as .jar or .ear
+     *
+     * @param inString the string to use as the suffix.
+     */
+    public void setGenericjarsuffix(String inString) {
+        this.genericJarSuffix = inString;
+    }
+
+    /**
+     * The string which terminates the bean name.
+     * The convention used by this task is
+     * that bean descriptors are named as the BeanName with some suffix. The
+     * baseNameTerminator string separates the bean name and the suffix and
+     * is used to determine the bean name.
+     *
+     * @param inValue a string which marks the end of the basename.
+     */
+    public void setBasenameterminator(String inValue) {
+        config.baseNameTerminator = inValue;
+    }
+
+    /**
+     * Validate the config that has been configured from the build file
+     *
+     * @throws BuildException if the config is not valid
+     */
+    private void validateConfig() throws BuildException {
+        if (config.srcDir == null) {
+            throw new BuildException("The srcDir attribute must be specified");
+        }
+
+        if (config.descriptorDir == null) {
+            config.descriptorDir = config.srcDir;
+        }
+
+        if (config.namingScheme == null) {
+            config.namingScheme = new NamingScheme();
+            config.namingScheme.setValue(NamingScheme.DESCRIPTOR);
+        } else if (config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME)
+                    && config.baseJarName == null) {
+            throw new BuildException("The basejarname attribute must "
+                + "be specified with the basejarname naming scheme");
+        }
+    }
+
+    /**
+     * Invoked by Ant after the task is prepared, when it is ready to execute
+     * this task.
+     *
+     * This will configure all of the nested deployment tools to allow them to
+     * process the jar. If no deployment tools have been configured a generic
+     * tool is created to handle the jar.
+     *
+     * A parser is configured and then each descriptor found is passed to all
+     * the deployment tool elements for processing.
+     *
+     * @exception BuildException thrown whenever a problem is
+     *            encountered that cannot be recovered from, to signal to ant
+     *            that a major problem occurred within this task.
+     */
+    public void execute() throws BuildException {
+        validateConfig();
+
+        if (deploymentTools.size() == 0) {
+            GenericDeploymentTool genericTool = new GenericDeploymentTool();
+            genericTool.setTask(this);
+            genericTool.setDestdir(destDir);
+            genericTool.setGenericJarSuffix(genericJarSuffix);
+            deploymentTools.add(genericTool);
+        }
+
+        for (Iterator i = deploymentTools.iterator(); i.hasNext();) {
+            EJBDeploymentTool tool = (EJBDeploymentTool) i.next();
+            tool.configure(config);
+            tool.validateConfigured();
+        }
+
+        try {
+            // Create the parser using whatever parser the system dictates
+            SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+            saxParserFactory.setValidating(true);
+            SAXParser saxParser = saxParserFactory.newSAXParser();
+
+
+            DirectoryScanner ds = getDirectoryScanner(config.descriptorDir);
+            ds.scan();
+            String[] files = ds.getIncludedFiles();
+
+            log(files.length + " deployment descriptors located.",
+                Project.MSG_VERBOSE);
+
+            // Loop through the files. Each file represents one deployment
+            // descriptor, and hence one bean in our model.
+            for (int index = 0; index < files.length; ++index) {
+                // process the deployment descriptor in each tool
+                for (Iterator i = deploymentTools.iterator(); i.hasNext();) {
+                    EJBDeploymentTool tool = (EJBDeploymentTool) i.next();
+                    tool.processDescriptor(files[index], saxParser);
+                }
+            }
+        } catch (SAXException se) {
+            String msg = "SAXException while creating parser."
+                + "  Details: "
+                + se.getMessage();
+            throw new BuildException(msg, se);
+        } catch (ParserConfigurationException pce) {
+            String msg = "ParserConfigurationException while creating parser. "
+                       + "Details: " + pce.getMessage();
+            throw new BuildException(msg, pce);
+        }
+    } // end of execute()
+
+}
+
+
+
+
+
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/Ejbc.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/Ejbc.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/Ejbc.java	(revision 16139)
@@ -0,0 +1,187 @@
+/*
+ * Copyright  2000,2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Builds EJB support classes using WebLogic's ejbc tool from a directory containing
+ * a set of deployment descriptors.
+ *
+ *
+ */
+public class Ejbc extends MatchingTask {
+    /**
+     * The root directory of the tree containing the serialised deployment desciptors. The actual
+     * deployment descriptor files are selected using include and exclude constructs
+     * on the ejbc task provided by the MatchingTask superclass.
+     */
+    private File descriptorDirectory;
+
+    /**
+     * The directory where generated files are placed.
+     */
+    private File generatedFilesDirectory;
+
+    /**
+     * The name of the manifest file generated for the EJB jar.
+     */
+    private File generatedManifestFile;
+
+    /**
+     * The classpath to be used in the weblogic ejbc calls. It must contain the weblogic
+     * classes <b>and</b> the implementation classes of the home and remote interfaces.
+     */
+    private String classpath;
+
+    /**
+     * The source directory for the home and remote interfaces. This is used to determine if
+     * the generated deployment classes are out of date.
+     */
+    private File sourceDirectory;
+
+    public boolean keepgenerated;
+
+    /**
+     * Do the work.
+     *
+     * The work is actually done by creating a separate JVM to run a helper task.
+     * This approach allows the classpath of the helper task to be set. Since the
+     * weblogic tools require the class files of the project's home and remote
+     * interfaces to be available in the classpath, this also avoids having to
+     * start ant with the class path of the project it is building.
+     *
+     * @exception BuildException if someting goes wrong with the build
+     */
+    public void execute() throws BuildException {
+        if (descriptorDirectory == null
+            || !descriptorDirectory.isDirectory()) {
+            throw new BuildException("descriptors directory "
+                + descriptorDirectory.getPath() + " is not valid");
+        }
+        if (generatedFilesDirectory == null
+            || !generatedFilesDirectory.isDirectory()) {
+            throw new BuildException("dest directory "
+                + generatedFilesDirectory.getPath() + " is not valid");
+        }
+
+        if (sourceDirectory == null
+            || !sourceDirectory.isDirectory()) {
+            throw new BuildException("src directory "
+                + sourceDirectory.getPath() + " is not valid");
+        }
+
+        String systemClassPath = System.getProperty("java.class.path");
+        String execClassPath
+            = getProject().translatePath(systemClassPath + ":" + classpath
+                                         + ":" + generatedFilesDirectory);
+        // get all the files in the descriptor directory
+        DirectoryScanner ds = super.getDirectoryScanner(descriptorDirectory);
+
+        String[] files = ds.getIncludedFiles();
+
+        Java helperTask = (Java) getProject().createTask("java");
+        helperTask.setTaskName(getTaskName());
+        helperTask.setFork(true);
+        helperTask.setClassname("org.apache.tools.ant.taskdefs.optional.ejb.EjbcHelper");
+        String args = "";
+        args += " " + descriptorDirectory;
+        args += " " + generatedFilesDirectory;
+        args += " " + sourceDirectory;
+        args += " " + generatedManifestFile;
+        args += " " + keepgenerated;
+
+        for (int i = 0; i < files.length; ++i) {
+            args += " " + files[i];
+        }
+
+        Commandline.Argument arguments = helperTask.createArg();
+        arguments.setLine(args);
+        helperTask.setClasspath(new Path(getProject(), execClassPath));
+        if (helperTask.executeJava() != 0) {
+            throw new BuildException("Execution of ejbc helper failed");
+        }
+    }
+
+    public boolean getKeepgenerated() {
+        return keepgenerated;
+    }
+
+    /**
+     * Set the directory from where the serialized deployment descriptors are
+     * to be read.
+     *
+     * @param dirName the name of the directory containing the serialised deployment descriptors.
+     */
+    public void setDescriptors(String dirName) {
+        descriptorDirectory = new File(dirName);
+    }
+
+    /**
+     * Set the directory into which the support classes, RMI stubs, etc are to be written.
+     *
+     * @param dirName the name of the directory into which code is generated
+     */
+    public void setDest(String dirName) {
+        generatedFilesDirectory = new File(dirName);
+    }
+
+    /**
+     * If true, ejbc will keep the
+     * intermediate Java files used to build the class files.
+     * This can be useful when debugging.
+     */
+    public void setKeepgenerated(String newKeepgenerated) {
+        keepgenerated = Boolean.valueOf(newKeepgenerated.trim()).booleanValue();
+
+    }
+
+    /**
+     * Set the name of the generated manifest file.
+     *
+     * For each EJB that is processed an entry is created in this file. This can then be used
+     * to create a jar file for dploying the beans.
+     *
+     * @param manifestFilename the name of the manifest file to be generated.
+     */
+    public void setManifest(String manifestFilename) {
+        generatedManifestFile = new File(manifestFilename);
+    }
+
+    /**
+     * Set the classpath to be used for this compilation.
+     */
+    public void setClasspath(String s) {
+        this.classpath = getProject().translatePath(s);
+    }
+
+    /**
+     * Set the directory containing the source code for the home interface, remote interface
+     * and public key class definitions.
+     *
+     * @param dirName the directory containg the source tree for the EJB's interface classes.
+     */
+    public void setSrc(String dirName) {
+        sourceDirectory = new File(dirName);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbcHelper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbcHelper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbcHelper.java	(revision 16139)
@@ -0,0 +1,254 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.PrintWriter;
+import java.util.Vector;
+import javax.ejb.deployment.DeploymentDescriptor;
+import javax.ejb.deployment.EntityDescriptor;
+
+
+/**
+ * A helper class which performs the actual work of the ejbc task.
+ *
+ * This class is run with a classpath which includes the weblogic tools and the home and remote
+ * interface class files referenced in the deployment descriptors being processed.
+ *
+ */
+public class EjbcHelper {
+    /**
+     * The root directory of the tree containing the serialised deployment desciptors.
+     */
+    private File descriptorDirectory;
+
+    /**
+     * The directory where generated files are placed.
+     */
+    private File generatedFilesDirectory;
+
+    /**
+     * The name of the manifest file generated for the EJB jar.
+     */
+    private File manifestFile;
+
+    /**
+     * The source directory for the home and remote interfaces. This is used to determine if
+     * the generated deployment classes are out of date.
+     */
+    private File sourceDirectory;
+
+    /**
+     * The names of the serialised deployment descriptors
+     */
+    String[] descriptors;
+
+    private boolean keepGenerated;
+
+    /**
+     * Command line interface for the ejbc helper task.
+     */
+    public static void main(String[] args) throws Exception {
+        EjbcHelper helper = new EjbcHelper(args);
+        helper.process();
+    }
+
+    /**
+     * Initialise the EjbcHelper by reading the command arguments.
+     */
+    private EjbcHelper(String[] args) {
+        int index = 0;
+        descriptorDirectory = new File(args[index++]);
+        generatedFilesDirectory = new File(args[index++]);
+        sourceDirectory = new File(args[index++]);
+        manifestFile = new File(args[index++]);
+        keepGenerated = Boolean.valueOf(args[index++]).booleanValue();
+
+        descriptors = new String[args.length - index];
+        for (int i = 0; index < args.length; ++i) {
+            descriptors[i] = args[index++];
+        }
+    }
+
+    private String[] getCommandLine(boolean debug, File descriptorFile) {
+        Vector v = new Vector();
+        if (!debug) {
+            v.addElement("-noexit");
+        }
+        if (keepGenerated) {
+            v.addElement("-keepgenerated");
+        }
+        v.addElement("-d");
+        v.addElement(generatedFilesDirectory.getPath());
+        v.addElement(descriptorFile.getPath());
+
+        String[] args = new String[v.size()];
+        v.copyInto(args);
+        return args;
+    }
+
+    /**
+     * Determine if the weblogic EJB support classes need to be regenerated
+     * for a given deployment descriptor.
+     *
+     * This process attempts to determine if the support classes need to be
+     * rebuilt. It does this by examining only some of the support classes
+     * which are typically generated. If the ejbc task is interrupted generating
+     * the support classes for a bean, all of the support classes should be removed
+     * to force regeneration of the support classes.
+     *
+     * @param descriptorFile the serialised deployment descriptor
+     *
+     * @return true if the support classes need to be regenerated.
+     *
+     * @throws IOException if the descriptor file cannot be closed.
+     */
+    private boolean isRegenRequired(File descriptorFile) throws IOException {
+        // read in the descriptor. Under weblogic, the descriptor is a weblogic
+        // specific subclass which has references to the implementation classes.
+        // These classes must, therefore, be in the classpath when the deployment
+        // descriptor is loaded from the .ser file
+        FileInputStream fis = null;
+        try {
+            fis = new FileInputStream(descriptorFile);
+            ObjectInputStream ois = new ObjectInputStream(fis);
+            DeploymentDescriptor dd = (DeploymentDescriptor) ois.readObject();
+            fis.close();
+
+            String homeInterfacePath
+                = dd.getHomeInterfaceClassName().replace('.', '/') + ".java";
+            String remoteInterfacePath
+                = dd.getRemoteInterfaceClassName().replace('.', '/') + ".java";
+            String primaryKeyClassPath = null;
+            if (dd instanceof EntityDescriptor) {
+                primaryKeyClassPath
+                    = ((EntityDescriptor) dd).getPrimaryKeyClassName();
+                primaryKeyClassPath
+                    = primaryKeyClassPath.replace('.', '/') + ".java";;
+            }
+
+            File homeInterfaceSource = new File(sourceDirectory, homeInterfacePath);
+            File remoteInterfaceSource = new File(sourceDirectory, remoteInterfacePath);
+            File primaryKeyClassSource = null;
+            if (primaryKeyClassPath != null) {
+                primaryKeyClassSource = new File(sourceDirectory, remoteInterfacePath);
+            }
+
+            // are any of the above out of date.
+            // we find the implementation classes and see if they are older than any
+            // of the above or the .ser file itself.
+            String beanClassBase = dd.getEnterpriseBeanClassName().replace('.', '/');
+            File ejbImplentationClass
+                = new File(generatedFilesDirectory, beanClassBase + "EOImpl.class");
+            File homeImplementationClass
+                = new File(generatedFilesDirectory, beanClassBase + "HomeImpl.class");
+            File beanStubClass
+                = new File(generatedFilesDirectory, beanClassBase + "EOImpl_WLStub.class");
+
+            // if the implementation classes don;t exist regenerate
+            if (!ejbImplentationClass.exists()
+                || !homeImplementationClass.exists()
+                || !beanStubClass.exists()) {
+                return true;
+            }
+
+            // Is the ser file or any of the source files newer then the class files.
+            // firstly find the oldest of the two class files.
+            long classModificationTime = ejbImplentationClass.lastModified();
+            if (homeImplementationClass.lastModified() < classModificationTime) {
+                classModificationTime = homeImplementationClass.lastModified();
+            }
+            if (beanStubClass.lastModified() < classModificationTime) {
+                classModificationTime = beanStubClass.lastModified();
+            }
+
+            if (descriptorFile.lastModified() > classModificationTime
+                || homeInterfaceSource.lastModified() > classModificationTime
+                || remoteInterfaceSource.lastModified() > classModificationTime) {
+                return true;
+            }
+
+            if (primaryKeyClassSource != null
+                && primaryKeyClassSource.lastModified() > classModificationTime) {
+                return true;
+            }
+        } catch (Throwable descriptorLoadException) {
+            System.out.println("Exception occurred reading "
+                + descriptorFile.getName() + " - continuing");
+            // any problems - just regenerate
+            return true;
+        } finally {
+            if (fis != null) {
+                fis.close();
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Process the descriptors in turn generating support classes for each and a manifest
+     * file for all of the beans.
+     */
+    private void process() throws Exception {
+        String manifest = "Manifest-Version: 1.0\n\n";
+        for (int i = 0; i < descriptors.length; ++i) {
+            String descriptorName = descriptors[i];
+            File descriptorFile = new File(descriptorDirectory, descriptorName);
+
+            if (isRegenRequired(descriptorFile)) {
+                System.out.println("Running ejbc for " + descriptorFile.getName());
+                regenerateSupportClasses(descriptorFile);
+            } else {
+                System.out.println(descriptorFile.getName() + " is up to date");
+            }
+            manifest += "Name: " + descriptorName.replace('\\', '/')
+                        + "\nEnterprise-Bean: True\n\n";
+        }
+
+        FileWriter fw = new FileWriter(manifestFile);
+        PrintWriter pw = new PrintWriter(fw);
+        pw.print(manifest);
+        fw.flush();
+        fw.close();
+    }
+
+    /**
+     * Perform the weblogic.ejbc call to regenerate the support classes.
+     *
+     * Note that this method relies on an undocumented -noexit option to the
+     * ejbc tool to stop the ejbc tool exiting the VM altogether.
+     */
+    private void regenerateSupportClasses(File descriptorFile) throws Exception {
+        // create a Java task to do the rebuild
+
+
+        String[] args = getCommandLine(false, descriptorFile);
+
+        try {
+            weblogic.ejbc.main(args);
+        } catch (Exception e) {
+            // run with no exit for better reporting
+            String[] newArgs = getCommandLine(true, descriptorFile);
+            weblogic.ejbc.main(newArgs);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java	(revision 16139)
@@ -0,0 +1,919 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+import java.util.zip.ZipEntry;
+import javax.xml.parsers.SAXParser;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.depend.DependencyAnalyzer;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+
+/**
+ * A deployment tool which creates generic EJB jars. Generic jars contains
+ * only those classes and META-INF entries specified in the EJB 1.1 standard
+ *
+ * This class is also used as a framework for the creation of vendor specific
+ * deployment tools. A number of template methods are provided through which the
+ * vendor specific tool can hook into the EJB creation process.
+ *
+ */
+public class GenericDeploymentTool implements EJBDeploymentTool {
+    /** The standard META-INF directory in jar files */
+    protected static final String META_DIR  = "META-INF/";
+
+    /** The standard MANIFEST file */
+    protected static final String MANIFEST  = META_DIR + "MANIFEST.MF";
+
+    /** Name for EJB Deployment descriptor within EJB jars */
+    protected static final String EJB_DD    = "ejb-jar.xml";
+
+    /** A dependency analyzer name to find ancestor classes */
+    public static final String ANALYZER_SUPER = "super";
+    /** A dependency analyzer name to find all related classes */
+    public static final String ANALYZER_FULL = "full";
+    /** A dependency analyzer name for no analyzer */
+    public static final String ANALYZER_NONE = "none";
+
+    /** The default analyzer */
+    public static final String DEFAULT_ANALYZER = ANALYZER_SUPER;
+
+    /** The analyzer class for the super analyzer */
+    public static final String ANALYZER_CLASS_SUPER
+        = "org.apache.tools.ant.util.depend.bcel.AncestorAnalyzer";
+    /** The analyzer class for the super analyzer */
+    public static final String ANALYZER_CLASS_FULL
+        = "org.apache.tools.ant.util.depend.bcel.FullAnalyzer";
+
+    /**
+     * The configuration from the containing task. This config combined
+     * with the settings of the individual attributes here constitues the
+     * complete config for this deployment tool.
+     */
+    private EjbJar.Config config;
+
+    /** Stores a handle to the directory to put the Jar files in */
+    private File destDir;
+
+    /** The classpath to use with this deployment tool. This is appended to
+        any paths from the ejbjar task itself.*/
+    private Path classpath;
+
+    /** Instance variable that stores the suffix for the generated jarfile. */
+    private String genericJarSuffix = "-generic.jar";
+
+    /**
+     * The task to which this tool belongs. This is used to access services
+     * provided by the ant core, such as logging.
+     */
+    private Task task;
+
+    /**
+     * The classloader generated from the given classpath to load
+     * the super classes and super interfaces.
+     */
+    private ClassLoader classpathLoader = null;
+
+     /**
+     * List of files have been loaded into the EJB jar
+     */
+    private List addedfiles;
+
+    /**
+     * Handler used to parse the EJB XML descriptor
+     */
+    private DescriptorHandler handler;
+
+    /**
+     * Dependency analyzer used to collect class dependencies
+     */
+    private DependencyAnalyzer dependencyAnalyzer;
+
+    public GenericDeploymentTool() {
+    }
+
+
+    /**
+     * Set the destination directory; required.
+     * @param inDir the destination directory.
+     */
+    public void setDestdir(File inDir) {
+        this.destDir = inDir;
+    }
+
+    /**
+     * Get the destination directory.
+     *
+     * @return the destination directory into which EJB jars are to be written
+     */
+    protected File getDestDir() {
+        return destDir;
+    }
+
+
+    /**
+     * Set the task which owns this tool
+     *
+     * @param task the Task to which this deployment tool is associated.
+     */
+    public void setTask(Task task) {
+        this.task = task;
+    }
+
+    /**
+     * Get the task for this tool.
+     *
+     * @return the Task instance this tool is associated with.
+     */
+    protected Task getTask() {
+        return task;
+    }
+
+    /**
+     * Get the basename terminator.
+     *
+     * @return an ejbjar task configuration
+     */
+    protected EjbJar.Config getConfig() {
+        return config;
+    }
+
+    /**
+     * Indicate if this build is using the base jar name.
+     *
+     * @return true if the name of the generated jar is coming from the
+     *              basejarname attribute
+     */
+    protected boolean usingBaseJarName() {
+        return config.baseJarName != null;
+    }
+
+    /**
+     * Set the suffix for the generated jar file.
+     * @param inString the string to use as the suffix.
+     */
+    public void setGenericJarSuffix(String inString) {
+        this.genericJarSuffix = inString;
+    }
+
+    /**
+     * Add the classpath for the user classes
+     *
+     * @return a Path instance to be configured by Ant.
+     */
+    public Path createClasspath() {
+        if (classpath == null) {
+            classpath = new Path(task.getProject());
+        }
+        return classpath.createPath();
+    }
+
+    /**
+     * Set the classpath to be used for this compilation.
+     *
+     * @param classpath the classpath to be used for this build.
+     */
+    public void setClasspath(Path classpath) {
+        this.classpath = classpath;
+    }
+
+    /**
+     * Get the classpath by combining the one from the surrounding task, if any
+     * and the one from this tool.
+     *
+     * @return the combined classpath
+     */
+    protected Path getCombinedClasspath() {
+        Path combinedPath = classpath;
+        if (config.classpath != null) {
+            if (combinedPath == null) {
+                combinedPath = config.classpath;
+            } else {
+                combinedPath.append(config.classpath);
+            }
+        }
+
+        return combinedPath;
+    }
+
+    /**
+     * Log a message to the Ant output.
+     *
+     * @param message the message to be logged.
+     * @param level the severity of this message.
+     */
+    protected void log(String message, int level) {
+        getTask().log(message, level);
+    }
+
+    /**
+     * Get the build file location associated with this element's task.
+     *
+     * @return the task's location instance.
+     */
+    protected Location getLocation() {
+        return getTask().getLocation();
+    }
+
+    private void createAnalyzer() {
+        String analyzer = config.analyzer;
+        if (analyzer == null) {
+            analyzer = DEFAULT_ANALYZER;
+        }
+
+        if (analyzer.equals(ANALYZER_NONE)) {
+            return;
+        }
+
+        String analyzerClassName = null;
+        if (analyzer.equals(ANALYZER_SUPER)) {
+            analyzerClassName = ANALYZER_CLASS_SUPER;
+        } else if (analyzer.equals(ANALYZER_FULL)) {
+            analyzerClassName = ANALYZER_CLASS_FULL;
+        } else {
+            analyzerClassName = analyzer;
+        }
+
+        try {
+            Class analyzerClass = Class.forName(analyzerClassName);
+            dependencyAnalyzer
+                = (DependencyAnalyzer) analyzerClass.newInstance();
+            dependencyAnalyzer.addClassPath(new Path(task.getProject(),
+                config.srcDir.getPath()));
+            dependencyAnalyzer.addClassPath(config.classpath);
+        } catch (NoClassDefFoundError e) {
+            dependencyAnalyzer = null;
+            task.log("Unable to load dependency analyzer: " + analyzerClassName
+                + " - dependent class not found: " + e.getMessage(),
+                Project.MSG_WARN);
+        } catch (Exception e) {
+            dependencyAnalyzer = null;
+            task.log("Unable to load dependency analyzer: " + analyzerClassName
+                     + " - exception: " + e.getMessage(),
+                Project.MSG_WARN);
+        }
+    }
+
+
+    /**
+     * Configure this tool for use in the ejbjar task.
+     *
+     * @param config the configuration from the surrounding ejbjar task.
+     */
+    public void configure(EjbJar.Config config) {
+        this.config = config;
+
+        createAnalyzer();
+        classpathLoader = null;
+    }
+
+    /**
+     * Utility method that encapsulates the logic of adding a file entry to
+     * a .jar file.  Used by execute() to add entries to the jar file as it is
+     * constructed.
+     * @param jStream A JarOutputStream into which to write the
+     *        jar entry.
+     * @param inputFile A File from which to read the
+     *        contents the file being added.
+     * @param logicalFilename A String representing the name, including
+     *        all relevant path information, that should be stored for the entry
+     *        being added.
+     */
+    protected void addFileToJar(JarOutputStream jStream,
+                                File inputFile,
+                                String logicalFilename)
+        throws BuildException {
+        FileInputStream iStream = null;
+        try {
+            if (!addedfiles.contains(logicalFilename)) {
+                iStream = new FileInputStream(inputFile);
+                // Create the zip entry and add it to the jar file
+                ZipEntry zipEntry = new ZipEntry(logicalFilename.replace('\\', '/'));
+                jStream.putNextEntry(zipEntry);
+
+                // Create the file input stream, and buffer everything over
+                // to the jar output stream
+                byte[] byteBuffer = new byte[2 * 1024];
+                int count = 0;
+                do {
+                    jStream.write(byteBuffer, 0, count);
+                    count = iStream.read(byteBuffer, 0, byteBuffer.length);
+                } while (count != -1);
+
+                //add it to list of files in jar
+                addedfiles.add(logicalFilename);
+           }
+        } catch (IOException ioe) {
+            log("WARNING: IOException while adding entry "
+                + logicalFilename + " to jarfile from "
+                + inputFile.getPath() + " "  + ioe.getClass().getName()
+                + "-" + ioe.getMessage(), Project.MSG_WARN);
+        } finally {
+            // Close up the file input stream for the class file
+            if (iStream != null) {
+                try {
+                    iStream.close();
+                } catch (IOException closeException) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    protected DescriptorHandler getDescriptorHandler(File srcDir) {
+        DescriptorHandler handler = new DescriptorHandler(getTask(), srcDir);
+
+        registerKnownDTDs(handler);
+
+        // register any DTDs supplied by the user
+        for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) {
+            EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next();
+            handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation());
+        }
+        return handler;
+    }
+
+    /**
+     * Register the locations of all known DTDs.
+     *
+     * vendor-specific subclasses should override this method to define
+     * the vendor-specific locations of the EJB DTDs
+     */
+    protected void registerKnownDTDs(DescriptorHandler handler) {
+        // none to register for generic
+    }
+
+    public void processDescriptor(String descriptorFileName, SAXParser saxParser) {
+
+        checkConfiguration(descriptorFileName, saxParser);
+
+        try {
+            handler = getDescriptorHandler(config.srcDir);
+
+            // Retrive the files to be added to JAR from EJB descriptor
+            Hashtable ejbFiles = parseEjbFiles(descriptorFileName, saxParser);
+
+            // Add any support classes specified in the build file
+            addSupportClasses(ejbFiles);
+
+            // Determine the JAR filename (without filename extension)
+            String baseName = getJarBaseName(descriptorFileName);
+
+            String ddPrefix = getVendorDDPrefix(baseName, descriptorFileName);
+
+            File manifestFile = getManifestFile(ddPrefix);
+            if (manifestFile != null) {
+                ejbFiles.put(MANIFEST, manifestFile);
+            }
+
+
+
+            // First the regular deployment descriptor
+            ejbFiles.put(META_DIR + EJB_DD,
+                         new File(config.descriptorDir, descriptorFileName));
+
+            // now the vendor specific files, if any
+            addVendorFiles(ejbFiles, ddPrefix);
+
+            // add any dependent files
+            checkAndAddDependants(ejbFiles);
+
+            // Lastly create File object for the Jar files. If we are using
+            // a flat destination dir, then we need to redefine baseName!
+            if (config.flatDestDir && baseName.length() != 0) {
+                int startName = baseName.lastIndexOf(File.separator);
+                if (startName == -1) {
+                    startName = 0;
+                }
+
+                int endName   = baseName.length();
+                baseName = baseName.substring(startName, endName);
+            }
+
+            File jarFile = getVendorOutputJarFile(baseName);
+
+
+            // Check to see if we need a build and start doing the work!
+            if (needToRebuild(ejbFiles, jarFile)) {
+                // Log that we are going to build...
+                log("building "
+                              + jarFile.getName()
+                              + " with "
+                              + String.valueOf(ejbFiles.size())
+                              + " files",
+                              Project.MSG_INFO);
+
+                // Use helper method to write the jarfile
+                String publicId = getPublicId();
+                writeJar(baseName, jarFile, ejbFiles, publicId);
+
+            } else {
+                // Log that the file is up to date...
+                log(jarFile.toString() + " is up to date.",
+                              Project.MSG_VERBOSE);
+            }
+
+        } catch (SAXException se) {
+            String msg = "SAXException while parsing '"
+                + descriptorFileName.toString()
+                + "'. This probably indicates badly-formed XML."
+                + "  Details: "
+                + se.getMessage();
+            throw new BuildException(msg, se);
+        } catch (IOException ioe) {
+            String msg = "IOException while parsing'"
+                + descriptorFileName.toString()
+                + "'.  This probably indicates that the descriptor"
+                + " doesn't exist. Details: "
+                + ioe.getMessage();
+            throw new BuildException(msg, ioe);
+        }
+    }
+
+    /**
+     * This method is called as the first step in the processDescriptor method
+     * to allow vendor-specific subclasses to validate the task configuration
+     * prior to processing the descriptor.  If the configuration is invalid,
+     * a BuildException should be thrown.
+     *
+     * @param descriptorFileName String representing the file name of an EJB
+     *                           descriptor to be processed
+     * @param saxParser          SAXParser which may be used to parse the XML
+     *                           descriptor
+     * @exception BuildException     Thrown if the configuration is invalid
+     */
+    protected void checkConfiguration(String descriptorFileName,
+                                    SAXParser saxParser) throws BuildException {
+
+        /*
+         * For the GenericDeploymentTool, do nothing.  Vendor specific
+         * subclasses should throw a BuildException if the configuration is
+         * invalid for their server.
+         */
+    }
+
+    /**
+     * This method returns a list of EJB files found when the specified EJB
+     * descriptor is parsed and processed.
+     *
+     * @param descriptorFileName String representing the file name of an EJB
+     *                           descriptor to be processed
+     * @param saxParser          SAXParser which may be used to parse the XML
+     *                           descriptor
+     * @return                   Hashtable of EJB class (and other) files to be
+     *                           added to the completed JAR file
+     * @throws SAXException      Any SAX exception, possibly wrapping another
+     *                           exception
+     * @throws IOException       An IOException from the parser, possibly from a
+     *                           the byte stream or character stream
+     */
+    protected Hashtable parseEjbFiles(String descriptorFileName, SAXParser saxParser)
+                            throws IOException, SAXException {
+        FileInputStream descriptorStream = null;
+        Hashtable ejbFiles = null;
+
+        try {
+
+            /* Parse the ejb deployment descriptor.  While it may not
+             * look like much, we use a SAXParser and an inner class to
+             * get hold of all the classfile names for the descriptor.
+             */
+            descriptorStream
+                = new FileInputStream(new File(config.descriptorDir, descriptorFileName));
+            saxParser.parse(new InputSource(descriptorStream), handler);
+
+            ejbFiles = handler.getFiles();
+
+        } finally {
+            if (descriptorStream != null) {
+                try {
+                    descriptorStream.close();
+                } catch (IOException closeException) {
+                    // ignore
+                }
+            }
+        }
+
+        return ejbFiles;
+    }
+
+    /**
+     * Adds any classes the user specifies using <i>support</i> nested elements
+     * to the <code>ejbFiles</code> Hashtable.
+     *
+     * @param ejbFiles Hashtable of EJB classes (and other) files that will be
+     *                 added to the completed JAR file
+     */
+    protected void addSupportClasses(Hashtable ejbFiles) {
+        // add in support classes if any
+        Project project = task.getProject();
+        for (Iterator i = config.supportFileSets.iterator(); i.hasNext();) {
+            FileSet supportFileSet = (FileSet) i.next();
+            File supportBaseDir = supportFileSet.getDir(project);
+            DirectoryScanner supportScanner = supportFileSet.getDirectoryScanner(project);
+            supportScanner.scan();
+            String[] supportFiles = supportScanner.getIncludedFiles();
+            for (int j = 0; j < supportFiles.length; ++j) {
+                ejbFiles.put(supportFiles[j], new File(supportBaseDir, supportFiles[j]));
+            }
+        }
+    }
+
+
+    /**
+     * Using the EJB descriptor file name passed from the <code>ejbjar</code>
+     * task, this method returns the "basename" which will be used to name the
+     * completed JAR file.
+     *
+     * @param descriptorFileName String representing the file name of an EJB
+     *                           descriptor to be processed
+     * @return                   The "basename" which will be used to name the
+     *                           completed JAR file
+     */
+    protected String getJarBaseName(String descriptorFileName) {
+
+        String baseName = "";
+
+        // Work out what the base name is
+        if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.BASEJARNAME)) {
+            String canonicalDescriptor = descriptorFileName.replace('\\', '/');
+            int index = canonicalDescriptor.lastIndexOf('/');
+            if (index != -1) {
+                baseName = descriptorFileName.substring(0, index + 1);
+            }
+            baseName += config.baseJarName;
+        } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
+            int lastSeparatorIndex = descriptorFileName.lastIndexOf(File.separator);
+            int endBaseName = -1;
+            if (lastSeparatorIndex != -1) {
+                endBaseName = descriptorFileName.indexOf(config.baseNameTerminator,
+                                                            lastSeparatorIndex);
+            } else {
+                endBaseName = descriptorFileName.indexOf(config.baseNameTerminator);
+            }
+
+            if (endBaseName != -1) {
+                baseName = descriptorFileName.substring(0, endBaseName);
+            } else {
+                throw new BuildException("Unable to determine jar name "
+                    + "from descriptor \"" + descriptorFileName + "\"");
+            }
+        } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DIRECTORY)) {
+            File descriptorFile = new File(config.descriptorDir, descriptorFileName);
+            String path = descriptorFile.getAbsolutePath();
+            int lastSeparatorIndex
+                = path.lastIndexOf(File.separator);
+            if (lastSeparatorIndex == -1) {
+                throw new BuildException("Unable to determine directory name holding descriptor");
+            }
+            String dirName = path.substring(0, lastSeparatorIndex);
+            int dirSeparatorIndex = dirName.lastIndexOf(File.separator);
+            if (dirSeparatorIndex != -1) {
+                dirName = dirName.substring(dirSeparatorIndex + 1);
+            }
+
+            baseName = dirName;
+        } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.EJB_NAME)) {
+            baseName = handler.getEjbName();
+        }
+        return baseName;
+    }
+
+    /**
+     * Get the prefix for vendor deployment descriptors.
+     *
+     * This will contain the path and the start of the descriptor name,
+     * depending on the naming scheme
+     */
+    public String getVendorDDPrefix(String baseName, String descriptorFileName) {
+        String ddPrefix = null;
+
+        if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
+            ddPrefix = baseName + config.baseNameTerminator;
+        } else if (config.namingScheme.getValue().equals(EjbJar.NamingScheme.BASEJARNAME)
+            || config.namingScheme.getValue().equals(EjbJar.NamingScheme.EJB_NAME)
+            || config.namingScheme.getValue().equals(EjbJar.NamingScheme.DIRECTORY)) {
+            String canonicalDescriptor = descriptorFileName.replace('\\', '/');
+            int index = canonicalDescriptor.lastIndexOf('/');
+            if (index == -1) {
+                ddPrefix = "";
+            } else {
+                ddPrefix = descriptorFileName.substring(0, index + 1);
+            }
+        }
+        return ddPrefix;
+    }
+
+    /**
+     * Add any vendor specific files which should be included in the
+     * EJB Jar.
+     */
+    protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
+        // nothing to add for generic tool.
+    }
+
+
+    /**
+     * Get the vendor specific name of the Jar that will be output. The modification date
+     * of this jar will be checked against the dependent bean classes.
+     */
+    File getVendorOutputJarFile(String baseName) {
+        return new File(destDir, baseName + genericJarSuffix);
+    }
+
+    /**
+     * This method checks the timestamp on each file listed in the <code>
+     * ejbFiles</code> and compares them to the timestamp on the <code>jarFile
+     * </code>.  If the <code>jarFile</code>'s timestamp is more recent than
+     * each EJB file, <code>true</code> is returned.  Otherwise, <code>false
+     * </code> is returned.
+     * TODO: find a way to check the manifest-file, that is found by naming convention
+     *
+     * @param ejbFiles Hashtable of EJB classes (and other) files that will be
+     *                 added to the completed JAR file
+     * @param jarFile  JAR file which will contain all of the EJB classes (and
+     *                 other) files
+     * @return         boolean indicating whether or not the <code>jarFile</code>
+     *                 is up to date
+     */
+    protected boolean needToRebuild(Hashtable ejbFiles, File jarFile) {
+        if (jarFile.exists()) {
+            long lastBuild = jarFile.lastModified();
+
+            Iterator fileIter = ejbFiles.values().iterator();
+
+            // Loop through the files seeing if any has been touched
+            // more recently than the destination jar.
+            while (fileIter.hasNext()) {
+                File currentFile = (File) fileIter.next();
+                if (lastBuild < currentFile.lastModified()) {
+                    log("Build needed because " + currentFile.getPath() + " is out of date",
+                        Project.MSG_VERBOSE);
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * Returns the Public ID of the DTD specified in the EJB descriptor.  Not
+     * every vendor-specific <code>DeploymentTool</code> will need to reference
+     * this value or may want to determine this value in a vendor-specific way.
+     *
+     * @return Public ID of the DTD specified in the EJB descriptor.
+     */
+    protected String getPublicId() {
+        return handler.getPublicId();
+    }
+
+    /**
+     * Get the manifets file to use for building the generic jar.
+     *
+     * If the file does not exist the global manifest from the config is used
+     * otherwise the default Ant manifest will be used.
+     *
+     * @param prefix the prefix where to llook for the manifest file based on
+     *        the naming convention.
+     *
+     * @return the manifest file or null if the manifest file does not exist
+     */
+    protected File getManifestFile(String prefix) {
+        File manifestFile
+            = new File(getConfig().descriptorDir, prefix + "manifest.mf");
+        if (manifestFile.exists()) {
+            return manifestFile;
+        }
+
+        if (config.manifest != null) {
+            return config.manifest;
+        }
+        return null;
+    }
+
+    /**
+     * Method used to encapsulate the writing of the JAR file. Iterates over the
+     * filenames/java.io.Files in the Hashtable stored on the instance variable
+     * ejbFiles.
+     */
+    protected void writeJar(String baseName, File jarfile, Hashtable files,
+                            String publicId) throws BuildException {
+
+        JarOutputStream jarStream = null;
+        try {
+            // clean the addedfiles Vector
+            addedfiles = new ArrayList();
+
+            /* If the jarfile already exists then whack it and recreate it.
+             * Should probably think of a more elegant way to handle this
+             * so that in case of errors we don't leave people worse off
+             * than when we started =)
+             */
+            if (jarfile.exists()) {
+                jarfile.delete();
+            }
+            jarfile.getParentFile().mkdirs();
+            jarfile.createNewFile();
+
+            InputStream in = null;
+            Manifest manifest = null;
+            try {
+                File manifestFile = (File) files.get(MANIFEST);
+                if (manifestFile != null && manifestFile.exists()) {
+                    in = new FileInputStream(manifestFile);
+                } else {
+                    String defaultManifest = "/org/apache/tools/ant/defaultManifest.mf";
+                    in = this.getClass().getResourceAsStream(defaultManifest);
+                    if (in == null) {
+                        throw new BuildException("Could not find "
+                            + "default manifest: " + defaultManifest);
+                    }
+                }
+
+                manifest = new Manifest(in);
+            } catch (IOException e) {
+                throw new BuildException ("Unable to read manifest", e, getLocation());
+            } finally {
+                if (in != null) {
+                    in.close();
+                }
+            }
+
+            // Create the streams necessary to write the jarfile
+
+            jarStream = new JarOutputStream(new FileOutputStream(jarfile), manifest);
+            jarStream.setMethod(JarOutputStream.DEFLATED);
+
+            // Loop through all the class files found and add them to the jar
+            for (Iterator entryIterator = files.keySet().iterator(); entryIterator.hasNext();) {
+                String entryName = (String) entryIterator.next();
+                if (entryName.equals(MANIFEST)) {
+                    continue;
+                }
+
+                File entryFile = (File) files.get(entryName);
+
+                log("adding file '" + entryName + "'",
+                              Project.MSG_VERBOSE);
+
+                addFileToJar(jarStream, entryFile, entryName);
+
+                // See if there are any inner classes for this class and add them in if there are
+                InnerClassFilenameFilter flt = new InnerClassFilenameFilter(entryFile.getName());
+                File entryDir = entryFile.getParentFile();
+                String[] innerfiles = entryDir.list(flt);
+                if (innerfiles != null) {
+                    for (int i = 0, n = innerfiles.length; i < n; i++) {
+
+                        //get and clean up innerclass name
+                        int entryIndex = entryName.lastIndexOf(entryFile.getName()) - 1;
+                        if (entryIndex < 0) {
+                            entryName = innerfiles[i];
+                        } else {
+                            entryName = entryName.substring(0, entryIndex)
+                                + File.separatorChar + innerfiles[i];
+                        }
+                        // link the file
+                        entryFile = new File(config.srcDir, entryName);
+
+                        log("adding innerclass file '" + entryName + "'",
+                                Project.MSG_VERBOSE);
+
+                        addFileToJar(jarStream, entryFile, entryName);
+
+                    }
+                }
+            }
+        } catch (IOException ioe) {
+            String msg = "IOException while processing ejb-jar file '"
+                + jarfile.toString()
+                + "'. Details: "
+                + ioe.getMessage();
+            throw new BuildException(msg, ioe);
+        } finally {
+            if (jarStream != null) {
+                try {
+                    jarStream.close();
+                } catch (IOException closeException) {
+                    // ignore
+                }
+            }
+        }
+    } // end of writeJar
+
+
+    /**
+     * Add all available classes, that depend on Remote, Home, Bean, PK
+     * @param checkEntries files, that are extracted from the deployment descriptor
+     */
+    protected void checkAndAddDependants(Hashtable checkEntries)
+        throws BuildException {
+
+        if (dependencyAnalyzer == null) {
+            return;
+        }
+
+        dependencyAnalyzer.reset();
+
+        Iterator i = checkEntries.keySet().iterator();
+        while (i.hasNext()) {
+            String entryName = (String) i.next();
+            if (entryName.endsWith(".class")) {
+                String className = entryName.substring(0,
+                    entryName.length() - ".class".length());
+                className = className.replace(File.separatorChar, '/');
+                className = className.replace('/', '.');
+
+                dependencyAnalyzer.addRootClass(className);
+            }
+        }
+
+        Enumeration e = dependencyAnalyzer.getClassDependencies();
+
+        while (e.hasMoreElements()) {
+            String classname = (String) e.nextElement();
+            String location
+                = classname.replace('.', File.separatorChar) + ".class";
+            File classFile = new File(config.srcDir, location);
+            if (classFile.exists()) {
+                checkEntries.put(location, classFile);
+                log("dependent class: " + classname + " - " + classFile,
+                    Project.MSG_VERBOSE);
+            }
+        }
+    }
+
+
+    /**
+     * Returns a Classloader object which parses the passed in generic EjbJar classpath.
+     * The loader is used to dynamically load classes from javax.ejb.* and the classes
+     * being added to the jar.
+     *
+     */
+    protected ClassLoader getClassLoaderForBuild() {
+        if (classpathLoader != null) {
+            return classpathLoader;
+        }
+
+        Path combinedClasspath = getCombinedClasspath();
+
+        // only generate a new ClassLoader if we have a classpath
+        if (combinedClasspath == null) {
+            classpathLoader = getClass().getClassLoader();
+        } else {
+            classpathLoader
+                = getTask().getProject().createClassLoader(combinedClasspath);
+        }
+
+        return classpathLoader;
+    }
+
+    /**
+     * Called to validate that the tool parameters have been configured.
+     *
+     * @throws BuildException If the Deployment Tool's configuration isn't
+     *                        valid
+     */
+    public void validateConfigured() throws BuildException {
+        if ((destDir == null) || (!destDir.isDirectory())) {
+            String msg = "A valid destination directory must be specified "
+                            + "using the \"destdir\" attribute.";
+            throw new BuildException(msg, getLocation());
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetDeploymentTool.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetDeploymentTool.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetDeploymentTool.java	(revision 16139)
@@ -0,0 +1,382 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Hashtable;
+import javax.xml.parsers.SAXParser;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.xml.sax.SAXException;
+
+/**
+ * This class is used to generate iPlanet Application Server (iAS) 6.0 stubs and
+ * skeletons and build an EJB Jar file.  It is designed to be used with the Ant
+ * <code>ejbjar</code> task.  If only stubs and skeletons need to be generated
+ * (in other words, if no JAR file needs to be created), refer to the
+ * <code>iplanet-ejbc</code> task and the <code>IPlanetEjbcTask</code> class.
+ * <p>
+ * The following attributes may be specified by the user:
+ *   <ul>
+ *     <li><i>destdir</i> -- The base directory into which the generated JAR
+ *                           files will be written.  Each JAR file is written
+ *                           in directories which correspond to their location
+ *                           within the "descriptordir" namespace.  This is a
+ *                           required attribute.
+ *     <li><i>classpath</i> -- The classpath used when generating EJB stubs and
+ *                             skeletons.  This is an optional attribute (if
+ *                             omitted, the classpath specified in the "ejbjar"
+ *                             parent task will be used).  If specified, the
+ *                             classpath elements will be prepended to the
+ *                             classpath specified in the parent "ejbjar" task.
+ *                             Note that nested "classpath" elements may also be
+ *                             used.
+ *     <li><i>keepgenerated</i> -- Indicates whether or not the Java source
+ *                                 files which are generated by ejbc will be
+ *                                 saved or automatically deleted.  If "yes",
+ *                                 the source files will be retained.  This is
+ *                                 an optional attribute (if omitted, it
+ *                                 defaults to "no").
+ *     <li><i>debug</i> -- Indicates whether or not the ejbc utility should
+ *                         log additional debugging statements to the standard
+ *                         output.  If "yes", the additional debugging statements
+ *                         will be generated (if omitted, it defaults to "no").
+ *     <li><i>iashome</i> -- May be used to specify the "home" directory for
+ *                           this iPlanet Application server installation.  This
+ *                           is used to find the ejbc utility if it isn't
+ *                           included in the user's system path.  This is an
+ *                           optional attribute (if specified, it should refer
+ *                           to the <code>[install-location]/iplanet/ias6/ias
+ *                           </code> directory).  If omitted, the ejbc utility
+ *                           must be on the user's system path.
+ *     <li><i>suffix</i> -- String value appended to the JAR filename when
+ *                          creating each JAR.  This attribute is not required
+ *                          (if omitted, it defaults to ".jar").
+ *   </ul>
+ * <p>
+ * For each EJB descriptor found in the "ejbjar" parent task, this deployment
+ * tool will locate the three classes that comprise the EJB.  If these class
+ * files cannot be located in the specified <code>srcdir</code> directory, the
+ * task will fail.  The task will also attempt to locate the EJB stubs and
+ * skeletons in this directory.  If found, the timestamps on the stubs and
+ * skeletons will be checked to ensure they are up to date.  Only if these files
+ * cannot be found or if they are out of date will ejbc be called.
+ *
+ * @see    IPlanetEjbc
+ */
+public class IPlanetDeploymentTool extends GenericDeploymentTool {
+
+    /* Attributes set by the Ant build file */
+    private File    iashome;
+    private String  jarSuffix     = ".jar";
+    private boolean keepgenerated = false;
+    private boolean debug         = false;
+
+    /*
+     * Filenames of the standard EJB descriptor (which is passed to this class
+     * from the parent "ejbjar" task) and the iAS-specific EJB descriptor
+     * (whose name is determined by this class).  Both filenames are relative
+     * to the directory specified by the "srcdir" attribute in the ejbjar task.
+     */
+    private String  descriptorName;
+    private String  iasDescriptorName;
+
+    /*
+     * The displayName variable stores the value of the "display-name" element
+     * from the standard EJB descriptor.  As a future enhancement to this task,
+     * we may determine the name of the EJB JAR file using this display-name,
+     * but this has not be implemented yet.
+     */
+    private String  displayName;
+
+    /*
+     * Regardless of the name of the iAS-specific EJB descriptor file, it will
+     * written in the completed JAR file as "ias-ejb-jar.xml".  This is the
+     * naming convention implemented by iAS.
+     */
+    private static final String IAS_DD = "ias-ejb-jar.xml";
+
+    /**
+     * Setter method used to store the "home" directory of the user's iAS
+     * installation.  The directory specified should typically be
+     * <code>[install-location]/iplanet/ias6/ias</code>.
+     *
+     * @param iashome The home directory for the user's iAS installation.
+     */
+    public void setIashome(File iashome) {
+        this.iashome = iashome;
+    }
+
+    /**
+     * Setter method used to specify whether the Java source files generated by
+     * the ejbc utility should be saved or automatically deleted.
+     *
+     * @param keepgenerated boolean which, if <code>true</code>, indicates that
+     *                      Java source files generated by ejbc for the stubs
+     *                      and skeletons should be kept.
+     */
+    public void setKeepgenerated(boolean keepgenerated) {
+        this.keepgenerated = keepgenerated;
+    }
+
+    /**
+     * Sets whether or not debugging output will be generated when ejbc is
+     * executed.
+     *
+     * @param debug A boolean indicating if debugging output should be generated
+     */
+    public void setDebug(boolean debug) {
+        this.debug = debug;
+    }
+
+    /**
+     * Setter method used to specify the filename suffix (for example, ".jar")
+     * for the JAR files to be created.
+     *
+     * @param jarSuffix The string to use as the JAR filename suffix.
+     */
+    public void setSuffix(String jarSuffix) {
+        this.jarSuffix = jarSuffix;
+    }
+
+    /**
+     * Since iAS doesn't generate a "generic" JAR as part of its processing,
+     * this attribute is ignored and a warning message is displayed to the user.
+     *
+     * @param inString the string to use as the suffix.  This parameter is
+     *                 ignored.
+     */
+    public void setGenericJarSuffix(String inString) {
+        log("Since a generic JAR file is not created during processing, the "
+                + "iPlanet Deployment Tool does not support the "
+                + "\"genericjarsuffix\" attribute.  It will be ignored.",
+            Project.MSG_WARN);
+    }
+
+    public void processDescriptor(String descriptorName, SAXParser saxParser) {
+        this.descriptorName = descriptorName;
+        this.iasDescriptorName = null;
+
+        log("iPlanet Deployment Tool processing: " + descriptorName + " (and "
+                + getIasDescriptorName() + ")", Project.MSG_VERBOSE);
+
+        super.processDescriptor(descriptorName, saxParser);
+    }
+
+    /**
+     * Verifies that the user selections are valid.
+     *
+     * @param descriptorFileName String representing the file name of an EJB
+     *                           descriptor to be processed
+     * @param saxParser          SAXParser which may be used to parse the XML
+     *                           descriptor
+     * @throws BuildException If the user selections are invalid.
+     */
+    protected void checkConfiguration(String descriptorFileName,
+                                    SAXParser saxParser) throws BuildException {
+
+        int startOfName = descriptorFileName.lastIndexOf(File.separatorChar) + 1;
+        String stdXml = descriptorFileName.substring(startOfName);
+        if (stdXml.equals(EJB_DD) && (getConfig().baseJarName == null)) {
+            String msg = "No name specified for the completed JAR file.  The EJB"
+                            + " descriptor should be prepended with the JAR "
+                            + "name or it should be specified using the "
+                            + "attribute \"basejarname\" in the \"ejbjar\" task.";
+            throw new BuildException(msg, getLocation());
+        }
+
+        File iasDescriptor = new File(getConfig().descriptorDir,
+                                        getIasDescriptorName());
+        if ((!iasDescriptor.exists()) || (!iasDescriptor.isFile())) {
+            String msg = "The iAS-specific EJB descriptor ("
+                            + iasDescriptor + ") was not found.";
+            throw new BuildException(msg, getLocation());
+        }
+
+        if ((iashome != null) && (!iashome.isDirectory())) {
+            String msg = "If \"iashome\" is specified, it must be a valid "
+                            + "directory (it was set to " + iashome + ").";
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+    /**
+     * This method returns a list of EJB files found when the specified EJB
+     * descriptor is parsed and processed.
+     *
+     * @param descriptorFileName String representing the file name of an EJB
+     *                           descriptor to be processed
+     * @param saxParser          SAXParser which may be used to parse the XML
+     *                           descriptor
+     * @return                   Hashtable of EJB class (and other) files to be
+     *                           added to the completed JAR file
+     * @throws IOException       An IOException from the parser, possibly from
+     *                           the byte stream or character stream
+     * @throws SAXException      Any SAX exception, possibly wrapping another
+     *                           exception
+     */
+    protected Hashtable parseEjbFiles(String descriptorFileName,
+                         SAXParser saxParser) throws IOException, SAXException {
+
+        Hashtable files;
+
+        /* Build and populate an instance of the ejbc utility */
+        IPlanetEjbc ejbc = new IPlanetEjbc(
+                                    new File(getConfig().descriptorDir,
+                                                descriptorFileName),
+                                    new File(getConfig().descriptorDir,
+                                                getIasDescriptorName()),
+                                    getConfig().srcDir,
+                                    getCombinedClasspath().toString(),
+                                    saxParser);
+        ejbc.setRetainSource(keepgenerated);
+        ejbc.setDebugOutput(debug);
+        if (iashome != null) {
+            ejbc.setIasHomeDir(iashome);
+        }
+
+        /* Execute the ejbc utility -- stubs/skeletons are rebuilt, if needed */
+        try {
+            ejbc.execute();
+        } catch (IPlanetEjbc.EjbcException e) {
+            throw new BuildException("An error has occurred while trying to "
+                        + "execute the iAS ejbc utility", e, getLocation());
+        }
+
+        displayName    = ejbc.getDisplayName();
+        files          = ejbc.getEjbFiles();
+
+        /* Add CMP descriptors to the list of EJB files */
+        String[] cmpDescriptors = ejbc.getCmpDescriptors();
+        if (cmpDescriptors.length > 0) {
+            File baseDir = getConfig().descriptorDir;
+
+            int endOfPath = descriptorFileName.lastIndexOf(File.separator);
+            String relativePath = descriptorFileName.substring(0, endOfPath + 1);
+
+            for (int i = 0; i < cmpDescriptors.length; i++) {
+                int endOfCmp = cmpDescriptors[i].lastIndexOf('/');
+                String cmpDescriptor = cmpDescriptors[i].substring(endOfCmp + 1);
+
+                File   cmpFile = new File(baseDir, relativePath + cmpDescriptor);
+                if (!cmpFile.exists()) {
+                    throw new BuildException("The CMP descriptor file ("
+                            + cmpFile + ") could not be found.", getLocation());
+                }
+                files.put(cmpDescriptors[i], cmpFile);
+            }
+        }
+
+        return files;
+    }
+
+    /**
+     * Add the iAS-specific EJB descriptor to the list of files which will be
+     * written to the JAR file.
+     *
+     * @param ejbFiles Hashtable of EJB class (and other) files to be added to
+     *                 the completed JAR file.
+     * @param ddPrefix not used
+     */
+    protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
+        ejbFiles.put(META_DIR + IAS_DD, new File(getConfig().descriptorDir,
+                     getIasDescriptorName()));
+    }
+
+    /**
+     * Get the name of the Jar that will be written. The modification date
+     * of this jar will be checked against the dependent bean classes.
+     *
+     * @param baseName String name of the EJB JAR file to be written (without
+     *                 a filename extension).
+     *
+     * @return File representing the JAR file which will be written.
+     */
+    File getVendorOutputJarFile(String baseName) {
+        File jarFile = new File(getDestDir(), baseName + jarSuffix);
+        log("JAR file name: " + jarFile.toString(), Project.MSG_VERBOSE);
+        return jarFile;
+    }
+
+    /**
+     * The iAS ejbc utility doesn't require the Public ID of the descriptor's
+     * DTD for it to process correctly--this method always returns <code>null
+     * </code>.
+     *
+     * @return <code>null</code>.
+     */
+    protected String getPublicId() {
+        return null;
+    }
+
+    /**
+     * Determines the name of the iAS-specific EJB descriptor using the
+     * specified standard EJB descriptor name.  In general, the standard
+     * descriptor will be named "[basename]-ejb-jar.xml", and this method will
+     * return "[basename]-ias-ejb-jar.xml".
+     *
+     * @return The name of the iAS-specific EJB descriptor file.
+     */
+    private String getIasDescriptorName() {
+
+        /* Only calculate the descriptor name once */
+        if (iasDescriptorName != null) {
+            return iasDescriptorName;
+        }
+
+        String path = "";   // Directory path of the EJB descriptor
+        String basename;    // Filename appearing before name terminator
+        String remainder;   // Filename appearing after the name terminator
+
+        /* Find the end of the standard descriptor's relative path */
+        int startOfFileName = descriptorName.lastIndexOf(File.separatorChar);
+        if (startOfFileName != -1) {
+            path = descriptorName.substring(0, startOfFileName + 1);
+        }
+
+        /* Check to see if the standard name is used (there's no basename) */
+        if (descriptorName.substring(startOfFileName + 1).equals(EJB_DD)) {
+            basename = "";
+            remainder = EJB_DD;
+
+        } else {
+            int endOfBaseName = descriptorName.indexOf(
+                                                getConfig().baseNameTerminator,
+                                                startOfFileName);
+            /*
+             * Check for the odd case where the terminator and/or filename
+             * extension aren't found.  These will ensure "ias-" appears at the
+             * end of the name and before the '.' (if present).
+             */
+            if (endOfBaseName < 0) {
+                endOfBaseName = descriptorName.lastIndexOf('.') - 1;
+                if (endOfBaseName < 0) {
+                    endOfBaseName = descriptorName.length() - 1;
+                }
+            }
+
+            basename = descriptorName.substring(startOfFileName + 1,
+                                                endOfBaseName + 1);
+            remainder = descriptorName.substring(endOfBaseName + 1);
+        }
+
+        iasDescriptorName = path + basename + "ias-" + remainder;
+        return iasDescriptorName;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbc.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbc.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbc.java	(revision 16139)
@@ -0,0 +1,1483 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.xml.sax.AttributeList;
+import org.xml.sax.HandlerBase;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+/**
+ * Compiles EJB stubs and skeletons for the iPlanet Application
+ * Server (iAS).  The class will read a standard EJB descriptor (as well as an
+ * EJB descriptor specific to iPlanet Application Server) to identify one or
+ * more EJBs to process.  It will search for EJB "source" classes (the remote
+; * interface, home interface, and EJB implementation class) and the EJB stubs
+ * and skeletons in the specified destination directory.  Only if the stubs and
+ * skeletons cannot be found or if they're out of date will the iPlanet
+ * Application Server ejbc utility be run.
+ * <p>
+ * Because this class (and it's assorted inner classes) may be bundled into the
+ * iPlanet Application Server distribution at some point (and removed from the
+ * Ant distribution), the class has been written to be independent of all
+ * Ant-specific classes.  It is also for this reason (and to avoid cluttering
+ * the Apache Ant source files) that this utility has been packaged into a
+ * single source file.
+ * <p>
+ * For more information on Ant Tasks for iPlanet Application Server, see the
+ * <code>IPlanetDeploymentTool</code> and <code>IPlanetEjbcTask</code> classes.
+ *
+ * @see    IPlanetDeploymentTool
+ * @see    IPlanetEjbcTask
+ * @ant.task ignore="true"
+ */
+public class IPlanetEjbc {
+
+    /* Constants used for the "beantype" attribute */
+    private static final String ENTITY_BEAN       = "entity";
+    private static final String STATELESS_SESSION = "stateless";
+    private static final String STATEFUL_SESSION  = "stateful";
+
+    /* Filenames of the standard EJB descriptor and the iAS-specific descriptor */
+    private File        stdDescriptor;
+    private File        iasDescriptor;
+
+    /*
+     * Directory where "source" EJB files are stored and where stubs and
+     * skeletons will also be written.
+     */
+    private File        destDirectory;
+
+    /* Classpath used when the iAS ejbc is called */
+    private String      classpath;
+    private String[]    classpathElements;
+
+    /* Options passed to the iAS ejbc */
+    private boolean     retainSource = false;
+    private boolean     debugOutput  = false;
+
+    /* iAS installation directory (used if ejbc isn't on user's PATH) */
+    private File        iasHomeDir;
+
+    /* Parser and handler used to process both EJB descriptor files */
+    private SAXParser   parser;
+    private EjbcHandler handler = new EjbcHandler();
+
+    /*
+     * This Hashtable maintains a list of EJB class files processed by the ejbc
+     * utility (both "source" class files as well as stubs and skeletons). The
+     * key for the Hashtable is a String representing the path to the class file
+     * (relative to the destination directory).  The value for the Hashtable is
+     * a File object which reference the actual class file.
+     */
+    private Hashtable   ejbFiles     = new Hashtable();
+
+    /* Value of the display-name element read from the standard EJB descriptor */
+    private String      displayName;
+
+    /**
+     * Constructs an instance which may be used to process EJB descriptors and
+     * generate EJB stubs and skeletons, if needed.
+     *
+     * @param stdDescriptor File referencing a standard EJB descriptor.
+     * @param iasDescriptor File referencing an iAS-specific EJB descriptor.
+     * @param destDirectory File referencing the base directory where both
+     *                      EJB "source" files are found and where stubs and
+     *                      skeletons will be written.
+     * @param classpath     String representation of the classpath to be used
+     *                      by the iAS ejbc utility.
+     * @param parser        SAXParser to be used to process both of the EJB
+     *                      descriptors.
+     * @todo classpathElements is not needed here, its never used
+     *       (at least IDEA tells me so! :)
+     */
+    public IPlanetEjbc(File stdDescriptor,
+                       File iasDescriptor,
+                       File destDirectory,
+                       String classpath,
+                       SAXParser parser) {
+        this.stdDescriptor = stdDescriptor;
+        this.iasDescriptor      = iasDescriptor;
+        this.destDirectory      = destDirectory;
+        this.classpath          = classpath;
+        this.parser             = parser;
+
+        /*
+         * Parse the classpath into it's individual elements and store the
+         * results in the "classpathElements" instance variable.
+         */
+        List elements = new ArrayList();
+        if (classpath != null) {
+            StringTokenizer st = new StringTokenizer(classpath,
+                                                        File.pathSeparator);
+            while (st.hasMoreTokens()) {
+                elements.add(st.nextToken());
+            }
+            classpathElements
+                    = (String[]) elements.toArray(new String[elements.size()]);
+        }
+    }
+
+    /**
+     * If true, the Java source files which are generated by the
+     * ejbc process are retained.
+     *
+     * @param retainSource A boolean indicating if the Java source files for
+     *                     the stubs and skeletons should be retained.
+     * @todo This is not documented in the HTML. On purpose?
+     */
+    public void setRetainSource(boolean retainSource) {
+        this.retainSource = retainSource;
+    }
+
+    /**
+     * If true, enables debugging output when ejbc is executed.
+     *
+     * @param debugOutput A boolean indicating if debugging output should be
+     *                    generated
+     */
+    public void setDebugOutput(boolean debugOutput) {
+        this.debugOutput = debugOutput;
+    }
+
+    /**
+     * Registers the location of a local DTD file or resource.  By registering
+     * a local DTD, EJB descriptors can be parsed even when the remote servers
+     * which contain the "public" DTDs cannot be accessed.
+     *
+     * @param publicID The public DTD identifier found in an XML document.
+     * @param location The file or resource name for the appropriate DTD stored
+     *                 on the local machine.
+     */
+    public void registerDTD(String publicID, String location) {
+        handler.registerDTD(publicID, location);
+    }
+
+    /**
+     * May be used to specify the "home" directory for this iAS installation.
+     * The directory specified should typically be
+     * <code>[install-location]/iplanet/ias6/ias</code>.
+     *
+     * @param iasHomeDir The home directory for the user's iAS installation.
+     */
+    public void setIasHomeDir(File iasHomeDir) {
+        this.iasHomeDir = iasHomeDir;
+    }
+
+    /**
+     * Returns a Hashtable which contains a list of EJB class files processed by
+     * the ejbc utility (both "source" class files as well as stubs and
+     * skeletons). The key for the Hashtable is a String representing the path
+     * to the class file (relative to the destination directory).  The value for
+     * the Hashtable is a File object which reference the actual class file.
+     *
+     * @return The list of EJB files processed by the ejbc utility.
+     */
+    public Hashtable getEjbFiles() {
+        return ejbFiles;
+    }
+
+    /**
+     * Returns the display-name element read from the standard EJB descriptor.
+     *
+     * @return The EJB-JAR display name.
+     */
+    public String getDisplayName() {
+        return displayName;
+    }
+
+    /**
+     * Returns the list of CMP descriptors referenced in the EJB descriptors.
+     *
+     * @return An array of CMP descriptors.
+     */
+    public String[] getCmpDescriptors() {
+        List returnList = new ArrayList();
+
+        EjbInfo[] ejbs = handler.getEjbs();
+
+        for (int i = 0; i < ejbs.length; i++) {
+            List descriptors = (List) ejbs[i].getCmpDescriptors();
+            returnList.addAll(descriptors);
+        }
+
+        return (String[]) returnList.toArray(new String[returnList.size()]);
+    }
+
+    /**
+     * Main application method for the iPlanet Application Server ejbc utility.
+     * If the application is run with no commandline arguments, a usage
+     * statement is printed for the user.
+     *
+     * @param args The commandline arguments passed to the application.
+     */
+    public static void main(String[] args) {
+        File        stdDescriptor;
+        File        iasDescriptor;
+        File        destDirectory = null;
+        String      classpath     = null;
+        SAXParser   parser        = null;
+        boolean     debug         = false;
+        boolean     retainSource  = false;
+        IPlanetEjbc ejbc;
+
+        if ((args.length < 2) || (args.length > 8)) {
+            usage();
+            return;
+        }
+
+        stdDescriptor = new File(args[args.length - 2]);
+        iasDescriptor = new File(args[args.length - 1]);
+
+        for (int i = 0; i < args.length - 2; i++) {
+            if (args[i].equals("-classpath")) {
+                classpath = args[++i];
+            } else if (args[i].equals("-d")) {
+                destDirectory = new File(args[++i]);
+            } else if (args[i].equals("-debug")) {
+                debug = true;
+            } else if (args[i].equals("-keepsource")) {
+                retainSource = true;
+            } else {
+                usage();
+                return;
+            }
+        }
+
+        /* If the -classpath flag isn't specified, use the system classpath */
+        if (classpath == null) {
+            Properties props = System.getProperties();
+            classpath = props.getProperty("java.class.path");
+        }
+
+        /*
+         * If the -d flag isn't specified, use the working directory as the
+         * destination directory
+         */
+        if (destDirectory == null) {
+            Properties props = System.getProperties();
+            destDirectory = new File(props.getProperty("user.dir"));
+        }
+
+        /* Construct a SAXParser used to process the descriptors */
+        SAXParserFactory parserFactory = SAXParserFactory.newInstance();
+        parserFactory.setValidating(true);
+        try {
+            parser = parserFactory.newSAXParser();
+        } catch (Exception e) {
+            // SAXException or ParserConfigurationException may be thrown
+            System.out.println("An exception was generated while trying to ");
+            System.out.println("create a new SAXParser.");
+            e.printStackTrace();
+            return;
+        }
+
+        /* Build and populate an instance of the ejbc utility */
+        ejbc = new IPlanetEjbc(stdDescriptor, iasDescriptor, destDirectory,
+                                classpath, parser);
+        ejbc.setDebugOutput(debug);
+        ejbc.setRetainSource(retainSource);
+
+        /* Execute the ejbc utility -- stubs/skeletons are rebuilt, if needed */
+        try {
+            ejbc.execute();
+        } catch (IOException e) {
+            System.out.println("An IOException has occurred while reading the "
+                    + "XML descriptors (" + e.getMessage() + ").");
+            return;
+        } catch (SAXException e) {
+            System.out.println("A SAXException has occurred while reading the "
+                    + "XML descriptors (" + e.getMessage() + ").");
+            return;
+        } catch (IPlanetEjbc.EjbcException e) {
+            System.out.println("An error has occurred while executing the ejbc "
+                    + "utility (" + e.getMessage() + ").");
+            return;
+        }
+    }
+
+    /**
+     * Print a usage statement.
+     */
+    private static void usage() {
+        System.out.println("java org.apache.tools.ant.taskdefs.optional.ejb.IPlanetEjbc \\");
+        System.out.println("  [OPTIONS] [EJB 1.1 descriptor] [iAS EJB descriptor]");
+        System.out.println("");
+        System.out.println("Where OPTIONS are:");
+        System.out.println("  -debug -- for additional debugging output");
+        System.out.println("  -keepsource -- to retain Java source files generated");
+        System.out.println("  -classpath [classpath] -- classpath used for compilation");
+        System.out.println("  -d [destination directory] -- directory for compiled classes");
+        System.out.println("");
+        System.out.println("If a classpath is not specified, the system classpath");
+        System.out.println("will be used.  If a destination directory is not specified,");
+        System.out.println("the current working directory will be used (classes will");
+        System.out.println("still be placed in subfolders which correspond to their");
+        System.out.println("package name).");
+        System.out.println("");
+        System.out.println("The EJB home interface, remote interface, and implementation");
+        System.out.println("class must be found in the destination directory.  In");
+        System.out.println("addition, the destination will look for the stubs and skeletons");
+        System.out.println("in the destination directory to ensure they are up to date.");
+    }
+
+    /**
+     * Compiles the stub and skeletons for the specified EJBs, if they need to
+     * be updated.
+     *
+     * @throws EjbcException If the ejbc utility cannot be correctly configured
+     *                       or if one or more of the EJB "source" classes
+     *                       cannot be found in the destination directory
+     * @throws IOException   If the parser encounters a problem reading the XML
+     *                       file
+     * @throws SAXException  If the parser encounters a problem processing the
+     *                       XML descriptor (it may wrap another exception)
+     */
+    public void execute() throws EjbcException, IOException, SAXException {
+
+        checkConfiguration();   // Throws EjbcException if unsuccessful
+
+        EjbInfo[] ejbs = getEjbs(); // Returns list of EJBs for processing
+
+        for (int i = 0; i < ejbs.length; i++) {
+            log("EJBInfo...");
+            log(ejbs[i].toString());
+        }
+
+        for (int i = 0; i < ejbs.length; i++) {
+            EjbInfo ejb = ejbs[i];
+
+            ejb.checkConfiguration(destDirectory);  // Throws EjbcException
+
+            if (ejb.mustBeRecompiled(destDirectory)) {
+                log(ejb.getName() + " must be recompiled using ejbc.");
+
+                String[] arguments = buildArgumentList(ejb);
+                callEjbc(arguments);
+
+            } else {
+                log(ejb.getName() + " is up to date.");
+            }
+        }
+    }
+
+    /**
+     * Executes the iPlanet Application Server ejbc command-line utility.
+     *
+     * @param arguments Command line arguments to be passed to the ejbc utility.
+     */
+    private void callEjbc(String[] arguments) {
+
+        /* Concatenate all of the command line arguments into a single String */
+        StringBuffer args = new StringBuffer();
+        for (int i = 0; i < arguments.length; i++) {
+            args.append(arguments[i]).append(" ");
+        }
+
+        /* If an iAS home directory is specified, prepend it to the commmand */
+        String command;
+        if (iasHomeDir == null) {
+            command = "";
+        } else {
+            command = iasHomeDir.toString() + File.separator + "bin"
+                                                        + File.separator;
+        }
+        command += "ejbc ";
+
+        log(command + args);
+
+        /*
+         * Use the Runtime object to execute an external command.  Use the
+         * RedirectOutput inner class to direct the standard and error output
+         * from the command to the JRE's standard output
+         */
+        try {
+            Process p = Runtime.getRuntime().exec(command + args);
+            RedirectOutput output = new RedirectOutput(p.getInputStream());
+            RedirectOutput error  = new RedirectOutput(p.getErrorStream());
+            output.start();
+            error.start();
+            p.waitFor();
+            p.destroy();
+        } catch (IOException e) {
+            log("An IOException has occurred while trying to execute ejbc.");
+            e.printStackTrace();
+        } catch (InterruptedException e) {
+            // Do nothing
+        }
+    }
+
+    /**
+     * Verifies that the user selections are valid.
+     *
+     * @throws EjbcException If the user selections are invalid.
+     */
+    protected void checkConfiguration() throws EjbcException {
+
+        String msg = "";
+
+        if (stdDescriptor == null) {
+            msg += "A standard XML descriptor file must be specified.  ";
+        }
+        if (iasDescriptor == null) {
+            msg += "An iAS-specific XML descriptor file must be specified.  ";
+        }
+        if (classpath == null) {
+            msg += "A classpath must be specified.    ";
+        }
+        if (parser == null) {
+            msg += "An XML parser must be specified.    ";
+        }
+
+        if (destDirectory == null) {
+            msg += "A destination directory must be specified.  ";
+        } else if (!destDirectory.exists()) {
+            msg += "The destination directory specified does not exist.  ";
+        } else if (!destDirectory.isDirectory()) {
+            msg += "The destination specified is not a directory.  ";
+        }
+
+        if (msg.length() > 0) {
+            throw new EjbcException(msg);
+        }
+    }
+
+    /**
+     * Parses the EJB descriptors and returns a list of EJBs which may need to
+     * be compiled.
+     *
+     * @return               An array of objects which describe the EJBs to be
+     *                       processed.
+     * @throws IOException   If the parser encounters a problem reading the XML
+     *                       files
+     * @throws SAXException  If the parser encounters a problem processing the
+     *                       XML descriptor (it may wrap another exception)
+     */
+    private EjbInfo[] getEjbs() throws IOException, SAXException {
+        EjbInfo[] ejbs = null;
+
+        /*
+         * The EJB information is gathered from the standard XML EJB descriptor
+         * and the iAS-specific XML EJB descriptor using a SAX parser.
+         */
+
+        parser.parse(stdDescriptor, handler);
+        parser.parse(iasDescriptor, handler);
+        ejbs = handler.getEjbs();
+
+        return ejbs;
+    }
+
+    /**
+     * Based on this object's instance variables as well as the EJB to be
+     * processed, the correct flags and parameters are set for the ejbc
+     * command-line utility.
+     * @param ejb The EJB for which stubs and skeletons will be compiled.
+     * @return    An array of Strings which are the command-line parameters for
+     *            for the ejbc utility.
+     */
+    private String[] buildArgumentList(EjbInfo ejb) {
+
+        List arguments = new ArrayList();
+
+        /* OPTIONAL COMMAND LINE PARAMETERS */
+
+        if (debugOutput) {
+            arguments.add("-debug");
+        }
+
+        /* No beantype flag is needed for an entity bean */
+        if (ejb.getBeantype().equals(STATELESS_SESSION)) {
+            arguments.add("-sl");
+        } else if (ejb.getBeantype().equals(STATEFUL_SESSION)) {
+            arguments.add("-sf");
+        }
+
+        if (ejb.getIiop()) {
+            arguments.add("-iiop");
+        }
+
+        if (ejb.getCmp()) {
+            arguments.add("-cmp");
+        }
+
+        if (retainSource) {
+            arguments.add("-gs");
+        }
+
+        if (ejb.getHasession()) {
+            arguments.add("-fo");
+        }
+
+        /* REQUIRED COMMAND LINE PARAMETERS */
+
+        arguments.add("-classpath");
+        arguments.add(classpath);
+
+        arguments.add("-d");
+        arguments.add(destDirectory.toString());
+
+        arguments.add(ejb.getHome().getQualifiedClassName());
+        arguments.add(ejb.getRemote().getQualifiedClassName());
+        arguments.add(ejb.getImplementation().getQualifiedClassName());
+
+        /* Convert the List into an Array and return it */
+        return (String[]) arguments.toArray(new String[arguments.size()]);
+    }
+
+    /**
+     * Convenience method used to print messages to the user if debugging
+     * messages are enabled.
+     *
+     * @param msg The String to print to standard output.
+     */
+    private void log(String msg) {
+        if (debugOutput) {
+            System.out.println(msg);
+        }
+    }
+
+
+    /* Inner classes follow */
+
+
+    /**
+     * This inner class is used to signal any problems during the execution of
+     * the ejbc compiler.
+     *
+     */
+    public class EjbcException extends Exception {
+
+        /**
+         * Constructs an exception with the given descriptive message.
+         *
+         * @param msg Description of the exception which has occurred.
+         */
+        public EjbcException(String msg) {
+            super(msg);
+        }
+    }  // End of EjbcException inner class
+
+
+    /**
+     * This inner class is an XML document handler that can be used to parse EJB
+     * descriptors (both the standard EJB descriptor as well as the iAS-specific
+     * descriptor that stores additional values for iAS).  Once the descriptors
+     * have been processed, the list of EJBs found can be obtained by calling
+     * the <code>getEjbs()</code> method.
+     *
+     * @see    IPlanetEjbc.EjbInfo
+     */
+    private class EjbcHandler extends HandlerBase {
+
+        /*
+         * Two Maps are used to track local DTDs that will be used in case the
+         * remote copies of these DTDs cannot be accessed.  The key for the Map
+         * is the DTDs public ID and the value is the local location for the DTD
+         */
+        private Map       resourceDtds = new HashMap();
+        private Map       fileDtds = new HashMap();
+
+        private Map       ejbs = new HashMap();      // List of EJBs found in XML
+        private EjbInfo   currentEjb;             // One item within the Map
+        private boolean   iasDescriptor = false;  // Is doc iAS or EJB descriptor
+
+        private String    currentLoc = "";        // Tracks current element
+        private String    currentText;            // Tracks current text data
+        private String    ejbType;                // "session" or "entity"
+
+        /**
+         * Constructs a new instance of the handler and registers local copies
+         * of the standard EJB 1.1 descriptor DTD as well as iAS's EJB
+         * descriptor DTD.
+         */
+        public EjbcHandler() {
+            final String PUBLICID_EJB11 =
+                "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN";
+            final String PUBLICID_IPLANET_EJB_60 =
+                "-//Sun Microsystems, Inc.//DTD iAS Enterprise JavaBeans 1.0//EN";
+
+
+            final String DEFAULT_IAS60_EJB11_DTD_LOCATION =
+                "ejb-jar_1_1.dtd";
+            final String DEFAULT_IAS60_DTD_LOCATION =
+                "IASEjb_jar_1_0.dtd";
+
+            registerDTD(PUBLICID_EJB11, DEFAULT_IAS60_EJB11_DTD_LOCATION);
+            registerDTD(PUBLICID_IPLANET_EJB_60, DEFAULT_IAS60_DTD_LOCATION);
+        }
+
+        /**
+         * Returns the list of EJB objects found during the processing of the
+         * standard EJB 1.1 descriptor and iAS-specific EJB descriptor.
+         *
+         * @return An array of EJBs which were found during the descriptor
+         *         parsing.
+         */
+        public EjbInfo[] getEjbs() {
+            return (EjbInfo[]) ejbs.values().toArray(new EjbInfo[ejbs.size()]);
+        }
+
+        /**
+         * Returns the value of the display-name element found in the standard
+         * EJB 1.1 descriptor.
+         *
+         * @return String display-name value.
+         */
+        public String getDisplayName() {
+            return displayName;
+        }
+
+        /**
+         * Registers a local DTD that will be used when parsing an EJB
+         * descriptor.  When the DTD's public identifier is found in an XML
+         * document, the parser will reference the local DTD rather than the
+         * remote DTD.  This enables XML documents to be processed even when the
+         * public DTD isn't available.
+         *
+         * @param publicID The DTD's public identifier.
+         * @param location The location of the local DTD copy -- the location
+         *                 may either be a resource found on the classpath or a
+         *                 local file.
+         */
+        public void registerDTD(String publicID, String location) {
+            log("Registering: " + location);
+            if ((publicID == null) || (location == null)) {
+                return;
+            }
+
+            if (ClassLoader.getSystemResource(location) != null) {
+                log("Found resource: " + location);
+                resourceDtds.put(publicID, location);
+            } else {
+                File dtdFile = new File(location);
+                if (dtdFile.exists() && dtdFile.isFile()) {
+                    log("Found file: " + location);
+                    fileDtds.put(publicID, location);
+                }
+            }
+        }
+
+        /**
+         * Resolves an external entity found during XML processing.  If a public
+         * ID is found that has been registered with the handler, an <code>
+         * InputSource</code> will be returned which refers to the local copy.
+         * If the public ID hasn't been registered or if an error occurs, the
+         * superclass implementation is used.
+         *
+         * @param publicId The DTD's public identifier.
+         * @param systemId The location of the DTD, as found in the XML document.
+         */
+        public InputSource resolveEntity(String publicId, String systemId)
+                throws SAXException {
+            InputStream inputStream = null;
+
+
+            try {
+
+                /* Search the resource Map and (if not found) file Map */
+
+                String location = (String) resourceDtds.get(publicId);
+                if (location != null) {
+                    inputStream
+                        = ClassLoader.getSystemResource(location).openStream();
+                } else {
+                    location = (String) fileDtds.get(publicId);
+                    if (location != null) {
+                        inputStream = new FileInputStream(location);
+                    }
+                }
+            } catch (IOException e) {
+                return super.resolveEntity(publicId, systemId);
+            }
+
+            if (inputStream == null) {
+                return super.resolveEntity(publicId, systemId);
+            } else {
+                return new InputSource(inputStream);
+            }
+        }
+
+        /**
+         * Receive notification that the start of an XML element has been found.
+         *
+         * @param name String name of the element found.
+         * @param atts AttributeList of the attributes included with the element
+         *             (if any).
+         * @throws SAXException If the parser cannot process the document.
+         */
+        public void startElement(String name, AttributeList atts)
+                throws SAXException {
+
+            /*
+             * I need to "push" the element onto the String (currentLoc) which
+             * always represents the current location in the XML document.
+             */
+            currentLoc += "\\" + name;
+
+            /* A new element has started, so reset the text being captured */
+            currentText = "";
+
+            if (currentLoc.equals("\\ejb-jar")) {
+                iasDescriptor = false;
+            } else if (currentLoc.equals("\\ias-ejb-jar")) {
+                iasDescriptor = true;
+            }
+
+            if ((name.equals("session")) || (name.equals("entity"))) {
+                ejbType = name;
+            }
+        }
+
+        /**
+         * Receive notification that character data has been found in the XML
+         * document
+         *
+         * @param ch Array of characters which have been found in the document.
+         * @param start Starting index of the data found in the document.
+         * @param len The number of characters found in the document.
+         * @throws SAXException If the parser cannot process the document.
+         */
+        public void characters(char[] ch, int start, int len)
+                throws SAXException {
+
+            currentText += new String(ch).substring(start, start + len);
+        }
+
+        /**
+         * Receive notification that the end of an XML element has been found.
+         *
+         * @param name String name of the element.
+         * @throws SAXException If the parser cannot process the document.
+         */
+        public void endElement(String name) throws SAXException {
+
+            /*
+             * If this is a standard EJB 1.1 descriptor, we are looking for one
+             * set of data, while if this is an iAS-specific descriptor, we're
+             * looking for different set of data.  Hand the processing off to
+             * the appropriate method.
+             */
+            if (iasDescriptor) {
+                iasCharacters(currentText);
+            } else {
+                stdCharacters(currentText);
+            }
+
+            /*
+             * I need to "pop" the element off the String (currentLoc) which
+             * always represents my current location in the XML document.
+             */
+
+            int nameLength = name.length() + 1; // Add one for the "\"
+            int locLength  = currentLoc.length();
+
+            currentLoc = currentLoc.substring(0, locLength - nameLength);
+        }
+
+        /**
+         * Receive notification that character data has been found in a standard
+         * EJB 1.1 descriptor.  We're interested in retrieving the home
+         * interface, remote interface, implementation class, the type of bean,
+         * and if the bean uses CMP.
+         *
+         * @param value String data found in the XML document.
+         */
+        private void stdCharacters(String value) {
+
+            if (currentLoc.equals("\\ejb-jar\\display-name")) {
+                displayName = value;
+                return;
+            }
+
+            String base = "\\ejb-jar\\enterprise-beans\\" + ejbType;
+
+            if (currentLoc.equals(base + "\\ejb-name")) {
+                currentEjb = (EjbInfo) ejbs.get(value);
+                if (currentEjb == null) {
+                    currentEjb = new EjbInfo(value);
+                    ejbs.put(value, currentEjb);
+                }
+            } else if (currentLoc.equals(base + "\\home")) {
+                currentEjb.setHome(value);
+            } else if (currentLoc.equals(base + "\\remote")) {
+                currentEjb.setRemote(value);
+            } else if (currentLoc.equals(base + "\\ejb-class")) {
+                currentEjb.setImplementation(value);
+            } else if (currentLoc.equals(base + "\\prim-key-class")) {
+                currentEjb.setPrimaryKey(value);
+            } else if (currentLoc.equals(base + "\\session-type")) {
+                currentEjb.setBeantype(value);
+            } else if (currentLoc.equals(base + "\\persistence-type")) {
+                currentEjb.setCmp(value);
+            }
+        }
+
+        /**
+         * Receive notification that character data has been found in an
+         * iAS-specific descriptor.  We're interested in retrieving data
+         * indicating whether the bean must support RMI/IIOP access, whether
+         * the bean must provide highly available stubs and skeletons (in the
+         * case of stateful session beans), and if this bean uses additional
+         * CMP XML descriptors (in the case of entity beans with CMP).
+         *
+         * @param value String data found in the XML document.
+         */
+        private void iasCharacters(String value) {
+            String base = "\\ias-ejb-jar\\enterprise-beans\\" + ejbType;
+
+            if (currentLoc.equals(base + "\\ejb-name")) {
+                currentEjb = (EjbInfo) ejbs.get(value);
+                if (currentEjb == null) {
+                    currentEjb = new EjbInfo(value);
+                    ejbs.put(value, currentEjb);
+                }
+            } else if (currentLoc.equals(base + "\\iiop")) {
+                currentEjb.setIiop(value);
+            } else if (currentLoc.equals(base + "\\failover-required")) {
+                currentEjb.setHasession(value);
+            } else if (currentLoc.equals(base + "\\persistence-manager"
+                                              + "\\properties-file-location")) {
+                currentEjb.addCmpDescriptor(value);
+            }
+        }
+    }  // End of EjbcHandler inner class
+
+
+    /**
+     * This inner class represents an EJB that will be compiled using ejbc.
+     *
+     */
+    private class EjbInfo {
+        private String     name;              // EJB's display name
+        private Classname  home;              // EJB's home interface name
+        private Classname  remote;            // EJB's remote interface name
+        private Classname  implementation;      // EJB's implementation class
+        private Classname  primaryKey;        // EJB's primary key class
+        private String  beantype = "entity";  // or "stateful" or "stateless"
+        private boolean cmp       = false;      // Does this EJB support CMP?
+        private boolean iiop      = false;      // Does this EJB support IIOP?
+        private boolean hasession = false;      // Does this EJB require failover?
+        private List cmpDescriptors = new ArrayList();  // CMP descriptor list
+
+        /**
+         * Construct a new EJBInfo object with the given name.
+         *
+         * @param name The display name for the EJB.
+         */
+        public EjbInfo(String name) {
+            this.name = name;
+        }
+
+        /**
+         * Returns the display name of the EJB.  If a display name has not been
+         * set, it returns the EJB implementation classname (if the
+         * implementation class is not set, it returns "[unnamed]").
+         *
+         * @return The display name for the EJB.
+         */
+        public String getName() {
+            if (name == null) {
+                if (implementation == null) {
+                    return "[unnamed]";
+                } else {
+                    return implementation.getClassName();
+                }
+            }
+            return name;
+        }
+
+        /*
+         * Below are getter's and setter's for each of the instance variables.
+         * Note that (in addition to supporting setters with the same type as
+         * the instance variable) a setter is provided with takes a String
+         * argument -- this are provided so the XML document handler can set
+         * the EJB values using the Strings it parses.
+         */
+
+        public void setHome(String home) {
+            setHome(new Classname(home));
+        }
+
+        public void setHome(Classname home) {
+            this.home = home;
+        }
+
+        public Classname getHome() {
+            return home;
+        }
+
+        public void setRemote(String remote) {
+            setRemote(new Classname(remote));
+        }
+
+        public void setRemote(Classname remote) {
+            this.remote = remote;
+        }
+
+        public Classname getRemote() {
+            return remote;
+        }
+
+        public void setImplementation(String implementation) {
+            setImplementation(new Classname(implementation));
+        }
+
+        public void setImplementation(Classname implementation) {
+            this.implementation = implementation;
+        }
+
+        public Classname getImplementation() {
+            return implementation;
+        }
+
+        public void setPrimaryKey(String primaryKey) {
+            setPrimaryKey(new Classname(primaryKey));
+        }
+
+        public void setPrimaryKey(Classname primaryKey) {
+            this.primaryKey = primaryKey;
+        }
+
+        public Classname getPrimaryKey() {
+            return primaryKey;
+        }
+
+        public void setBeantype(String beantype) {
+            this.beantype = beantype.toLowerCase();
+        }
+
+        public String getBeantype() {
+            return beantype;
+        }
+
+        public void setCmp(boolean cmp) {
+            this.cmp = cmp;
+        }
+
+        public void setCmp(String cmp) {
+            setCmp(cmp.equals("Container"));
+        }
+
+        public boolean getCmp() {
+            return cmp;
+        }
+
+        public void setIiop(boolean iiop) {
+            this.iiop = iiop;
+        }
+
+        public void setIiop(String iiop) {
+            setIiop(iiop.equals("true"));
+        }
+
+        public boolean getIiop() {
+            return iiop;
+        }
+
+        public void setHasession(boolean hasession) {
+            this.hasession = hasession;
+        }
+
+        public void setHasession(String hasession) {
+            setHasession(hasession.equals("true"));
+        }
+
+        public boolean getHasession() {
+            return hasession;
+        }
+
+        public void addCmpDescriptor(String descriptor) {
+            cmpDescriptors.add(descriptor);
+        }
+
+        public List getCmpDescriptors() {
+            return cmpDescriptors;
+        }
+
+        /**
+         * Verifies that the EJB is valid--if it is invalid, an exception is
+         * thrown
+         *
+         *
+         * @param buildDir The directory where the EJB remote interface, home
+         *                 interface, and implementation class must be found.
+         * @throws EjbcException If the EJB is invalid.
+         */
+        private void checkConfiguration(File buildDir) throws EjbcException  {
+
+            /* Check that the specified instance variables are valid */
+            if (home == null) {
+                throw new EjbcException("A home interface was not found "
+                            + "for the " + name + " EJB.");
+            }
+            if (remote == null) {
+                throw new EjbcException("A remote interface was not found "
+                            + "for the " + name + " EJB.");
+            }
+            if (implementation == null) {
+                throw new EjbcException("An EJB implementation class was not "
+                            + "found for the " + name + " EJB.");
+            }
+
+            if ((!beantype.equals(ENTITY_BEAN))
+                        && (!beantype.equals(STATELESS_SESSION))
+                        && (!beantype.equals(STATEFUL_SESSION))) {
+                throw new EjbcException("The beantype found (" + beantype + ") "
+                            + "isn't valid in the " + name + " EJB.");
+            }
+
+            if (cmp && (!beantype.equals(ENTITY_BEAN))) {
+                System.out.println("CMP stubs and skeletons may not be generated"
+                    + " for a Session Bean -- the \"cmp\" attribute will be"
+                    + " ignoredfor the " + name + " EJB.");
+            }
+
+            if (hasession && (!beantype.equals(STATEFUL_SESSION))) {
+                System.out.println("Highly available stubs and skeletons may "
+                    + "only be generated for a Stateful Session Bean -- the "
+                    + "\"hasession\" attribute will be ignored for the "
+                    + name + " EJB.");
+            }
+
+            /* Check that the EJB "source" classes all exist */
+            if (!remote.getClassFile(buildDir).exists()) {
+                throw new EjbcException("The remote interface "
+                            + remote.getQualifiedClassName() + " could not be "
+                            + "found.");
+            }
+            if (!home.getClassFile(buildDir).exists()) {
+                throw new EjbcException("The home interface "
+                            + home.getQualifiedClassName() + " could not be "
+                            + "found.");
+            }
+            if (!implementation.getClassFile(buildDir).exists()) {
+                throw new EjbcException("The EJB implementation class "
+                            + implementation.getQualifiedClassName() + " could "
+                            + "not be found.");
+            }
+        }
+
+        /**
+         * Determines if the ejbc utility needs to be run or not.  If the stubs
+         * and skeletons can all be found in the destination directory AND all
+         * of their timestamps are more recent than the EJB source classes
+         * (home, remote, and implementation classes), the method returns
+         * <code>false</code>.  Otherwise, the method returns <code>true</code>.
+         *
+         * @param destDir The directory where the EJB source classes, stubs and
+         *                skeletons are located.
+         * @return A boolean indicating whether or not the ejbc utility needs to
+         *         be run to bring the stubs and skeletons up to date.
+         */
+        public boolean mustBeRecompiled(File destDir) {
+
+            long sourceModified = sourceClassesModified(destDir);
+
+            long destModified = destClassesModified(destDir);
+
+            return (destModified < sourceModified);
+        }
+
+        /**
+         * Examines each of the EJB source classes (home, remote, and
+         * implementation) and returns the modification timestamp for the
+         * "oldest" class.
+         *
+         * @param classpath The classpath to be used to find the source EJB
+         *                  classes.  If <code>null</code>, the system classpath
+         *                  is used.
+         * @return The modification timestamp for the "oldest" EJB source class.
+         * @throws BuildException If one of the EJB source classes cannot be
+         *                        found on the classpath.
+         */
+        private long sourceClassesModified(File buildDir) {
+            long latestModified; // The timestamp of the "newest" class
+            long modified;       // Timestamp for a given class
+            File remoteFile;     // File for the remote interface class
+            File homeFile;       // File for the home interface class
+            File implFile;       // File for the EJB implementation class
+            File pkFile;         // File for the EJB primary key class
+
+            /* Check the timestamp on the remote interface */
+            remoteFile = remote.getClassFile(buildDir);
+            modified = remoteFile.lastModified();
+            if (modified == -1) {
+                System.out.println("The class "
+                                + remote.getQualifiedClassName() + " couldn't "
+                                + "be found on the classpath");
+                return -1;
+            }
+            latestModified = modified;
+
+            /* Check the timestamp on the home interface */
+            homeFile = home.getClassFile(buildDir);
+            modified = homeFile.lastModified();
+            if (modified == -1) {
+                System.out.println("The class "
+                                + home.getQualifiedClassName() + " couldn't be "
+                                + "found on the classpath");
+                return -1;
+            }
+            latestModified = Math.max(latestModified, modified);
+
+            /* Check the timestamp of the primary key class */
+            if (primaryKey != null) {
+                pkFile = primaryKey.getClassFile(buildDir);
+                modified = pkFile.lastModified();
+                if (modified == -1) {
+                    System.out.println("The class "
+                                    + primaryKey.getQualifiedClassName() + "couldn't be "
+                                    + "found on the classpath");
+                    return -1;
+                }
+                latestModified = Math.max(latestModified, modified);
+            } else {
+                pkFile = null;
+            }
+
+            /* Check the timestamp on the EJB implementation class.
+             *
+             * Note that if ONLY the implementation class has changed, it's not
+             * necessary to rebuild the EJB stubs and skeletons.  For this
+             * reason, we ensure the file exists (using lastModified above), but
+             * we DON'T compare it's timestamp with the timestamps of the home
+             * and remote interfaces (because it's irrelevant in determining if
+             * ejbc must be run)
+             */
+            implFile = implementation.getClassFile(buildDir);
+            modified = implFile.lastModified();
+            if (modified == -1) {
+                System.out.println("The class "
+                                + implementation.getQualifiedClassName()
+                                + " couldn't be found on the classpath");
+                return -1;
+            }
+
+            String pathToFile = remote.getQualifiedClassName();
+            pathToFile = pathToFile.replace('.', File.separatorChar) + ".class";
+            ejbFiles.put(pathToFile, remoteFile);
+
+            pathToFile = home.getQualifiedClassName();
+            pathToFile = pathToFile.replace('.', File.separatorChar) + ".class";
+            ejbFiles.put(pathToFile, homeFile);
+
+            pathToFile = implementation.getQualifiedClassName();
+            pathToFile = pathToFile.replace('.', File.separatorChar) + ".class";
+            ejbFiles.put(pathToFile, implFile);
+
+            if (pkFile != null) {
+                pathToFile = primaryKey.getQualifiedClassName();
+                pathToFile = pathToFile.replace('.', File.separatorChar) + ".class";
+                ejbFiles.put(pathToFile, pkFile);
+            }
+
+            return latestModified;
+        }
+
+        /**
+         * Examines each of the EJB stubs and skeletons in the destination
+         * directory and returns the modification timestamp for the "oldest"
+         * class. If one of the stubs or skeletons cannot be found, <code>-1
+         * </code> is returned.
+         *
+         * @param dest The directory in which the EJB stubs and skeletons are
+         *             stored.
+         * @return The modification timestamp for the "oldest" EJB stub or
+         *         skeleton.  If one of the classes cannot be found, <code>-1
+         *         </code> is returned.
+         * @throws BuildException If the canonical path of the destination
+         *                        directory cannot be found.
+         */
+        private long destClassesModified(File destDir) {
+            String[] classnames = classesToGenerate(); // List of all stubs & skels
+            long destClassesModified = new Date().getTime(); // Earliest mod time
+            boolean allClassesFound  = true;           // Has each been found?
+
+            /*
+             * Loop through each stub/skeleton class that must be generated, and
+             * determine (if all exist) which file has the most recent timestamp
+             */
+            for (int i = 0; i < classnames.length; i++) {
+
+                String pathToClass =
+                        classnames[i].replace('.', File.separatorChar) + ".class";
+                File classFile = new File(destDir, pathToClass);
+
+                /*
+                 * Add each stub/skeleton class to the list of EJB files.  Note
+                 * that each class is added even if it doesn't exist now.
+                 */
+                ejbFiles.put(pathToClass, classFile);
+
+                allClassesFound = allClassesFound && classFile.exists();
+
+                if (allClassesFound) {
+                    long fileMod = classFile.lastModified();
+
+                    /* Keep track of the oldest modification timestamp */
+                    destClassesModified = Math.min(destClassesModified, fileMod);
+                }
+            }
+
+            return (allClassesFound) ? destClassesModified : -1;
+        }
+
+        /**
+         * Builds an array of class names which represent the stubs and
+         * skeletons which need to be generated for a given EJB.  The class
+         * names are fully qualified.  Nine classes are generated for all EJBs
+         * while an additional six classes are generated for beans requiring
+         * RMI/IIOP access.
+         *
+         * @return An array of Strings representing the fully-qualified class
+         *         names for the stubs and skeletons to be generated.
+         */
+        private String[] classesToGenerate() {
+            String[] classnames = (iiop) ? new String[15] : new String[9];
+
+            final String remotePkg     = remote.getPackageName() + ".";
+            final String remoteClass   = remote.getClassName();
+            final String homePkg       = home.getPackageName() + ".";
+            final String homeClass     = home.getClassName();
+            final String implPkg       = implementation.getPackageName() + ".";
+            final String implFullClass = implementation.getQualifiedWithUnderscores();
+            int index = 0;
+
+            classnames[index++] = implPkg + "ejb_fac_" + implFullClass;
+            classnames[index++] = implPkg + "ejb_home_" + implFullClass;
+            classnames[index++] = implPkg + "ejb_skel_" + implFullClass;
+            classnames[index++] = remotePkg + "ejb_kcp_skel_" + remoteClass;
+            classnames[index++] = homePkg + "ejb_kcp_skel_" + homeClass;
+            classnames[index++] = remotePkg + "ejb_kcp_stub_" + remoteClass;
+            classnames[index++] = homePkg + "ejb_kcp_stub_" + homeClass;
+            classnames[index++] = remotePkg + "ejb_stub_" + remoteClass;
+            classnames[index++] = homePkg + "ejb_stub_" + homeClass;
+
+            if (!iiop) {
+                return classnames;
+            }
+
+            classnames[index++] = "org.omg.stub." + remotePkg + "_"
+                                    + remoteClass + "_Stub";
+            classnames[index++] = "org.omg.stub." + homePkg + "_"
+                                    + homeClass + "_Stub";
+            classnames[index++] = "org.omg.stub." + remotePkg
+                                    + "_ejb_RmiCorbaBridge_"
+                                    + remoteClass + "_Tie";
+            classnames[index++] = "org.omg.stub." + homePkg
+                                    + "_ejb_RmiCorbaBridge_"
+                                    + homeClass + "_Tie";
+
+            classnames[index++] = remotePkg + "ejb_RmiCorbaBridge_"
+                                                        + remoteClass;
+            classnames[index++] = homePkg + "ejb_RmiCorbaBridge_" + homeClass;
+
+            return classnames;
+        }
+
+        /**
+         * Convenience method which creates a String representation of all the
+         * instance variables of an EjbInfo object.
+         *
+         * @return A String representing the EjbInfo instance.
+         */
+        public String toString() {
+            String s = "EJB name: " + name
+                        + "\n\r              home:      " + home
+                        + "\n\r              remote:    " + remote
+                        + "\n\r              impl:      " + implementation
+                        + "\n\r              primaryKey: " + primaryKey
+                        + "\n\r              beantype:  " + beantype
+                        + "\n\r              cmp:       " + cmp
+                        + "\n\r              iiop:      " + iiop
+                        + "\n\r              hasession: " + hasession;
+
+            Iterator i = cmpDescriptors.iterator();
+            while (i.hasNext()) {
+                s += "\n\r              CMP Descriptor: " + i.next();
+            }
+
+            return s;
+        }
+
+    } // End of EjbInfo inner class
+
+    /**
+     * Convenience class used to represent the fully qualified name of a Java
+     * class.  It provides an easy way to retrieve components of the class name
+     * in a format that is convenient for building iAS stubs and skeletons.
+     *
+     */
+    private static class Classname {
+        private String qualifiedName;  // Fully qualified name of the Java class
+        private String packageName;    // Name of the package for this class
+        private String className;      // Name of the class without the package
+
+        /**
+         * This constructor builds an object which represents the name of a Java
+         * class.
+         *
+         * @param qualifiedName String representing the fully qualified class
+         *                      name of the Java class.
+         */
+        public Classname(String qualifiedName) {
+            if (qualifiedName == null) {
+                return;
+            }
+
+            this.qualifiedName = qualifiedName;
+
+            int index = qualifiedName.lastIndexOf('.');
+            if (index == -1) {
+                className = qualifiedName;
+                packageName = "";
+            } else {
+                packageName = qualifiedName.substring(0, index);
+                className   = qualifiedName.substring(index + 1);
+            }
+        }
+
+        /**
+         * Gets the fully qualified name of the Java class.
+         *
+         * @return String representing the fully qualified class name.
+         */
+        public String getQualifiedClassName() {
+            return qualifiedName;
+        }
+
+        /**
+         * Gets the package name for the Java class.
+         *
+         * @return String representing the package name for the class.
+         */
+        public String getPackageName() {
+            return packageName;
+        }
+
+        /**
+         * Gets the Java class name without the package structure.
+         *
+         * @return String representing the name for the class.
+         */
+        public String getClassName() {
+            return className;
+        }
+
+        /**
+         * Gets the fully qualified name of the Java class with underscores
+         * separating the components of the class name rather than periods.
+         * This format is used in naming some of the stub and skeleton classes
+         * for the iPlanet Application Server.
+         *
+         * @return String representing the fully qualified class name using
+         *         underscores instead of periods.
+         */
+        public String getQualifiedWithUnderscores() {
+            return qualifiedName.replace('.', '_');
+        }
+
+        /**
+         * Returns a File which references the class relative to the specified
+         * directory.  Note that the class file may or may not exist.
+         *
+         * @param  directory A File referencing the base directory containing
+         *                   class files.
+         * @return File referencing this class.
+         */
+        public File getClassFile(File directory) {
+            String pathToFile = qualifiedName.replace('.', File.separatorChar)
+                                            + ".class";
+            return new File(directory, pathToFile);
+        }
+
+        /**
+         * String representation of this class name.  It returns the fully
+         * qualified class name.
+         *
+         * @return String representing the fully qualified class name.
+         */
+        public String toString() {
+            return getQualifiedClassName();
+        }
+    }  // End of Classname inner class
+
+
+    /**
+     * Thread class used to redirect output from an <code>InputStream</code> to
+     * the JRE standard output.  This class may be used to redirect output from
+     * an external process to the standard output.
+     *
+     */
+    private static class RedirectOutput extends Thread {
+        InputStream stream;  // Stream to read and redirect to standard output
+
+        /**
+         * Constructs a new instance that will redirect output from the
+         * specified stream to the standard output.
+         *
+         * @param stream InputStream which will be read and redirected to the
+         *               standard output.
+         */
+        public RedirectOutput(InputStream stream) {
+            this.stream = stream;
+        }
+
+        /**
+         * Reads text from the input stream and redirects it to standard output
+         * using a separate thread.
+         */
+        public void run() {
+            BufferedReader reader = new BufferedReader(
+                                            new InputStreamReader(stream));
+            String text;
+            try {
+                while ((text = reader.readLine()) != null) {
+                    System.out.println(text);
+                }
+            } catch (IOException e) {
+                e.printStackTrace();
+            } finally {
+                try {
+                    reader.close();
+                } catch (IOException e) {
+                    // Do nothing
+                }
+            }
+        }
+    }  // End of RedirectOutput inner class
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbcTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbcTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/IPlanetEjbcTask.java	(revision 16139)
@@ -0,0 +1,316 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.IOException;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.xml.sax.SAXException;
+
+/**
+ * Compiles EJB stubs and skeletons for the iPlanet Application Server.
+ * The EJBs to be processed are specified by the EJB 1.1 standard XML
+ * descriptor, and additional attributes are obtained from the iPlanet Application
+ * Server-specific XML descriptor.  Since the XML descriptors can include
+ * multiple EJBs, this is a convenient way of specifying many EJBs in a single
+ * Ant task.  The following attributes are allowed:
+ *   <ul>
+ *     <li><i>ejbdescriptor</i> -- Standard EJB 1.1 XML descriptor (typically
+ *                                 titled "ejb-jar.xml").  This attribute is
+ *                                 required.
+ *     <li><i>iasdescriptor</i> -- EJB XML descriptor for iPlanet Application
+ *                                 Server (typically titled "ias-ejb-jar.xml).
+ *                                 This attribute is required.
+ *     <li><i>dest</i> -- The is the base directory where the RMI stubs and
+ *                        skeletons are written.  In addition, the class files
+ *                        for each bean (home interface, remote interface, and
+ *                        EJB implementation) must be found in this directory.
+ *                        This attribute is required.
+ *     <li><i>classpath</i> -- The classpath used when generating EJB stubs and
+ *                             skeletons.  This is an optional attribute (if
+ *                             omitted, the classpath specified when Ant was
+ *                             started will be used).  Nested "classpath"
+ *                             elements may also be used.
+ *     <li><i>keepgenerated</i> -- Indicates whether or not the Java source
+ *                                 files which are generated by ejbc will be
+ *                                 saved or automatically deleted.  If "yes",
+ *                                 the source files will be retained.  This is
+ *                                 an optional attribute (if omitted, it
+ *                                 defaults to "no").
+ *     <li><i>debug</i> -- Indicates whether or not the ejbc utility should
+ *                         log additional debugging statements to the standard
+ *                         output.  If "yes", the additional debugging statements
+ *                         will be generated (if omitted, it defaults to "no").
+ *     <li><i>iashome</i> -- May be used to specify the "home" directory for
+ *                           this iPlanet Application Server installation.  This
+ *                           is used to find the ejbc utility if it isn't
+ *                           included in the user's system path.  This is an
+ *                           optional attribute (if specified, it should refer
+ *                           to the <code>[install-location]/iplanet/ias6/ias
+ *                           </code> directory).  If omitted, the ejbc utility
+ *                           must be on the user's system path.
+ *   </ul>
+ * <p>
+ * For each EJB specified, this task will locate the three classes that comprise
+ * the EJB.  If these class files cannot be located in the <code>dest</code>
+ * directory, the task will fail.  The task will also attempt to locate the EJB
+ * stubs and skeletons in this directory.  If found, the timestamps on the
+ * stubs and skeletons will be checked to ensure they are up to date.  Only if
+ * these files cannot be found or if they are out of date will ejbc be called
+ * to generate new stubs and skeletons.
+ *
+ * @see    IPlanetEjbc
+ *
+ * @ant.task name="iplanet-ejbc" category="ejb"
+ */
+public class IPlanetEjbcTask extends Task {
+
+    /* Attributes set by the Ant build file */
+    private File    ejbdescriptor;
+    private File    iasdescriptor;
+    private File    dest;
+    private Path    classpath;
+    private boolean keepgenerated = false;
+    private boolean debug         = false;
+    private File    iashome;
+
+    /**
+     * Sets the location of the standard XML EJB descriptor.  Typically, this
+     * file is named "ejb-jar.xml".
+     *
+     * @param ejbdescriptor The name and location of the EJB descriptor.
+     */
+    public void setEjbdescriptor(File ejbdescriptor) {
+        this.ejbdescriptor = ejbdescriptor;
+    }
+
+    /**
+     * Sets the location of the iAS-specific XML EJB descriptor.  Typically,
+     * this file is named "ias-ejb-jar.xml".
+     *
+     * @param iasdescriptor The name and location of the iAS-specific EJB
+     *                      descriptor.
+     */
+    public void setIasdescriptor (File iasdescriptor) {
+        this.iasdescriptor = iasdescriptor;
+    }
+
+    /**
+     * Sets the destination directory where the EJB source classes must exist
+     * and where the stubs and skeletons will be written.  The destination
+     * directory must exist before this task is executed.
+     *
+     * @param dest The directory where the compiled classes will be written.
+     */
+    public void setDest(File dest) {
+        this.dest = dest;
+    }
+
+    /**
+     * Sets the classpath to be used when compiling the EJB stubs and skeletons.
+     *
+     * @param classpath The classpath to be used.
+     */
+    public void setClasspath(Path classpath) {
+        if (this.classpath == null) {
+            this.classpath = classpath;
+        } else {
+            this.classpath.append(classpath);
+        }
+    }
+
+    /**
+     * Adds to the classpath used when compiling the EJB stubs and skeletons.
+     */
+    public Path createClasspath() {
+        if (classpath == null) {
+            classpath = new Path(getProject());
+        }
+        return classpath.createPath();
+    }
+
+    /**
+     * If true, the Java source files which are generated by ejbc will be saved .
+     *
+     * @param keepgenerated A boolean indicating if the Java source files for
+     *                      the stubs and skeletons should be retained.
+     */
+    public void setKeepgenerated(boolean keepgenerated) {
+        this.keepgenerated = keepgenerated;
+    }
+
+    /**
+     * If true, debugging output will be generated when ejbc is
+     * executed.
+     *
+     * @param debug A boolean indicating if debugging output should be generated
+     */
+    public void setDebug(boolean debug) {
+        this.debug = debug;
+    }
+
+    /**
+     * May be used to specify the "home" directory for this iAS installation.
+     * The directory specified should typically be
+     * <code>[install-location]/iplanet/ias6/ias</code>.
+     *
+     * @param iashome The home directory for the user's iAS installation.
+     */
+    public void setIashome(File iashome) {
+        this.iashome = iashome;
+    }
+
+    /**
+     * Does the work.
+     */
+    public void execute() throws BuildException {
+        checkConfiguration();
+
+        executeEjbc(getParser());
+    }
+
+    /**
+     * Verifies that the user selections are valid.
+     *
+     * @throws BuildException If the user selections are invalid.
+     */
+    private void checkConfiguration() throws BuildException {
+
+        if (ejbdescriptor == null) {
+            String msg = "The standard EJB descriptor must be specified using "
+                            + "the \"ejbdescriptor\" attribute.";
+            throw new BuildException(msg, getLocation());
+        }
+        if ((!ejbdescriptor.exists()) || (!ejbdescriptor.isFile())) {
+            String msg = "The standard EJB descriptor (" + ejbdescriptor
+                            + ") was not found or isn't a file.";
+            throw new BuildException(msg, getLocation());
+        }
+
+        if (iasdescriptor == null) {
+            String msg = "The iAS-speific XML descriptor must be specified using"
+                            + " the \"iasdescriptor\" attribute.";
+            throw new BuildException(msg, getLocation());
+        }
+        if ((!iasdescriptor.exists()) || (!iasdescriptor.isFile())) {
+            String msg = "The iAS-specific XML descriptor (" + iasdescriptor
+                            + ") was not found or isn't a file.";
+            throw new BuildException(msg, getLocation());
+        }
+
+        if (dest == null) {
+            String msg = "The destination directory must be specified using "
+                            + "the \"dest\" attribute.";
+            throw new BuildException(msg, getLocation());
+        }
+        if ((!dest.exists()) || (!dest.isDirectory())) {
+            String msg = "The destination directory (" + dest + ") was not "
+                            + "found or isn't a directory.";
+            throw new BuildException(msg, getLocation());
+        }
+
+        if ((iashome != null) && (!iashome.isDirectory())) {
+            String msg = "If \"iashome\" is specified, it must be a valid "
+                            + "directory (it was set to " + iashome + ").";
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+    /**
+     * Returns a SAXParser that may be used to process the XML descriptors.
+     *
+     * @return Parser which may be used to process the EJB descriptors.
+     * @throws BuildException If the parser cannot be created or configured.
+     */
+    private SAXParser getParser() throws BuildException {
+
+        SAXParser saxParser = null;
+        try {
+            SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+            saxParserFactory.setValidating(true);
+            saxParser = saxParserFactory.newSAXParser();
+        } catch (SAXException e) {
+            String msg = "Unable to create a SAXParser: " + e.getMessage();
+            throw new BuildException(msg, e, getLocation());
+        } catch (ParserConfigurationException e) {
+            String msg = "Unable to create a SAXParser: " + e.getMessage();
+            throw new BuildException(msg, e, getLocation());
+        }
+
+        return saxParser;
+    }
+
+    /**
+     * Executes the EJBc utility using the SAXParser provided.
+     *
+     * @param saxParser SAXParser that may be used to process the EJB
+     *                  descriptors
+     * @throws BuildException If there is an error reading or parsing the XML
+     *                        descriptors
+     */
+    private void executeEjbc(SAXParser saxParser) throws BuildException {
+        IPlanetEjbc ejbc = new IPlanetEjbc(ejbdescriptor,
+                                            iasdescriptor,
+                                            dest,
+                                            getClasspath().toString(),
+                                            saxParser);
+        ejbc.setRetainSource(keepgenerated);
+        ejbc.setDebugOutput(debug);
+        if (iashome != null) {
+            ejbc.setIasHomeDir(iashome);
+        }
+
+        try {
+            ejbc.execute();
+        } catch (IOException e) {
+            String msg = "An IOException occurred while trying to read the XML "
+                            + "descriptor file: " + e.getMessage();
+            throw new BuildException(msg, e, getLocation());
+        } catch (SAXException e) {
+            String msg = "A SAXException occurred while trying to read the XML "
+                            + "descriptor file: " + e.getMessage();
+            throw new BuildException(msg, e, getLocation());
+        } catch (IPlanetEjbc.EjbcException e) {
+            String msg = "An exception occurred while trying to run the ejbc "
+                            + "utility: " + e.getMessage();
+            throw new BuildException(msg, e, getLocation());
+        }
+    }
+
+    /**
+     * Returns the CLASSPATH to be used when calling EJBc.  If no user CLASSPATH
+     * is specified, the System classpath is returned instead.
+     *
+     * @return Path The classpath to be used for EJBc.
+     */
+    private Path getClasspath() {
+        Path cp = null;
+        if (classpath == null) {
+            cp = (new Path(getProject())).concatSystemClasspath("last");
+        } else {
+            cp = classpath.concatSystemClasspath("ignore");
+        }
+
+        return cp;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/InnerClassFilenameFilter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/InnerClassFilenameFilter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/InnerClassFilenameFilter.java	(revision 16139)
@@ -0,0 +1,40 @@
+/*
+ * Copyright  2000,2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.FilenameFilter;
+
+public class InnerClassFilenameFilter implements FilenameFilter {
+    private String baseClassName;
+
+    InnerClassFilenameFilter(String baseclass) {
+        int extidx = baseclass.lastIndexOf(".class");
+        if (extidx == -1) {
+            extidx = baseclass.length() - 1;
+        }
+        baseClassName = baseclass.substring(0, extidx);
+    }
+
+    public boolean accept (File Dir, String filename) {
+        if ((filename.lastIndexOf(".") != filename.lastIndexOf(".class"))
+            || (filename.indexOf(baseClassName + "$") != 0)) {
+            return false;
+        }
+        return true;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JbossDeploymentTool.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JbossDeploymentTool.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JbossDeploymentTool.java	(revision 16139)
@@ -0,0 +1,107 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.util.Hashtable;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * The deployment tool to add the jboss specific deployment descriptor to the ejb jar file.
+ * Jboss only requires one additional file jboss.xml and does not require any additional
+ * compilation.
+ *
+ * @version 1.0
+ * @see EjbJar#createJboss
+ */
+public class JbossDeploymentTool extends GenericDeploymentTool {
+    protected static final String JBOSS_DD = "jboss.xml";
+    protected static final String JBOSS_CMP10D = "jaws.xml";
+    protected static final String JBOSS_CMP20D = "jbosscmp-jdbc.xml";
+
+    /** Instance variable that stores the suffix for the jboss jarfile. */
+    private String jarSuffix = ".jar";
+
+    /**
+     * Setter used to store the suffix for the generated JBoss jar file.
+     * @param inString the string to use as the suffix.
+     */
+    public void setSuffix(String inString) {
+        jarSuffix = inString;
+    }
+
+    /**
+     * Add any vendor specific files which should be included in the
+     * EJB Jar.
+     */
+    protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
+        File jbossDD = new File(getConfig().descriptorDir, ddPrefix + JBOSS_DD);
+        if (jbossDD.exists()) {
+            ejbFiles.put(META_DIR + JBOSS_DD, jbossDD);
+        } else {
+            log("Unable to locate jboss deployment descriptor. "
+                + "It was expected to be in " + jbossDD.getPath(),
+                Project.MSG_WARN);
+            return;
+        }
+        String descriptorFileName = JBOSS_CMP10D;
+        if (EjbJar.CMPVersion.CMP2_0.equals(getParent().getCmpversion())) {
+            descriptorFileName = JBOSS_CMP20D;
+        }
+        File jbossCMPD
+            = new File(getConfig().descriptorDir, ddPrefix + descriptorFileName);
+
+        if (jbossCMPD.exists()) {
+            ejbFiles.put(META_DIR + descriptorFileName, jbossCMPD);
+        } else {
+            log("Unable to locate jboss cmp descriptor. "
+                + "It was expected to be in "
+                + jbossCMPD.getPath(), Project.MSG_VERBOSE);
+            return;
+        }
+    }
+
+    /**
+     * Get the vendor specific name of the Jar that will be output. The modification date
+     * of this jar will be checked against the dependent bean classes.
+     */
+    File getVendorOutputJarFile(String baseName) {
+        if (getDestDir() == null && getParent().getDestdir() == null) {
+            throw new BuildException("DestDir not specified");
+        }
+        if (getDestDir() == null) {
+            return new File(getParent().getDestdir(), baseName + jarSuffix);
+        } else {
+            return new File(getDestDir(), baseName + jarSuffix);
+        }
+    }
+
+    /**
+     * Called to validate that the tool parameters have been configured.
+     *
+     * @throws BuildException If the Deployment Tool's configuration isn't
+     *                        valid
+     * @since ant 1.6
+     */
+    public void validateConfigured() throws BuildException {
+    }
+
+    private EjbJar getParent() {
+        return (EjbJar) this.getTask();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JonasDeploymentTool.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JonasDeploymentTool.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/JonasDeploymentTool.java	(revision 16139)
@@ -0,0 +1,813 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import javax.xml.parsers.SAXParser;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * The deployment tool to add the jonas specific deployment descriptors to the
+ * ejb JAR file. JONAS only requires one additional file jonas-ejb-jar.xml.
+ *
+ * @version 1.0
+ * @see EjbJar#createJonas
+ */
+public class JonasDeploymentTool extends GenericDeploymentTool {
+
+    /** Public Id of the standard deployment descriptor DTD. */
+    protected static final String EJB_JAR_1_1_PUBLIC_ID
+        = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN";
+    protected static final String EJB_JAR_2_0_PUBLIC_ID
+        = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN";
+
+    /** Public Id of the JOnAS-specific deployment descriptor DTD. */
+    protected static final String JONAS_EJB_JAR_2_4_PUBLIC_ID
+        = "-//ObjectWeb//DTD JOnAS 2.4//EN";
+    protected static final String JONAS_EJB_JAR_2_5_PUBLIC_ID
+        = "-//ObjectWeb//DTD JOnAS 2.5//EN";
+
+    /** RMI ORB. */
+    protected static final String RMI_ORB = "RMI";
+
+    /** JEREMIE ORB. */
+    protected static final String JEREMIE_ORB = "JEREMIE";
+
+    /** DAVID ORB. */
+    protected static final String DAVID_ORB = "DAVID";
+
+    /**
+     * Name of the standard deployment descriptor DTD (these files are stored in
+     * the ${JONAS_ROOT}/xml directory).
+     */
+    protected static final String EJB_JAR_1_1_DTD = "ejb-jar_1_1.dtd";
+    protected static final String EJB_JAR_2_0_DTD = "ejb-jar_2_0.dtd";
+
+    /**
+     * Name of the JOnAS-specific deployment descriptor DTD (these files are
+     * stored in the ${JONAS_ROOT}/xml directory).
+     */
+    protected static final String JONAS_EJB_JAR_2_4_DTD
+        = "jonas-ejb-jar_2_4.dtd";
+    protected static final String JONAS_EJB_JAR_2_5_DTD
+        = "jonas-ejb-jar_2_5.dtd";
+
+    /** Default JOnAS deployment descriptor name. */
+    protected static final String JONAS_DD = "jonas-ejb-jar.xml";
+
+    /** GenIC class name (JOnAS 2.5) */
+    protected static final String GENIC_CLASS =
+    "org.objectweb.jonas_ejb.genic.GenIC";
+
+    /** Old GenIC class name (JOnAS 2.4.x). */
+    protected static final String OLD_GENIC_CLASS_1 =
+        "org.objectweb.jonas_ejb.tools.GenWholeIC";
+
+    /** Old GenIC class name. */
+    protected static final String OLD_GENIC_CLASS_2 =
+        "org.objectweb.jonas_ejb.tools.GenIC";
+
+    /**
+     * Filename of the standard EJB descriptor (which is passed to this class
+     * from the parent "ejbjar" task). This file is relative to the directory
+     * specified by the "srcdir" attribute in the ejbjar task.
+     */
+    private String descriptorName;
+
+    /**
+     * Filename of the JOnAS-specific EJB descriptor (which is passed to this
+     * class from the parent "ejbjar" task). This file is relative to the
+     * directory specified by the "srcdir" attribute in the ejbjar task.
+     */
+    private String jonasDescriptorName;
+
+    /* ------------- */
+    /* GenIC options */
+    /* ------------- */
+
+    /**
+     * Temporary output directory used by GenIC.
+     */
+    private File outputdir;
+
+    /**
+     * <code>true</code> if the intermediate Java source files generated by
+     * GenIC must be deleted or not. The default is <code>false</code>
+     */
+    private boolean keepgenerated = false;
+
+    /**
+     * <code>true</code> if the generated source files must not be compiled via
+     * the java and rmi compilers. The default is <code>false</code>.
+     */
+    private boolean nocompil = false;
+
+    /**
+     * <code>true</code> if the XML deployment descriptors must be parsed
+     * without validation. The default is <code>false</code>.
+     */
+    private boolean novalidation = false;
+
+    /**
+     * Java compiler to use. The default is the value of
+     * <code>build.compiler</code> property.
+     */
+    private String javac;
+
+    /** Options to pass to the java compiler. */
+    private String javacopts;
+
+    /** Options to pass to the rmi compiler. */
+    private String rmicopts;
+
+    /**
+     * Whether or not the RMI skeleton and stub must be modified to
+     * implement the implicit propagation of the security context (the
+     * transactional context is always provided). The default is
+     * <code>false</code>.
+     */
+    private boolean secpropag = false;
+
+    /**
+     * <code>true</code> if the GenIC call must be verbose. The default
+     * is <code>false</code>.
+     */
+    private boolean verbose = false;
+
+    /** Additional args to send to GenIC. */
+    private String additionalargs;
+
+    /* ------------- */
+    /* other options */
+    /* ------------- */
+
+    /** JOnAS root directory. */
+    private File jonasroot;
+
+    /**
+     * <code>true</code> if the generic JAR file used as input to GenIC must be
+     * retained. The default is <code>false</code>.
+     */
+    private boolean keepgeneric = false;
+
+    /** Stores the suffix for the JOnAS JAR file. The default is '.jar'. */
+    private String suffix = ".jar";
+
+    /**
+     *  ORB to use (RMI, JEREMIE or DAVID). If omitted, it defaults to the one
+     *  present in classpath. If specified, the corresponding JOnAS JAR is
+     *  automatically added to the classpath.
+     */
+    private String orb;
+
+    /**
+     * <code>true</code> if GenIC must not be run on the EJB JAR.
+     * The default is <code>false</code>.
+     */
+    private boolean nogenic = false;
+
+    /* -------------------- */
+    /* GenIC options setter */
+    /* -------------------- */
+
+    /**
+     * Sets the <code>keepgenerated</code> flag.
+     *
+     * @param aBoolean <code>true</code> if the flag must be set.
+     */
+    public void setKeepgenerated(boolean aBoolean) {
+        keepgenerated = aBoolean;
+    }
+
+    /**
+     * Sets the additional arguments.
+     *
+     * @param aString additional args.
+     */
+    public void setAdditionalargs(String aString) {
+        additionalargs = aString;
+    }
+
+    /**
+     * Sets the <code>nocompil</code> flag.
+     *
+     * @param aBoolean <code>true</code> if the flag must be set.
+     */
+    public void setNocompil(boolean aBoolean) {
+        nocompil = aBoolean;
+    }
+
+    /**
+     * Sets the <code>novalidation</code> flag.
+     *
+     * @param aBoolean <code>true</code> if the flag must be set.
+     */
+    public void setNovalidation(boolean aBoolean) {
+        novalidation = aBoolean;
+    }
+
+    /**
+     * Sets the java compiler to use.
+     *
+     * @param aString the java compiler.
+     */
+    public void setJavac(String aString) {
+        javac = aString;
+    }
+
+    /**
+     * Set the options to pass to the java compiler.
+     *
+     * @param aString the options.
+     */
+    public void setJavacopts(String aString) {
+        javacopts = aString;
+    }
+
+    /**
+     * Set the options to pass to the rmi compiler.
+     *
+     * @param aString the options.
+     */
+    public void setRmicopts(String aString) {
+        rmicopts = aString;
+    }
+
+    /**
+     * Sets the <code>secpropag</code> flag.
+     *
+     * @param aBoolean <code>true</code> if the flag must be set.
+     */
+    public void setSecpropag(boolean aBoolean) {
+        secpropag = aBoolean;
+    }
+
+    /**
+     * Sets the <code>verbose</code> flag.
+     *
+     * @param aBoolean <code>true</code> if the flag must be set.
+     */
+    public void setVerbose(boolean aBoolean) {
+        verbose = aBoolean;
+    }
+
+    /* -------------------- */
+    /* other options setter */
+    /* -------------------- */
+
+    /**
+     * Set the JOnAS root directory.
+     *
+     * @param aFile the JOnAS root directory.
+     */
+    public void setJonasroot(File aFile) {
+        jonasroot = aFile;
+    }
+
+    /**
+     * Sets the <code>keepgeneric</code> flag.
+     *
+     * @param aBoolean <code>true</code> if the flag must be set.
+     */
+    public void setKeepgeneric(boolean aBoolean) {
+        keepgeneric = aBoolean;
+    }
+
+    /**
+     * Sets the jar suffix.
+     *
+     * @param aString the string to use as the suffix.
+     */
+    public void setJarsuffix(String aString) {
+        suffix = aString;
+    }
+
+    /**
+     * Sets the <code>orb</code> to construct classpath.
+     *
+     * @param aString 'RMI', 'JEREMIE', or 'DAVID'.
+     */
+    public void setOrb(String aString) {
+        orb = aString;
+    }
+
+    /**
+     * Sets the <code>nogenic</code> flag.
+     *
+     * @param aBoolean <code>true</code> if the flag must be set.
+     */
+    public void setNogenic(boolean aBoolean) {
+        nogenic = aBoolean;
+    }
+
+    /* ------------- */
+    /* other methods */
+    /* ------------- */
+
+    public void processDescriptor(String aDescriptorName, SAXParser saxParser) {
+
+        descriptorName = aDescriptorName;
+
+        log("JOnAS Deployment Tool processing: " + descriptorName,
+            Project.MSG_VERBOSE);
+
+        super.processDescriptor(descriptorName, saxParser);
+
+        if (outputdir != null) {
+            // the method deleteOnExit() do not work because the directory is not empty
+            log("Deleting temp output directory '" + outputdir + "'.", Project.MSG_VERBOSE);
+            deleteAllFiles(outputdir);
+        }
+    }
+
+    protected void writeJar(String baseName, File jarfile, Hashtable ejbFiles, String publicId)
+    throws BuildException {
+
+        // create the generic jar first
+        File genericJarFile = super.getVendorOutputJarFile(baseName);
+        super.writeJar(baseName, genericJarFile, ejbFiles, publicId);
+
+        // GenIC call on generic jar
+        addGenICGeneratedFiles(genericJarFile, ejbFiles);
+
+        // create the real jar
+        super.writeJar(baseName, getVendorOutputJarFile(baseName), ejbFiles, publicId);
+
+        if (!keepgeneric) {
+            log("Deleting generic JAR " + genericJarFile.toString(), Project.MSG_VERBOSE);
+            genericJarFile.delete();
+        }
+    }
+
+    protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
+
+    // JOnAS-specific descriptor deployment
+    jonasDescriptorName = getJonasDescriptorName();
+        File jonasDD = new File(getConfig().descriptorDir, jonasDescriptorName);
+
+        if (jonasDD.exists()) {
+            ejbFiles.put(META_DIR + JONAS_DD, jonasDD);
+        } else {
+            log("Unable to locate the JOnAS deployment descriptor. It was expected to be in: "
+                + jonasDD.getPath() + ".", Project.MSG_WARN);
+        }
+    }
+
+    protected File getVendorOutputJarFile(String baseName) {
+        return new File(getDestDir(), baseName + suffix);
+    }
+
+    /**
+     * Determines the name of the JOnAS-specific EJB descriptor using the
+     * specified standard EJB descriptor name. In general, the standard
+     * descriptor will be named "[basename]-ejb-jar.xml", and this method will
+     * return "[basename]-jonas-ejb-jar.xml" or "jonas-[basename].xml"
+     *
+     * @return The name of the JOnAS-specific EJB descriptor file.
+     */
+    private String getJonasDescriptorName() {
+
+        // descriptorName = <path><basename><basenameterminator><remainder>
+        // examples = /org/objectweb/fooAppli/foo/Foo-ejb-jar.xml
+        // examples = /org/objectweb/fooAppli/foo/Foo.xml (JOnAS convention)
+
+        String jonasDescriptorName; // JOnAS-specific DD
+        boolean jonasConvention = false; // true if the JOnAS convention is used for the DD
+        String path;            // Directory path of the EJB descriptor
+        String fileName;        // EJB descriptor file name
+        String baseName;        // Filename appearing before name terminator
+        String remainder;       // Filename appearing after the name terminator
+
+        int startOfFileName = descriptorName.lastIndexOf(File.separatorChar);
+        if (startOfFileName != -1) {
+            // extract path info
+            path = descriptorName.substring(0, startOfFileName + 1);
+            fileName = descriptorName.substring(startOfFileName + 1);
+        } else {
+            // descriptorName is just a file without path
+            path = "";
+            fileName = descriptorName;
+        }
+
+        if (fileName.startsWith(EJB_DD)) {
+            return path + JONAS_DD;
+        }
+
+        int endOfBaseName = descriptorName.indexOf(getConfig().baseNameTerminator, startOfFileName);
+
+        /*
+         * Check for the odd case where the terminator and/or filename
+         * extension aren't found.  These will ensure "jonas-" appears at the
+         * end of the name and before the '.' (if present).
+         */
+        if (endOfBaseName < 0) {
+            // baseNameTerminator not found: the descriptor use the
+            // JOnAS naming convention, ie [Foo.xml,jonas-Foo.xml] and
+            // not [Foo<baseNameTerminator>-ejb-jar.xml,
+            // Foo<baseNameTerminator>-jonas-ejb-jar.xml].
+            endOfBaseName = descriptorName.lastIndexOf('.') - 1;
+            if (endOfBaseName < 0) {
+                // no . found
+                endOfBaseName = descriptorName.length() - 1;
+            }
+
+            jonasConvention = true;
+        }
+
+        baseName = descriptorName.substring(startOfFileName + 1, endOfBaseName + 1);
+        remainder = descriptorName.substring(endOfBaseName + 1);
+
+        if (jonasConvention) {
+            jonasDescriptorName = path + "jonas-" + baseName + ".xml";
+        } else {
+            jonasDescriptorName = path + baseName + "jonas-" + remainder;
+        }
+
+        log("Standard EJB descriptor name: " + descriptorName, Project.MSG_VERBOSE);
+        log("JOnAS-specific descriptor name: " + jonasDescriptorName, Project.MSG_VERBOSE);
+
+        return jonasDescriptorName;
+    }
+
+    protected String getJarBaseName(String descriptorFileName) {
+
+        String baseName = null;
+
+        if (getConfig().namingScheme.getValue().equals(EjbJar.NamingScheme.DESCRIPTOR)) {
+
+            // try to find JOnAS specific convention name
+            if (descriptorFileName.indexOf(getConfig().baseNameTerminator) == -1) {
+
+                // baseNameTerminator not found: the descriptor use the
+                // JOnAS naming convention, ie [Foo.xml,jonas-Foo.xml] and
+                // not [Foo<baseNameTerminator>-ejb-jar.xml,
+                // Foo<baseNameTerminator>-jonas-ejb-jar.xml].
+
+                String aCanonicalDescriptor = descriptorFileName.replace('\\', '/');
+                int lastSeparatorIndex = aCanonicalDescriptor.lastIndexOf('/');
+                int endOfBaseName;
+
+                if (lastSeparatorIndex != -1) {
+                    endOfBaseName = descriptorFileName.indexOf(".xml", lastSeparatorIndex);
+                } else {
+                    endOfBaseName = descriptorFileName.indexOf(".xml");
+                }
+
+                if (endOfBaseName != -1) {
+                    baseName = descriptorFileName.substring(0, endOfBaseName);
+                }
+            }
+        }
+
+        if (baseName == null) {
+            // else get standard baseName
+            baseName = super.getJarBaseName(descriptorFileName);
+        }
+
+        log("JAR base name: " + baseName, Project.MSG_VERBOSE);
+
+        return baseName;
+    }
+
+    protected void registerKnownDTDs(DescriptorHandler handler) {
+        handler.registerDTD(EJB_JAR_1_1_PUBLIC_ID,
+                    jonasroot + File.separator + "xml" + File.separator + EJB_JAR_1_1_DTD);
+        handler.registerDTD(EJB_JAR_2_0_PUBLIC_ID,
+                    jonasroot + File.separator + "xml" + File.separator + EJB_JAR_2_0_DTD);
+
+        handler.registerDTD(JONAS_EJB_JAR_2_4_PUBLIC_ID,
+                    jonasroot + File.separator + "xml" + File.separator + JONAS_EJB_JAR_2_4_DTD);
+        handler.registerDTD(JONAS_EJB_JAR_2_5_PUBLIC_ID,
+                    jonasroot + File.separator + "xml" + File.separator + JONAS_EJB_JAR_2_5_DTD);
+    }
+
+    /**
+     * Add to the given hashtable all the file generated by GenIC.
+     *
+     * @param genericJarFile jar file.
+     * @param ejbFiles the hashtable.
+     */
+    private void addGenICGeneratedFiles(File genericJarFile, Hashtable ejbFiles) {
+        // GenIC task
+        Java genicTask = null;
+
+        // GenIC class (3 GenIC classes for various versions of JOnAS
+        // are supported)
+        String genicClass = null;
+
+        if (nogenic) {
+            return;
+        }
+
+        genicTask = (Java) getTask().getProject().createTask("java");
+        genicTask.setTaskName("genic");
+        genicTask.setFork(true);
+
+        // jonasroot
+        genicTask.createJvmarg().setValue("-Dinstall.root=" + jonasroot);
+
+        // java policy file
+        String jonasConfigDir = jonasroot + File.separator + "config";
+        File javaPolicyFile = new File(jonasConfigDir, "java.policy");
+        if (javaPolicyFile.exists()) {
+            genicTask.createJvmarg().setValue("-Djava.security.policy="
+                              + javaPolicyFile.toString());
+        }
+
+        // outputdir
+        try {
+            outputdir = createTempDir();
+        } catch (IOException aIOException) {
+            String msg = "Cannot create temp dir: " + aIOException.getMessage();
+            throw new BuildException(msg, aIOException);
+        }
+        log("Using temporary output directory: " + outputdir, Project.MSG_VERBOSE);
+
+        genicTask.createArg().setValue("-d");
+        genicTask.createArg().setFile(outputdir);
+
+        // work around a bug of GenIC 2.5
+        String key;
+        File f;
+        Enumeration keys = ejbFiles.keys();
+        while (keys.hasMoreElements()) {
+            key = (String) keys.nextElement();
+            f = new File(outputdir + File.separator + key);
+            f.getParentFile().mkdirs();
+        }
+        log("Worked around a bug of GenIC 2.5.", Project.MSG_VERBOSE);
+
+        // classpath
+        Path classpath = getCombinedClasspath();
+        if (classpath == null) {
+            classpath = new Path(getTask().getProject());
+        }
+        classpath.append(new Path(classpath.getProject(), jonasConfigDir));
+        classpath.append(new Path(classpath.getProject(), outputdir.toString()));
+
+        // try to create the classpath for the correct ORB
+        if (orb != null) {
+            String orbJar = jonasroot + File.separator + "lib"
+                + File.separator + orb + "_jonas.jar";
+            classpath.append(new Path(classpath.getProject(), orbJar));
+        }
+
+        log("Using classpath: " + classpath.toString(), Project.MSG_VERBOSE);
+        genicTask.setClasspath(classpath);
+
+        // class name (search in the classpath provided for the ejbjar element)
+        genicClass = getGenicClassName(classpath);
+        if (genicClass == null) {
+            log("Cannot find GenIC class in classpath.", Project.MSG_ERR);
+            throw new BuildException("GenIC class not found, please check the classpath.");
+        } else {
+            log("Using '" + genicClass + "' GenIC class." , Project.MSG_VERBOSE);
+            genicTask.setClassname(genicClass);
+        }
+
+        // keepgenerated
+        if (keepgenerated) {
+            genicTask.createArg().setValue("-keepgenerated");
+        }
+
+        // nocompil
+        if (nocompil) {
+            genicTask.createArg().setValue("-nocompil");
+        }
+
+        // novalidation
+        if (novalidation) {
+            genicTask.createArg().setValue("-novalidation");
+        }
+
+        // javac
+        if (javac != null) {
+            genicTask.createArg().setValue("-javac");
+            genicTask.createArg().setLine(javac);
+        }
+
+        // javacopts
+        if (javacopts != null && !javacopts.equals("")) {
+            genicTask.createArg().setValue("-javacopts");
+            genicTask.createArg().setLine(javacopts);
+        }
+
+        // rmicopts
+        if (rmicopts != null && !rmicopts.equals("")) {
+            genicTask.createArg().setValue("-rmicopts");
+            genicTask.createArg().setLine(rmicopts);
+        }
+
+        // secpropag
+        if (secpropag) {
+            genicTask.createArg().setValue("-secpropag");
+        }
+
+        // verbose
+        if (verbose) {
+            genicTask.createArg().setValue("-verbose");
+                }
+
+        // additionalargs
+        if (additionalargs != null) {
+            genicTask.createArg().setValue(additionalargs);
+        }
+
+        // the generated classes must not be added in the generic JAR!
+        // is that buggy on old JOnAS (2.4) ??
+        genicTask.createArg().setValue("-noaddinjar");
+
+        // input file to process by GenIC
+        genicTask.createArg().setValue(genericJarFile.getPath());
+
+        // calling GenIC task
+        log("Calling " + genicClass + " for " + getConfig().descriptorDir
+            + File.separator + descriptorName + ".", Project.MSG_VERBOSE);
+
+        if (genicTask.executeJava() != 0) {
+
+            // the method deleteOnExit() do not work because the directory is not empty
+            log("Deleting temp output directory '" + outputdir + "'.", Project.MSG_VERBOSE);
+            deleteAllFiles(outputdir);
+
+            if (!keepgeneric) {
+            log("Deleting generic JAR " + genericJarFile.toString(), Project.MSG_VERBOSE);
+            genericJarFile.delete();
+            }
+
+            throw new BuildException("GenIC reported an error.");
+        }
+
+        // add the generated files to the ejbFiles
+        addAllFiles(outputdir, "", ejbFiles);
+    }
+
+    /**
+     * Get the GenIC class name to use in the given classpath.
+     *
+     * @param classpath classpath where the GenIC class must be searched.
+     * @return the GenIC class name. Return <code>null</code> if the class name
+     * cannot be found.
+     */
+    String getGenicClassName(Path classpath) {
+
+        log("Looking for GenIC class in classpath: "
+            + classpath.toString(), Project.MSG_VERBOSE);
+
+        AntClassLoader cl = classpath.getProject().createClassLoader(classpath);
+
+        try {
+            cl.loadClass(JonasDeploymentTool.GENIC_CLASS);
+            log("Found GenIC class '" + JonasDeploymentTool.GENIC_CLASS
+                + "' in classpath.", Project.MSG_VERBOSE);
+            return JonasDeploymentTool.GENIC_CLASS;
+
+        } catch (ClassNotFoundException cnf1) {
+            log("GenIC class '" + JonasDeploymentTool.GENIC_CLASS
+                + "' not found in classpath.",
+            Project.MSG_VERBOSE);
+        }
+
+        try {
+            cl.loadClass(JonasDeploymentTool.OLD_GENIC_CLASS_1);
+            log("Found GenIC class '" + JonasDeploymentTool.OLD_GENIC_CLASS_1
+                + "' in classpath.", Project.MSG_VERBOSE);
+            return JonasDeploymentTool.OLD_GENIC_CLASS_1;
+
+        } catch (ClassNotFoundException cnf2) {
+            log("GenIC class '" + JonasDeploymentTool.OLD_GENIC_CLASS_1
+                + "' not found in classpath.",
+            Project.MSG_VERBOSE);
+        }
+
+        try {
+            cl.loadClass(JonasDeploymentTool.OLD_GENIC_CLASS_2);
+            log("Found GenIC class '" + JonasDeploymentTool.OLD_GENIC_CLASS_2
+                + "' in classpath.", Project.MSG_VERBOSE);
+            return JonasDeploymentTool.OLD_GENIC_CLASS_2;
+
+        } catch (ClassNotFoundException cnf3) {
+            log("GenIC class '" + JonasDeploymentTool.OLD_GENIC_CLASS_2
+                + "' not found in classpath.",
+            Project.MSG_VERBOSE);
+        }
+        return null;
+    }
+
+    protected void checkConfiguration(String descriptorFileName,
+                      SAXParser saxParser) throws BuildException {
+
+        // jonasroot
+        if (jonasroot == null) {
+            throw new BuildException("The jonasroot attribut is not set.");
+        } else if (!jonasroot.isDirectory()) {
+            throw new BuildException("The jonasroot attribut '" + jonasroot
+                + "' is not a valid directory.");
+        }
+
+        // orb
+        if (orb != null && !orb.equals(RMI_ORB) && !orb.equals(JEREMIE_ORB)
+            && !orb.equals(DAVID_ORB)) {
+            throw new BuildException("The orb attribut '" + orb
+                + "' is not valid (must be either "
+                + RMI_ORB + ", " + JEREMIE_ORB + " or " + DAVID_ORB + ").");
+        }
+
+        // additionalargs
+        if (additionalargs != null && additionalargs.equals("")) {
+            throw new BuildException("Empty additionalargs attribut.");
+        }
+
+        // javac
+        if (javac != null && javac.equals("")) {
+            throw new BuildException("Empty javac attribut.");
+        }
+    }
+
+    /* ----------------------------------------------------------------------------------- */
+    /* utilitary methods */
+    /* ----------------------------------------------------------------------------------- */
+
+    /**
+     * Create a temporary directory for GenIC output.
+     *
+     * @return the temp directory.
+     * @throws BuildException if a temp directory cannot be created.
+     */
+    private File createTempDir() throws IOException {
+        File tmpDir = File.createTempFile("genic", null, null);
+        tmpDir.delete();
+        if (!tmpDir.mkdir()) {
+            throw new IOException("Cannot create the temporary directory '" + tmpDir + "'.");
+        }
+        return tmpDir;
+    }
+
+    /**
+     * Delete a file. If the file is a directory, delete recursivly all the
+     * files inside.
+     *
+     * @param aFile file to delete.
+     */
+    private void deleteAllFiles(File aFile) {
+        if (aFile.isDirectory()) {
+            File[] someFiles = aFile.listFiles();
+
+            for (int i = 0; i < someFiles.length; i++) {
+                deleteAllFiles(someFiles[i]);
+            }
+        }
+        aFile.delete();
+    }
+
+    /**
+     * Add a file to the a given hashtable. If the file is a directory, add
+     * recursivly all the files inside to the hashtable.
+     *
+     * @param file the file to add.
+     * @param rootDir the current sub-directory to scan.
+     * @param hashtable the hashtable where to add the files.
+     */
+    private void addAllFiles(File file, String rootDir, Hashtable hashtable) {
+
+        if (!file.exists()) {
+            throw new IllegalArgumentException();
+        }
+
+        String newRootDir;
+        if (file.isDirectory()) {
+            File[] files = file.listFiles();
+            for (int i = 0; i < files.length; i++) {
+                if (rootDir.length() > 0) {
+                    newRootDir = rootDir + File.separator + files[i].getName();
+                } else {
+                    newRootDir = files[i].getName();
+                }
+                addAllFiles(files[i], newRootDir, hashtable);
+            }
+        } else {
+            hashtable.put(rootDir, file);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WLRun.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WLRun.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WLRun.java	(revision 16139)
@@ -0,0 +1,417 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Starts a WebLogic server.
+ * A number of parameters are used to control the operation of the weblogic
+ * instance. Note that the task, and hence ant, will not complete until the
+ * weblogic instance is stopped.</p>
+ *
+ */
+public class WLRun extends Task {
+    protected static final String DEFAULT_WL51_POLICY_FILE = "weblogic.policy";
+    protected static final String DEFAULT_WL60_POLICY_FILE = "lib/weblogic.policy";
+    protected static final String DEFAULT_PROPERTIES_FILE = "weblogic.properties";
+
+    /**
+     * The classpath to be used when running the Java VM. It must contain the
+     * weblogic classes <b>and</b> the implementation classes of the home and
+     * remote interfaces.
+     */
+    private Path classpath;
+
+    /**
+     * The weblogic classpath to the be used when running weblogic.
+     */
+    private Path weblogicClasspath;
+
+    private String weblogicMainClass = "weblogic.Server";
+
+    /**
+     * Addional arguments to pass to the JVM used to run weblogic
+     */
+    private String additionalArgs = "";
+
+    /**
+     * The security policy to use when running the weblogic server
+     */
+    private String securityPolicy;
+
+    /**
+     * The weblogic system home directory
+     */
+    private File weblogicSystemHome;
+
+    /**
+     * The weblogic domain
+     */
+    private String weblogicDomainName;
+
+    /**
+     * The name of the weblogic server - used to select the server's directory in the
+     * weblogic home directory.
+     */
+    private String weblogicSystemName = "myserver";
+
+    /**
+     * The file containing the weblogic properties for this server.
+     */
+    private String weblogicPropertiesFile = null;
+
+    /**
+     * additional args to pass to the spawned jvm
+     */
+    private String additionalJvmArgs = "";
+
+    /**
+     * The location of the BEA Home under which this server is run.
+     * WL6 only
+     */
+    private File beaHome = null;
+
+    /**
+     * The management username
+     */
+    private String managementUsername = "system";
+
+    /**
+     * The management password
+     */
+    private String managementPassword = null;
+
+    /**
+     * The provate key password - used for SSL
+     */
+    private String pkPassword = null;
+
+    /**
+     * Add the classpath for the user classes
+     */
+    public Path createClasspath() {
+        if (classpath == null) {
+            classpath = new Path(getProject());
+        }
+        return classpath.createPath();
+    }
+
+    /**
+     * Get the classpath to the weblogic classpaths
+     */
+    public Path createWLClasspath() {
+        if (weblogicClasspath == null) {
+            weblogicClasspath = new Path(getProject());
+        }
+        return weblogicClasspath.createPath();
+    }
+
+    /**
+     * Do the work.
+     *
+     * The work is actually done by creating a separate JVM to run a helper task.
+     * This approach allows the classpath of the helper task to be set. Since the
+     * weblogic tools require the class files of the project's home and remote
+     * interfaces to be available in the classpath, this also avoids having to
+     * start ant with the class path of the project it is building.
+     *
+     * @exception BuildException if someting goes wrong with the build
+     */
+    public void execute() throws BuildException {
+        if (weblogicSystemHome == null) {
+            throw new BuildException("weblogic home must be set");
+        }
+        if (!weblogicSystemHome.isDirectory()) {
+            throw new BuildException("weblogic home directory "
+                + weblogicSystemHome.getPath() + " is not valid");
+        }
+
+        if (beaHome != null) {
+            executeWLS6();
+        } else {
+            executeWLS();
+        }
+    }
+
+    private File findSecurityPolicyFile(String defaultSecurityPolicy) {
+        String securityPolicy = this.securityPolicy;
+        if (securityPolicy == null) {
+            securityPolicy = defaultSecurityPolicy;
+        }
+        File securityPolicyFile = new File(weblogicSystemHome, securityPolicy);
+        // If an explicit securityPolicy file was specified, it maybe an
+        // absolute path.  Use the project to resolve it.
+        if (this.securityPolicy != null && !securityPolicyFile.exists()) {
+            securityPolicyFile = getProject().resolveFile(securityPolicy);
+        }
+        // If we still can't find it, complain
+        if (!securityPolicyFile.exists()) {
+            throw new BuildException("Security policy " + securityPolicy
+                                    + " was not found.");
+        }
+        return securityPolicyFile;
+    }
+
+    private void executeWLS6() {
+        File securityPolicyFile
+            = findSecurityPolicyFile(DEFAULT_WL60_POLICY_FILE);
+        if (!beaHome.isDirectory()) {
+            throw new BuildException("BEA home " + beaHome.getPath()
+                                     + " is not valid");
+        }
+
+        File configFile = new File(weblogicSystemHome, "config/"
+            + weblogicDomainName + "/config.xml");
+        if (!configFile.exists()) {
+            throw new BuildException("Server config file " + configFile
+                + " not found.");
+        }
+
+        if (managementPassword == null) {
+            throw new BuildException("You must supply a management password "
+                                    + "to start the server");
+        }
+
+        Java weblogicServer = (Java) getProject().createTask("java");
+        weblogicServer.setTaskName(getTaskName());
+        weblogicServer.setFork(true);
+        weblogicServer.setDir(weblogicSystemHome);
+        weblogicServer.setClassname(weblogicMainClass);
+
+        String jvmArgs = additionalJvmArgs;
+
+        jvmArgs += " -Dweblogic.Domain=" + weblogicDomainName;
+        jvmArgs += " -Dweblogic.Name=" + weblogicSystemName;
+        jvmArgs += " -Dweblogic.system.home=" + weblogicSystemHome;
+
+        jvmArgs += " -Dbea.home=" + beaHome;
+        jvmArgs += " -Djava.security.policy==" + securityPolicyFile;
+
+        jvmArgs += " -Dweblogic.management.username=" + managementUsername;
+        jvmArgs += " -Dweblogic.management.password=" + managementPassword;
+        if (pkPassword != null) {
+            jvmArgs += " -Dweblogic.pkpassword=" + pkPassword;
+        }
+
+
+        weblogicServer.createJvmarg().setLine(jvmArgs);
+        weblogicServer.createArg().setLine(additionalArgs);
+
+        if (classpath != null) {
+            weblogicServer.setClasspath(classpath);
+        }
+
+        if (weblogicServer.executeJava() != 0) {
+            throw new BuildException("Execution of weblogic server failed");
+        }
+     }
+
+    private void executeWLS() {
+        File securityPolicyFile
+            = findSecurityPolicyFile(DEFAULT_WL51_POLICY_FILE);
+        File propertiesFile = null;
+
+
+        if (weblogicPropertiesFile == null) {
+            weblogicPropertiesFile = DEFAULT_PROPERTIES_FILE;
+        }
+        propertiesFile = new File(weblogicSystemHome, weblogicPropertiesFile);
+        if (!propertiesFile.exists()) {
+            // OK, properties file may be absolute
+            propertiesFile = getProject().resolveFile(weblogicPropertiesFile);
+            if (!propertiesFile.exists()) {
+                throw new BuildException("Properties file "
+                    + weblogicPropertiesFile
+                    + " not found in weblogic home " + weblogicSystemHome
+                    + " or as absolute file");
+            }
+        }
+
+        Java weblogicServer = (Java) getProject().createTask("java");
+        weblogicServer.setTaskName(getTaskName());
+        weblogicServer.setFork(true);
+        weblogicServer.setClassname(weblogicMainClass);
+
+        String jvmArgs = additionalJvmArgs;
+
+        if (weblogicClasspath != null) {
+            jvmArgs += " -Dweblogic.class.path=" + weblogicClasspath;
+        }
+
+        jvmArgs += " -Djava.security.manager -Djava.security.policy==" + securityPolicyFile;
+        jvmArgs += " -Dweblogic.system.home=" + weblogicSystemHome;
+        jvmArgs += " -Dweblogic.system.name=" + weblogicSystemName;
+        jvmArgs += " -Dweblogic.system.propertiesFile=" + weblogicPropertiesFile;
+
+        weblogicServer.createJvmarg().setLine(jvmArgs);
+        weblogicServer.createArg().setLine(additionalArgs);
+
+        if (classpath != null) {
+            weblogicServer.setClasspath(classpath);
+        }
+        if (weblogicServer.executeJava() != 0) {
+            throw new BuildException("Execution of weblogic server failed");
+        }
+    }
+
+
+    /**
+     * The classpath to be used with the Java Virtual Machine that runs the Weblogic
+     * Server; required. Prior to Weblogic 6.0, this is typically set to the Weblogic
+     * boot classpath. Under Weblogic 6.0 this should include all the
+     * weblogic jars
+     *
+     * @param classpath the classpath to use when executing the weblogic server.
+     */
+    public void setClasspath(Path classpath) {
+        this.classpath = classpath;
+    }
+
+    /**
+     * Set the weblogic classpath used by the Weblogic Server;
+     * optional, and only applicable to WL4.5.1
+     *
+     * The weblogic classpath is used by weblogic to support dynamic class loading.
+     *
+     * @param weblogicClasspath the weblogic classpath
+     */
+    public void setWlclasspath(Path weblogicClasspath) {
+        this.weblogicClasspath = weblogicClasspath;
+    }
+
+    /**
+     * The name of the security policy file within the weblogic home directory that
+     * is to be used. If not specified, the default policy file <code>weblogic.policy</code>
+     * is used.
+     *
+     * @param securityPolicy the security policy to use.
+     */
+    public void setPolicy(String securityPolicy) {
+        this.securityPolicy = securityPolicy;
+    }
+
+    /**
+     * The location where weblogic lives.
+     * Required. This is the absolute location, not relative to
+     *  BEA home.
+     * @param weblogicHome the home directory of weblogic.
+     *
+     */
+    public void setHome(File weblogicHome) {
+        weblogicSystemHome = weblogicHome;
+    }
+
+    /**
+     * The location of the BEA Home; implicitly
+     * selects Weblogic 6.0; optional.
+     *
+     * @param beaHome the BEA Home directory.
+     *
+     */
+    public void setBEAHome(File beaHome) {
+        this.beaHome = beaHome;
+    }
+
+    /**
+     * The name of the weblogic server within the weblogic home which is to be run.
+     * Optiona, defaults to &quot;myserver&quot;
+     *
+     * @param serverName the name of the server.
+     */
+    public void setName(String serverName) {
+        this.weblogicSystemName = serverName;
+    }
+
+    /**
+     * Set the Domain to run in; required for WL6.0
+     *
+     * @param domain the domain
+     */
+    public void setDomain(String domain) {
+        this.weblogicDomainName = domain;
+    }
+
+    /**
+     * The name of the server's properties file within the weblogic home directory
+     * used to control the weblogic instance;
+     * required for WL4.5.1
+     *
+     *
+     * @param propertiesFilename the properties file name
+     */
+    public void setProperties(String propertiesFilename) {
+        this.weblogicPropertiesFile = propertiesFilename;
+    }
+
+    /**
+     * Set the additional arguments to pass to the weblogic JVM
+     * @param args the arguments to be passed to the JVM
+     */
+    public void setJvmargs(String args) {
+        this.additionalJvmArgs = args;
+    }
+
+    /**
+     * Set the management username to run the server;
+     * optional and only applicable to WL6.0.
+     *
+     * @param username the management username of the server.
+     */
+    public void setUsername(String username) {
+        this.managementUsername = username;
+    }
+
+
+    /**
+     * Set the management password of the server;
+     * optional and only applicable to WL6.0.
+     * @param password the management pasword of the server.
+     */
+    public void setPassword(String password) {
+        this.managementPassword = password;
+    }
+
+    /**
+     * Set the private key password so the server can decrypt the SSL private key file;
+     * optional and only applicable to WL6.0.
+     * @param pkpassword the private key password,
+     */
+    public void setPKPassword(String pkpassword) {
+        this.pkPassword = pkpassword;
+    }
+
+    /**
+     * Additional argument string passed to the Weblogic instance;
+     * optional.
+     */
+    public void setArgs(String args) {
+        additionalArgs = args;
+    }
+
+    /**
+     * name of the main class for weblogic; optional.
+     */
+    public void setWeblogicMainClass(String c) {
+        weblogicMainClass = c;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WLStop.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WLStop.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WLStop.java	(revision 16139)
@@ -0,0 +1,173 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Shuts down a WebLogic server.
+ * To shut down an instance you must supply both a username and
+ * a password.
+ *
+ */
+public class WLStop extends Task {
+    /**
+     * The classpath to be used. It must contains the weblogic.Admin class.
+     */
+    private Path classpath;
+
+    /**
+     * The weblogic username to use to request the shutdown.
+     */
+    private String username;
+
+    /**
+     * The password to use to shutdown the weblogic server.
+     */
+    private String password;
+
+    /**
+     * The URL which the weblogic server is listening on.
+     */
+    private String serverURL;
+
+    /**
+     * The delay (in seconds) to wait before shutting down.
+     */
+    private int delay = 0;
+
+    /**
+     * The location of the BEA Home under which this server is run.
+     * WL6 only
+     */
+    private File beaHome = null;
+
+    /**
+     * Do the work.
+     *
+     * The work is actually done by creating a separate JVM to run the weblogic admin task
+     * This approach allows the classpath of the helper task to be set.
+     *
+     * @exception BuildException if someting goes wrong with the build
+     */
+    public void execute() throws BuildException {
+        if (username == null || password == null) {
+            throw new BuildException("weblogic username and password must both be set");
+        }
+
+        if (serverURL == null) {
+            throw new BuildException("The url of the weblogic server must be provided.");
+        }
+
+        Java weblogicAdmin = (Java) getProject().createTask("java");
+        weblogicAdmin.setFork(true);
+        weblogicAdmin.setClassname("weblogic.Admin");
+        String args;
+
+        if (beaHome == null) {
+            args = serverURL + " SHUTDOWN " + username + " " + password + " " + delay;
+        } else {
+            args = " -url " + serverURL
+                    + " -username " + username
+                    + " -password " + password
+                    + " SHUTDOWN " + " " + delay;
+        }
+
+        weblogicAdmin.createArg().setLine(args);
+        weblogicAdmin.setClasspath(classpath);
+        weblogicAdmin.execute();
+    }
+
+    /**
+     * The classpath to be used with the Java Virtual Machine that runs the Weblogic
+     * Shutdown command;
+     *
+     * @param path the classpath to use when executing the weblogic admin task.
+     */
+    public void setClasspath(Path path) {
+        this.classpath = path;
+    }
+
+    /**
+     * The classpath to be used with the Java Virtual Machine that runs the Weblogic
+     * Shutdown command;
+     */
+    public Path createClasspath() {
+        if (classpath == null) {
+            classpath = new Path(getProject());
+        }
+        return classpath.createPath();
+    }
+
+    /**
+     * The username of the account which will be used to shutdown the server;
+     * required.
+     *
+     * @param s the username.
+     */
+    public void setUser(String s) {
+        this.username = s;
+    }
+
+    /**
+     * The password for the account specified in the
+     * user parameter; required
+     *
+     * @param s the password.
+     */
+    public void setPassword(String s) {
+        this.password = s;
+    }
+
+    /**
+     * Set the URL to which the weblogic server is listening
+     * for T3 connections; required.
+     *
+     * @param s the url.
+     */
+    public void setUrl(String s) {
+        this.serverURL = s;
+    }
+
+
+    /**
+     * Set the delay (in seconds) before shutting down the server;
+     * optional.
+     *
+     * @param s the selay.
+     */
+    public void setDelay(String s) {
+        delay = Integer.parseInt(s);
+    }
+
+    /**
+     * The location of the BEA Home; implicitly
+     * selects Weblogic 6.0 shutdown; optional.
+     *
+     * @param beaHome the BEA Home directory.
+     *
+     */
+    public void setBEAHome(File beaHome) {
+        this.beaHome = beaHome;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicDeploymentTool.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicDeploymentTool.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicDeploymentTool.java	(revision 16139)
@@ -0,0 +1,875 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Vector;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.types.Environment;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+import org.xml.sax.InputSource;
+
+/**
+    The weblogic element is used to control the weblogic.ejbc compiler for
+    generating weblogic EJB jars. Prior to Ant 1.3, the method of locating CMP
+    descriptors was to use the ejbjar naming convention. So if your ejb-jar was
+    called, Customer-ejb-jar.xml, your weblogic descriptor was called Customer-
+    weblogic-ejb-jar.xml and your CMP descriptor had to be Customer-weblogic-cmp-
+    rdbms-jar.xml. In addition, the &lt;type-storage&gt; element in the weblogic
+    descriptor had to be set to the standard name META-INF/weblogic-cmp-rdbms-
+    jar.xml, as that is where the CMP descriptor was mapped to in the generated
+    jar.
+*/
+public class WeblogicDeploymentTool extends GenericDeploymentTool {
+    public static final String PUBLICID_EJB11
+         = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN";
+    public static final String PUBLICID_EJB20
+         = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN";
+    public static final String PUBLICID_WEBLOGIC_EJB510
+         = "-//BEA Systems, Inc.//DTD WebLogic 5.1.0 EJB//EN";
+    public static final String PUBLICID_WEBLOGIC_EJB600
+         = "-//BEA Systems, Inc.//DTD WebLogic 6.0.0 EJB//EN";
+    public static final String PUBLICID_WEBLOGIC_EJB700
+         = "-//BEA Systems, Inc.//DTD WebLogic 7.0.0 EJB//EN";
+
+    protected static final String DEFAULT_WL51_EJB11_DTD_LOCATION
+         = "/weblogic/ejb/deployment/xml/ejb-jar.dtd";
+    protected static final String DEFAULT_WL60_EJB11_DTD_LOCATION
+         = "/weblogic/ejb20/dd/xml/ejb11-jar.dtd";
+    protected static final String DEFAULT_WL60_EJB20_DTD_LOCATION
+         = "/weblogic/ejb20/dd/xml/ejb20-jar.dtd";
+
+    protected static final String DEFAULT_WL51_DTD_LOCATION
+         = "/weblogic/ejb/deployment/xml/weblogic-ejb-jar.dtd";
+    protected static final String DEFAULT_WL60_51_DTD_LOCATION
+         = "/weblogic/ejb20/dd/xml/weblogic510-ejb-jar.dtd";
+    protected static final String DEFAULT_WL60_DTD_LOCATION
+         = "/weblogic/ejb20/dd/xml/weblogic600-ejb-jar.dtd";
+    protected static final String DEFAULT_WL70_DTD_LOCATION
+         = "/weblogic/ejb20/dd/xml/weblogic700-ejb-jar.dtd";
+
+    protected static final String DEFAULT_COMPILER = "default";
+
+    protected static final String WL_DD = "weblogic-ejb-jar.xml";
+    protected static final String WL_CMP_DD = "weblogic-cmp-rdbms-jar.xml";
+
+    protected static final String COMPILER_EJB11 = "weblogic.ejbc";
+    protected static final String COMPILER_EJB20 = "weblogic.ejbc20";
+
+    /** Instance variable that stores the suffix for the weblogic jarfile. */
+    private String jarSuffix = ".jar";
+
+    /** Instance variable that stores the location of the weblogic DTD file. */
+    private String weblogicDTD;
+
+    /** Instance variable that stores the location of the ejb 1.1 DTD file. */
+    private String ejb11DTD;
+
+    /** Instance variable that determines whether generic ejb jars are kept. */
+    private boolean keepgenerated = false;
+
+    /**
+     * Instance variable that stores the fully qualified classname of the
+     * weblogic EJBC compiler
+     */
+    private String ejbcClass = null;
+
+    private String additionalArgs = "";
+
+    /**
+     * additional args to pass to the spawned jvm
+     */
+    private String additionalJvmArgs = "";
+
+    private boolean keepGeneric = false;
+
+    private String compiler = null;
+
+    private boolean alwaysRebuild = true;
+
+    /** controls whether ejbc is run on the generated jar */
+    private boolean noEJBC = false;
+
+    /** Indicates if the old CMP location convention is to be used.  */
+    private boolean newCMP = false;
+
+    /** The classpath to the weblogic classes. */
+    private Path wlClasspath = null;
+
+    /** System properties for the JVM. */
+    private Vector sysprops = new Vector();
+
+    /**
+     * The weblogic.StdoutSeverityLevel to use when running the JVM that
+     * executes ejbc. Set to 16 to avoid the warnings about EJB Home and
+     * Remotes being in the classpath
+     */
+    private Integer jvmDebugLevel = null;
+
+    /** File utilities instance for copying jars */
+    private FileUtils fileUtils = FileUtils.newFileUtils();
+
+    private File outputDir;
+
+    /**
+     * Add a nested sysproperty element.
+     */
+    public void addSysproperty(Environment.Variable sysp) {
+        sysprops.add(sysp);
+    }
+
+
+    /** Get the classpath to the weblogic classpaths  */
+    public Path createWLClasspath() {
+        if (wlClasspath == null) {
+            wlClasspath = new Path(getTask().getProject());
+        }
+        return wlClasspath.createPath();
+    }
+
+    /**
+     * If set ejbc will use this directory as the output
+     * destination rather than a jar file. This allows for the
+     * generation of &quot;exploded&quot; jars.
+     */
+    public void setOutputDir(File outputDir) {
+        this.outputDir = outputDir;
+    }
+
+
+    /**
+     * Optional classpath to WL6.0.
+     * Weblogic 6.0 will give a warning if the home and remote interfaces
+     * of a bean are on the system classpath used to run weblogic.ejbc.
+     * In that case, the standard weblogic classes should be set with
+     * this attribute (or equivalent nested element) and the
+     * home and remote interfaces located with the standard classpath
+     * attribute
+     */
+    public void setWLClasspath(Path wlClasspath) {
+        this.wlClasspath = wlClasspath;
+    }
+
+
+    /**
+     * The compiler (switch <code>-compiler</code>) to use; optional.
+     * This allows for the selection of a different compiler
+     * to be used for the compilation of the generated Java
+     * files. This could be set, for example, to Jikes to
+     * compile with the Jikes compiler. If this is not set
+     * and the <code>build.compiler</code> property is set
+     * to jikes, the Jikes compiler will be used. If this
+     * is not desired, the value &quot;<code>default</code>&quot;
+     * may be given to use the default compiler
+     */
+    public void setCompiler(String compiler) {
+        this.compiler = compiler;
+    }
+
+
+    /**
+     * Set the rebuild flag to false to only update changes in the jar rather
+     * than rerunning ejbc; optional, default true.
+     * This flag controls whether weblogic.ejbc is always
+     * invoked to build the jar file. In certain circumstances,
+     * such as when only a bean class has been changed, the jar
+     * can be generated by merely replacing the changed classes
+     * and not rerunning ejbc. Setting this to false will reduce
+     * the time to run ejbjar.
+     */
+    public void setRebuild(boolean rebuild) {
+        this.alwaysRebuild = rebuild;
+    }
+
+
+    /**
+     * Sets the weblogic.StdoutSeverityLevel to use when running the JVM that
+     * executes ejbc; optional. Set to 16 to avoid the warnings about EJB Home and
+     * Remotes being in the classpath
+     */
+    public void setJvmDebugLevel(Integer jvmDebugLevel) {
+        this.jvmDebugLevel = jvmDebugLevel;
+    }
+
+
+    public Integer getJvmDebugLevel() {
+        return jvmDebugLevel;
+    }
+
+
+
+    /**
+     * Setter used to store the suffix for the generated weblogic jar file.
+     *
+     * @param inString the string to use as the suffix.
+     */
+    public void setSuffix(String inString) {
+        this.jarSuffix = inString;
+    }
+
+
+    /**
+     * controls whether the generic file used as input to
+     * ejbc is retained; defaults to false
+     *
+     * @param inValue true for keep generic
+     */
+    public void setKeepgeneric(boolean inValue) {
+        this.keepGeneric = inValue;
+    }
+
+
+    /**
+     * Controls whether weblogic will keep the generated Java
+     * files used to build the class files added to the
+     * jar. This can be useful when debugging; default is false.
+     *
+     * @param inValue either 'true' or 'false'
+     */
+    public void setKeepgenerated(String inValue) {
+        this.keepgenerated = Boolean.valueOf(inValue).booleanValue();
+    }
+
+
+    /**
+     * Any optional extra arguments pass to the weblogic.ejbc
+     * tool.
+     */
+    public void setArgs(String args) {
+        this.additionalArgs = args;
+    }
+
+
+    /**
+     * Set any additional arguments to pass to the weblogic JVM; optional.
+     * @param args the arguments to be passed to the JVM
+     */
+    public void setJvmargs(String args) {
+        this.additionalJvmArgs = args;
+    }
+
+    /**
+     * Set the classname of the ejbc compiler;  optional
+     * Normally ejbjar determines
+     * the appropriate class based on the DTD used for the EJB. The EJB 2.0 compiler
+     * featured in weblogic 6 has, however, been deprecated in version 7. When
+     * using with version 7 this attribute should be set to
+     * &quot;weblogic.ejbc&quot; to avoid the deprecation warning.
+     */
+    public void setEjbcClass(String ejbcClass) {
+        this.ejbcClass = ejbcClass;
+    }
+
+
+    /** Get the ejbc compiler class  */
+    public String getEjbcClass() {
+        return ejbcClass;
+    }
+
+
+    /**
+     * <b>Deprecated</b>. Defines the location of the ejb-jar DTD in
+     *  the weblogic class hierarchy. Should not be needed, and the
+     * nested &lt;dtd&gt; element is recommended when it is.
+     *
+     * @param inString the string to use as the DTD location.
+     */
+    public void setWeblogicdtd(String inString) {
+        setEJBdtd(inString);
+    }
+
+
+    /**
+     * <b>Deprecated</b>. Defines the location of weblogic DTD in
+     *  the weblogic class hierarchy. Should not be needed, and the
+     * nested &lt;dtd&gt; element is recommended when it is.
+     *
+     * @param inString the string to use as the DTD location.
+     */
+    public void setWLdtd(String inString) {
+        this.weblogicDTD = inString;
+    }
+
+
+    /**
+     * <b>Deprecated</b>. Defines the location of Sun's EJB DTD in
+     *  the weblogic class hierarchy. Should not be needed, and the
+     * nested &lt;dtd&gt; element is recommended when it is.
+     *
+     * @param inString the string to use as the DTD location.
+     */
+    public void setEJBdtd(String inString) {
+        this.ejb11DTD = inString;
+    }
+
+
+    /**
+     * Set the value of the oldCMP scheme. This is an antonym for newCMP
+     * @ant.attribute ignore="true'
+     */
+    public void setOldCMP(boolean oldCMP) {
+        this.newCMP = !oldCMP;
+    }
+
+
+    /**
+     * If this is set to true, the new method for locating
+     * CMP descriptors will be used; optional, default false.
+     * <P>
+     * The old CMP scheme locates the
+     * weblogic CMP descriptor based on the naming convention where the
+     * weblogic CMP file is expected to be named with the bean name as the
+     * prefix. Under this scheme the name of the CMP descriptor does not match
+     * the name actually used in the main weblogic EJB descriptor. Also,
+     * descriptors which contain multiple CMP references could not be used.
+     */
+    public void setNewCMP(boolean newCMP) {
+        this.newCMP = newCMP;
+    }
+
+
+    /**
+     * Do not EJBC the jar after it has been put together;
+     * optional, default false
+     */
+    public void setNoEJBC(boolean noEJBC) {
+        this.noEJBC = noEJBC;
+    }
+
+
+    protected void registerKnownDTDs(DescriptorHandler handler) {
+        // register all the known DTDs
+        handler.registerDTD(PUBLICID_EJB11, DEFAULT_WL51_EJB11_DTD_LOCATION);
+        handler.registerDTD(PUBLICID_EJB11, DEFAULT_WL60_EJB11_DTD_LOCATION);
+        handler.registerDTD(PUBLICID_EJB11, ejb11DTD);
+        handler.registerDTD(PUBLICID_EJB20, DEFAULT_WL60_EJB20_DTD_LOCATION);
+    }
+
+
+    protected DescriptorHandler getWeblogicDescriptorHandler(final File srcDir) {
+        DescriptorHandler handler =
+            new DescriptorHandler(getTask(), srcDir) {
+                protected void processElement() {
+                    if (currentElement.equals("type-storage")) {
+                        // Get the filename of vendor specific descriptor
+                        String fileNameWithMETA = currentText;
+                        //trim the META_INF\ off of the file name
+                        String fileName
+                             = fileNameWithMETA.substring(META_DIR.length(),
+                            fileNameWithMETA.length());
+                        File descriptorFile = new File(srcDir, fileName);
+
+                        ejbFiles.put(fileNameWithMETA, descriptorFile);
+                    }
+                }
+            };
+
+        handler.registerDTD(PUBLICID_WEBLOGIC_EJB510, DEFAULT_WL51_DTD_LOCATION);
+        handler.registerDTD(PUBLICID_WEBLOGIC_EJB510, DEFAULT_WL60_51_DTD_LOCATION);
+        handler.registerDTD(PUBLICID_WEBLOGIC_EJB600, DEFAULT_WL60_DTD_LOCATION);
+        handler.registerDTD(PUBLICID_WEBLOGIC_EJB700, DEFAULT_WL70_DTD_LOCATION);
+        handler.registerDTD(PUBLICID_WEBLOGIC_EJB510, weblogicDTD);
+        handler.registerDTD(PUBLICID_WEBLOGIC_EJB600, weblogicDTD);
+
+        for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) {
+            EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next();
+
+            handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation());
+        }
+        return handler;
+    }
+
+
+    /**
+     * Add any vendor specific files which should be included in the EJB Jar.
+     */
+    protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
+        File weblogicDD = new File(getConfig().descriptorDir, ddPrefix + WL_DD);
+
+        if (weblogicDD.exists()) {
+            ejbFiles.put(META_DIR + WL_DD,
+                weblogicDD);
+        } else {
+            log("Unable to locate weblogic deployment descriptor. "
+                + "It was expected to be in "
+                + weblogicDD.getPath(), Project.MSG_WARN);
+            return;
+        }
+
+        if (!newCMP) {
+            log("The old method for locating CMP files has been DEPRECATED.", Project.MSG_VERBOSE);
+            log("Please adjust your weblogic descriptor and set "
+                + "newCMP=\"true\" to use the new CMP descriptor "
+                + "inclusion mechanism. ", Project.MSG_VERBOSE);
+            // The the weblogic cmp deployment descriptor
+            File weblogicCMPDD = new File(getConfig().descriptorDir, ddPrefix + WL_CMP_DD);
+
+            if (weblogicCMPDD.exists()) {
+                ejbFiles.put(META_DIR + WL_CMP_DD,
+                    weblogicCMPDD);
+            }
+        } else {
+            // now that we have the weblogic descriptor, we parse the file
+            // to find other descriptors needed to deploy the bean.
+            // this could be the weblogic-cmp-rdbms.xml or any other O/R
+            // mapping tool descriptors.
+            try {
+                File ejbDescriptor = (File) ejbFiles.get(META_DIR + EJB_DD);
+                SAXParserFactory saxParserFactory = SAXParserFactory.newInstance();
+
+                saxParserFactory.setValidating(true);
+
+                SAXParser saxParser = saxParserFactory.newSAXParser();
+                DescriptorHandler handler
+                    = getWeblogicDescriptorHandler(ejbDescriptor.getParentFile());
+
+                saxParser.parse(new InputSource
+                    (new FileInputStream(weblogicDD)),
+                        handler);
+
+                Hashtable ht = handler.getFiles();
+                Enumeration e = ht.keys();
+
+                while (e.hasMoreElements()) {
+                    String key = (String) e.nextElement();
+
+                    ejbFiles.put(key, ht.get(key));
+                }
+            } catch (Exception e) {
+                String msg = "Exception while adding Vendor specific files: " + e.toString();
+
+                throw new BuildException(msg, e);
+            }
+        }
+    }
+
+
+    /**
+     * Get the vendor specific name of the Jar that will be output. The
+     * modification date of this jar will be checked against the dependent
+     * bean classes.
+     */
+    File getVendorOutputJarFile(String baseName) {
+        return new File(getDestDir(), baseName + jarSuffix);
+    }
+
+
+    /**
+     * Helper method invoked by execute() for each WebLogic jar to be built.
+     * Encapsulates the logic of constructing a java task for calling
+     * weblogic.ejbc and executing it.
+     *
+     * @param sourceJar java.io.File representing the source (EJB1.1) jarfile.
+     * @param destJar java.io.File representing the destination, WebLogic
+     *      jarfile.
+     */
+    private void buildWeblogicJar(File sourceJar, File destJar, String publicId) {
+        Java javaTask = null;
+
+        if (noEJBC) {
+            try {
+                fileUtils.copyFile(sourceJar, destJar);
+                if (!keepgenerated) {
+                    sourceJar.delete();
+                }
+                return;
+            } catch (IOException e) {
+                throw new BuildException("Unable to write EJB jar", e);
+            }
+        }
+
+        String ejbcClassName = ejbcClass;
+
+        try {
+            javaTask = (Java) getTask().getProject().createTask("java");
+            javaTask.setTaskName("ejbc");
+
+            javaTask.createJvmarg().setLine(additionalJvmArgs);
+            if (!(sysprops.isEmpty())) {
+                for (Enumeration en = sysprops.elements(); en.hasMoreElements();) {
+                    Environment.Variable entry
+                        = (Environment.Variable) en.nextElement();
+                    javaTask.addSysproperty(entry);
+                }
+            }
+
+            if (getJvmDebugLevel() != null) {
+                javaTask.createJvmarg().setLine(" -Dweblogic.StdoutSeverityLevel=" + jvmDebugLevel);
+            }
+
+            if (ejbcClassName == null) {
+                // try to determine it from publicId
+                if (PUBLICID_EJB11.equals(publicId)) {
+                    ejbcClassName = COMPILER_EJB11;
+                } else if (PUBLICID_EJB20.equals(publicId)) {
+                    ejbcClassName = COMPILER_EJB20;
+                } else {
+                    log("Unrecognized publicId " + publicId
+                        + " - using EJB 1.1 compiler", Project.MSG_WARN);
+                    ejbcClassName = COMPILER_EJB11;
+                }
+            }
+
+            javaTask.setClassname(ejbcClassName);
+            javaTask.createArg().setLine(additionalArgs);
+            if (keepgenerated) {
+                javaTask.createArg().setValue("-keepgenerated");
+            }
+            if (compiler == null) {
+                // try to use the compiler specified by build.compiler.
+                // Right now we are just going to allow Jikes
+                String buildCompiler
+                    = getTask().getProject().getProperty("build.compiler");
+
+                if (buildCompiler != null && buildCompiler.equals("jikes")) {
+                    javaTask.createArg().setValue("-compiler");
+                    javaTask.createArg().setValue("jikes");
+                }
+            } else {
+                if (!compiler.equals(DEFAULT_COMPILER)) {
+                    javaTask.createArg().setValue("-compiler");
+                    javaTask.createArg().setLine(compiler);
+                }
+            }
+
+            Path combinedClasspath = getCombinedClasspath();
+            if (wlClasspath != null && combinedClasspath != null
+                 && combinedClasspath.toString().trim().length() > 0) {
+                javaTask.createArg().setValue("-classpath");
+                javaTask.createArg().setPath(combinedClasspath);
+            }
+
+            javaTask.createArg().setValue(sourceJar.getPath());
+            if (outputDir == null) {
+                javaTask.createArg().setValue(destJar.getPath());
+            } else {
+                javaTask.createArg().setValue(outputDir.getPath());
+            }
+
+            Path classpath = wlClasspath;
+
+            if (classpath == null) {
+                classpath = getCombinedClasspath();
+            }
+
+            javaTask.setFork(true);
+            if (classpath != null) {
+                javaTask.setClasspath(classpath);
+            }
+
+            log("Calling " + ejbcClassName + " for " + sourceJar.toString(),
+                Project.MSG_VERBOSE);
+
+            if (javaTask.executeJava() != 0) {
+                throw new BuildException("Ejbc reported an error");
+            }
+        } catch (Exception e) {
+            // Have to catch this because of the semantics of calling main()
+            String msg = "Exception while calling " + ejbcClassName
+                + ". Details: " + e.toString();
+
+            throw new BuildException(msg, e);
+        }
+    }
+
+
+    /**
+     * Method used to encapsulate the writing of the JAR file. Iterates over
+     * the filenames/java.io.Files in the Hashtable stored on the instance
+     * variable ejbFiles.
+     */
+    protected void writeJar(String baseName, File jarFile, Hashtable files,
+                            String publicId) throws BuildException {
+        // need to create a generic jar first.
+        File genericJarFile = super.getVendorOutputJarFile(baseName);
+
+        super.writeJar(baseName, genericJarFile, files, publicId);
+
+        if (alwaysRebuild || isRebuildRequired(genericJarFile, jarFile)) {
+            buildWeblogicJar(genericJarFile, jarFile, publicId);
+        }
+        if (!keepGeneric) {
+            log("deleting generic jar " + genericJarFile.toString(),
+                Project.MSG_VERBOSE);
+            genericJarFile.delete();
+        }
+    }
+
+
+    /**
+     * Called to validate that the tool parameters have been configured.
+     */
+    public void validateConfigured() throws BuildException {
+        super.validateConfigured();
+    }
+
+
+    /**
+     * Helper method to check to see if a weblogic EBJ1.1 jar needs to be
+     * rebuilt using ejbc. Called from writeJar it sees if the "Bean" classes
+     * are the only thing that needs to be updated and either updates the Jar
+     * with the Bean classfile or returns true, saying that the whole weblogic
+     * jar needs to be regened with ejbc. This allows faster build times for
+     * working developers. <p>
+     *
+     * The way weblogic ejbc works is it creates wrappers for the publicly
+     * defined methods as they are exposed in the remote interface. If the
+     * actual bean changes without changing the the method signatures then
+     * only the bean classfile needs to be updated and the rest of the
+     * weblogic jar file can remain the same. If the Interfaces, ie. the
+     * method signatures change or if the xml deployment descriptors changed,
+     * the whole jar needs to be rebuilt with ejbc. This is not strictly true
+     * for the xml files. If the JNDI name changes then the jar doesnt have to
+     * be rebuild, but if the resources references change then it does. At
+     * this point the weblogic jar gets rebuilt if the xml files change at
+     * all.
+     *
+     * @param genericJarFile java.io.File The generic jar file.
+     * @param weblogicJarFile java.io.File The weblogic jar file to check to
+     *      see if it needs to be rebuilt.
+     */
+    protected boolean isRebuildRequired(File genericJarFile, File weblogicJarFile) {
+        boolean rebuild = false;
+
+        JarFile genericJar = null;
+        JarFile wlJar = null;
+        File newWLJarFile = null;
+        JarOutputStream newJarStream = null;
+
+        try {
+            log("Checking if weblogic Jar needs to be rebuilt for jar " + weblogicJarFile.getName(),
+                Project.MSG_VERBOSE);
+            // Only go forward if the generic and the weblogic file both exist
+            if (genericJarFile.exists() && genericJarFile.isFile()
+                 && weblogicJarFile.exists() && weblogicJarFile.isFile()) {
+                //open jar files
+                genericJar = new JarFile(genericJarFile);
+                wlJar = new JarFile(weblogicJarFile);
+
+                Hashtable genericEntries = new Hashtable();
+                Hashtable wlEntries = new Hashtable();
+                Hashtable replaceEntries = new Hashtable();
+
+                //get the list of generic jar entries
+                for (Enumeration e = genericJar.entries(); e.hasMoreElements();) {
+                    JarEntry je = (JarEntry) e.nextElement();
+
+                    genericEntries.put(je.getName().replace('\\', '/'), je);
+                }
+                //get the list of weblogic jar entries
+                for (Enumeration e = wlJar.entries(); e.hasMoreElements();) {
+                    JarEntry je = (JarEntry) e.nextElement();
+
+                    wlEntries.put(je.getName(), je);
+                }
+
+                //Cycle Through generic and make sure its in weblogic
+                ClassLoader genericLoader
+                    = getClassLoaderFromJar(genericJarFile);
+
+                for (Enumeration e = genericEntries.keys(); e.hasMoreElements();) {
+                    String filepath = (String) e.nextElement();
+
+                    if (wlEntries.containsKey(filepath)) {
+                        // File name/path match
+
+                        // Check files see if same
+                        JarEntry genericEntry = (JarEntry) genericEntries.get(filepath);
+                        JarEntry wlEntry = (JarEntry) wlEntries.get(filepath);
+
+                        if ((genericEntry.getCrc() != wlEntry.getCrc())
+                            || (genericEntry.getSize() != wlEntry.getSize())) {
+
+                            if (genericEntry.getName().endsWith(".class")) {
+                                //File are different see if its an object or an interface
+                                String classname
+                                    = genericEntry.getName().replace(File.separatorChar, '.');
+
+                                classname = classname.substring(0, classname.lastIndexOf(".class"));
+
+                                Class genclass = genericLoader.loadClass(classname);
+
+                                if (genclass.isInterface()) {
+                                    //Interface changed   rebuild jar.
+                                    log("Interface " + genclass.getName()
+                                        + " has changed", Project.MSG_VERBOSE);
+                                    rebuild = true;
+                                    break;
+                                } else {
+                                    //Object class Changed   update it.
+                                    replaceEntries.put(filepath, genericEntry);
+                                }
+                            } else {
+                                // is it the manifest. If so ignore it
+                                if (!genericEntry.getName().equals("META-INF/MANIFEST.MF")) {
+                                    //File other then class changed   rebuild
+                                    log("Non class file " + genericEntry.getName()
+                                        + " has changed", Project.MSG_VERBOSE);
+                                    rebuild = true;
+                                    break;
+                                }
+                            }
+                        }
+                    } else {
+                        // a file doesnt exist rebuild
+
+                        log("File " + filepath + " not present in weblogic jar",
+                            Project.MSG_VERBOSE);
+                        rebuild = true;
+                        break;
+                    }
+                }
+
+                if (!rebuild) {
+                    log("No rebuild needed - updating jar", Project.MSG_VERBOSE);
+                    newWLJarFile = new File(weblogicJarFile.getAbsolutePath() + ".temp");
+                    if (newWLJarFile.exists()) {
+                        newWLJarFile.delete();
+                    }
+
+                    newJarStream = new JarOutputStream(new FileOutputStream(newWLJarFile));
+                    newJarStream.setLevel(0);
+
+                    //Copy files from old weblogic jar
+                    for (Enumeration e = wlEntries.elements(); e.hasMoreElements();) {
+                        byte[] buffer = new byte[1024];
+                        int bytesRead;
+                        InputStream is;
+                        JarEntry je = (JarEntry) e.nextElement();
+
+                        if (je.getCompressedSize() == -1
+                            || je.getCompressedSize() == je.getSize()) {
+                            newJarStream.setLevel(0);
+                        } else {
+                            newJarStream.setLevel(9);
+                        }
+
+                        // Update with changed Bean class
+                        if (replaceEntries.containsKey(je.getName())) {
+                            log("Updating Bean class from generic Jar "
+                                + je.getName(), Project.MSG_VERBOSE);
+                            // Use the entry from the generic jar
+                            je = (JarEntry) replaceEntries.get(je.getName());
+                            is = genericJar.getInputStream(je);
+                        } else {
+                            //use fle from original weblogic jar
+
+                            is = wlJar.getInputStream(je);
+                        }
+                        newJarStream.putNextEntry(new JarEntry(je.getName()));
+
+                        while ((bytesRead = is.read(buffer)) != -1) {
+                            newJarStream.write(buffer, 0, bytesRead);
+                        }
+                        is.close();
+                    }
+                } else {
+                    log("Weblogic Jar rebuild needed due to changed "
+                         + "interface or XML", Project.MSG_VERBOSE);
+                }
+
+                if (genericLoader instanceof AntClassLoader) {
+                    AntClassLoader loader = (AntClassLoader) genericLoader;
+                    loader.cleanup();
+                }
+            } else {
+                rebuild = true;
+            }
+        } catch (ClassNotFoundException cnfe) {
+            String cnfmsg = "ClassNotFoundException while processing ejb-jar file"
+                 + ". Details: "
+                 + cnfe.getMessage();
+
+            throw new BuildException(cnfmsg, cnfe);
+        } catch (IOException ioe) {
+            String msg = "IOException while processing ejb-jar file "
+                 + ". Details: "
+                 + ioe.getMessage();
+
+            throw new BuildException(msg, ioe);
+        } finally {
+            // need to close files and perhaps rename output
+            if (genericJar != null) {
+                try {
+                    genericJar.close();
+                } catch (IOException closeException) {
+                }
+            }
+
+            if (wlJar != null) {
+                try {
+                    wlJar.close();
+                } catch (IOException closeException) {
+                }
+            }
+
+            if (newJarStream != null) {
+                try {
+                    newJarStream.close();
+                } catch (IOException closeException) {
+                }
+
+                try {
+                    fileUtils.rename(newWLJarFile, weblogicJarFile);
+                } catch (IOException renameException) {
+                    log(renameException.getMessage(), Project.MSG_WARN);
+                    rebuild = true;
+                }
+            }
+        }
+
+        return rebuild;
+    }
+
+
+    /**
+     * Helper method invoked by isRebuildRequired to get a ClassLoader for a
+     * Jar File passed to it.
+     *
+     * @param classjar java.io.File representing jar file to get classes from.
+     */
+    protected ClassLoader getClassLoaderFromJar(File classjar) throws IOException {
+        Path lookupPath = new Path(getTask().getProject());
+
+        lookupPath.setLocation(classjar);
+
+        Path classpath = getCombinedClasspath();
+
+        if (classpath != null) {
+            lookupPath.append(classpath);
+        }
+
+        return getTask().getProject().createClassLoader(lookupPath);
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicTOPLinkDeploymentTool.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicTOPLinkDeploymentTool.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WeblogicTOPLinkDeploymentTool.java	(revision 16139)
@@ -0,0 +1,101 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.util.Hashtable;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+public class WeblogicTOPLinkDeploymentTool extends WeblogicDeploymentTool {
+
+    private static final String TL_DTD_LOC
+        = "http://www.objectpeople.com/tlwl/dtd/toplink-cmp_2_5_1.dtd";
+
+    private String toplinkDescriptor;
+    private String toplinkDTD;
+
+    /**
+     * Setter used to store the name of the toplink descriptor.
+     * @param inString the string to use as the descriptor name.
+     */
+    public void setToplinkdescriptor(String inString) {
+        this.toplinkDescriptor = inString;
+    }
+
+    /**
+     * Setter used to store the location of the toplink DTD file.
+     * This is expected to be an URL (file or otherwise). If running
+     * this on NT using a file URL, the safest thing would be to not use a
+     * drive spec in the URL and make sure the file resides on the drive that
+     * ANT is running from.  This will keep the setting in the build XML
+     * platform independent.
+     *
+     * @param inString the string to use as the DTD location.
+     */
+    public void setToplinkdtd(String inString) {
+        this.toplinkDTD = inString;
+    }
+
+    protected DescriptorHandler getDescriptorHandler(File srcDir) {
+        DescriptorHandler handler = super.getDescriptorHandler(srcDir);
+        if (toplinkDTD != null) {
+            handler.registerDTD("-//The Object People, Inc.//"
+                + "DTD TOPLink for WebLogic CMP 2.5.1//EN", toplinkDTD);
+        } else {
+            handler.registerDTD("-//The Object People, Inc.//"
+                + "DTD TOPLink for WebLogic CMP 2.5.1//EN", TL_DTD_LOC);
+        }
+        return handler;
+    }
+
+    /**
+     * Add any vendor specific files which should be included in the
+     * EJB Jar.
+     */
+    protected void addVendorFiles(Hashtable ejbFiles, String ddPrefix) {
+        super.addVendorFiles(ejbFiles, ddPrefix);
+        // Then the toplink deployment descriptor
+
+        // Setup a naming standard here?.
+
+
+        File toplinkDD = new File(getConfig().descriptorDir, ddPrefix + toplinkDescriptor);
+
+        if (toplinkDD.exists()) {
+            ejbFiles.put(META_DIR + toplinkDescriptor,
+                         toplinkDD);
+        } else {
+            log("Unable to locate toplink deployment descriptor. "
+                + "It was expected to be in "
+                + toplinkDD.getPath(), Project.MSG_WARN);
+        }
+    }
+
+    /**
+     * Called to validate that the tool parameters have been configured.
+     *
+     */
+    public void validateConfigured() throws BuildException {
+        super.validateConfigured();
+        if (toplinkDescriptor == null) {
+            throw new BuildException("The toplinkdescriptor attribute must "
+                + "be specified");
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WebsphereDeploymentTool.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WebsphereDeploymentTool.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ejb/WebsphereDeploymentTool.java	(revision 16139)
@@ -0,0 +1,876 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.ejb;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.jar.JarOutputStream;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Environment;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Websphere deployment tool that augments the ejbjar task.
+ * Searches for the websphere specific deployment descriptors and
+ * adds them to the final ejb jar file. Websphere has two specific descriptors for session
+ * beans:
+ * <ul>
+ *    <li>ibm-ejb-jar-bnd.xmi</li>
+ *    <li>ibm-ejb-jar-ext.xmi</li>
+ * </ul>
+ * and another two for container managed entity beans:
+ * <ul>
+ *    <li>Map.mapxmi</li>
+ *    <li>Schema.dbxmi</li>
+ * </ul>
+ * In terms of WebSphere, the generation of container code and stubs is
+ * called <code>deployment</code>. This step can be performed by the websphere
+ * element as part of the jar generation process. If the switch
+ * <code>ejbdeploy</code> is on, the ejbdeploy tool from the websphere toolset
+ * is called for every ejb-jar. Unfortunately, this step only works, if you
+ * use the ibm jdk. Otherwise, the rmic (called by ejbdeploy) throws a
+ * ClassFormatError. Be sure to switch ejbdeploy off, if run ant with
+ * sun jdk.
+ *
+ */
+public class WebsphereDeploymentTool extends GenericDeploymentTool {
+    /**
+     * Enumerated attribute with the values for the database vendor types
+     *
+     */
+    public static class DBVendor extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[]{
+                "SQL92", "SQL99", "DB2UDBWIN_V71", "DB2UDBOS390_V6", "DB2UDBAS400_V4R5",
+                "ORACLE_V8", "INFORMIX_V92", "SYBASE_V1192", "MSSQLSERVER_V7", "MYSQL_V323"
+                };
+        }
+    }
+
+
+    public static final String PUBLICID_EJB11
+         = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 1.1//EN";
+    public static final String PUBLICID_EJB20
+         = "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN";
+    protected static final String SCHEMA_DIR = "Schema/";
+
+    protected static final String WAS_EXT = "ibm-ejb-jar-ext.xmi";
+    protected static final String WAS_BND = "ibm-ejb-jar-bnd.xmi";
+    protected static final String WAS_CMP_MAP = "Map.mapxmi";
+    protected static final String WAS_CMP_SCHEMA = "Schema.dbxmi";
+
+    /** Instance variable that stores the suffix for the websphere jarfile. */
+    private String jarSuffix = ".jar";
+
+    /** Instance variable that stores the location of the ejb 1.1 DTD file. */
+    private String ejb11DTD;
+
+    /** Instance variable that determines whether generic ejb jars are kept. */
+
+    private boolean keepGeneric = false;
+
+    private boolean alwaysRebuild = true;
+
+    private boolean ejbdeploy = true;
+
+    /** Indicates if the old CMP location convention is to be used. */
+    private boolean newCMP = false;
+
+    /** The classpath to the websphere classes. */
+    private Path wasClasspath = null;
+
+    /** The DB Vendor name, the EJB is persisted against */
+    private String dbVendor;
+
+    /** The name of the database to create. (For top-down mapping only) */
+    private String dbName;
+
+    /** The name of the schema to create. (For top-down mappings only) */
+    private String dbSchema;
+
+    /** true - Only generate the deployment code, do not run RMIC or Javac */
+    private boolean codegen;
+
+    /** true - Only output error messages, suppress informational messages */
+    private boolean quiet = true;
+
+    /** true - Disable the validation steps */
+    private boolean novalidate;
+
+    /** true - Disable warning and informational messages */
+    private boolean nowarn;
+
+    /** true - Disable informational messages */
+    private boolean noinform;
+
+    /** true - Enable internal tracing */
+    private boolean trace;
+
+    /** Additional options for RMIC */
+    private String rmicOptions;
+
+    /** true- Use the WebSphere 3.5 compatible mapping rules */
+    private boolean use35MappingRules;
+
+    /** the scratchdir for the ejbdeploy operation */
+    private String tempdir = "_ejbdeploy_temp";
+
+    /** the home directory for websphere */
+    private File websphereHome;
+
+    /** Get the classpath to the websphere classpaths */
+    public Path createWASClasspath() {
+        if (wasClasspath == null) {
+            wasClasspath = new Path(getTask().getProject());
+        }
+        return wasClasspath.createPath();
+    }
+
+
+    public void setWASClasspath(Path wasClasspath) {
+        this.wasClasspath = wasClasspath;
+    }
+
+
+    /** Sets the DB Vendor for the Entity Bean mapping ; optional.
+     * Valid options are for example:
+     * <ul>
+     * <li>SQL92</li> <li>SQL99</li> <li>DB2UDBWIN_V71</li>
+     * <li>DB2UDBOS390_V6</li> <li>DB2UDBAS400_V4R5</li> <li>ORACLE_V8</li>
+     * <li>INFORMIX_V92</li> <li>SYBASE_V1192</li> <li>MYSQL_V323</li>
+     * </ul>
+     * This is also used to determine the name of the Map.mapxmi and
+     * Schema.dbxmi files, for example Account-DB2UDBWIN_V71-Map.mapxmi
+     * and Account-DB2UDBWIN_V71-Schema.dbxmi.
+     */
+    public void setDbvendor(DBVendor dbvendor) {
+        this.dbVendor = dbvendor.getValue();
+    }
+
+
+    /**
+     * Sets the name of the Database to create; optional.
+     *
+     * @param dbName name of the database
+     */
+    public void setDbname(String dbName) {
+        this.dbName = dbName;
+    }
+
+
+    /**
+     * Sets the name of the schema to create; optional.
+     *
+     * @param dbSchema name of the schema
+     */
+    public void setDbschema(String dbSchema) {
+        this.dbSchema = dbSchema;
+    }
+
+
+    /**
+     * Flag, default false, to only generate the deployment
+     * code, do not run RMIC or Javac
+     *
+     * @param codegen option
+     */
+    public void setCodegen(boolean codegen) {
+        this.codegen = codegen;
+    }
+
+
+    /**
+     * Flag, default true, to only output error messages.
+     *
+     * @param quiet option
+     */
+    public void setQuiet(boolean quiet) {
+        this.quiet = quiet;
+    }
+
+
+    /**
+     * Flag to disable the validation steps; optional, default false.
+     *
+     * @param novalidate option
+     */
+    public void setNovalidate(boolean novalidate) {
+        this.novalidate = novalidate;
+    }
+
+
+    /**
+     * Flag to disable warning and informational messages; optional, default false.
+     *
+     * @param nowarn option
+     */
+    public void setNowarn(boolean nowarn) {
+        this.nowarn = nowarn;
+    }
+
+
+    /**
+     * Flag to disable informational messages; optional, default false.
+     *
+     * @param noinform if true disables informational messages
+     */
+    public void setNoinform(boolean noinform) {
+        this.noinform = noinform;
+    }
+
+
+    /**
+     * Flag to enable internal tracing when set, optional, default false.
+     *
+     * @param trace
+     */
+    public void setTrace(boolean trace) {
+        this.trace = trace;
+    }
+
+    /**
+     * Set the rmic options.
+     *
+     * @param options
+     */
+    public void setRmicoptions(String options) {
+        this.rmicOptions = options;
+    }
+
+    /**
+     * Flag to use the WebSphere 3.5 compatible mapping rules ; optional, default false.
+     *
+     * @param attr
+     */
+    public void setUse35(boolean attr) {
+        use35MappingRules = attr;
+    }
+
+
+    /**
+     * Set the rebuild flag to false to only update changes in the jar rather
+     * than rerunning ejbdeploy; optional, default true.
+     */
+    public void setRebuild(boolean rebuild) {
+        this.alwaysRebuild = rebuild;
+    }
+
+
+    /**
+     * String value appended to the basename of the deployment
+     * descriptor to create the filename of the WebLogic EJB
+     * jar file. Optional, default '.jar'.
+     * @param inString the string to use as the suffix.
+     */
+    public void setSuffix(String inString) {
+        this.jarSuffix = inString;
+    }
+
+
+    /**
+     * This controls whether the generic file used as input to
+     * ejbdeploy is retained; optional, default false.
+     * @param inValue either 'true' or 'false'.
+     */
+    public void setKeepgeneric(boolean inValue) {
+        this.keepGeneric = inValue;
+    }
+
+
+    /**
+     * Decide, wether ejbdeploy should be called or not;
+     * optional, default true.
+     *
+     * @param ejbdeploy
+     */
+    public void setEjbdeploy(boolean ejbdeploy) {
+        this.ejbdeploy = ejbdeploy;
+    }
+
+
+    /**
+     * Setter used to store the location of the Sun's Generic EJB DTD. This
+     * can be a file on the system or a resource on the classpath.
+     *
+     * @param inString the string to use as the DTD location.
+     */
+    public void setEJBdtd(String inString) {
+        this.ejb11DTD = inString;
+    }
+
+
+    /**
+     * Set the value of the oldCMP scheme. This is an antonym for newCMP
+     * @ant.attribute ignore="true"
+     */
+    public void setOldCMP(boolean oldCMP) {
+        this.newCMP = !oldCMP;
+    }
+
+
+    /**
+     * Set the value of the newCMP scheme. The old CMP scheme locates the
+     * websphere CMP descriptor based on the naming convention where the
+     * websphere CMP file is expected to be named with the bean name as the
+     * prefix. Under this scheme the name of the CMP descriptor does not match
+     * the name actually used in the main websphere EJB descriptor. Also,
+     * descriptors which contain multiple CMP references could not be used.
+     */
+    public void setNewCMP(boolean newCMP) {
+        this.newCMP = newCMP;
+    }
+
+
+    /**
+     * The directory, where ejbdeploy will write temporary files;
+     * optional, defaults to '_ejbdeploy_temp'.
+     */
+
+    public void setTempdir(String tempdir) {
+        this.tempdir = tempdir;
+    }
+
+
+    protected DescriptorHandler getDescriptorHandler(File srcDir) {
+        DescriptorHandler handler = new DescriptorHandler(getTask(), srcDir);
+        // register all the DTDs, both the ones that are known and
+        // any supplied by the user
+        handler.registerDTD(PUBLICID_EJB11, ejb11DTD);
+
+        for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) {
+            EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next();
+
+            handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation());
+        }
+
+        return handler;
+    }
+
+
+    protected DescriptorHandler getWebsphereDescriptorHandler(final File srcDir) {
+        DescriptorHandler handler =
+            new DescriptorHandler(getTask(), srcDir) {
+                protected void processElement() {
+                }
+            };
+
+        for (Iterator i = getConfig().dtdLocations.iterator(); i.hasNext();) {
+            EjbJar.DTDLocation dtdLocation = (EjbJar.DTDLocation) i.next();
+
+            handler.registerDTD(dtdLocation.getPublicId(), dtdLocation.getLocation());
+        }
+        return handler;
+    }
+
+
+    /**
+     * Add any vendor specific files which should be included in the EJB Jar.
+     */
+    protected void addVendorFiles(Hashtable ejbFiles, String baseName) {
+
+        String ddPrefix = (usingBaseJarName() ? "" : baseName);
+        String dbPrefix = (dbVendor == null) ? "" : dbVendor + "-";
+
+        // Get the Extensions document
+        File websphereEXT = new File(getConfig().descriptorDir, ddPrefix + WAS_EXT);
+
+        if (websphereEXT.exists()) {
+            ejbFiles.put(META_DIR + WAS_EXT,
+                websphereEXT);
+        } else {
+            log("Unable to locate websphere extensions. "
+                + "It was expected to be in "
+                + websphereEXT.getPath(), Project.MSG_VERBOSE);
+        }
+
+        File websphereBND = new File(getConfig().descriptorDir, ddPrefix + WAS_BND);
+
+        if (websphereBND.exists()) {
+            ejbFiles.put(META_DIR + WAS_BND,
+                websphereBND);
+        } else {
+            log("Unable to locate websphere bindings. "
+                + "It was expected to be in "
+                + websphereBND.getPath(), Project.MSG_VERBOSE);
+        }
+
+        if (!newCMP) {
+            log("The old method for locating CMP files has been DEPRECATED.",
+                Project.MSG_VERBOSE);
+            log("Please adjust your websphere descriptor and set "
+                + "newCMP=\"true\" to use the new CMP descriptor "
+                + "inclusion mechanism. ", Project.MSG_VERBOSE);
+        } else {
+            // We attempt to put in the MAP and Schema files of CMP beans
+            try {
+                // Add the Map file
+                File websphereMAP = new File(getConfig().descriptorDir,
+                    ddPrefix + dbPrefix + WAS_CMP_MAP);
+
+                if (websphereMAP.exists()) {
+                    ejbFiles.put(META_DIR + WAS_CMP_MAP,
+                        websphereMAP);
+                } else {
+                    log("Unable to locate the websphere Map: "
+                        + websphereMAP.getPath(), Project.MSG_VERBOSE);
+                }
+
+                File websphereSchema = new File(getConfig().descriptorDir,
+                    ddPrefix + dbPrefix + WAS_CMP_SCHEMA);
+
+                if (websphereSchema.exists()) {
+                    ejbFiles.put(META_DIR + SCHEMA_DIR + WAS_CMP_SCHEMA,
+                        websphereSchema);
+                } else {
+                    log("Unable to locate the websphere Schema: "
+                        + websphereSchema.getPath(), Project.MSG_VERBOSE);
+                }
+                // Theres nothing else to see here...keep moving sonny
+            } catch (Exception e) {
+                String msg = "Exception while adding Vendor specific files: "
+                    + e.toString();
+
+                throw new BuildException(msg, e);
+            }
+        }
+    }
+
+
+    /**
+     * Get the vendor specific name of the Jar that will be output. The
+     * modification date of this jar will be checked against the dependent
+     * bean classes.
+     */
+    File getVendorOutputJarFile(String baseName) {
+        return new File(getDestDir(), baseName + jarSuffix);
+    }
+
+
+    /**
+     * Gets the options for the EJB Deploy operation
+     *
+     * @return String
+     */
+    protected String getOptions() {
+        // Set the options
+        StringBuffer options = new StringBuffer();
+
+        if (dbVendor != null) {
+            options.append(" -dbvendor ").append(dbVendor);
+        }
+        if (dbName != null) {
+            options.append(" -dbname \"").append(dbName).append("\"");
+        }
+
+        if (dbSchema != null) {
+            options.append(" -dbschema \"").append(dbSchema).append("\"");
+        }
+
+        if (codegen) {
+            options.append(" -codegen");
+        }
+
+        if (quiet) {
+            options.append(" -quiet");
+        }
+
+        if (novalidate) {
+            options.append(" -novalidate");
+        }
+
+        if (nowarn) {
+            options.append(" -nowarn");
+        }
+
+        if (noinform) {
+            options.append(" -noinform");
+        }
+
+        if (trace) {
+            options.append(" -trace");
+        }
+
+        if (use35MappingRules) {
+            options.append(" -35");
+        }
+
+        if (rmicOptions != null) {
+            options.append(" -rmic \"").append(rmicOptions).append("\"");
+        }
+
+        return options.toString();
+    }
+
+
+    /**
+     * Helper method invoked by execute() for each websphere jar to be built.
+     * Encapsulates the logic of constructing a java task for calling
+     * websphere.ejbdeploy and executing it.
+     *
+     * @param sourceJar java.io.File representing the source (EJB1.1) jarfile.
+     * @param destJar java.io.File representing the destination, websphere
+     *      jarfile.
+     */
+    private void buildWebsphereJar(File sourceJar, File destJar) {
+        try {
+            if (ejbdeploy) {
+                Java javaTask = (Java) getTask().getProject().createTask("java");
+                // Set the JvmArgs
+                javaTask.createJvmarg().setValue("-Xms64m");
+                javaTask.createJvmarg().setValue("-Xmx128m");
+
+                // Set the Environment variable
+                Environment.Variable var = new Environment.Variable();
+
+                var.setKey("websphere.lib.dir");
+                File libdir = new File(websphereHome, "lib");
+                var.setValue(libdir.getAbsolutePath());
+                javaTask.addSysproperty(var);
+
+                // Set the working directory
+                javaTask.setDir(websphereHome);
+
+                // Set the Java class name
+                javaTask.setTaskName("ejbdeploy");
+                javaTask.setClassname("com.ibm.etools.ejbdeploy.EJBDeploy");
+
+                javaTask.createArg().setValue(sourceJar.getPath());
+                javaTask.createArg().setValue(tempdir);
+                javaTask.createArg().setValue(destJar.getPath());
+                javaTask.createArg().setLine(getOptions());
+                if (getCombinedClasspath() != null
+                    && getCombinedClasspath().toString().length() > 0) {
+                    javaTask.createArg().setValue("-cp");
+                    javaTask.createArg().setValue(getCombinedClasspath().toString());
+                }
+
+                Path classpath = wasClasspath;
+
+                if (classpath == null) {
+                    classpath = getCombinedClasspath();
+                }
+
+                if (classpath != null) {
+                    javaTask.setClasspath(classpath);
+                    javaTask.setFork(true);
+                } else {
+                    javaTask.setFork(true);
+                }
+
+                log("Calling websphere.ejbdeploy for " + sourceJar.toString(),
+                    Project.MSG_VERBOSE);
+
+                javaTask.execute();
+            }
+        } catch (Exception e) {
+            // Have to catch this because of the semantics of calling main()
+            String msg = "Exception while calling ejbdeploy. Details: " + e.toString();
+
+            throw new BuildException(msg, e);
+        }
+    }
+
+
+    /**
+     * Method used to encapsulate the writing of the JAR file. Iterates over
+     * the filenames/java.io.Files in the Hashtable stored on the instance
+     * variable ejbFiles.
+     */
+    protected void writeJar(String baseName, File jarFile, Hashtable files, String publicId)
+         throws BuildException {
+        if (ejbdeploy) {
+            // create the -generic.jar, if required
+            File genericJarFile = super.getVendorOutputJarFile(baseName);
+
+            super.writeJar(baseName, genericJarFile, files, publicId);
+
+            // create the output .jar, if required
+            if (alwaysRebuild || isRebuildRequired(genericJarFile, jarFile)) {
+                buildWebsphereJar(genericJarFile, jarFile);
+            }
+            if (!keepGeneric) {
+                log("deleting generic jar " + genericJarFile.toString(),
+                    Project.MSG_VERBOSE);
+                genericJarFile.delete();
+            }
+        } else {
+            // create the "undeployed" output .jar, if required
+            super.writeJar(baseName, jarFile, files, publicId);
+        }
+    }
+
+
+    /**
+     * Called to validate that the tool parameters have been configured.
+     */
+    public void validateConfigured() throws BuildException {
+        super.validateConfigured();
+        if (ejbdeploy) {
+            String home = getTask().getProject().getProperty("websphere.home");
+            if (home == null) {
+                throw new BuildException("The 'websphere.home' property must "
+                    + "be set when 'ejbdeploy=true'");
+            }
+            websphereHome = getTask().getProject().resolveFile(home);
+        }
+    }
+
+
+    /**
+     * Helper method to check to see if a websphere EBJ1.1 jar needs to be
+     * rebuilt using ejbdeploy. Called from writeJar it sees if the "Bean"
+     * classes are the only thing that needs to be updated and either updates
+     * the Jar with the Bean classfile or returns true, saying that the whole
+     * websphere jar needs to be regened with ejbdeploy. This allows faster
+     * build times for working developers. <p>
+     *
+     * The way websphere ejbdeploy works is it creates wrappers for the
+     * publicly defined methods as they are exposed in the remote interface.
+     * If the actual bean changes without changing the the method signatures
+     * then only the bean classfile needs to be updated and the rest of the
+     * websphere jar file can remain the same. If the Interfaces, ie. the
+     * method signatures change or if the xml deployment descriptors changed,
+     * the whole jar needs to be rebuilt with ejbdeploy. This is not strictly
+     * true for the xml files. If the JNDI name changes then the jar doesnt
+     * have to be rebuild, but if the resources references change then it
+     * does. At this point the websphere jar gets rebuilt if the xml files
+     * change at all.
+     *
+     * @param genericJarFile java.io.File The generic jar file.
+     * @param websphereJarFile java.io.File The websphere jar file to check to
+     *      see if it needs to be rebuilt.
+     */
+    protected boolean isRebuildRequired(File genericJarFile, File websphereJarFile) {
+        boolean rebuild = false;
+
+        JarFile genericJar = null;
+        JarFile wasJar = null;
+        File newwasJarFile = null;
+        JarOutputStream newJarStream = null;
+
+        try {
+            log("Checking if websphere Jar needs to be rebuilt for jar "
+                + websphereJarFile.getName(), Project.MSG_VERBOSE);
+            // Only go forward if the generic and the websphere file both exist
+            if (genericJarFile.exists() && genericJarFile.isFile()
+                 && websphereJarFile.exists() && websphereJarFile.isFile()) {
+                //open jar files
+                genericJar = new JarFile(genericJarFile);
+                wasJar = new JarFile(websphereJarFile);
+
+                Hashtable genericEntries = new Hashtable();
+                Hashtable wasEntries = new Hashtable();
+                Hashtable replaceEntries = new Hashtable();
+
+                //get the list of generic jar entries
+                for (Enumeration e = genericJar.entries(); e.hasMoreElements();) {
+                    JarEntry je = (JarEntry) e.nextElement();
+
+                    genericEntries.put(je.getName().replace('\\', '/'), je);
+                }
+                //get the list of websphere jar entries
+                for (Enumeration e = wasJar.entries(); e.hasMoreElements();) {
+                    JarEntry je = (JarEntry) e.nextElement();
+
+                    wasEntries.put(je.getName(), je);
+                }
+
+                //Cycle Through generic and make sure its in websphere
+                ClassLoader genericLoader = getClassLoaderFromJar(genericJarFile);
+
+                for (Enumeration e = genericEntries.keys(); e.hasMoreElements();) {
+                    String filepath = (String) e.nextElement();
+
+                    if (wasEntries.containsKey(filepath)) {
+                        // File name/path match
+                        // Check files see if same
+                        JarEntry genericEntry = (JarEntry) genericEntries.get(filepath);
+                        JarEntry wasEntry = (JarEntry) wasEntries.get(filepath);
+
+                        if ((genericEntry.getCrc() != wasEntry.getCrc())
+                            || (genericEntry.getSize() != wasEntry.getSize())) {
+
+                            if (genericEntry.getName().endsWith(".class")) {
+                                //File are different see if its an object or an interface
+                                String classname
+                                    = genericEntry.getName().replace(File.separatorChar, '.');
+
+                                classname = classname.substring(0, classname.lastIndexOf(".class"));
+
+                                Class genclass = genericLoader.loadClass(classname);
+
+                                if (genclass.isInterface()) {
+                                    //Interface changed   rebuild jar.
+                                    log("Interface " + genclass.getName()
+                                        + " has changed", Project.MSG_VERBOSE);
+                                    rebuild = true;
+                                    break;
+                                } else {
+                                    //Object class Changed   update it.
+                                    replaceEntries.put(filepath, genericEntry);
+                                }
+                            } else {
+                                // is it the manifest. If so ignore it
+                                if (!genericEntry.getName().equals("META-INF/MANIFEST.MF")) {
+                                    //File other then class changed   rebuild
+                                    log("Non class file " + genericEntry.getName()
+                                        + " has changed", Project.MSG_VERBOSE);
+                                    rebuild = true;
+                                }
+                                break;
+                            }
+                        }
+                    } else {
+                        // a file doesn't exist rebuild
+
+                        log("File " + filepath + " not present in websphere jar",
+                            Project.MSG_VERBOSE);
+                        rebuild = true;
+                        break;
+                    }
+                }
+
+                if (!rebuild) {
+                    log("No rebuild needed - updating jar", Project.MSG_VERBOSE);
+                    newwasJarFile = new File(websphereJarFile.getAbsolutePath() + ".temp");
+                    if (newwasJarFile.exists()) {
+                        newwasJarFile.delete();
+                    }
+
+                    newJarStream = new JarOutputStream(new FileOutputStream(newwasJarFile));
+                    newJarStream.setLevel(0);
+
+                    //Copy files from old websphere jar
+                    for (Enumeration e = wasEntries.elements(); e.hasMoreElements();) {
+                        byte[] buffer = new byte[1024];
+                        int bytesRead;
+                        InputStream is;
+                        JarEntry je = (JarEntry) e.nextElement();
+
+                        if (je.getCompressedSize() == -1
+                            || je.getCompressedSize() == je.getSize()) {
+                            newJarStream.setLevel(0);
+                        } else {
+                            newJarStream.setLevel(9);
+                        }
+
+                        // Update with changed Bean class
+                        if (replaceEntries.containsKey(je.getName())) {
+                            log("Updating Bean class from generic Jar " + je.getName(),
+                                Project.MSG_VERBOSE);
+                            // Use the entry from the generic jar
+                            je = (JarEntry) replaceEntries.get(je.getName());
+                            is = genericJar.getInputStream(je);
+                        } else {
+                            //use fle from original websphere jar
+
+                            is = wasJar.getInputStream(je);
+                        }
+                        newJarStream.putNextEntry(new JarEntry(je.getName()));
+
+                        while ((bytesRead = is.read(buffer)) != -1) {
+                            newJarStream.write(buffer, 0, bytesRead);
+                        }
+                        is.close();
+                    }
+                } else {
+                    log("websphere Jar rebuild needed due to changed "
+                        + "interface or XML", Project.MSG_VERBOSE);
+                }
+            } else {
+                rebuild = true;
+            }
+        } catch (ClassNotFoundException cnfe) {
+            String cnfmsg = "ClassNotFoundException while processing ejb-jar file"
+                 + ". Details: "
+                 + cnfe.getMessage();
+
+            throw new BuildException(cnfmsg, cnfe);
+        } catch (IOException ioe) {
+            String msg = "IOException while processing ejb-jar file "
+                 + ". Details: "
+                 + ioe.getMessage();
+
+            throw new BuildException(msg, ioe);
+        } finally {
+            // need to close files and perhaps rename output
+            if (genericJar != null) {
+                try {
+                    genericJar.close();
+                } catch (IOException closeException) {
+                }
+            }
+
+            if (wasJar != null) {
+                try {
+                    wasJar.close();
+                } catch (IOException closeException) {
+                }
+            }
+
+            if (newJarStream != null) {
+                try {
+                    newJarStream.close();
+                } catch (IOException closeException) {
+                }
+
+                try {
+                    FileUtils.newFileUtils().rename(newwasJarFile,
+                                                    websphereJarFile);
+                } catch (IOException renameException) {
+                    log(renameException.getMessage(), Project.MSG_WARN);
+                    rebuild = true;
+                }
+            }
+        }
+
+        return rebuild;
+    }
+
+
+    /**
+     * Helper method invoked by isRebuildRequired to get a ClassLoader for a
+     * Jar File passed to it.
+     *
+     * @param classjar java.io.File representing jar file to get classes from.
+     */
+    protected ClassLoader getClassLoaderFromJar(File classjar) throws IOException {
+        Path lookupPath = new Path(getTask().getProject());
+
+        lookupPath.setLocation(classjar);
+
+        Path classpath = getCombinedClasspath();
+
+        if (classpath != null) {
+            lookupPath.append(classpath);
+        }
+
+        return getTask().getProject().createClassLoader(lookupPath);
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatability.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatability.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatability.java	(revision 16139)
@@ -0,0 +1,56 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension;
+
+/**
+ * Enum used in (@link Extension) to indicate the compatability
+ * of one extension to another. See (@link Extension) for instances
+ * of object.
+ *
+ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+ *  This file is from excalibur.extension package. Dont edit this file
+ * directly as there is no unit tests to make sure it is operational
+ * in ant. Edit file in excalibur and run tests there before changing
+ * ants file.
+ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+ *
+ * @see Extension
+ */
+public final class Compatability {
+    /**
+     * A string representaiton of compatability level.
+     */
+    private final String name;
+
+    /**
+     * Create a compatability enum with specified name.
+     *
+     * @param name the name of compatability level
+     */
+    Compatability(final String name) {
+        this.name = name;
+    }
+
+    /**
+     * Return name of compatability level.
+     *
+     * @return the name of compatability level
+     */
+    public String toString() {
+        return name;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatibility.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatibility.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/Compatibility.java	(revision 16139)
@@ -0,0 +1,56 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension;
+
+/**
+ * Enum used in (@link Extension) to indicate the compatibility
+ * of one extension to another. See (@link Extension) for instances
+ * of object.
+ *
+ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+ *  This file is from excalibur.extension package. Dont edit this file
+ * directly as there is no unit tests to make sure it is operational
+ * in ant. Edit file in excalibur and run tests there before changing
+ * ants file.
+ * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
+ *
+ * @see Extension
+ */
+public final class Compatibility {
+    /**
+     * A string representaiton of compatibility level.
+     */
+    private final String name;
+
+    /**
+     * Create a compatibility enum with specified name.
+     *
+     * @param name the name of compatibility level
+     */
+    Compatibility(final String name) {
+        this.name = name;
+    }
+
+    /**
+     * Return name of compatibility level.
+     *
+     * @return the name of compatibility level
+     */
+    public String toString() {
+        return name;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/DeweyDecimal.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/DeweyDecimal.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/DeweyDecimal.java	(revision 16139)
@@ -0,0 +1,210 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension;
+
+import java.util.StringTokenizer;
+
+/**
+ * Utility class to contain version numbers in "Dewey Decimal"
+ * syntax.  Numbers in the "Dewey Decimal" syntax consist of positive
+ * decimal integers separated by periods ".".  For example, "2.0" or
+ * "1.2.3.4.5.6.7".  This allows an extensible number to be used to
+ * represent major, minor, micro, etc versions.  The version number
+ * must begin with a number.
+ *
+ */
+public final class DeweyDecimal {
+    /** Array of components that make up DeweyDecimal */
+    private int[] components;
+
+    /**
+     * Construct a DeweyDecimal from an array of integer components.
+     *
+     * @param components an array of integer components.
+     */
+    public DeweyDecimal(final int[] components) {
+        this.components = new int[components.length];
+
+        for (int i = 0; i < components.length; i++) {
+            this.components[i] = components[i];
+        }
+    }
+
+    /**
+     * Construct a DeweyDecimal from string in DeweyDecimal format.
+     *
+     * @param string the string in dewey decimal format
+     * @exception NumberFormatException if string is malformed
+     */
+    public DeweyDecimal(final String string)
+        throws NumberFormatException {
+        final StringTokenizer tokenizer = new StringTokenizer(string, ".", true);
+        final int size = tokenizer.countTokens();
+
+        components = new int[ (size + 1) / 2 ];
+
+        for (int i = 0; i < components.length; i++) {
+            final String component = tokenizer.nextToken();
+            if (component.equals("")) {
+                throw new NumberFormatException("Empty component in string");
+            }
+
+            components[ i ] = Integer.parseInt(component);
+
+            //Strip '.' token
+            if (tokenizer.hasMoreTokens()) {
+                tokenizer.nextToken();
+
+                //If it ended in a dot, throw an exception
+                if (!tokenizer.hasMoreTokens()) {
+                    throw new NumberFormatException("DeweyDecimal ended in a '.'");
+                }
+            }
+        }
+    }
+
+    /**
+     * Return number of components in <code>DeweyDecimal</code>.
+     *
+     * @return the number of components in dewey decimal
+     */
+    public int getSize() {
+        return components.length;
+    }
+
+    /**
+     * Return the component at specified index.
+     *
+     * @param index the index of components
+     * @return the value of component at index
+     */
+    public int get(final int index) {
+        return components[ index ];
+    }
+
+    /**
+     * Return <code>true</code> if this <code>DeweyDecimal</code> is
+     * equal to the other <code>DeweyDecimal</code>.
+     *
+     * @param other the other DeweyDecimal
+     * @return true if equal to other DeweyDecimal, false otherwise
+     */
+    public boolean isEqual(final DeweyDecimal other) {
+        final int max = Math.max(other.components.length, components.length);
+
+        for (int i = 0; i < max; i++) {
+            final int component1 = (i < components.length) ? components[ i ] : 0;
+            final int component2 = (i < other.components.length) ? other.components[ i ] : 0;
+
+            if (component2 != component1) {
+                return false;
+            }
+        }
+
+        return true; // Exact match
+    }
+
+    /**
+     * Return <code>true</code> if this <code>DeweyDecimal</code> is
+     * less than the other <code>DeweyDecimal</code>.
+     *
+     * @param other the other DeweyDecimal
+     * @return true if less than other DeweyDecimal, false otherwise
+     */
+    public boolean isLessThan(final DeweyDecimal other) {
+        return !isGreaterThanOrEqual(other);
+    }
+
+    /**
+     * Return <code>true</code> if this <code>DeweyDecimal</code> is
+     * less than or equal to the other <code>DeweyDecimal</code>.
+     *
+     * @param other the other DeweyDecimal
+     * @return true if less than or equal to other DeweyDecimal, false otherwise
+     */
+    public boolean isLessThanOrEqual(final DeweyDecimal other) {
+        return !isGreaterThan(other);
+    }
+
+    /**
+     * Return <code>true</code> if this <code>DeweyDecimal</code> is
+     * greater than the other <code>DeweyDecimal</code>.
+     *
+     * @param other the other DeweyDecimal
+     * @return true if greater than other DeweyDecimal, false otherwise
+     */
+    public boolean isGreaterThan(final DeweyDecimal other) {
+        final int max = Math.max(other.components.length, components.length);
+
+        for (int i = 0; i < max; i++) {
+            final int component1 = (i < components.length) ? components[ i ] : 0;
+            final int component2 = (i < other.components.length) ? other.components[ i ] : 0;
+
+            if (component2 > component1) {
+                return false;
+            }
+            if (component2 < component1) {
+                return true;
+            }
+        }
+
+        return false; // Exact match
+    }
+
+    /**
+     * Return <code>true</code> if this <code>DeweyDecimal</code> is
+     * greater than or equal to the other <code>DeweyDecimal</code>.
+     *
+     * @param other the other DeweyDecimal
+     * @return true if greater than or equal to other DeweyDecimal, false otherwise
+     */
+    public boolean isGreaterThanOrEqual(final DeweyDecimal other) {
+        final int max = Math.max(other.components.length, components.length);
+
+        for (int i = 0; i < max; i++) {
+            final int component1 = (i < components.length) ? components[ i ] : 0;
+            final int component2 = (i < other.components.length) ? other.components[ i ] : 0;
+
+            if (component2 > component1) {
+                return false;
+            }
+            if (component2 < component1) {
+                return true;
+            }
+        }
+
+        return true; // Exact match
+    }
+
+    /**
+     * Return string representation of <code>DeweyDecimal</code>.
+     *
+     * @return the string representation of DeweyDecimal.
+     */
+    public String toString() {
+        final StringBuffer sb = new StringBuffer();
+
+        for (int i = 0; i < components.length; i++) {
+            if (i != 0) {
+                sb.append('.');
+            }
+            sb.append(components[ i ]);
+        }
+
+        return sb.toString();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/Extension.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/Extension.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/Extension.java	(revision 16139)
@@ -0,0 +1,691 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+/**
+ * <p>Utility class that represents either an available "Optional Package"
+ * (formerly known as "Standard Extension") as described in the manifest
+ * of a JAR file, or the requirement for such an optional package.</p>
+ *
+ * <p>For more information about optional packages, see the document
+ * <em>Optional Package Versioning</em> in the documentation bundle for your
+ * Java2 Standard Edition package, in file
+ * <code>guide/extensions/versioning.html</code>.</p>
+ *
+ */
+public final class Extension {
+    /**
+     * Manifest Attribute Name object for EXTENSION_LIST.
+     */
+    public static final Attributes.Name EXTENSION_LIST
+        = new Attributes.Name("Extension-List");
+
+    /**
+     * <code>Name</code> object for <code>Optional-Extension-List</code>
+     * manifest attribute used for declaring optional dependencies on
+     * installed extensions. Note that the dependencies declared by this method
+     * are not required for the library to operate but if present will be used.
+     * It is NOT part of the official "Optional Package" specification.
+     *
+     * @see <a href="http://java.sun.com/j2se/1.3/docs/guide/extensions/spec.html#dependnecy">
+     *      Installed extension dependency</a>
+     */
+    public static final Attributes.Name OPTIONAL_EXTENSION_LIST
+        = new Attributes.Name("Optional-Extension-List");
+
+    /**
+     * Manifest Attribute Name object for EXTENSION_NAME.
+     */
+    public static final Attributes.Name EXTENSION_NAME =
+        new Attributes.Name("Extension-Name");
+    /**
+     * Manifest Attribute Name object for SPECIFICATION_VERSION.
+     */
+    public static final Attributes.Name SPECIFICATION_VERSION
+        = Attributes.Name.SPECIFICATION_VERSION;
+
+    /**
+     * Manifest Attribute Name object for SPECIFICATION_VENDOR.
+     */
+    public static final Attributes.Name SPECIFICATION_VENDOR
+        = Attributes.Name.SPECIFICATION_VENDOR;
+
+    /**
+     * Manifest Attribute Name object for IMPLEMENTATION_VERSION.
+     */
+    public static final Attributes.Name IMPLEMENTATION_VERSION
+        = Attributes.Name.IMPLEMENTATION_VERSION;
+
+    /**
+     * Manifest Attribute Name object for IMPLEMENTATION_VENDOR.
+     */
+    public static final Attributes.Name IMPLEMENTATION_VENDOR
+        = Attributes.Name.IMPLEMENTATION_VENDOR;
+
+    /**
+     * Manifest Attribute Name object for IMPLEMENTATION_URL.
+     */
+    public static final Attributes.Name IMPLEMENTATION_URL
+        = new Attributes.Name("Implementation-URL");
+
+    /**
+     * Manifest Attribute Name object for IMPLEMENTATION_VENDOR_ID.
+     */
+    public static final Attributes.Name IMPLEMENTATION_VENDOR_ID
+        = new Attributes.Name("Implementation-Vendor-Id");
+
+    /**
+     * Enum indicating that extension is compatible with other extension.
+     */
+    public static final Compatibility COMPATIBLE
+        = new Compatibility("COMPATIBLE");
+
+    /**
+     * Enum indicating that extension requires an upgrade
+     * of specification to be compatible with other extension.
+     */
+    public static final Compatibility REQUIRE_SPECIFICATION_UPGRADE
+        = new Compatibility("REQUIRE_SPECIFICATION_UPGRADE");
+
+    /**
+     * Enum indicating that extension requires a vendor
+     * switch to be compatible with other extension.
+     */
+    public static final Compatibility REQUIRE_VENDOR_SWITCH
+        = new Compatibility("REQUIRE_VENDOR_SWITCH");
+
+    /**
+     * Enum indicating that extension requires an upgrade
+     * of implementation to be compatible with other extension.
+     */
+    public static final Compatibility REQUIRE_IMPLEMENTATION_UPGRADE
+        = new Compatibility("REQUIRE_IMPLEMENTATION_UPGRADE");
+
+    /**
+     * Enum indicating that extension is incompatible with
+     * other extension in ways other than other enums
+     * indicate). For example the other extension may have
+     * a different ID.
+     */
+    public static final Compatibility INCOMPATIBLE
+        = new Compatibility("INCOMPATIBLE");
+
+    /**
+     * The name of the optional package being made available, or required.
+     */
+    private String extensionName;
+
+    /**
+     * The version number (dotted decimal notation) of the specification
+     * to which this optional package conforms.
+     */
+    private DeweyDecimal specificationVersion;
+
+    /**
+     * The name of the company or organization that originated the
+     * specification to which this optional package conforms.
+     */
+    private String specificationVendor;
+
+    /**
+     * The unique identifier of the company that produced the optional
+     * package contained in this JAR file.
+     */
+    private String implementationVendorID;
+
+    /**
+     * The name of the company or organization that produced this
+     * implementation of this optional package.
+     */
+    private String implementationVendor;
+
+    /**
+     * The version number (dotted decimal notation) for this implementation
+     * of the optional package.
+     */
+    private DeweyDecimal implementationVersion;
+
+    /**
+     * The URL from which the most recent version of this optional package
+     * can be obtained if it is not already installed.
+     */
+    private String implementationURL;
+
+    /**
+     * Return an array of <code>Extension</code> objects representing optional
+     * packages that are available in the JAR file associated with the
+     * specified <code>Manifest</code>.  If there are no such optional
+     * packages, a zero-length array is returned.
+     *
+     * @param manifest Manifest to be parsed
+     * @return the "available" extensions in specified manifest
+     */
+    public static Extension[] getAvailable(final Manifest manifest) {
+        if (null == manifest) {
+            return new Extension[ 0 ];
+        }
+
+        final ArrayList results = new ArrayList();
+
+        final Attributes mainAttributes = manifest.getMainAttributes();
+        if (null != mainAttributes) {
+            final Extension extension = getExtension("", mainAttributes);
+            if (null != extension) {
+                results.add(extension);
+            }
+        }
+
+        final Map entries = manifest.getEntries();
+        final Iterator keys = entries.keySet().iterator();
+        while (keys.hasNext()) {
+            final String key = (String) keys.next();
+            final Attributes attributes = (Attributes) entries.get(key);
+            final Extension extension = getExtension("", attributes);
+            if (null != extension) {
+                results.add(extension);
+            }
+        }
+
+        return (Extension[]) results.toArray(new Extension[0]);
+    }
+
+    /**
+     * Return the set of <code>Extension</code> objects representing optional
+     * packages that are required by the application contained in the JAR
+     * file associated with the specified <code>Manifest</code>.  If there
+     * are no such optional packages, a zero-length list is returned.
+     *
+     * @param manifest Manifest to be parsed
+     * @return the dependencies that are specified in manifes
+     */
+    public static Extension[] getRequired(final Manifest manifest) {
+        return getListed(manifest, Attributes.Name.EXTENSION_LIST);
+    }
+
+    /**
+     * Return the set of <code>Extension</code> objects representing "Optional
+     * Packages" that the application declares they will use if present. If
+     * there are no such optional packages, a zero-length list is returned.
+     *
+     * @param manifest Manifest to be parsed
+     * @return the optional dependencies that are specified in manifest
+     */
+    public static Extension[] getOptions(final Manifest manifest) {
+        return getListed(manifest, OPTIONAL_EXTENSION_LIST);
+    }
+
+    /**
+     * Add Extension to the specified manifest Attributes.
+     *
+     * @param attributes the attributes of manifest to add to
+     * @param extension the extension
+     */
+    public static void addExtension(final Extension extension,
+                                     final Attributes attributes) {
+        addExtension(extension, "", attributes);
+    }
+
+    /**
+     * Add Extension to the specified manifest Attributes.
+     * Use the specified prefix so that dependencies can added
+     * with a prefix such as "java3d-" etc.
+     *
+     * @param attributes the attributes of manifest to add to
+     * @param extension the extension
+     * @param prefix the name to prefix to extension
+     */
+    public static void addExtension(final Extension extension,
+                                     final String prefix,
+                                     final Attributes attributes) {
+        attributes.putValue(prefix + EXTENSION_NAME,
+                             extension.getExtensionName());
+
+        final String specificationVendor = extension.getSpecificationVendor();
+        if (null != specificationVendor) {
+            attributes.putValue(prefix + SPECIFICATION_VENDOR,
+                                 specificationVendor);
+        }
+
+        final DeweyDecimal specificationVersion
+            = extension.getSpecificationVersion();
+        if (null != specificationVersion) {
+            attributes.putValue(prefix + SPECIFICATION_VERSION,
+                                 specificationVersion.toString());
+        }
+
+        final String implementationVendorID
+            = extension.getImplementationVendorID();
+        if (null != implementationVendorID) {
+            attributes.putValue(prefix + IMPLEMENTATION_VENDOR_ID,
+                                 implementationVendorID);
+        }
+
+        final String implementationVendor = extension.getImplementationVendor();
+        if (null != implementationVendor) {
+            attributes.putValue(prefix + IMPLEMENTATION_VENDOR,
+                                 implementationVendor);
+        }
+
+        final DeweyDecimal implementationVersion
+            = extension.getImplementationVersion();
+        if (null != implementationVersion) {
+            attributes.putValue(prefix + IMPLEMENTATION_VERSION,
+                                 implementationVersion.toString());
+        }
+
+        final String implementationURL = extension.getImplementationURL();
+        if (null != implementationURL) {
+            attributes.putValue(prefix + IMPLEMENTATION_URL,
+                                 implementationURL);
+        }
+    }
+
+    /**
+     * The constructor to create Extension object.
+     * Note that every component is allowed to be specified
+     * but only the extensionName is mandatory.
+     *
+     * @param extensionName the name of extension.
+     * @param specificationVersion the specification Version of extension.
+     * @param specificationVendor the specification Vendor of extension.
+     * @param implementationVersion the implementation Version of extension.
+     * @param implementationVendor the implementation Vendor of extension.
+     * @param implementationVendorId the implementation VendorId of extension.
+     * @param implementationURL the implementation URL of extension.
+     */
+    public Extension(final String extensionName,
+                      final String specificationVersion,
+                      final String specificationVendor,
+                      final String implementationVersion,
+                      final String implementationVendor,
+                      final String implementationVendorId,
+                      final String implementationURL) {
+        this.extensionName = extensionName;
+        this.specificationVendor = specificationVendor;
+
+        if (null != specificationVersion) {
+            try {
+                this.specificationVersion
+                    = new DeweyDecimal(specificationVersion);
+            } catch (final NumberFormatException nfe) {
+                final String error = "Bad specification version format '"
+                    + specificationVersion + "' in '" + extensionName
+                    + "'. (Reason: " + nfe + ")";
+                throw new IllegalArgumentException(error);
+            }
+        }
+
+        this.implementationURL = implementationURL;
+        this.implementationVendor = implementationVendor;
+        this.implementationVendorID = implementationVendorId;
+
+        if (null != implementationVersion) {
+            try {
+                this.implementationVersion
+                    = new DeweyDecimal(implementationVersion);
+            } catch (final NumberFormatException nfe) {
+                final String error = "Bad implementation version format '"
+                    + implementationVersion + "' in '" + extensionName
+                    + "'. (Reason: " + nfe + ")";
+                throw new IllegalArgumentException(error);
+            }
+        }
+
+        if (null == this.extensionName) {
+            throw new NullPointerException("extensionName property is null");
+        }
+    }
+
+    /**
+     * Get the name of the extension.
+     *
+     * @return the name of the extension
+     */
+    public String getExtensionName() {
+        return extensionName;
+    }
+
+    /**
+     * Get the vendor of the extensions specification.
+     *
+     * @return the vendor of the extensions specification.
+     */
+    public String getSpecificationVendor() {
+        return specificationVendor;
+    }
+
+    /**
+     * Get the version of the extensions specification.
+     *
+     * @return the version of the extensions specification.
+     */
+    public DeweyDecimal getSpecificationVersion() {
+        return specificationVersion;
+    }
+
+    /**
+     * Get the url of the extensions implementation.
+     *
+     * @return the url of the extensions implementation.
+     */
+    public String getImplementationURL() {
+        return implementationURL;
+    }
+
+    /**
+     * Get the vendor of the extensions implementation.
+     *
+     * @return the vendor of the extensions implementation.
+     */
+    public String getImplementationVendor() {
+        return implementationVendor;
+    }
+
+    /**
+     * Get the vendorID of the extensions implementation.
+     *
+     * @return the vendorID of the extensions implementation.
+     */
+    public String getImplementationVendorID() {
+        return implementationVendorID;
+    }
+
+    /**
+     * Get the version of the extensions implementation.
+     *
+     * @return the version of the extensions implementation.
+     */
+    public DeweyDecimal getImplementationVersion() {
+        return implementationVersion;
+    }
+
+    /**
+     * Return a Compatibility enum indicating the relationship of this
+     * <code>Extension</code> with the specified <code>Extension</code>.
+     *
+     * @param required Description of the required optional package
+     * @return the enum indicating the compatibility (or lack thereof)
+     *         of specifed extension
+     */
+    public Compatibility getCompatibilityWith(final Extension required) {
+        // Extension Name must match
+        if (!extensionName.equals(required.getExtensionName())) {
+            return INCOMPATIBLE;
+        }
+
+        // Available specification version must be >= required
+        final DeweyDecimal requiredSpecificationVersion
+            = required.getSpecificationVersion();
+        if (null != requiredSpecificationVersion) {
+            if (null == specificationVersion
+                || !isCompatible(specificationVersion, requiredSpecificationVersion)) {
+                return REQUIRE_SPECIFICATION_UPGRADE;
+            }
+        }
+
+        // Implementation Vendor ID must match
+        final String requiredImplementationVendorID
+            = required.getImplementationVendorID();
+        if (null != requiredImplementationVendorID) {
+            if (null == implementationVendorID
+                || !implementationVendorID.equals(requiredImplementationVendorID)) {
+                return REQUIRE_VENDOR_SWITCH;
+            }
+        }
+
+        // Implementation version must be >= required
+        final DeweyDecimal requiredImplementationVersion
+            = required.getImplementationVersion();
+        if (null != requiredImplementationVersion) {
+            if (null == implementationVersion
+                || !isCompatible(implementationVersion, requiredImplementationVersion)) {
+                return REQUIRE_IMPLEMENTATION_UPGRADE;
+            }
+        }
+
+        // This available optional package satisfies the requirements
+        return COMPATIBLE;
+    }
+
+    /**
+     * Return <code>true</code> if the specified <code>Extension</code>
+     * (which represents an optional package required by an application)
+     * is satisfied by this <code>Extension</code> (which represents an
+     * optional package that is already installed.  Otherwise, return
+     * <code>false</code>.
+     *
+     * @param required Description of the required optional package
+     * @return true if the specified extension is compatible with this extension
+     */
+    public boolean isCompatibleWith(final Extension required) {
+        return (COMPATIBLE == getCompatibilityWith(required));
+    }
+
+    /**
+     * Return a String representation of this object.
+     *
+     * @return string representation of object.
+     */
+    public String toString() {
+        final String lineSeparator = System.getProperty("line.separator");
+        final String brace = ": ";
+
+        final StringBuffer sb = new StringBuffer(EXTENSION_NAME.toString());
+        sb.append(brace);
+        sb.append(extensionName);
+        sb.append(lineSeparator);
+
+        if (null != specificationVersion) {
+            sb.append(SPECIFICATION_VERSION);
+            sb.append(brace);
+            sb.append(specificationVersion);
+            sb.append(lineSeparator);
+        }
+
+        if (null != specificationVendor) {
+            sb.append(SPECIFICATION_VENDOR);
+            sb.append(brace);
+            sb.append(specificationVendor);
+            sb.append(lineSeparator);
+        }
+
+        if (null != implementationVersion) {
+            sb.append(IMPLEMENTATION_VERSION);
+            sb.append(brace);
+            sb.append(implementationVersion);
+            sb.append(lineSeparator);
+        }
+
+        if (null != implementationVendorID) {
+            sb.append(IMPLEMENTATION_VENDOR_ID);
+            sb.append(brace);
+            sb.append(implementationVendorID);
+            sb.append(lineSeparator);
+        }
+
+        if (null != implementationVendor) {
+            sb.append(IMPLEMENTATION_VENDOR);
+            sb.append(brace);
+            sb.append(implementationVendor);
+            sb.append(lineSeparator);
+        }
+
+        if (null != implementationURL) {
+            sb.append(IMPLEMENTATION_URL);
+            sb.append(brace);
+            sb.append(implementationURL);
+            sb.append(lineSeparator);
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Return <code>true</code> if the first version number is greater than
+     * or equal to the second; otherwise return <code>false</code>.
+     *
+     * @param first First version number (dotted decimal)
+     * @param second Second version number (dotted decimal)
+     */
+    private boolean isCompatible(final DeweyDecimal first,
+                                 final DeweyDecimal second) {
+        return first.isGreaterThanOrEqual(second);
+    }
+
+    /**
+     * Retrieve all the extensions listed under a particular key
+     * (Usually EXTENSION_LIST or OPTIONAL_EXTENSION_LIST).
+     *
+     * @param manifest the manifest to extract extensions from
+     * @param listKey the key used to get list (Usually
+     *        EXTENSION_LIST or OPTIONAL_EXTENSION_LIST)
+     * @return the list of listed extensions
+     */
+    private static Extension[] getListed(final Manifest manifest,
+                                          final Attributes.Name listKey) {
+        final ArrayList results = new ArrayList();
+        final Attributes mainAttributes = manifest.getMainAttributes();
+
+        if (null != mainAttributes) {
+            getExtension(mainAttributes, results, listKey);
+        }
+
+        final Map entries = manifest.getEntries();
+        final Iterator keys = entries.keySet().iterator();
+        while (keys.hasNext()) {
+            final String key = (String) keys.next();
+            final Attributes attributes = (Attributes) entries.get(key);
+            getExtension(attributes, results, listKey);
+        }
+
+        return (Extension[]) results.toArray(new Extension[ 0 ]);
+    }
+
+    /**
+     * Add required optional packages defined in the specified
+     * attributes entry, if any.
+     *
+     * @param attributes Attributes to be parsed
+     * @param required list to add required optional packages to
+     * @param listKey the key to use to lookup list, usually EXTENSION_LIST
+     *    or OPTIONAL_EXTENSION_LIST
+     */
+    private static void getExtension(final Attributes attributes,
+                                     final ArrayList required,
+                                     final Attributes.Name listKey) {
+        final String names = attributes.getValue(listKey);
+        if (null == names) {
+            return;
+        }
+
+        final String[] extentions = split(names, " ");
+        for (int i = 0; i < extentions.length; i++) {
+            final String prefix = extentions[ i ] + "-";
+            final Extension extension = getExtension(prefix, attributes);
+
+            if (null != extension) {
+                required.add(extension);
+            }
+        }
+    }
+
+    /**
+     * Splits the string on every token into an array of strings.
+     *
+     * @param string the string
+     * @param onToken the token
+     * @return the resultant array
+     */
+    private static final String[] split(final String string,
+                                        final String onToken) {
+        final StringTokenizer tokenizer = new StringTokenizer(string, onToken);
+        final String[] result = new String[ tokenizer.countTokens() ];
+
+        for (int i = 0; i < result.length; i++) {
+            result[ i ] = tokenizer.nextToken();
+        }
+
+        return result;
+    }
+
+    /**
+     * Extract an Extension from Attributes.
+     * Prefix indicates the prefix checked for each string.
+     * Usually the prefix is <em>"&lt;extension&gt;-"</em> if looking for a
+     * <b>Required</b> extension. If you are looking for an
+     * <b>Available</b> extension
+     * then the prefix is <em>""</em>.
+     *
+     * @param prefix the prefix for each attribute name
+     * @param attributes Attributes to searched
+     * @return the new Extension object, or null
+     */
+    private static Extension getExtension(final String prefix,
+                                          final Attributes attributes) {
+        //WARNING: We trim the values of all the attributes because
+        //Some extension declarations are badly defined (ie have spaces
+        //after version or vendorID)
+        final String nameKey = prefix + EXTENSION_NAME;
+        final String name = getTrimmedString(attributes.getValue(nameKey));
+        if (null == name) {
+            return null;
+        }
+
+        final String specVendorKey = prefix + SPECIFICATION_VENDOR;
+        final String specVendor
+            = getTrimmedString(attributes.getValue(specVendorKey));
+        final String specVersionKey = prefix + SPECIFICATION_VERSION;
+        final String specVersion
+            = getTrimmedString(attributes.getValue(specVersionKey));
+
+        final String impVersionKey = prefix + IMPLEMENTATION_VERSION;
+        final String impVersion
+            = getTrimmedString(attributes.getValue(impVersionKey));
+        final String impVendorKey = prefix + IMPLEMENTATION_VENDOR;
+        final String impVendor
+            = getTrimmedString(attributes.getValue(impVendorKey));
+        final String impVendorIDKey = prefix + IMPLEMENTATION_VENDOR_ID;
+        final String impVendorId
+            = getTrimmedString(attributes.getValue(impVendorIDKey));
+        final String impURLKey = prefix + IMPLEMENTATION_URL;
+        final String impURL = getTrimmedString(attributes.getValue(impURLKey));
+
+        return new Extension(name, specVersion, specVendor, impVersion,
+                              impVendor, impVendorId, impURL);
+    }
+
+    /**
+     * Trim the supplied string if the string is non-null
+     *
+     * @param value the string to trim or null
+     * @return the trimmed string or null
+     */
+    private static String getTrimmedString(final String value) {
+        if (null == value) {
+            return null;
+        } else {
+            return value.trim();
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionAdapter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionAdapter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionAdapter.java	(revision 16139)
@@ -0,0 +1,224 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Simple class that represents an Extension and conforms to Ants
+ * patterns.
+ *
+ * @ant.data-type name="extension"
+ */
+public class ExtensionAdapter extends DataType {
+    /**
+     * The name of the optional package being made available, or required.
+     */
+    private String extensionName;
+
+    /**
+     * The version number (dotted decimal notation) of the specification
+     * to which this optional package conforms.
+     */
+    private DeweyDecimal specificationVersion;
+
+    /**
+     * The name of the company or organization that originated the
+     * specification to which this optional package conforms.
+     */
+    private String specificationVendor;
+
+    /**
+     * The unique identifier of the company that produced the optional
+     * package contained in this JAR file.
+     */
+    private String implementationVendorID;
+
+    /**
+     * The name of the company or organization that produced this
+     * implementation of this optional package.
+     */
+    private String implementationVendor;
+
+    /**
+     * The version number (dotted decimal notation) for this implementation
+     * of the optional package.
+     */
+    private DeweyDecimal implementationVersion;
+
+    /**
+     * The URL from which the most recent version of this optional package
+     * can be obtained if it is not already installed.
+     */
+    private String implementationURL;
+
+    /**
+     * Set the name of extension.
+     *
+     * @param extensionName the name of extension
+     */
+    public void setExtensionName(final String extensionName) {
+        verifyNotAReference();
+        this.extensionName = extensionName;
+    }
+
+    /**
+     * Set the specificationVersion of extension.
+     *
+     * @param specificationVersion the specificationVersion of extension
+     */
+    public void setSpecificationVersion(final String specificationVersion) {
+        verifyNotAReference();
+        this.specificationVersion = new DeweyDecimal(specificationVersion);
+    }
+
+    /**
+     * Set the specificationVendor of extension.
+     *
+     * @param specificationVendor the specificationVendor of extension
+     */
+    public void setSpecificationVendor(final String specificationVendor) {
+        verifyNotAReference();
+        this.specificationVendor = specificationVendor;
+    }
+
+    /**
+     * Set the implementationVendorID of extension.
+     *
+     * @param implementationVendorID the implementationVendorID of extension
+     */
+    public void setImplementationVendorId(final String implementationVendorID) {
+        verifyNotAReference();
+        this.implementationVendorID = implementationVendorID;
+    }
+
+    /**
+     * Set the implementationVendor of extension.
+     *
+     * @param implementationVendor the implementationVendor of extension
+     */
+    public void setImplementationVendor(final String implementationVendor) {
+        verifyNotAReference();
+        this.implementationVendor = implementationVendor;
+    }
+
+    /**
+     * Set the implementationVersion of extension.
+     *
+     * @param implementationVersion the implementationVersion of extension
+     */
+    public void setImplementationVersion(final String implementationVersion) {
+        verifyNotAReference();
+        this.implementationVersion = new DeweyDecimal(implementationVersion);
+    }
+
+    /**
+     * Set the implementationURL of extension.
+     *
+     * @param implementationURL the implementationURL of extension
+     */
+    public void setImplementationUrl(final String implementationURL) {
+        verifyNotAReference();
+        this.implementationURL = implementationURL;
+    }
+
+    /**
+     * Makes this instance in effect a reference to another ExtensionAdapter
+     * instance.
+     *
+     * <p>You must not set another attribute or nest elements inside
+     * this element if you make it a reference.</p>
+     *
+     * @param reference the reference to which this instance is associated
+     * @exception BuildException if this instance already has been configured.
+     */
+    public void setRefid(final Reference reference)
+        throws BuildException {
+        if (null != extensionName
+            || null != specificationVersion
+            || null != specificationVendor
+            || null != implementationVersion
+            || null != implementationVendorID
+            || null != implementationVendor
+            || null != implementationURL) {
+            throw tooManyAttributes();
+        }
+        // change this to get the objects from the other reference
+        Object o = reference.getReferencedObject(getProject());
+        if (o instanceof ExtensionAdapter) {
+            final ExtensionAdapter other = (ExtensionAdapter) o;
+            extensionName = other.extensionName;
+            specificationVersion = other.specificationVersion;
+            specificationVendor = other.specificationVendor;
+            implementationVersion = other.implementationVersion;
+            implementationVendorID = other.implementationVendorID;
+            implementationVendor = other.implementationVendor;
+            implementationURL = other.implementationURL;
+        } else {
+            final String message =
+                reference.getRefId() + " doesn\'t refer to a Extension";
+            throw new BuildException(message);
+        }
+
+        super.setRefid(reference);
+    }
+
+    private void verifyNotAReference()
+        throws BuildException {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+    }
+
+    /**
+     * Convert this adpater object into an extension object.
+     *
+     * @return the extension object
+     */
+    Extension toExtension()
+        throws BuildException {
+        if (null == extensionName) {
+            final String message = "Extension is missing name.";
+            throw new BuildException(message);
+        }
+
+        String specificationVersionString = null;
+        if (null != specificationVersion) {
+            specificationVersionString = specificationVersion.toString();
+        }
+        String implementationVersionString = null;
+        if (null != implementationVersion) {
+            implementationVersionString = implementationVersion.toString();
+        }
+        return new Extension(extensionName,
+                              specificationVersionString,
+                              specificationVendor,
+                              implementationVersionString,
+                              implementationVendor,
+                              implementationVendorID,
+                              implementationURL);
+    }
+
+    /**
+     * @see java.lang.Object#toString()
+     */
+    public String toString() {
+        return "{" + toExtension().toString() + "}";
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionResolver.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionResolver.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionResolver.java	(revision 16139)
@@ -0,0 +1,41 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Interface to locate a File that satisfies extension.
+ *
+ */
+public interface ExtensionResolver {
+    /**
+     * Attempt to locate File that satisfies
+     * extension via resolver.
+     *
+     * @param extension the extension
+     * @param project the Ant project instance
+     * @return the File satisfying extension, null
+     *         if can not resolve extension
+     * @throws BuildException if error occurs attempting to
+     *         resolve extension
+     */
+    File resolve(Extension extension, Project project)
+        throws BuildException;
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionSet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionSet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionSet.java	(revision 16139)
@@ -0,0 +1,123 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * The Extension set lists a set of "Optional Packages" /
+ * "Extensions".
+ *
+ * @ant.data-type name="extension-set"
+ */
+public class ExtensionSet
+    extends DataType {
+    /**
+     * ExtensionAdapter objects representing extensions.
+     */
+    private final ArrayList extensions = new ArrayList();
+
+    /**
+     * Filesets specifying all the extensions wanted.
+     */
+    private final ArrayList extensionsFilesets = new ArrayList();
+
+    /**
+     * Adds an extension that this library requires.
+     *
+     * @param extensionAdapter an extension that this library requires.
+     */
+    public void addExtension(final ExtensionAdapter extensionAdapter) {
+        extensions.add(extensionAdapter);
+    }
+
+    /**
+     * Adds a set of files about which extensions data will be extracted.
+     *
+     * @param fileSet a set of files about which extensions data will be extracted.
+     */
+    public void addLibfileset(final LibFileSet fileSet) {
+        extensionsFilesets.add(fileSet);
+    }
+
+    /**
+     * Adds a set of files about which extensions data will be extracted.
+     *
+     * @param fileSet a set of files about which extensions data will be extracted.
+     */
+    public void addFileset(final FileSet fileSet) {
+        extensionsFilesets.add(fileSet);
+    }
+
+    /**
+     * Extract a set of Extension objects from the ExtensionSet.
+     *
+     * @param project the project instance.
+     * @return an array containing the Extensions from this set
+     * @throws BuildException if an error occurs
+     */
+    public Extension[] toExtensions(final Project project)
+        throws BuildException {
+        final ArrayList extensionsList = ExtensionUtil.toExtensions(extensions);
+        ExtensionUtil.extractExtensions(project, extensionsList, extensionsFilesets);
+        return (Extension[]) extensionsList.toArray(new Extension[extensionsList.size()]);
+    }
+
+    /**
+     * Makes this instance in effect a reference to another ExtensionSet
+     * instance.
+     *
+     * <p>You must not set another attribute or nest elements inside
+     * this element if you make it a reference.</p>
+     *
+     * @param reference the reference to which this instance is associated
+     * @exception BuildException if this instance already has been configured.
+     */
+    public void setRefid(final Reference reference)
+        throws BuildException {
+        if (!extensions.isEmpty() || !extensionsFilesets.isEmpty()) {
+            throw tooManyAttributes();
+        }
+        // change this to get the objects from the other reference
+        final Object object =
+            reference.getReferencedObject(getProject());
+        if (object instanceof ExtensionSet) {
+            final ExtensionSet other = (ExtensionSet) object;
+            extensions.addAll(other.extensions);
+            extensionsFilesets.addAll(other.extensionsFilesets);
+        } else {
+            final String message =
+                reference.getRefId() + " doesn\'t refer to a ExtensionSet";
+            throw new BuildException(message);
+        }
+
+        super.setRefid(reference);
+    }
+
+    /**
+     * @see java.lang.Object#toString()
+     */
+    public String toString() {
+        return "ExtensionSet" + Arrays.asList(toExtensions(getProject()));
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionUtil.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionUtil.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtensionUtil.java	(revision 16139)
@@ -0,0 +1,211 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.jar.JarFile;
+import java.util.jar.Manifest;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * A set of useful methods relating to extensions.
+ *
+ */
+public class ExtensionUtil {
+    /**
+     * Class is not meant to be instantiated.
+     */
+    private ExtensionUtil() {
+    }
+
+    /**
+     * Convert a list of extensionAdapter objects to extensions.
+     *
+     * @param adapters the list of ExtensionAdapterss to add to convert
+     * @throws BuildException if an error occurs
+     */
+    static ArrayList toExtensions(final ArrayList adapters)
+        throws BuildException {
+        final ArrayList results = new ArrayList();
+
+        final int size = adapters.size();
+        for (int i = 0; i < size; i++) {
+            final ExtensionAdapter adapter =
+                (ExtensionAdapter) adapters.get(i);
+            final Extension extension = adapter.toExtension();
+            results.add(extension);
+        }
+
+        return results;
+    }
+
+    /**
+     * Generate a list of extensions from a specified fileset.
+     *
+     * @param librarys the list to add extensions to
+     * @param fileset the filesets containing librarys
+     * @throws BuildException if an error occurs
+     */
+    static void extractExtensions(final Project project,
+                                   final ArrayList librarys,
+                                   final ArrayList fileset)
+        throws BuildException {
+        if (!fileset.isEmpty()) {
+            final Extension[] extensions = getExtensions(project,
+                                                          fileset);
+            for (int i = 0; i < extensions.length; i++) {
+                librarys.add(extensions[ i ]);
+            }
+        }
+    }
+
+    /**
+     * Retrieve extensions from the specified librarys.
+     *
+     * @param librarys the filesets for librarys
+     * @return the extensions contained in librarys
+     * @throws BuildException if failing to scan librarys
+     */
+    private static Extension[] getExtensions(final Project project,
+                                              final ArrayList librarys)
+        throws BuildException {
+        final ArrayList extensions = new ArrayList();
+        final Iterator iterator = librarys.iterator();
+        while (iterator.hasNext()) {
+            final FileSet fileSet = (FileSet) iterator.next();
+
+            boolean includeImpl = true;
+            boolean includeURL = true;
+
+            if (fileSet instanceof LibFileSet) {
+                LibFileSet libFileSet = (LibFileSet) fileSet;
+                includeImpl = libFileSet.isIncludeImpl();
+                includeURL = libFileSet.isIncludeURL();
+            }
+
+            final DirectoryScanner scanner = fileSet.getDirectoryScanner(project);
+            final File basedir = scanner.getBasedir();
+            final String[] files = scanner.getIncludedFiles();
+            for (int i = 0; i < files.length; i++) {
+                final File file = new File(basedir, files[ i ]);
+                loadExtensions(file, extensions, includeImpl, includeURL);
+            }
+        }
+        return (Extension[]) extensions.toArray(new Extension[extensions.size()]);
+    }
+
+    /**
+     * Load list of available extensions from specified file.
+     *
+     * @param file the file
+     * @param extensionList the list to add available extensions to
+     * @throws BuildException if there is an error
+     */
+    private static void loadExtensions(final File file,
+                                        final ArrayList extensionList,
+                                        final boolean includeImpl,
+                                        final boolean includeURL)
+        throws BuildException {
+        try {
+            final JarFile jarFile = new JarFile(file);
+            final Extension[] extensions =
+                Extension.getAvailable(jarFile.getManifest());
+            for (int i = 0; i < extensions.length; i++) {
+                final Extension extension = extensions[ i ];
+                addExtension(extensionList, extension, includeImpl, includeURL);
+            }
+        } catch (final Exception e) {
+            throw new BuildException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Add extension to list.
+     * If extension should not have implementation details but
+     * does strip them. If extension should not have url but does
+     * then strip it.
+     *
+     * @param extensionList the list of extensions to add to
+     * @param originalExtension the extension
+     * @param includeImpl false to exclude implementation details
+     * @param includeURL false to exclude implementation URL
+     */
+    private static void addExtension(final ArrayList extensionList,
+                                      final Extension originalExtension,
+                                      final boolean includeImpl,
+                                      final boolean includeURL) {
+        Extension extension = originalExtension;
+        if (!includeURL
+            && null != extension.getImplementationURL()) {
+            extension =
+                new Extension(extension.getExtensionName(),
+                               extension.getSpecificationVersion().toString(),
+                               extension.getSpecificationVendor(),
+                               extension.getImplementationVersion().toString(),
+                               extension.getImplementationVendor(),
+                               extension.getImplementationVendorID(),
+                               null);
+        }
+
+        final boolean hasImplAttributes =
+            null != extension.getImplementationURL()
+            || null != extension.getImplementationVersion()
+            || null != extension.getImplementationVendorID()
+            || null != extension.getImplementationVendor();
+
+        if (!includeImpl && hasImplAttributes) {
+            extension =
+                new Extension(extension.getExtensionName(),
+                               extension.getSpecificationVersion().toString(),
+                               extension.getSpecificationVendor(),
+                               null,
+                               null,
+                               null,
+                               extension.getImplementationURL());
+        }
+
+        extensionList.add(extension);
+    }
+
+    /**
+     * retrieve manifest for specified file.
+     *
+     * @param file the file
+     * @return the manifest
+     * @throws BuildException if errror occurs (file not exist,
+     *         file not a jar, manifest not exist in file)
+     */
+    static Manifest getManifest(final File file)
+        throws BuildException {
+        try {
+            final JarFile jarFile = new JarFile(file);
+            Manifest m = jarFile.getManifest();
+            if (m == null) {
+                throw new BuildException(file + " doesn't have a MANIFEST");
+            }
+            return m;
+        } catch (final IOException ioe) {
+            throw new BuildException(ioe.getMessage(), ioe);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtraAttribute.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtraAttribute.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/ExtraAttribute.java	(revision 16139)
@@ -0,0 +1,82 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Simple holder for extra attributes in main section of manifest.
+ *
+ * @todo Refactor this and all the other parameter, sysproperty,
+ *   property etc into a single class in framework
+ */
+public class ExtraAttribute {
+    private String name;
+    private String value;
+
+    /**
+     * Set the name of the parameter.
+     *
+     * @param name the name of parameter
+     */
+    public void setName(final String name) {
+        this.name = name;
+    }
+
+    /**
+     * Set the value of the parameter.
+     *
+     * @param value the parameter value
+     */
+    public void setValue(final String value) {
+        this.value = value;
+    }
+
+    /**
+     * Retrieve name of parameter.
+     *
+     * @return the name of parameter.
+     */
+    String getName() {
+        return name;
+    }
+
+    /**
+     * Retrieve the value of parameter.
+     *
+     * @return the value of parameter.
+     */
+    String getValue() {
+        return value;
+    }
+
+    /**
+     * Make sure that neither the name or the value
+     * is null.
+     *
+     * @throws BuildException if the attribute is invalid.
+     */
+    public void validate() throws BuildException {
+        if (null == name) {
+            final String message = "Missing name from parameter.";
+            throw new BuildException(message);
+        } else if (null == value) {
+            final String message = "Missing value from parameter " + name + ".";
+            throw new BuildException(message);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibAvailableTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibAvailableTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibAvailableTask.java	(revision 16139)
@@ -0,0 +1,155 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Vector;
+import java.util.jar.Manifest;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Checks whether an extension is present in a fileset or an extensionSet.
+ *
+ * @ant.task name="jarlib-available"
+ */
+public class JarLibAvailableTask extends Task {
+    /**
+     * The library to display information about.
+     */
+    private File libraryFile;
+
+    /**
+     * Filesets specifying all the librarys
+     * to display information about.
+     */
+    private final Vector extensionFileSets = new Vector();
+
+    /**
+     * The name of the property to set if extension is available.
+     */
+    private String propertyName;
+
+    /**
+     * The extension that is required.
+     */
+    private ExtensionAdapter requiredExtension;
+
+    /**
+     * The name of property to set if extensions are available.
+     *
+     * @param property The name of property to set if extensions is available.
+     */
+    public void setProperty(final String property) {
+        this.propertyName = property;
+    }
+
+    /**
+     * The JAR library to check.
+     *
+     * @param file The jar library to check.
+     */
+    public void setFile(final File file) {
+        this.libraryFile = file;
+    }
+
+    /**
+     * Set the Extension looking for.
+     *
+     * @param extension Set the Extension looking for.
+     */
+    public void addConfiguredExtension(final ExtensionAdapter extension) {
+        if (null != requiredExtension) {
+            final String message = "Can not specify extension to "
+                + "search for multiple times.";
+            throw new BuildException(message);
+        }
+        requiredExtension = extension;
+    }
+
+    /**
+     * Adds a set of extensions to search in.
+     *
+     * @param extensionSet a set of extensions to search in.
+     */
+    public void addConfiguredExtensionSet(final ExtensionSet extensionSet) {
+        extensionFileSets.addElement(extensionSet);
+    }
+
+    /**
+     * Execute the task.
+     *
+     * @throws BuildException if somethign goes wrong.
+     */
+    public void execute() throws BuildException {
+        validate();
+
+        final Extension test = requiredExtension.toExtension();
+
+        // Check if list of files to check has been specified
+        if (!extensionFileSets.isEmpty()) {
+            final Iterator iterator = extensionFileSets.iterator();
+            while (iterator.hasNext()) {
+                final ExtensionSet extensionSet
+                    = (ExtensionSet) iterator.next();
+                final Extension[] extensions =
+                    extensionSet.toExtensions(getProject());
+                for (int i = 0; i < extensions.length; i++) {
+                    final Extension extension = extensions[ i ];
+                    if (extension.isCompatibleWith(test)) {
+                        getProject().setNewProperty(propertyName, "true");
+                    }
+                }
+            }
+        } else {
+            final Manifest manifest = ExtensionUtil.getManifest(libraryFile);
+            final Extension[] extensions = Extension.getAvailable(manifest);
+            for (int i = 0; i < extensions.length; i++) {
+                final Extension extension = extensions[ i ];
+                if (extension.isCompatibleWith(test)) {
+                    getProject().setNewProperty(propertyName, "true");
+                }
+            }
+        }
+    }
+
+    /**
+     * Validate the tasks parameters.
+     *
+     * @throws BuildException if invalid parameters found
+     */
+    private void validate() throws BuildException {
+        if (null == requiredExtension) {
+            final String message = "Extension element must be specified.";
+            throw new BuildException(message);
+        }
+
+        if (null == libraryFile && extensionFileSets.isEmpty()) {
+            final String message = "File attribute not specified.";
+            throw new BuildException(message);
+        }
+        if (null != libraryFile && !libraryFile.exists()) {
+            final String message = "File '" + libraryFile + "' does not exist.";
+            throw new BuildException(message);
+        }
+        if (null != libraryFile && !libraryFile.isFile()) {
+            final String message = "\'" + libraryFile + "\' is not a file.";
+            throw new BuildException(message);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibDisplayTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibDisplayTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibDisplayTask.java	(revision 16139)
@@ -0,0 +1,117 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * Displays the "Optional Package" and "Package Specification" information
+ * contained within the specified JARs.
+ *
+ * <p>Prior to JDK1.3, an "Optional Package" was known as an Extension.
+ * The specification for this mechanism is available in the JDK1.3
+ * documentation in the directory
+ * $JDK_HOME/docs/guide/extensions/versioning.html. Alternatively it is
+ * available online at <a href="http://java.sun.com/j2se/1.3/docs/guide/extensions/versioning.html">
+ * http://java.sun.com/j2se/1.3/docs/guide/extensions/versioning.html</a>.</p>
+ *
+ * @ant.task name="jarlib-display"
+ */
+public class JarLibDisplayTask extends Task {
+    /**
+     * The library to display information about.
+     */
+    private File libraryFile;
+
+    /**
+     * Filesets specifying all the librarys
+     * to display information about.
+     */
+    private final Vector libraryFileSets = new Vector();
+
+    /**
+     * The JAR library to display information for.
+     *
+     * @param file The jar library to display information for.
+     */
+    public void setFile(final File file) {
+        this.libraryFile = file;
+    }
+
+    /**
+     * Adds a set of files about which library data will be displayed.
+     *
+     * @param fileSet a set of files about which library data will be displayed.
+     */
+    public void addFileset(final FileSet fileSet) {
+        libraryFileSets.addElement(fileSet);
+    }
+
+    /**
+     * Execute the task.
+     *
+     * @throws BuildException if the task fails.
+     */
+    public void execute() throws BuildException {
+        validate();
+
+        final LibraryDisplayer displayer = new LibraryDisplayer();
+        // Check if list of files to check has been specified
+        if (!libraryFileSets.isEmpty()) {
+            final Iterator iterator = libraryFileSets.iterator();
+            while (iterator.hasNext()) {
+                final FileSet fileSet = (FileSet) iterator.next();
+                final DirectoryScanner scanner
+                    = fileSet.getDirectoryScanner(getProject());
+                final File basedir = scanner.getBasedir();
+                final String[] files = scanner.getIncludedFiles();
+                for (int i = 0; i < files.length; i++) {
+                    final File file = new File(basedir, files[ i ]);
+                    displayer.displayLibrary(file);
+                }
+            }
+        } else {
+            displayer.displayLibrary(libraryFile);
+        }
+    }
+
+    /**
+     * Validate the tasks parameters.
+     *
+     * @throws BuildException if invalid parameters found
+     */
+    private void validate() throws BuildException {
+        if (null == libraryFile && libraryFileSets.isEmpty()) {
+            final String message = "File attribute not specified.";
+            throw new BuildException(message);
+        }
+        if (null != libraryFile && !libraryFile.exists()) {
+            final String message = "File '" + libraryFile + "' does not exist.";
+            throw new BuildException(message);
+        }
+        if (null != libraryFile && !libraryFile.isFile()) {
+            final String message = "\'" + libraryFile + "\' is not a file.";
+            throw new BuildException(message);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibManifestTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibManifestTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibManifestTask.java	(revision 16139)
@@ -0,0 +1,312 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Generates a manifest that declares all the dependencies.
+ * The dependencies are determined by looking in the
+ * specified path and searching for Extension / "Optional Package"
+ * specifications in the manifests of the jars.
+ *
+ * <p>Prior to JDK1.3, an "Optional Package" was known as an Extension.
+ * The specification for this mechanism is available in the JDK1.3
+ * documentation in the directory
+ * $JDK_HOME/docs/guide/extensions/versioning.html. Alternatively it is
+ * available online at <a href="http://java.sun.com/j2se/1.3/docs/guide/extensions/versioning.html">
+ * http://java.sun.com/j2se/1.3/docs/guide/extensions/versioning.html</a>.</p>
+ *
+ * @ant.task name="jarlib-manifest"
+ */
+public final class JarLibManifestTask extends Task {
+    /**
+     * Version of manifest spec that task generates.
+     */
+    private static final String MANIFEST_VERSION = "1.0";
+
+    /**
+     * "Created-By" string used when creating manifest.
+     */
+    private static final String CREATED_BY = "Created-By";
+
+    /**
+     * The library to display information about.
+     */
+    private File destFile;
+
+    /**
+     * The extension supported by this library (if any).
+     */
+    private Extension extension;
+
+    /**
+     * ExtensionAdapter objects representing
+     * dependencies required by library.
+     */
+    private final ArrayList dependencies = new ArrayList();
+
+    /**
+     * ExtensionAdapter objects representing optional
+     * dependencies required by library.
+     */
+    private final ArrayList optionals = new ArrayList();
+
+    /**
+     * Extra attributes the user specifies for main section
+     * in manifest.
+     */
+    private final ArrayList extraAttributes = new ArrayList();
+
+    /**
+     * The location where generated manifest is placed.
+     *
+     * @param destFile The location where generated manifest is placed.
+     */
+    public void setDestfile(final File destFile) {
+        this.destFile = destFile;
+    }
+
+    /**
+     * Adds an extension that this library implements.
+     *
+     * @param extensionAdapter an extension that this library implements.
+     *
+     * @throws BuildException if there is multiple extensions detected
+     *         in the library.
+     */
+    public void addConfiguredExtension(final ExtensionAdapter extensionAdapter)
+        throws BuildException {
+        if (null != extension) {
+            final String message =
+                "Can not have multiple extensions defined in one library.";
+            throw new BuildException(message);
+        } else {
+            extension = extensionAdapter.toExtension();
+        }
+    }
+
+    /**
+     * Adds a set of extensions that this library requires.
+     *
+     * @param extensionSet a set of extensions that this library requires.
+     */
+    public void addConfiguredDepends(final ExtensionSet extensionSet) {
+        dependencies.add(extensionSet);
+    }
+
+    /**
+     * Adds a set of extensions that this library optionally requires.
+     *
+     * @param extensionSet a set of extensions that this library optionally requires.
+     */
+    public void addConfiguredOptions(final ExtensionSet extensionSet) {
+        optionals.add(extensionSet);
+    }
+
+    /**
+     * Adds an attribute that is to be put in main section of manifest.
+     *
+     * @param attribute an attribute that is to be put in main section of manifest.
+     */
+    public void addConfiguredAttribute(final ExtraAttribute attribute) {
+        extraAttributes.add(attribute);
+    }
+
+    /**
+     * Execute the task.
+     *
+     * @throws BuildException if the task fails.
+     */
+    public void execute() throws BuildException {
+        validate();
+
+        final Manifest manifest = new Manifest();
+        final Attributes attributes = manifest.getMainAttributes();
+
+        attributes.put(Attributes.Name.MANIFEST_VERSION, MANIFEST_VERSION);
+        final String createdBy = "Apache Ant " + getProject().getProperty("ant.version");
+        attributes.putValue(CREATED_BY, createdBy);
+
+        appendExtraAttributes(attributes);
+
+        if (null != extension) {
+            Extension.addExtension(extension, attributes);
+        }
+
+        //Add all the dependency data to manifest for dependencies
+        final ArrayList depends = toExtensions(dependencies);
+        appendExtensionList(attributes,
+                             Extension.EXTENSION_LIST,
+                             "lib",
+                             depends.size());
+        appendLibraryList(attributes, "lib", depends);
+
+        //Add all the dependency data to manifest for "optional"
+        //dependencies
+        final ArrayList option = toExtensions(optionals);
+        appendExtensionList(attributes,
+                             Extension.OPTIONAL_EXTENSION_LIST,
+                             "opt",
+                             option.size());
+        appendLibraryList(attributes, "opt", option);
+
+        try {
+            final String message = "Generating manifest " + destFile.getAbsoluteFile();
+            log(message, Project.MSG_INFO);
+            writeManifest(manifest);
+        } catch (final IOException ioe) {
+            throw new BuildException(ioe.getMessage(), ioe);
+        }
+    }
+
+    /**
+     * Validate the tasks parameters.
+     *
+     * @throws BuildException if invalid parameters found
+     */
+    private void validate() throws BuildException {
+        if (null == destFile) {
+            final String message = "Destfile attribute not specified.";
+            throw new BuildException(message);
+        }
+        if (destFile.exists() && !destFile.isFile()) {
+            final String message = destFile + " is not a file.";
+            throw new BuildException(message);
+        }
+    }
+
+    /**
+     * Add any extra attributes to the manifest.
+     *
+     * @param attributes the manifest section to write
+     *        attributes to
+     */
+    private void appendExtraAttributes(final Attributes attributes) {
+        final Iterator iterator = extraAttributes.iterator();
+        while (iterator.hasNext()) {
+            final ExtraAttribute attribute =
+                (ExtraAttribute) iterator.next();
+            attributes.putValue(attribute.getName(),
+                                 attribute.getValue());
+        }
+    }
+
+    /**
+     * Write out manifest to destfile.
+     *
+     * @param manifest the manifest
+     * @throws IOException if error writing file
+     */
+    private void writeManifest(final Manifest manifest)
+        throws IOException {
+        FileOutputStream output = null;
+        try {
+            output = new FileOutputStream(destFile);
+            manifest.write(output);
+            output.flush();
+        } finally {
+            if (null != output) {
+                try {
+                    output.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+    /**
+     * Append specified extensions to specified attributes.
+     * Use the extensionKey to list the extensions, usually "Extension-List:"
+     * for required dependencies and "Optional-Extension-List:" for optional
+     * dependencies. NOTE: "Optional" dependencies are not part of the
+     * specification.
+     *
+     * @param attributes the attributes to add extensions to
+     * @param extensions the list of extensions
+     * @throws BuildException if an error occurs
+     */
+    private void appendLibraryList(final Attributes attributes,
+                                    final String listPrefix,
+                                    final ArrayList extensions)
+        throws BuildException {
+        final int size = extensions.size();
+        for (int i = 0; i < size; i++) {
+            final Extension extension = (Extension) extensions.get(i);
+            final String prefix = listPrefix + i + "-";
+            Extension.addExtension(extension, prefix, attributes);
+        }
+    }
+
+    /**
+     * Append an attribute such as "Extension-List: lib0 lib1 lib2"
+     * using specified prefix and counting up to specified size.
+     * Also use specified extensionKey so that can generate list of
+     * optional dependencies aswell.
+     *
+     * @param size the number of librarys to list
+     * @param listPrefix the prefix for all librarys
+     * @param attributes the attributes to add key-value to
+     * @param extensionKey the key to use
+     */
+    private void appendExtensionList(final Attributes attributes,
+                                      final Attributes.Name extensionKey,
+                                      final String listPrefix,
+                                      final int size) {
+        final StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < size; i++) {
+            sb.append(listPrefix + i);
+            sb.append(' ');
+        }
+
+        //add in something like
+        //"Extension-List: javahelp java3d"
+        attributes.put(extensionKey, sb.toString());
+    }
+
+    /**
+     * Convert a list of ExtensionSet objects to extensions.
+     *
+     * @param extensionSets the list of ExtensionSets to add to list
+     * @throws BuildException if an error occurs
+     */
+    private ArrayList toExtensions(final ArrayList extensionSets)
+        throws BuildException {
+        final ArrayList results = new ArrayList();
+
+        final int size = extensionSets.size();
+        for (int i = 0; i < size; i++) {
+            final ExtensionSet set = (ExtensionSet) extensionSets.get(i);
+            final Extension[] extensions = set.toExtensions(getProject());
+            for (int j = 0; j < extensions.length; j++) {
+                results.add(extensions[ j ]);
+            }
+        }
+
+        return results;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibResolveTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibResolveTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/JarLibResolveTask.java	(revision 16139)
@@ -0,0 +1,286 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.jar.Manifest;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.optional.extension.resolvers.AntResolver;
+import org.apache.tools.ant.taskdefs.optional.extension.resolvers.LocationResolver;
+import org.apache.tools.ant.taskdefs.optional.extension.resolvers.URLResolver;
+
+/**
+ * Tries to locate a JAR to satisfy an extension and place
+ * location of JAR into property.
+ *
+ * @ant.task name="jarlib-resolve"
+ */
+public class JarLibResolveTask extends Task {
+    /**
+     * The name of the property in which the location of
+     * library is stored.
+     */
+    private String propertyName;
+
+    /**
+     * The extension that is required.
+     */
+    private Extension requiredExtension;
+
+    /**
+     * The set of resolvers to use to attempt to locate library.
+     */
+    private final ArrayList resolvers = new ArrayList();
+
+    /**
+     * Flag to indicate that you should check that
+     * the librarys resolved actually contain
+     * extension and if they don't then raise
+     * an exception.
+     */
+    private boolean checkExtension = true;
+
+    /**
+     * Flag indicating whether or not you should
+     * throw a BuildException if you cannot resolve
+     * library.
+     */
+    private boolean failOnError = true;
+
+    /**
+     * The name of the property in which the location of
+     * library is stored.
+     *
+     * @param property The name of the property in which the location of
+     *                 library is stored.
+     */
+    public void setProperty(final String property) {
+        this.propertyName = property;
+    }
+
+    /**
+     * Check nested libraries for extensions
+     *
+     * @param checkExtension if true, libraries returned by nested
+     * resolvers should be checked to see if they supply extension.
+     */
+    public void setCheckExtension(final boolean checkExtension) {
+        this.checkExtension = checkExtension;
+    }
+
+    /**
+     * Set whether to fail if error.
+     *
+     * @param failOnError if true, failure to locate library should fail build.
+     */
+    public void setFailOnError(final boolean failOnError) {
+        this.failOnError = failOnError;
+    }
+
+    /**
+     * Adds location resolver to look for a library in a location
+     * relative to project directory.
+     *
+     * @param location the resolver location to search.
+     */
+    public void addConfiguredLocation(final LocationResolver location) {
+        resolvers.add(location);
+    }
+
+    /**
+     * Adds a URL resolver to download a library from a URL
+     * to a local file.
+     *
+     * @param url the URL resolver from which to download the library
+     */
+    public void addConfiguredUrl(final URLResolver url) {
+        resolvers.add(url);
+    }
+
+    /**
+     * Adds Ant resolver to run an Ant build file to generate a library.
+     *
+     * @param ant the AntResolver to generate the library.
+     */
+    public void addConfiguredAnt(final AntResolver ant) {
+        resolvers.add(ant);
+    }
+
+    /**
+     * Set the Extension looking for.
+     *
+     * @param extension Set the Extension looking for.
+     */
+    public void addConfiguredExtension(final ExtensionAdapter extension) {
+        if (null != requiredExtension) {
+            final String message = "Can not specify extension to "
+                + "resolve multiple times.";
+            throw new BuildException(message);
+        }
+        requiredExtension = extension.toExtension();
+    }
+
+    /**
+     * Execute the task.
+     *
+     * @throws BuildException if the task fails.
+     */
+    public void execute() throws BuildException {
+        validate();
+
+        getProject().log("Resolving extension: " + requiredExtension,
+                          Project.MSG_VERBOSE);
+
+        String candidate =
+            getProject().getProperty(propertyName);
+
+        if (null != candidate) {
+            final String message = "Property Already set to: " + candidate;
+            if (failOnError) {
+                throw new BuildException(message);
+            } else {
+                getProject().log(message, Project.MSG_ERR);
+                return;
+            }
+        }
+
+        final int size = resolvers.size();
+        for (int i = 0; i < size; i++) {
+            final ExtensionResolver resolver =
+                (ExtensionResolver) resolvers.get(i);
+
+            getProject().log("Searching for extension using Resolver:" + resolver,
+                              Project.MSG_VERBOSE);
+
+            try {
+                final File file =
+                    resolver.resolve(requiredExtension, getProject());
+                try {
+                    checkExtension(file);
+                    return;
+                } catch (final BuildException be) {
+                    final String message = "File " + file + " returned by "
+                        + "resolver failed to satisfy extension due to: "
+                        + be.getMessage();
+                    getProject().log(message, Project.MSG_WARN);
+                }
+            } catch (final BuildException be) {
+                final String message = "Failed to resolve extension to file "
+                    + "using resolver " + resolver + " due to: " + be;
+                getProject().log(message, Project.MSG_WARN);
+            }
+        }
+
+        missingExtension();
+    }
+
+    /**
+     * Utility method that will throw a {@link BuildException}
+     * if {@link #failOnError} is true else it just displays
+     * a warning.
+     */
+    private void missingExtension() {
+        final String message =
+            "Unable to resolve extension to a file";
+        if (failOnError) {
+            throw new BuildException(message);
+        } else {
+            getProject().log(message, Project.MSG_ERR);
+        }
+    }
+
+    /**
+     * Check if specified file satisfies extension.
+     * If it does then set the relevent property
+     * else throw a BuildException.
+     *
+     * @param file the candidate library
+     * @throws BuildException if library does not satisfy extension
+     */
+    private void checkExtension(final File file) {
+        if (!file.exists()) {
+            final String message =
+                "File " + file + " does not exist";
+            throw new BuildException(message);
+        }
+        if (!file.isFile()) {
+            final String message =
+                "File " + file + " is not a file";
+            throw new BuildException(message);
+        }
+
+        if (!checkExtension) {
+            final String message = "Setting property to " + file
+                + " without verifying library satisfies extension";
+            getProject().log(message, Project.MSG_VERBOSE);
+            setLibraryProperty(file);
+        } else {
+            getProject().log("Checking file " + file
+                + " to see if it satisfies extension", Project.MSG_VERBOSE);
+            final Manifest manifest =
+                ExtensionUtil.getManifest(file);
+            final Extension[] extensions =
+                Extension.getAvailable(manifest);
+            for (int i = 0; i < extensions.length; i++) {
+                final Extension extension = extensions[ i ];
+                if (extension.isCompatibleWith(requiredExtension)) {
+                    setLibraryProperty(file);
+                    return;
+                }
+            }
+
+            getProject().log("File " + file + " skipped as it "
+                + "does not satisfy extension", Project.MSG_VERBOSE);
+
+            final String message =
+                "File " + file + " does not satisfy extension";
+            throw new BuildException(message);
+        }
+    }
+
+    /**
+     * Utility method to set the appropriate property
+     * to indicate that specified file satisfies library
+     * requirements.
+     *
+     * @param file the library
+     */
+    private void setLibraryProperty(final File file) {
+        getProject().setNewProperty(propertyName,
+                                     file.getAbsolutePath());
+    }
+
+    /**
+     * Validate the tasks parameters.
+     *
+     * @throws BuildException if invalid parameters found
+     */
+    private void validate() throws BuildException {
+        if (null == propertyName) {
+            final String message = "Property attribute must be specified.";
+            throw new BuildException(message);
+        }
+
+        if (null == requiredExtension) {
+            final String message = "Extension element must be specified.";
+            throw new BuildException(message);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibFileSet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibFileSet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibFileSet.java	(revision 16139)
@@ -0,0 +1,116 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension;
+
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * LibFileSet represents a fileset containing libraries.
+ * Asociated with the libraries is data pertaining to
+ * how they are to be handled when building manifests.
+ *
+ */
+public class LibFileSet
+    extends FileSet {
+    /**
+     * Flag indicating whether should include the
+     * "Implementation-URL" attribute in manifest.
+     * Defaults to false.
+     */
+    private boolean includeURL;
+
+    /**
+     * Flag indicating whether should include the
+     * "Implementation-*" attributes in manifest.
+     * Defaults to false.
+     */
+    private boolean includeImpl;
+
+    /**
+     * String that is the base URL for the librarys
+     * when constructing the "Implementation-URL"
+     * attribute. For instance setting the base to
+     * "http://jakarta.apache.org/avalon/libs/" and then
+     * including the library "excalibur-cli-1.0.jar" in the
+     * fileset will result in the "Implementation-URL" attribute
+     * being set to "http://jakarta.apache.org/avalon/libs/excalibur-cli-1.0.jar"
+     *
+     * Note this is only used if the library does not define
+     * "Implementation-URL" itself.
+     *
+     * Note that this also implies includeURL=true
+     */
+    private String urlBase;
+
+    /**
+     * Flag indicating whether should include the
+     * "Implementation-URL" attribute in manifest.
+     * Defaults to false.
+     *
+     * @param includeURL the flag
+     */
+    public void setIncludeUrl(boolean includeURL) {
+        this.includeURL = includeURL;
+    }
+
+    /**
+     * Flag indicating whether should include the
+     * "Implementation-*" attributes in manifest.
+     * Defaults to false.
+     *
+     * @param includeImpl the flag
+     */
+    public void setIncludeImpl(boolean includeImpl) {
+        this.includeImpl = includeImpl;
+    }
+
+    /**
+     * Set the url base for fileset.
+     *
+     * @param urlBase the base url
+     */
+    public void setUrlBase(String urlBase) {
+        this.urlBase = urlBase;
+    }
+
+    /**
+     * Get the includeURL flag.
+     *
+     * @return the includeURL flag.
+     */
+    boolean isIncludeURL() {
+        return includeURL;
+    }
+
+    /**
+     * Get the includeImpl flag.
+     *
+     * @return the includeImpl flag.
+     */
+    boolean isIncludeImpl() {
+        return includeImpl;
+    }
+
+    /**
+     * Get the urlbase.
+     *
+     * @return the urlbase.
+     */
+    String getUrlBase() {
+        return urlBase;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibraryDisplayer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibraryDisplayer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/LibraryDisplayer.java	(revision 16139)
@@ -0,0 +1,148 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension;
+
+import java.io.File;
+import java.text.ParseException;
+import java.util.jar.Manifest;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Utility class to output the information in a jar relating
+ * to "Optional Packages" (formely known as "Extensions")
+ * and Package Specifications.
+ *
+ */
+class LibraryDisplayer {
+    /**
+     * Display the extensions and specifications contained
+     * within specified file.
+     *
+     * @param file the file
+     * @throws BuildException if fail to read file
+     */
+    void displayLibrary(final File file)
+        throws BuildException {
+        final Manifest manifest = ExtensionUtil.getManifest(file);
+        displayLibrary(file, manifest);
+    }
+
+    /**
+     * Display the extensions and specifications contained
+     * within specified file.
+     *
+     * @param file the file to use while reporting
+     * @param manifest the manifest of file
+     * @throws BuildException if fail to read file
+     */
+    void displayLibrary(final File file,
+                         final Manifest manifest)
+        throws BuildException {
+        final Extension[] available = Extension.getAvailable(manifest);
+        final Extension[] required = Extension.getRequired(manifest);
+        final Extension[] options = Extension.getOptions(manifest);
+        final Specification[] specifications = getSpecifications(manifest);
+
+        if (0 == available.length && 0 == required.length && 0 == options.length
+            && 0 == specifications.length) {
+            return;
+        }
+
+        final String message = "File: " + file;
+        final int size = message.length();
+        printLine(size);
+        System.out.println(message);
+        printLine(size);
+        if (0 != available.length) {
+            System.out.println("Extensions Supported By Library:");
+            for (int i = 0; i < available.length; i++) {
+                final Extension extension = available[ i ];
+                System.out.println(extension.toString());
+            }
+        }
+
+        if (0 != required.length) {
+            System.out.println("Extensions Required By Library:");
+            for (int i = 0; i < required.length; i++) {
+                final Extension extension = required[ i ];
+                System.out.println(extension.toString());
+            }
+        }
+
+        if (0 != options.length) {
+            System.out.println("Extensions that will be used by Library if present:");
+            for (int i = 0; i < options.length; i++) {
+                final Extension extension = options[ i ];
+                System.out.println(extension.toString());
+            }
+        }
+
+        if (0 != specifications.length) {
+            System.out.println("Specifications Supported By Library:");
+            for (int i = 0; i < specifications.length; i++) {
+                final Specification specification = specifications[ i ];
+                displaySpecification(specification);
+            }
+        }
+    }
+
+    /**
+     * Print out a line of '-'s equal to specified size.
+     *
+     * @param size the number of dashes to printout
+     */
+    private void printLine(final int size) {
+        for (int i = 0; i < size; i++) {
+            System.out.print("-");
+        }
+        System.out.println();
+    }
+
+    /**
+     * Get specifications from manifest.
+     *
+     * @param manifest the manifest
+     * @return the specifications or null if none
+     * @throws BuildException if malformed specification sections
+     */
+    private Specification[] getSpecifications(final Manifest manifest)
+        throws BuildException {
+        try {
+            return Specification.getSpecifications(manifest);
+        } catch (final ParseException pe) {
+            throw new BuildException(pe.getMessage(), pe);
+        }
+    }
+
+    /**
+     * Print out specification details.
+     *
+     * @param specification the specification
+     */
+    private void displaySpecification(final Specification specification) {
+        final String[] sections = specification.getSections();
+        if (null != sections) {
+            final StringBuffer sb = new StringBuffer("Sections: ");
+            for (int i = 0; i < sections.length; i++) {
+                sb.append(" ");
+                sb.append(sections[ i ]);
+            }
+            System.out.println(sb);
+        }
+        System.out.println(specification.toString());
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/Specification.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/Specification.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/Specification.java	(revision 16139)
@@ -0,0 +1,605 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension;
+
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.jar.Attributes;
+import java.util.jar.Manifest;
+
+/**
+ * <p>Utility class that represents either an available "Optional Package"
+ * (formerly known as "Standard Extension") as described in the manifest
+ * of a JAR file, or the requirement for such an optional package.</p>
+ *
+ * <p>For more information about optional packages, see the document
+ * <em>Optional Package Versioning</em> in the documentation bundle for your
+ * Java2 Standard Edition package, in file
+ * <code>guide/extensions/versioning.html</code>.</p>
+ *
+ */
+public final class Specification {
+    /**
+     * Manifest Attribute Name object for SPECIFICATION_TITLE.
+     */
+    public static final Attributes.Name SPECIFICATION_TITLE
+        = Attributes.Name.SPECIFICATION_TITLE;
+
+    /**
+     * Manifest Attribute Name object for SPECIFICATION_VERSION.
+     */
+    public static final Attributes.Name SPECIFICATION_VERSION
+        = Attributes.Name.SPECIFICATION_VERSION;
+
+    /**
+     * Manifest Attribute Name object for SPECIFICATION_VENDOR.
+     */
+    public static final Attributes.Name SPECIFICATION_VENDOR
+        = Attributes.Name.SPECIFICATION_VENDOR;
+
+    /**
+     * Manifest Attribute Name object for IMPLEMENTATION_TITLE.
+     */
+    public static final Attributes.Name IMPLEMENTATION_TITLE
+        = Attributes.Name.IMPLEMENTATION_TITLE;
+
+    /**
+     * Manifest Attribute Name object for IMPLEMENTATION_VERSION.
+     */
+    public static final Attributes.Name IMPLEMENTATION_VERSION
+        = Attributes.Name.IMPLEMENTATION_VERSION;
+
+    /**
+     * Manifest Attribute Name object for IMPLEMENTATION_VENDOR.
+     */
+    public static final Attributes.Name IMPLEMENTATION_VENDOR
+        = Attributes.Name.IMPLEMENTATION_VENDOR;
+
+    /**
+     * Enum indicating that extension is compatible with other Package
+     * Specification.
+     */
+    public static final Compatibility COMPATIBLE =
+        new Compatibility("COMPATIBLE");
+
+    /**
+     * Enum indicating that extension requires an upgrade
+     * of specification to be compatible with other Package Specification.
+     */
+    public static final Compatibility REQUIRE_SPECIFICATION_UPGRADE =
+        new Compatibility("REQUIRE_SPECIFICATION_UPGRADE");
+
+    /**
+     * Enum indicating that extension requires a vendor
+     * switch to be compatible with other Package Specification.
+     */
+    public static final Compatibility REQUIRE_VENDOR_SWITCH =
+        new Compatibility("REQUIRE_VENDOR_SWITCH");
+
+    /**
+     * Enum indicating that extension requires an upgrade
+     * of implementation to be compatible with other Package Specification.
+     */
+    public static final Compatibility REQUIRE_IMPLEMENTATION_CHANGE =
+        new Compatibility("REQUIRE_IMPLEMENTATION_CHANGE");
+
+    /**
+     * This enum indicates that an extension is incompatible with
+     * other Package Specification in ways other than other enums
+     * indicate. For example, the other Package Specification
+     * may have a different ID.
+     */
+    public static final Compatibility INCOMPATIBLE =
+        new Compatibility("INCOMPATIBLE");
+
+    /**
+     * The name of the Package Specification.
+     */
+    private String specificationTitle;
+
+    /**
+     * The version number (dotted decimal notation) of the specification
+     * to which this optional package conforms.
+     */
+    private DeweyDecimal specificationVersion;
+
+    /**
+     * The name of the company or organization that originated the
+     * specification to which this specification conforms.
+     */
+    private String specificationVendor;
+
+    /**
+     * The title of implementation.
+     */
+    private String implementationTitle;
+
+    /**
+     * The name of the company or organization that produced this
+     * implementation of this specification.
+     */
+    private String implementationVendor;
+
+    /**
+     * The version string for implementation. The version string is
+     * opaque.
+     */
+    private String implementationVersion;
+
+    /**
+     * The sections of jar that the specification applies to.
+     */
+    private String[] sections;
+
+    /**
+     * Return an array of <code>Package Specification</code> objects.
+     * If there are no such optional packages, a zero-length array is returned.
+     *
+     * @param manifest Manifest to be parsed
+     * @return the Package Specifications extensions in specified manifest
+     * @throws ParseException if the attributes of the specifications cannot
+     * be parsed according to their expected formats.
+     */
+    public static Specification[] getSpecifications(final Manifest manifest)
+        throws ParseException {
+        if (null == manifest) {
+            return new Specification[ 0 ];
+        }
+
+        final ArrayList results = new ArrayList();
+
+        final Map entries = manifest.getEntries();
+        final Iterator keys = entries.keySet().iterator();
+        while (keys.hasNext()) {
+            final String key = (String) keys.next();
+            final Attributes attributes = (Attributes) entries.get(key);
+            final Specification specification
+                = getSpecification(key, attributes);
+            if (null != specification) {
+                results.add(specification);
+            }
+        }
+
+        final ArrayList trimmedResults = removeDuplicates(results);
+        return (Specification[]) trimmedResults.toArray(new Specification[0]);
+    }
+
+    /**
+     * The constructor to create Package Specification object.
+     * Note that every component is allowed to be specified
+     * but only the specificationTitle is mandatory.
+     *
+     * @param specificationTitle the name of specification.
+     * @param specificationVersion the specification Version.
+     * @param specificationVendor the specification Vendor.
+     * @param implementationTitle the title of implementation.
+     * @param implementationVersion the implementation Version.
+     * @param implementationVendor the implementation Vendor.
+     */
+    public Specification(final String specificationTitle,
+                          final String specificationVersion,
+                          final String specificationVendor,
+                          final String implementationTitle,
+                          final String implementationVersion,
+                          final String implementationVendor) {
+        this(specificationTitle, specificationVersion, specificationVendor,
+              implementationTitle, implementationVersion, implementationVendor,
+              null);
+    }
+
+    /**
+     * The constructor to create Package Specification object.
+     * Note that every component is allowed to be specified
+     * but only the specificationTitle is mandatory.
+     *
+     * @param specificationTitle the name of specification.
+     * @param specificationVersion the specification Version.
+     * @param specificationVendor the specification Vendor.
+     * @param implementationTitle the title of implementation.
+     * @param implementationVersion the implementation Version.
+     * @param implementationVendor the implementation Vendor.
+     * @param sections the sections/packages that Specification applies to.
+     */
+    public Specification(final String specificationTitle,
+                          final String specificationVersion,
+                          final String specificationVendor,
+                          final String implementationTitle,
+                          final String implementationVersion,
+                          final String implementationVendor,
+                          final String[] sections) {
+        this.specificationTitle = specificationTitle;
+        this.specificationVendor = specificationVendor;
+
+        if (null != specificationVersion) {
+            try {
+                this.specificationVersion
+                    = new DeweyDecimal(specificationVersion);
+            } catch (final NumberFormatException nfe) {
+                final String error = "Bad specification version format '"
+                    + specificationVersion + "' in '" + specificationTitle
+                    + "'. (Reason: " + nfe + ")";
+                throw new IllegalArgumentException(error);
+            }
+        }
+
+        this.implementationTitle = implementationTitle;
+        this.implementationVendor = implementationVendor;
+        this.implementationVersion = implementationVersion;
+
+        if (null == this.specificationTitle) {
+            throw new NullPointerException("specificationTitle");
+        }
+
+        String[] copy = null;
+        if (null != sections) {
+            copy = new String[ sections.length ];
+            System.arraycopy(sections, 0, copy, 0, sections.length);
+        }
+        this.sections = copy;
+    }
+
+    /**
+     * Get the title of the specification.
+     *
+     * @return the title of speciication
+     */
+    public String getSpecificationTitle() {
+        return specificationTitle;
+    }
+
+    /**
+     * Get the vendor of the specification.
+     *
+     * @return the vendor of the specification.
+     */
+    public String getSpecificationVendor() {
+        return specificationVendor;
+    }
+
+    /**
+     * Get the title of the specification.
+     *
+     * @return the title of the specification.
+     */
+    public String getImplementationTitle() {
+        return implementationTitle;
+    }
+
+    /**
+     * Get the version of the specification.
+     *
+     * @return the version of the specification.
+     */
+    public DeweyDecimal getSpecificationVersion() {
+        return specificationVersion;
+    }
+
+    /**
+     * Get the vendor of the extensions implementation.
+     *
+     * @return the vendor of the extensions implementation.
+     */
+    public String getImplementationVendor() {
+        return implementationVendor;
+    }
+
+    /**
+     * Get the version of the implementation.
+     *
+     * @return the version of the implementation.
+     */
+    public String getImplementationVersion() {
+        return implementationVersion;
+    }
+
+    /**
+     * Return an array containing sections to which specification applies
+     * or null if relevent to no sections.
+     *
+     * @return an array containing sections to which specification applies
+     *         or null if relevent to no sections.
+     */
+    public String[] getSections() {
+        if (null == sections) {
+            return null;
+        } else {
+            final String[] newSections = new String[ sections.length ];
+            System.arraycopy(sections, 0, newSections, 0, sections.length);
+            return newSections;
+        }
+    }
+
+    /**
+     * Return a Compatibility enum indicating the relationship of this
+     * <code>Package Specification</code> with the specified
+     * <code>Extension</code>.
+     *
+     * @param other the other specification
+     * @return the enum indicating the compatibility (or lack thereof)
+     *         of specifed Package Specification
+     */
+    public Compatibility getCompatibilityWith(final Specification other) {
+        // Specification Name must match
+        if (!specificationTitle.equals(other.getSpecificationTitle())) {
+            return INCOMPATIBLE;
+        }
+
+        // Available specification version must be >= required
+        final DeweyDecimal otherSpecificationVersion
+            = other.getSpecificationVersion();
+        if (null != specificationVersion) {
+            if (null == otherSpecificationVersion
+                || !isCompatible(specificationVersion, otherSpecificationVersion)) {
+                return REQUIRE_SPECIFICATION_UPGRADE;
+            }
+        }
+
+        // Implementation Vendor ID must match
+        final String otherImplementationVendor 
+            = other.getImplementationVendor();
+        if (null != implementationVendor) {
+            if (null == otherImplementationVendor
+                || !implementationVendor.equals(otherImplementationVendor)) {
+                return REQUIRE_VENDOR_SWITCH;
+            }
+        }
+
+        // Implementation version must be >= required
+        final String otherImplementationVersion 
+            = other.getImplementationVersion();
+        if (null != implementationVersion) {
+            if (null == otherImplementationVersion
+                || !implementationVersion.equals(otherImplementationVersion)) {
+                return REQUIRE_IMPLEMENTATION_CHANGE;
+            }
+        }
+
+        // This available optional package satisfies the requirements
+        return COMPATIBLE;
+    }
+
+    /**
+     * Return <code>true</code> if the specified <code>package</code>
+     * is satisfied by this <code>Specification</code>. Otherwise, return
+     * <code>false</code>.
+     *
+     * @param other the specification
+     * @return true if the specification is compatible with this specification
+     */
+    public boolean isCompatibleWith(final Specification other) {
+        return (COMPATIBLE == getCompatibilityWith(other));
+    }
+
+    /**
+     * Return a String representation of this object.
+     *
+     * @return string representation of object.
+     */
+    public String toString() {
+        final String lineSeparator = System.getProperty("line.separator");
+        final String brace = ": ";
+
+        final StringBuffer sb
+            = new StringBuffer(SPECIFICATION_TITLE.toString());
+        sb.append(brace);
+        sb.append(specificationTitle);
+        sb.append(lineSeparator);
+
+        if (null != specificationVersion) {
+            sb.append(SPECIFICATION_VERSION);
+            sb.append(brace);
+            sb.append(specificationVersion);
+            sb.append(lineSeparator);
+        }
+
+        if (null != specificationVendor) {
+            sb.append(SPECIFICATION_VENDOR);
+            sb.append(brace);
+            sb.append(specificationVendor);
+            sb.append(lineSeparator);
+        }
+
+        if (null != implementationTitle) {
+            sb.append(IMPLEMENTATION_TITLE);
+            sb.append(brace);
+            sb.append(implementationTitle);
+            sb.append(lineSeparator);
+        }
+
+        if (null != implementationVersion) {
+            sb.append(IMPLEMENTATION_VERSION);
+            sb.append(brace);
+            sb.append(implementationVersion);
+            sb.append(lineSeparator);
+        }
+
+        if (null != implementationVendor) {
+            sb.append(IMPLEMENTATION_VENDOR);
+            sb.append(brace);
+            sb.append(implementationVendor);
+            sb.append(lineSeparator);
+        }
+
+        return sb.toString();
+    }
+
+    /**
+     * Return <code>true</code> if the first version number is greater than
+     * or equal to the second; otherwise return <code>false</code>.
+     *
+     * @param first First version number (dotted decimal)
+     * @param second Second version number (dotted decimal)
+     */
+    private boolean isCompatible(final DeweyDecimal first,
+                                 final DeweyDecimal second) {
+        return first.isGreaterThanOrEqual(second);
+    }
+
+    /**
+     * Combine all specifications objects that are identical except
+     * for the sections.
+     *
+     * <p>Note this is very inefficent and should probably be fixed
+     * in the future.</p>
+     *
+     * @param list the array of results to trim
+     * @return an array list with all duplicates removed
+     */
+    private static ArrayList removeDuplicates(final ArrayList list) {
+        final ArrayList results = new ArrayList();
+        final ArrayList sections = new ArrayList();
+        while (list.size() > 0) {
+            final Specification specification = (Specification) list.remove(0);
+            final Iterator iterator = list.iterator();
+            while (iterator.hasNext()) {
+                final Specification other = (Specification) iterator.next();
+                if (isEqual(specification, other)) {
+                    final String[] otherSections = other.getSections();
+                    if (null != sections) {
+                        sections.addAll(Arrays.asList(otherSections));
+                    }
+                    iterator.remove();
+                }
+            }
+
+            final Specification merged =
+                mergeInSections(specification, sections);
+            results.add(merged);
+            //Reset list of sections
+            sections.clear();
+        }
+
+        return results;
+    }
+
+    /**
+     * Test if two specifications are equal except for their sections.
+     *
+     * @param specification one specificaiton
+     * @param other the ohter specification
+     * @return true if two specifications are equal except for their
+     *         sections, else false
+     */
+    private static boolean isEqual(final Specification specification,
+                                    final Specification other) {
+        return
+            specification.getSpecificationTitle().equals(other.getSpecificationTitle())
+            && specification.getSpecificationVersion().isEqual(other.getSpecificationVersion())
+            && specification.getSpecificationVendor().equals(other.getSpecificationVendor())
+            && specification.getImplementationTitle().equals(other.getImplementationTitle())
+            && specification.getImplementationVersion().equals(other.getImplementationVersion())
+            && specification.getImplementationVendor().equals(other.getImplementationVendor());
+    }
+
+    /**
+     * Merge the specified sections into specified section and return result.
+     * If no sections to be added then just return original specification.
+     *
+     * @param specification the specification
+     * @param sectionsToAdd the list of sections to merge
+     * @return the merged specification
+     */
+    private static Specification mergeInSections(final Specification specification,
+                                              final ArrayList sectionsToAdd) {
+        if (0 == sectionsToAdd.size()) {
+            return specification;
+        } else {
+            sectionsToAdd.addAll(Arrays.asList(specification.getSections()));
+
+            final String[] sections =
+                (String[]) sectionsToAdd.toArray(new String[sectionsToAdd.size()]);
+
+            return new Specification(specification.getSpecificationTitle(),
+                                      specification.getSpecificationVersion().toString(),
+                                      specification.getSpecificationVendor(),
+                                      specification.getImplementationTitle(),
+                                      specification.getImplementationVersion(),
+                                      specification.getImplementationVendor(),
+                                      sections);
+        }
+    }
+
+    /**
+     * Trim the supplied string if the string is non-null
+     *
+     * @param value the string to trim or null
+     * @return the trimmed string or null
+     */
+    private static String getTrimmedString(final String value) {
+        if (null == value) {
+            return null;
+        } else {
+            return value.trim();
+        }
+    }
+
+    /**
+     * Extract an Package Specification from Attributes.
+     *
+     * @param attributes Attributes to searched
+     * @return the new Specification object, or null
+     */
+    private static Specification getSpecification(final String section,
+                                                   final Attributes attributes)
+        throws ParseException {
+        //WARNING: We trim the values of all the attributes because
+        //Some extension declarations are badly defined (ie have spaces
+        //after version or vendor)
+        final String name
+            = getTrimmedString(attributes.getValue(SPECIFICATION_TITLE));
+        if (null == name) {
+            return null;
+        }
+
+        final String specVendor
+            = getTrimmedString(attributes.getValue(SPECIFICATION_VENDOR));
+        if (null == specVendor) {
+            throw new ParseException("Missing " + SPECIFICATION_VENDOR, 0);
+        }
+
+        final String specVersion
+            = getTrimmedString(attributes.getValue(SPECIFICATION_VERSION));
+        if (null == specVersion) {
+            throw new ParseException("Missing " + SPECIFICATION_VERSION, 0);
+        }
+
+        final String impTitle
+            = getTrimmedString(attributes.getValue(IMPLEMENTATION_TITLE));
+        if (null == impTitle) {
+            throw new ParseException("Missing " + IMPLEMENTATION_TITLE, 0);
+        }
+
+        final String impVersion
+            = getTrimmedString(attributes.getValue(IMPLEMENTATION_VERSION));
+        if (null == impVersion) {
+            throw new ParseException("Missing " + IMPLEMENTATION_VERSION, 0);
+        }
+
+        final String impVendor
+            = getTrimmedString(attributes.getValue(IMPLEMENTATION_VENDOR));
+        if (null == impVendor) {
+            throw new ParseException("Missing " + IMPLEMENTATION_VENDOR, 0);
+        }
+
+        return new Specification(name, specVersion, specVendor,
+                                  impTitle, impVersion, impVendor,
+                                  new String[]{section});
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/AntResolver.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/AntResolver.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/AntResolver.java	(revision 16139)
@@ -0,0 +1,114 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension.resolvers;
+
+import java.io.File;
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Ant;
+import org.apache.tools.ant.taskdefs.optional.extension.Extension;
+import org.apache.tools.ant.taskdefs.optional.extension.ExtensionResolver;
+
+/**
+ * Resolver that just returns s specified location.
+ *
+ */
+public class AntResolver implements ExtensionResolver {
+    private File antfile;
+    private File destfile;
+    private String target;
+
+    /**
+     * Sets the ant file
+     * @param antfile the ant file to set
+     */
+    public void setAntfile(final File antfile) {
+        this.antfile = antfile;
+    }
+
+    /**
+     * Sets the destination file
+     * @param destfile the destination file
+     */
+    public void setDestfile(final File destfile) {
+        this.destfile = destfile;
+    }
+
+    /**
+     * Sets the target
+     * @param target the target
+     */
+    public void setTarget(final String target) {
+        this.target = target;
+    }
+
+    /**
+     * Returns the resolved file
+     * @param extension the extension
+     * @param project the project
+     * @return the file resolved
+     * @throws BuildException if the file cannot be resolved
+     */
+    public File resolve(final Extension extension,
+                         final Project project) throws BuildException {
+        validate();
+
+        final Ant ant = (Ant) project.createTask("ant");
+        ant.setInheritAll(false);
+        ant.setAntfile(antfile.getName());
+
+        try {
+            final File dir =
+                antfile.getParentFile().getCanonicalFile();
+            ant.setDir(dir);
+        } catch (final IOException ioe) {
+            throw new BuildException(ioe.getMessage(), ioe);
+        }
+
+        if (null != target) {
+            ant.setTarget(target);
+        }
+
+        ant.execute();
+
+        return destfile;
+    }
+    
+    /*
+     * Validates URL
+     */
+    private void validate() {
+        if (null == antfile) {
+            final String message = "Must specify Buildfile";
+            throw new BuildException(message);
+        }
+
+        if (null == destfile) {
+            final String message = "Must specify destination file";
+            throw new BuildException(message);
+        }
+    }
+
+    /**
+     * Returns a string representation
+     * @return the string representation
+     */
+    public String toString() {
+        return "Ant[" + antfile + "==>" + destfile + "]";
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/LocationResolver.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/LocationResolver.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/LocationResolver.java	(revision 16139)
@@ -0,0 +1,63 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension.resolvers;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.optional.extension.Extension;
+import org.apache.tools.ant.taskdefs.optional.extension.ExtensionResolver;
+
+/**
+ * Resolver that just returns s specified location.
+ *
+ */
+public class LocationResolver implements ExtensionResolver {
+    private String location;
+
+    /**
+     * Sets the location for this resolver
+     * @param location the location
+     */
+    public void setLocation(final String location) {
+        this.location = location;
+    }
+
+    /**
+     * Returns the resolved file
+     * @param extension the extension
+     * @param project the project
+     * @return the file resolved
+     * @throws BuildException if no location is set
+     */
+    public File resolve(final Extension extension,
+                        final Project project) throws BuildException {
+        if (null == location) {
+            final String message = "No location specified for resolver";
+            throw new BuildException(message);
+        }
+
+        return project.resolveFile(location);
+    }
+    /**
+     * Returns a string representation of the Location
+     * @return the string representation
+     */
+    public String toString() {
+        return "Location[" + location + "]";
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/URLResolver.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/URLResolver.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/extension/resolvers/URLResolver.java	(revision 16139)
@@ -0,0 +1,130 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.extension.resolvers;
+
+import java.io.File;
+import java.net.URL;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Get;
+import org.apache.tools.ant.taskdefs.optional.extension.Extension;
+import org.apache.tools.ant.taskdefs.optional.extension.ExtensionResolver;
+
+/**
+ * Resolver that just returns s specified location.
+ *
+ */
+public class URLResolver implements ExtensionResolver {
+    private File destfile;
+    private File destdir;
+    private URL url;
+
+    /**
+     * Sets the URL
+     * @param url the url
+     */
+    public void setUrl(final URL url) {
+        this.url = url;
+    }
+
+    /**
+     * Sets the destination file
+     * @param destfile the destination file
+     */
+    public void setDestfile(final File destfile) {
+        this.destfile = destfile;
+    }
+
+    /**
+     * Sets the destination directory
+     * @param destdir the destination directory
+     */
+    public void setDestdir(final File destdir) {
+        this.destdir = destdir;
+    }
+
+    /**
+     * Returns the file resolved from URL and directory
+     * @param extension the extention
+     * @param project the project
+     * @return file the file resolved
+     * @throws BuildException if the URL is invalid
+     */
+    public File resolve(final Extension extension,
+                         final Project project) throws BuildException {
+        validate();
+
+        final File file = getDest();
+
+        final Get get = (Get) project.createTask("get");
+        get.setDest(file);
+        get.setSrc(url);
+        get.execute();
+
+        return file;
+    }
+
+    /*
+     * Gets the destination file
+     */
+    private File getDest() {
+        File result;
+        if (null != destfile) {
+            result = destfile;
+        } else {
+            final String file = url.getFile();
+            String filename;
+            if (null == file || file.length() <= 1) {
+                filename = "default.file";
+            } else {
+                int index = file.lastIndexOf('/');
+                if (-1 == index) {
+                    index = 0;
+                }
+                filename = file.substring(index);
+            }
+            result = new File(destdir, filename);
+        }
+        return result;
+    }
+
+    /*
+     * Validates URL
+     */
+    private void validate() {
+        if (null == url) {
+            final String message = "Must specify URL";
+            throw new BuildException(message);
+        }
+
+        if (null == destdir && null == destfile) {
+            final String message = "Must specify destination file or directory";
+            throw new BuildException(message);
+        } else if (null != destdir && null != destfile) {
+            final String message = "Must not specify both destination file or directory";
+            throw new BuildException(message);
+        }
+    }
+
+    /**
+     * Returns a string representation of the URL
+     * @return the string representation
+     */
+    public String toString() {
+        return "URL[" + url + "]";
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/i18n/Translate.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/i18n/Translate.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/i18n/Translate.java	(revision 16139)
@@ -0,0 +1,621 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.i18n;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.util.Hashtable;
+import java.util.Locale;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.LineTokenizer;
+
+/**
+ * Translates text embedded in files using Resource Bundle files.
+ * Since ant 1.6 preserves line endings
+ *
+ */
+public class Translate extends MatchingTask {
+    /**
+     * search a bundle matching the specified language, the country and the variant
+     */
+    private static final int BUNDLE_SPECIFIED_LANGUAGE_COUNTRY_VARIANT = 0;
+    /**
+     * search a bundle matching the specified language, and the country
+     */
+    private static final int BUNDLE_SPECIFIED_LANGUAGE_COUNTRY = 1;
+    /**
+     * search a bundle matching the specified language only
+     */
+    private static final int BUNDLE_SPECIFIED_LANGUAGE = 2;
+    /**
+     * search a bundle matching nothing special
+     */
+    private static final int BUNDLE_NOMATCH = 3;
+    /**
+     * search a bundle matching the language, the country and the variant
+     * of the current locale of the computer
+     */
+    private static final int BUNDLE_DEFAULT_LANGUAGE_COUNTRY_VARIANT = 4;
+    /**
+     * search a bundle matching the language, and the country
+     * of the current locale of the computer
+     */
+    private static final int BUNDLE_DEFAULT_LANGUAGE_COUNTRY = 5;
+    /**
+     * search a bundle matching the language only
+     * of the current locale of the computer
+     */
+    private static final int BUNDLE_DEFAULT_LANGUAGE = 6;
+    /**
+     * number of possibilities for the search
+     */
+     private static final int BUNDLE_MAX_ALTERNATIVES = BUNDLE_DEFAULT_LANGUAGE + 1;
+    /**
+     * Family name of resource bundle
+     */
+    private String bundle;
+
+    /**
+     * Locale specific language of the resource bundle
+     */
+    private String bundleLanguage;
+
+    /**
+     * Locale specific country of the resource bundle
+     */
+    private String bundleCountry;
+
+    /**
+     * Locale specific variant of the resource bundle
+     */
+    private String bundleVariant;
+
+    /**
+     * Destination directory
+     */
+    private File toDir;
+
+    /**
+     * Source file encoding scheme
+     */
+    private String srcEncoding;
+
+    /**
+     * Destination file encoding scheme
+     */
+    private String destEncoding;
+
+    /**
+     * Resource Bundle file encoding scheme, defaults to srcEncoding
+     */
+    private String bundleEncoding;
+
+    /**
+     * Starting token to identify keys
+     */
+    private String startToken;
+
+    /**
+     * Ending token to identify keys
+     */
+    private String endToken;
+
+    /**
+     * Whether or not to create a new destination file.
+     * Defaults to <code>false</code>.
+     */
+    private boolean forceOverwrite;
+
+    /**
+     * Vector to hold source file sets.
+     */
+    private Vector filesets = new Vector();
+
+    /**
+     * Holds key value pairs loaded from resource bundle file
+     */
+    private Hashtable resourceMap = new Hashtable();
+    /**
+
+     * Used to resolve file names.
+     */
+    private FileUtils fileUtils = FileUtils.newFileUtils();
+
+    /**
+     * Last Modified Timestamp of resource bundle file being used.
+     */
+    private long[] bundleLastModified = new long[BUNDLE_MAX_ALTERNATIVES];
+
+    /**
+     * Last Modified Timestamp of source file being used.
+     */
+    private long srcLastModified;
+
+    /**
+     * Last Modified Timestamp of destination file being used.
+     */
+    private long destLastModified;
+
+    /**
+     * Has at least one file from the bundle been loaded?
+     */
+    private boolean loaded = false;
+
+    /**
+     * Sets Family name of resource bundle; required.
+     * @param bundle family name of resource bundle
+     */
+    public void setBundle(String bundle) {
+        this.bundle = bundle;
+    }
+
+    /**
+     * Sets locale specific language of resource bundle; optional.
+     * @param bundleLanguage langage of the bundle
+     */
+    public void setBundleLanguage(String bundleLanguage) {
+        this.bundleLanguage = bundleLanguage;
+    }
+
+    /**
+     * Sets locale specific country of resource bundle; optional.
+     * @param bundleCountry country of the bundle
+     */
+    public void setBundleCountry(String bundleCountry) {
+        this.bundleCountry = bundleCountry;
+    }
+
+    /**
+     * Sets locale specific variant of resource bundle; optional.
+     * @param bundleVariant locale variant of resource bundle
+     */
+    public void setBundleVariant(String bundleVariant) {
+        this.bundleVariant = bundleVariant;
+    }
+
+    /**
+     * Sets Destination directory; required.
+     * @param toDir destination directory
+     */
+    public void setToDir(File toDir) {
+        this.toDir = toDir;
+    }
+
+    /**
+     * Sets starting token to identify keys; required.
+     * @param startToken starting token to identify keys
+     */
+    public void setStartToken(String startToken) {
+        this.startToken = startToken;
+    }
+
+    /**
+     * Sets ending token to identify keys; required.
+     * @param endToken ending token to identify keys
+     */
+    public void setEndToken(String endToken) {
+        this.endToken = endToken;
+    }
+
+    /**
+     * Sets source file encoding scheme; optional,
+     * defaults to encoding of local system.
+     * @param srcEncoding source file encoding
+     */
+    public void setSrcEncoding(String srcEncoding) {
+        this.srcEncoding = srcEncoding;
+    }
+
+    /**
+     * Sets destination file encoding scheme; optional.  Defaults to source file
+     * encoding
+     * @param destEncoding destination file encoding scheme
+     */
+    public void setDestEncoding(String destEncoding) {
+        this.destEncoding = destEncoding;
+    }
+
+    /**
+     * Sets Resource Bundle file encoding scheme; optional.  Defaults to source file
+     * encoding
+     * @param bundleEncoding bundle file encoding scheme
+     */
+    public void setBundleEncoding(String bundleEncoding) {
+        this.bundleEncoding = bundleEncoding;
+    }
+
+    /**
+     * Whether or not to overwrite existing file irrespective of
+     * whether it is newer than the source file as well as the
+     * resource bundle file.
+     * Defaults to false.
+     * @param forceOverwrite whether or not to overwrite existing files
+     */
+    public void setForceOverwrite(boolean forceOverwrite) {
+        this.forceOverwrite = forceOverwrite;
+    }
+
+    /**
+     * Adds a set of files to translate as a nested fileset element.
+     * @param set the fileset to be added
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * Check attributes values, load resource map and translate
+     * @throws BuildException if the required attributes are not set
+     * Required : <ul>
+     *       <li>bundle</li>
+     *       <li>starttoken</li>
+     *       <li>endtoken</li>
+     *            </ul>
+     */
+    public void execute() throws BuildException {
+        if (bundle == null) {
+            throw new BuildException("The bundle attribute must be set.",
+                                     getLocation());
+        }
+
+        if (startToken == null) {
+            throw new BuildException("The starttoken attribute must be set.",
+                                     getLocation());
+        }
+
+        if (endToken == null) {
+            throw new BuildException("The endtoken attribute must be set.",
+                                     getLocation());
+        }
+
+        if (bundleLanguage == null) {
+            Locale l = Locale.getDefault();
+            bundleLanguage  = l.getLanguage();
+        }
+
+        if (bundleCountry == null) {
+            bundleCountry = Locale.getDefault().getCountry();
+        }
+
+        if (bundleVariant == null) {
+            Locale l = new Locale(bundleLanguage, bundleCountry);
+            bundleVariant = l.getVariant();
+        }
+
+        if (toDir == null) {
+            throw new BuildException("The todir attribute must be set.",
+                                     getLocation());
+        }
+
+        if (!toDir.exists()) {
+            toDir.mkdirs();
+        } else if (toDir.isFile()) {
+            throw new BuildException(toDir + " is not a directory");
+        }
+
+        if (srcEncoding == null) {
+            srcEncoding = System.getProperty("file.encoding");
+        }
+
+        if (destEncoding == null) {
+            destEncoding = srcEncoding;
+        }
+
+        if (bundleEncoding == null) {
+            bundleEncoding = srcEncoding;
+        }
+
+        loadResourceMaps();
+
+        translate();
+    }
+
+    /**
+     * Load resource maps based on resource bundle encoding scheme.
+     * The resource bundle lookup searches for resource files with various
+     * suffixes on the basis of (1) the desired locale and (2) the default
+     * locale (basebundlename), in the following order from lower-level
+     * (more specific) to parent-level (less specific):
+     *
+     * basebundlename + "_" + language1 + "_" + country1 + "_" + variant1
+     * basebundlename + "_" + language1 + "_" + country1
+     * basebundlename + "_" + language1
+     * basebundlename
+     * basebundlename + "_" + language2 + "_" + country2 + "_" + variant2
+     * basebundlename + "_" + language2 + "_" + country2
+     * basebundlename + "_" + language2
+     *
+     * To the generated name, a ".properties" string is appeneded and
+     * once this file is located, it is treated just like a properties file
+     * but with bundle encoding also considered while loading.
+     */
+    private void loadResourceMaps() throws BuildException {
+        Locale locale = new Locale(bundleLanguage,
+                                   bundleCountry,
+                                   bundleVariant);
+        String language = locale.getLanguage().length() > 0
+            ? "_" + locale.getLanguage() : "";
+        String country = locale.getCountry().length() > 0
+            ? "_" + locale.getCountry() : "";
+        String variant = locale.getVariant().length() > 0
+            ? "_" + locale.getVariant() : "";
+        String bundleFile = bundle + language + country + variant;
+        processBundle(bundleFile, BUNDLE_SPECIFIED_LANGUAGE_COUNTRY_VARIANT, false);
+
+        bundleFile = bundle + language + country;
+        processBundle(bundleFile, BUNDLE_SPECIFIED_LANGUAGE_COUNTRY, false);
+
+        bundleFile = bundle + language;
+        processBundle(bundleFile, BUNDLE_SPECIFIED_LANGUAGE, false);
+
+        bundleFile = bundle;
+        processBundle(bundleFile, BUNDLE_NOMATCH, false);
+
+        //Load default locale bundle files
+        //using default file encoding scheme.
+        locale = Locale.getDefault();
+
+        language = locale.getLanguage().length() > 0
+            ? "_" + locale.getLanguage() : "";
+        country = locale.getCountry().length() > 0
+            ? "_" + locale.getCountry() : "";
+        variant = locale.getVariant().length() > 0
+            ? "_" + locale.getVariant() : "";
+        bundleEncoding = System.getProperty("file.encoding");
+
+        bundleFile = bundle + language + country + variant;
+        processBundle(bundleFile, BUNDLE_DEFAULT_LANGUAGE_COUNTRY_VARIANT, false);
+
+        bundleFile = bundle + language + country;
+        processBundle(bundleFile, BUNDLE_DEFAULT_LANGUAGE_COUNTRY, false);
+
+        bundleFile = bundle + language;
+        processBundle(bundleFile, BUNDLE_DEFAULT_LANGUAGE, true);
+    }
+
+    /**
+     * Process each file that makes up this bundle.
+     */
+    private void processBundle(final String bundleFile, final int i,
+                               final boolean checkLoaded) throws BuildException {
+        final File propsFile = getProject().resolveFile(bundleFile + ".properties");
+        FileInputStream ins = null;
+        try {
+            ins = new FileInputStream(propsFile);
+            loaded = true;
+            bundleLastModified[i] = propsFile.lastModified();
+            log("Using " + propsFile, Project.MSG_DEBUG);
+            loadResourceMap(ins);
+        } catch (IOException ioe) {
+            log(propsFile + " not found.", Project.MSG_DEBUG);
+            //if all resource files associated with this bundle
+            //have been scanned for and still not able to
+            //find a single resrouce file, throw exception
+            if (!loaded && checkLoaded) {
+                throw new BuildException(ioe.getMessage(), getLocation());
+            }
+        }
+    }
+
+    /**
+     * Load resourceMap with key value pairs.  Values of existing keys
+     * are not overwritten.  Bundle's encoding scheme is used.
+     */
+    private void loadResourceMap(FileInputStream ins) throws BuildException {
+        try {
+            BufferedReader in = null;
+            InputStreamReader isr = new InputStreamReader(ins, bundleEncoding);
+            in = new BufferedReader(isr);
+            String line = null;
+            while ((line = in.readLine()) != null) {
+                //So long as the line isn't empty and isn't a comment...
+                if (line.trim().length() > 1 && '#' != line.charAt(0) && '!' != line.charAt(0)) {
+                    //Legal Key-Value separators are :, = and white space.
+                    int sepIndex = line.indexOf('=');
+                    if (-1 == sepIndex) {
+                        sepIndex = line.indexOf(':');
+                    }
+                    if (-1 == sepIndex) {
+                        for (int k = 0; k < line.length(); k++) {
+                            if (Character.isSpaceChar(line.charAt(k))) {
+                                sepIndex = k;
+                                break;
+                            }
+                        }
+                    }
+                    //Only if we do have a key is there going to be a value
+                    if (-1 != sepIndex) {
+                        String key = line.substring(0, sepIndex).trim();
+                        String value = line.substring(sepIndex + 1).trim();
+                        //Handle line continuations, if any
+                        while (value.endsWith("\\")) {
+                            value = value.substring(0, value.length() - 1);
+                            if ((line = in.readLine()) != null) {
+                                value = value + line.trim();
+                            } else {
+                                break;
+                            }
+                        }
+                        if (key.length() > 0) {
+                            //Has key already been loaded into resourceMap?
+                            if (resourceMap.get(key) == null) {
+                                resourceMap.put(key, value);
+                            }
+                        }
+                    }
+                }
+            }
+            if (in != null) {
+                in.close();
+            }
+        } catch (IOException ioe) {
+            throw new BuildException(ioe.getMessage(), getLocation());
+        }
+    }
+
+    /**
+     * Reads source file line by line using the source encoding and
+     * searches for keys that are sandwiched between the startToken
+     * and endToken.  The values for these keys are looked up from
+     * the hashtable and substituted.  If the hashtable doesn't
+     * contain the key, they key itself is used as the value.
+     * Detination files and directories are created as needed.
+     * The destination file is overwritten only if
+     * the forceoverwritten attribute is set to true if
+     * the source file or any associated bundle resource file is
+     * newer than the destination file.
+     */
+    private void translate() throws BuildException {
+        for (int i = 0; i < filesets.size(); i++) {
+            FileSet fs = (FileSet) filesets.elementAt(i);
+            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+            String[] srcFiles = ds.getIncludedFiles();
+            for (int j = 0; j < srcFiles.length; j++) {
+                try {
+                    File dest = fileUtils.resolveFile(toDir, srcFiles[j]);
+                    //Make sure parent dirs exist, else, create them.
+                    try {
+                        File destDir = new File(dest.getParent());
+                        if (!destDir.exists()) {
+                            destDir.mkdirs();
+                        }
+                    } catch (Exception e) {
+                        log("Exception occurred while trying to check/create "
+                            + " parent directory.  " + e.getMessage(),
+                            Project.MSG_DEBUG);
+                    }
+                    destLastModified = dest.lastModified();
+                    File src = fileUtils.resolveFile(ds.getBasedir(), srcFiles[j]);
+                    srcLastModified = src.lastModified();
+                    //Check to see if dest file has to be recreated
+                    boolean needsWork = forceOverwrite
+                        || destLastModified < srcLastModified;
+                    if (!needsWork) {
+                        for (int icounter = 0; icounter < BUNDLE_MAX_ALTERNATIVES; icounter++) {
+                            needsWork = (destLastModified < bundleLastModified[icounter]);
+                            if (needsWork) {
+                                break;
+                            }
+                        }
+                    }
+                    if (needsWork) {
+                        log("Processing " + srcFiles[j],
+                            Project.MSG_DEBUG);
+                        FileOutputStream fos = new FileOutputStream(dest);
+                        BufferedWriter out
+                            = new BufferedWriter(new OutputStreamWriter(fos, destEncoding));
+                        FileInputStream fis = new FileInputStream(src);
+                        BufferedReader in
+                            = new BufferedReader(new InputStreamReader(fis, srcEncoding));
+                        String line;
+                        LineTokenizer lineTokenizer = new LineTokenizer();
+                        lineTokenizer.setIncludeDelims(true);
+                        line = lineTokenizer.getToken(in);
+                        while ((line) != null) {
+                        // 2003-02-21 new replace algorithm by tbee (tbee@tbee.org)
+                        // because it wasn't able to replace something like "@aaa;@bbb;"
+
+                        // is there a startToken
+                        // and there is still stuff following the startToken
+                        int startIndex = line.indexOf(startToken);
+                        while (startIndex >= 0
+                            && (startIndex + startToken.length()) <= line.length()) {
+                            // the new value, this needs to be here
+                            // because it is required to calculate the next position to search from
+                            // at the end of the loop
+                            String replace = null;
+
+                            // we found a starttoken, is there an endtoken following?
+                            // start at token+tokenlength because start and end
+                            // token may be indentical
+                            int endIndex = line.indexOf(endToken, startIndex + startToken.length());
+                            if (endIndex < 0) {
+                                startIndex += 1;
+                            } else {
+                                // grab the token
+                                String token
+                                    = line.substring(startIndex + startToken.length(), endIndex);
+
+                                // If there is a white space or = or :, then
+                                // it isn't to be treated as a valid key.
+                                boolean validToken = true;
+                                for (int k = 0; k < token.length() && validToken; k++) {
+                                    char c = token.charAt(k);
+                                    if (c == ':' || c == '='
+                                        || Character.isSpaceChar(c)) {
+                                        validToken = false;
+                                    }
+                                }
+                                if (!validToken) {
+                                    startIndex += 1;
+                                } else {
+                                    // find the replace string
+                                    if (resourceMap.containsKey(token)) {
+                                        replace = (String) resourceMap.get(token);
+                                    } else {
+                                        replace = token;
+                                    }
+
+
+                                    // generate the new line
+                                    line = line.substring(0, startIndex)
+                                         + replace
+                                         + line.substring(endIndex + endToken.length());
+
+                                    // set start position for next search
+                                    startIndex += replace.length();
+                                }
+                            }
+
+                            // find next starttoken
+                            startIndex = line.indexOf(startToken, startIndex);
+                        }
+
+
+                            out.write(line);
+                            line = lineTokenizer.getToken(in);
+                        }
+                        if (in != null) {
+                            in.close();
+                        }
+                        if (out != null) {
+                            out.close();
+                        }
+                    } else {
+                        log("Skipping " + srcFiles[j]
+                            + " as destination file is up to date",
+                            Project.MSG_VERBOSE);
+                    }
+                } catch (IOException ioe) {
+                    throw new BuildException(ioe.getMessage(), getLocation());
+                }
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJAntTool.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJAntTool.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJAntTool.java	(revision 16139)
@@ -0,0 +1,111 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+
+import com.ibm.ivj.util.base.Project;
+import com.ibm.ivj.util.base.ToolData;
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * This class is the equivalent to org.apache.tools.ant.Main for the
+ * VAJ tool environment. It's main is called when the user selects
+ * Tools->Ant Build from the VAJ project menu.
+ * Additionally this class provides methods to save build info for
+ * a project in the repository and load it from the repository
+ *
+ */
+public class VAJAntTool {
+    private static final String TOOL_DATA_KEY = "AntTool";
+
+
+    /**
+     * Loads the BuildInfo for the specified VAJ project from the
+     * tool data for this project.
+     * If there is no build info stored for that project, a new
+     * default BuildInfo is returned
+     *
+     * @return BuildInfo buildInfo build info for the specified project
+     * @param projectName String project name
+     */
+    public static VAJBuildInfo loadBuildData(String projectName) {
+        VAJBuildInfo result = null;
+        try {
+            Project project =
+                VAJLocalUtil.getWorkspace().loadedProjectNamed(projectName);
+            if (project.testToolRepositoryData(TOOL_DATA_KEY)) {
+                ToolData td = project.getToolRepositoryData(TOOL_DATA_KEY);
+                String data = (String) td.getData();
+                result = VAJBuildInfo.parse(data);
+            } else {
+                result = new VAJBuildInfo();
+            }
+            result.setVAJProjectName(projectName);
+        } catch (Throwable t) {
+            throw new BuildException("BuildInfo for Project "
+                                     + projectName + " could not be loaded" + t);
+        }
+        return result;
+    }
+
+
+    /**
+     * Starts the application.
+     *
+     * @param args an array of command-line arguments. VAJ puts the
+     *             VAJ project name into args[1] when starting the
+     *             tool from the project context menu
+     */
+    public static void main(java.lang.String[] args) {
+        try {
+            VAJBuildInfo info;
+            if (args.length >= 2 && args[1] instanceof String) {
+                String projectName = (String) args[1];
+                info = loadBuildData(projectName);
+            } else {
+                info = new VAJBuildInfo();
+            }
+
+            VAJAntToolGUI mainFrame = new VAJAntToolGUI(info);
+            mainFrame.show();
+        } catch (Throwable t) {
+            // if all error handling fails, output at least
+            // something on the console
+            t.printStackTrace();
+        }
+    }
+
+
+    /**
+     * Saves the BuildInfo for a project in the VAJ repository.
+     *
+     * @param info BuildInfo build info to save
+     */
+    public static void saveBuildData(VAJBuildInfo info) {
+        String data = info.asDataString();
+        try {
+            ToolData td = new ToolData(TOOL_DATA_KEY, data);
+            VAJLocalUtil.getWorkspace().loadedProjectNamed(
+                info.getVAJProjectName()).setToolRepositoryData(td);
+        } catch (Throwable t) {
+            throw new BuildException("BuildInfo for Project "
+                                     + info.getVAJProjectName() + " could not be saved", t);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJAntToolGUI.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJAntToolGUI.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJAntToolGUI.java	(revision 16139)
@@ -0,0 +1,1385 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+
+import java.awt.BorderLayout;
+import java.awt.Button;
+import java.awt.Choice;
+import java.awt.Dialog;
+import java.awt.FileDialog;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.Frame;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Label;
+import java.awt.List;
+import java.awt.Menu;
+import java.awt.MenuBar;
+import java.awt.MenuItem;
+import java.awt.Panel;
+import java.awt.SystemColor;
+import java.awt.TextArea;
+import java.awt.TextField;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.ItemEvent;
+import java.awt.event.ItemListener;
+import java.awt.event.TextEvent;
+import java.awt.event.TextListener;
+import java.awt.event.WindowEvent;
+import java.awt.event.WindowListener;
+import java.beans.PropertyChangeListener;
+import java.util.Vector;
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.DateUtils;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * This is a simple grafical user interface to provide the information needed
+ * by ANT and to start the build-process within IBM VisualAge for Java.
+ * <p>
+ * I was using AWT to make it independent from the JDK-version. Please don't
+ * ask me for a Swing-version:I am very familiar with Swing and I really think
+ * that it's not necessary for such a simple gui!
+ * <p>
+ * It is completely developed in VAJ using the visual composition editor.
+ * About 90% of the code is generated by VAJ,
+ * but in fact I did a lot of <i>code-beautification</i> ;-).
+ * <p>
+ * @version 1.0 h
+ */
+public class VAJAntToolGUI extends Frame {
+    /**
+     * Members
+     */
+    private VAJBuildLogger logger = new VAJBuildLogger();
+    private static final String lineSeparator = "\r\n";
+    private PrivateEventHandler iEventHandler = new PrivateEventHandler();
+
+    /**
+     * Members of the main-window
+     */
+    // main model
+    private VAJBuildInfo iBuildInfo = null;
+    // Menue
+    private MenuBar iAntMakeMenuBar = null;
+    private Menu iFileMenu = null;
+    private MenuItem iSaveMenuItem = null;
+    private MenuItem iMenuSeparator = null;
+    private MenuItem iShowLogMenuItem = null;
+    private Menu iHelpMenu = null;
+    private MenuItem iAboutMenuItem = null;
+    // Container
+    private Panel iContentsPane = null;
+    private Panel iOptionenPanel = null;
+    private Panel iCommandButtonPanel = null;
+    // Project name
+    private Label iProjectLabel = null;
+    private Label iProjectText = null;
+    // XML-file
+    private Label iBuildFileLabel = null;
+    private TextField iBuildFileTextField = null;
+    private boolean iConnPtoP2Aligning = false;
+    private Button iBrowseButton = null;
+    private FileDialog iFileDialog = null;
+    // Options
+    private Choice iMessageOutputLevelChoice = null;
+    private Label iMessageOutputLevelLabel = null;
+    private Label iTargetLabel = null;
+    private List iTargetList = null;
+    // Command-buttons
+    private Button iBuildButton = null;
+    private Button iReloadButton = null;
+    private Button iCloseButton = null;
+    /**
+     * log-Window
+     */
+    // Container
+    private Frame iMessageFrame = null;
+    private Panel iMessageCommandPanel = null;
+    private Panel iMessageContentPanel = null;
+    // Components
+    private TextArea iMessageTextArea = null;
+    private Button iMessageOkButton = null;
+    private Button iMessageClearLogButton = null;
+    /**
+     * About-dialog
+     */
+    // Container
+    private Dialog iAboutDialog = null;
+    private Panel iAboutDialogContentPanel = null;
+    private Panel iAboutInfoPanel = null;
+    private Panel iAboutCommandPanel = null;
+    // Labels
+    private Label iAboutTitleLabel = null;
+    private Label iAboutDevLabel = null;
+    private Label iAboutContactLabel = null;
+    // Buttons
+    private Button iAboutOkButton = null;
+
+    /**
+     * This internal BuildLogger, to be honest, is just a BuildListener.
+     * It does nearly the same as the DefaultLogger, but uses the Loggin-Window for output.
+     */
+    private class VAJBuildLogger implements BuildListener {
+        private long startTime = System.currentTimeMillis();
+
+        /**
+         * VAJBuildLogger constructor comment.
+         */
+        public VAJBuildLogger() {
+            super();
+        }
+
+        /**
+         *  Fired after the last target has finished. This event
+         *  will still be thrown if an error occurred during the build.
+         *
+         *  @see BuildEvent#getException()
+         */
+        public void buildFinished(BuildEvent event) {
+            getStopButton().setEnabled(false);
+            getBuildButton().setEnabled(true);
+            getBuildButton().requestFocus();
+
+            Throwable error = event.getException();
+
+            if (error == null) {
+                getMessageTextArea().append(lineSeparator + "BUILD SUCCESSFUL");
+            } else {
+                logException(error);
+            }
+
+            getMessageTextArea().append(lineSeparator + "Total time: "
+                + DateUtils.formatElapsedTime(System.currentTimeMillis() - startTime));
+        }
+
+
+        /**
+         *  Outputs an exception.
+         */
+        public void logException(Throwable error) {
+            getMessageTextArea().append(lineSeparator + "BUILD FAILED" + lineSeparator);
+
+            if (error instanceof BuildException) {
+                getMessageTextArea().append(error.toString());
+
+                Throwable nested = ((BuildException) error).getException();
+                if (nested != null) {
+                    nested.printStackTrace(System.err);
+                }
+            } else {
+                error.printStackTrace(System.err);
+            }
+        }
+
+        /**
+         *  Fired before any targets are started.
+         */
+        public void buildStarted(BuildEvent event) {
+            getStopButton().setEnabled(true);
+            getBuildButton().setEnabled(false);
+            getStopButton().requestFocus();
+
+            startTime = System.currentTimeMillis();
+            getMessageTextArea().append(lineSeparator);
+        }
+
+        /**
+         *  Fired whenever a message is logged.
+         *
+         *  @see BuildEvent#getMessage()
+         *  @see BuildEvent#getPriority()
+         */
+        public void messageLogged(BuildEvent event) {
+            if (event.getPriority() <= getBuildInfo().getOutputMessageLevel()) {
+                String msg = "";
+                if (event.getTask() != null) {
+                    msg = "[" + event.getTask().getTaskName() + "] ";
+                }
+                getMessageTextArea().append(lineSeparator + msg + event.getMessage());
+            }
+        }
+
+        /**
+         *  Fired when a target has finished. This event will
+         *  still be thrown if an error occurred during the build.
+         *
+         *  @see BuildEvent#getException()
+         */
+        public void targetFinished(BuildEvent event) {
+        }
+
+        /**
+         *  Fired when a target is started.
+         *
+         *  @see BuildEvent#getTarget()
+         */
+        public void targetStarted(BuildEvent event) {
+            if (getBuildInfo().getOutputMessageLevel() <= Project.MSG_INFO) {
+                getMessageTextArea().append(lineSeparator + event.getTarget().getName() + ":");
+            }
+        }
+
+        /**
+         *  Fired when a task has finished. This event will still
+         *  be throw if an error occurred during the build.
+         *
+         *  @see BuildEvent#getException()
+         */
+        public void taskFinished(BuildEvent event) {
+        }
+
+        /**
+         *  Fired when a task is started.
+         *
+         *  @see BuildEvent#getTask()
+         */
+        public void taskStarted(BuildEvent event) {
+        }
+    }
+
+    /**
+     * Eventhandler to handle all AWT-events
+     */
+    private class PrivateEventHandler
+        implements ActionListener, ItemListener, TextListener,
+                   WindowListener, PropertyChangeListener {
+        /**
+         * ActionListener method
+         */
+        public void actionPerformed(ActionEvent e) {
+            try {
+                /* #### Main App-Frame #### */
+                // browse XML-File with filechooser
+                if (e.getSource() == VAJAntToolGUI.this.getBrowseButton()) {
+                    getFileDialog().setDirectory(getBuildFileTextField().getText().substring(0, getBuildFileTextField().getText().lastIndexOf('\\') + 1));
+                    getFileDialog().setFile("*.xml");
+                    getFileDialog().show();
+                    if (!getFileDialog().getFile().equals("")) {
+                        getBuildFileTextField().setText(getFileDialog().getDirectory()
+                        + getFileDialog().getFile());
+                    }
+                }
+                // dispose and exit application
+                if (e.getSource() == VAJAntToolGUI.this.getCloseButton()) {
+                    dispose();
+                    System.exit(0);
+                }
+                // start build-process
+                if (e.getSource() == VAJAntToolGUI.this.getBuildButton()) {
+                    executeTarget();
+                }
+                if (e.getSource() == VAJAntToolGUI.this.getStopButton()) {
+                    getBuildInfo().cancelBuild();
+                }
+                if (e.getSource() == VAJAntToolGUI.this.getReloadButton()) {
+                    try {
+                        getBuildInfo().updateTargetList();
+                        fillList();
+                    } catch (Throwable fileNotFound) {
+                        handleException(fileNotFound);
+                        getTargetList().removeAll();
+                        getBuildButton().setEnabled(false);
+                    }
+                }
+                // MenuItems
+                if (e.getSource() == VAJAntToolGUI.this.getSaveMenuItem()) {
+                    saveBuildInfo();
+                }
+                if (e.getSource() == VAJAntToolGUI.this.getAboutMenuItem()) {
+                    getAboutDialog().show();
+                }
+                if (e.getSource() == VAJAntToolGUI.this.getShowLogMenuItem()) {
+                    getMessageFrame().show();
+                }
+                /* #### About dialog #### */
+                if (e.getSource() == VAJAntToolGUI.this.getAboutOkButton()) {
+                    getAboutDialog().dispose();
+                }
+                /* #### Log frame #### */
+                if (e.getSource() == VAJAntToolGUI.this.getMessageOkButton()) {
+                    getMessageFrame().dispose();
+                }
+                if (e.getSource() == VAJAntToolGUI.this.getMessageClearLogButton()) {
+                    getMessageTextArea().setText("");
+                }
+                if (e.getSource() == VAJAntToolGUI.this.getMessageOkButton()) {
+                    getMessageFrame().dispose();
+                }
+            } catch (Throwable exc) {
+                handleException(exc);
+            }
+        }
+
+        /**
+         * ItemListener method
+         */
+        public void itemStateChanged(ItemEvent e) {
+            try {
+                if (e.getSource() == VAJAntToolGUI.this.getTargetList()) {
+                    getBuildButton().setEnabled(true);
+                }
+                if (e.getSource() == VAJAntToolGUI.this.getMessageOutputLevelChoice()) {
+                    getBuildInfo().setOutputMessageLevel(getMessageOutputLevelChoice().getSelectedIndex());
+                }
+                if (e.getSource() == VAJAntToolGUI.this.getTargetList()) {
+                    getBuildInfo().setTarget(getTargetList().getSelectedItem());
+                }
+            } catch (Throwable exc) {
+                handleException(exc);
+            }
+        }
+
+        /**
+         * PropertyChangeListener method
+         */
+        public void propertyChange(java.beans.PropertyChangeEvent evt) {
+            if (evt.getSource() == VAJAntToolGUI.this.getBuildInfo()
+                && (evt.getPropertyName().equals("projectName"))) {
+                connectProjectNameToLabel();
+            }
+            if (evt.getSource() == VAJAntToolGUI.this.getBuildInfo()
+                && (evt.getPropertyName().equals("buildFileName"))) {
+                connectBuildFileNameToTextField();
+            }
+        }
+
+        /**
+         * TextListener method
+         */
+        public void textValueChanged(TextEvent e) {
+            if (e.getSource() == VAJAntToolGUI.this.getBuildFileTextField()) {
+                connectTextFieldToBuildFileName();
+            }
+        }
+
+        /**
+         * WindowListener methods
+         */
+        public void windowClosing(WindowEvent e) {
+            try {
+                if (e.getSource() == VAJAntToolGUI.this) {
+                    dispose();
+                    System.exit(0);
+                }
+                if (e.getSource() == VAJAntToolGUI.this.getAboutDialog()) {
+                    getAboutDialog().dispose();
+                }
+                if (e.getSource() == VAJAntToolGUI.this.getMessageFrame()) {
+                    getMessageFrame().dispose();
+                }
+            } catch (Throwable exc) {
+                handleException(exc);
+            }
+        }
+        public void windowActivated(WindowEvent e) {
+        }
+        public void windowClosed(WindowEvent e) {
+        }
+        public void windowDeactivated(WindowEvent e) {
+        }
+        public void windowDeiconified(WindowEvent e) {
+        }
+        public void windowIconified(WindowEvent e) {
+        }
+        public void windowOpened(WindowEvent e) {
+        }
+    }
+
+    /**
+     * AntMake default-constructor.
+     */
+    private VAJAntToolGUI() {
+        super();
+        initialize();
+    }
+    /**
+     * AntMake constructor called by VAJAntTool integration.
+     * @param newBuildInfo VAJBuildInfo
+     */
+    public VAJAntToolGUI(VAJBuildInfo newBuildInfo) {
+        super();
+        setBuildInfo(newBuildInfo);
+        initialize();
+    }
+    /**
+     * This method is used to center dialogs.
+     */
+    public static void centerDialog(Dialog dialog) {
+        dialog.setLocation((Toolkit.getDefaultToolkit().getScreenSize().width / 2) - (dialog.getSize().width / 2), (java.awt.Toolkit.getDefaultToolkit().getScreenSize().height / 2) - (dialog.getSize().height / 2));
+    }
+    /**
+     * connectBuildFileNameToTextField:  (BuildInfo.buildFileName <--> BuildFileTextField.text)
+     */
+    private void connectBuildFileNameToTextField() {
+        /* Set the target from the source */
+        try {
+            if (iConnPtoP2Aligning == false) {
+                iConnPtoP2Aligning = true;
+                if ((getBuildInfo() != null)) {
+                    getBuildFileTextField().setText(getBuildInfo().getBuildFileName());
+                }
+                iConnPtoP2Aligning = false;
+            }
+        } catch (Throwable iExc) {
+            iConnPtoP2Aligning = false;
+            handleException(iExc);
+        }
+    }
+    /**
+     * connectProjectNameToLabel:  (BuildInfo.vajProjectName <--> ProjectText.text)
+     */
+    private void connectProjectNameToLabel() {
+        /* Set the target from the source */
+        try {
+            if ((getBuildInfo() != null)) {
+                getProjectText().setText(getBuildInfo().getVAJProjectName());
+            }
+        } catch (Throwable iExc) {
+            handleException(iExc);
+        }
+    }
+    /**
+     * connectTextFieldToBuildFileName:  (BuildInfo.buildFileName <--> BuildFileTextField.text)
+     */
+    private void connectTextFieldToBuildFileName() {
+        /* Set the source from the target */
+        try {
+            if (iConnPtoP2Aligning == false) {
+                iConnPtoP2Aligning = true;
+                if ((getBuildInfo() != null)) {
+                    getBuildInfo().setBuildFileName(getBuildFileTextField().getText());
+                }
+                iConnPtoP2Aligning = false;
+            }
+        } catch (Throwable iExc) {
+            iConnPtoP2Aligning = false;
+            handleException(iExc);
+        }
+    }
+    /**
+     * external build of a .jar-file
+     */
+    private void executeTarget() {
+        try {
+            getMessageFrame().show();
+            getBuildInfo().executeProject(logger);
+        } catch (Throwable exc) {
+            logger.logException(exc);
+        }
+        return;
+    }
+    /**
+     * Fills the taget-list with project-targets
+     */
+    private void fillList() {
+        getTargetList().removeAll();
+        Vector targets = getBuildInfo().getProjectTargets();
+        for (int i = 0; i < targets.size(); i++) {
+            getTargetList().add(targets.elementAt(i).toString());
+        }
+        getTargetList().select(iBuildInfo.getProjectTargets().indexOf(iBuildInfo.getTarget()));
+        if (getTargetList().getSelectedIndex() >= 0) {
+            getBuildButton().setEnabled(true);
+        }
+    }
+
+    /**
+     * Return the AboutCommandPanel property value.
+     * @return java.awt.Panel
+     */
+    private Panel getAboutCommandPanel() {
+        if (iAboutCommandPanel == null) {
+            try {
+                iAboutCommandPanel = new Panel();
+                iAboutCommandPanel.setName("AboutCommandPanel");
+                iAboutCommandPanel.setLayout(new java.awt.FlowLayout());
+                getAboutCommandPanel().add(getAboutOkButton(), getAboutOkButton().getName());
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iAboutCommandPanel;
+    }
+    /**
+     * Return the AboutContactLabel property value.
+     * @return java.awt.Label
+     */
+    private Label getAboutContactLabel() {
+        if (iAboutContactLabel == null) {
+            try {
+                iAboutContactLabel = new Label();
+                iAboutContactLabel.setName("AboutContactLabel");
+                iAboutContactLabel.setAlignment(java.awt.Label.CENTER);
+                iAboutContactLabel.setText("contact: wolf.siberski@tui.de or "
+                    + "christoph.wilhelms@tui.de");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iAboutContactLabel;
+    }
+    /**
+     * Return the AboutDevLabel property value.
+     * @return java.awt.Label
+     */
+    private Label getAboutDevLabel() {
+        if (iAboutDevLabel == null) {
+            try {
+                iAboutDevLabel = new Label();
+                iAboutDevLabel.setName("AboutDevLabel");
+                iAboutDevLabel.setAlignment(java.awt.Label.CENTER);
+                iAboutDevLabel.setText("developed by Wolf Siberski & Christoph Wilhelms");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iAboutDevLabel;
+    }
+    /**
+     * Return the AboutDialog property value.
+     * @return java.awt.Dialog
+     */
+    private Dialog getAboutDialog() {
+        if (iAboutDialog == null) {
+            try {
+                iAboutDialog = new Dialog(this);
+                iAboutDialog.setName("AboutDialog");
+                iAboutDialog.setResizable(false);
+                iAboutDialog.setLayout(new java.awt.BorderLayout());
+                iAboutDialog.setBounds(550, 14, 383, 142);
+                iAboutDialog.setModal(true);
+                iAboutDialog.setTitle("About...");
+                getAboutDialog().add(getAboutDialogContentPanel(), "Center");
+                iAboutDialog.pack();
+                centerDialog(iAboutDialog);
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iAboutDialog;
+    }
+    /**
+     * Return the AboutDialogContentPanel property value.
+     * @return java.awt.Panel
+     */
+    private Panel getAboutDialogContentPanel() {
+        if (iAboutDialogContentPanel == null) {
+            try {
+                iAboutDialogContentPanel = new Panel();
+                iAboutDialogContentPanel.setName("AboutDialogContentPanel");
+                iAboutDialogContentPanel.setLayout(new java.awt.BorderLayout());
+                getAboutDialogContentPanel().add(getAboutCommandPanel(), "South");
+                getAboutDialogContentPanel().add(getAboutInfoPanel(), "Center");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iAboutDialogContentPanel;
+    }
+    /**
+     * Return the AboutInfoPanel property value.
+     * @return java.awt.Panel
+     */
+    private Panel getAboutInfoPanel() {
+        if (iAboutInfoPanel == null) {
+            try {
+                iAboutInfoPanel = new Panel();
+                iAboutInfoPanel.setName("AboutInfoPanel");
+                iAboutInfoPanel.setLayout(new GridBagLayout());
+
+                GridBagConstraints constraintsAboutTitleLabel = new GridBagConstraints();
+                constraintsAboutTitleLabel.gridx = 0; constraintsAboutTitleLabel.gridy = 0;
+                constraintsAboutTitleLabel.fill = GridBagConstraints.HORIZONTAL;
+                constraintsAboutTitleLabel.weightx = 1.0;
+                constraintsAboutTitleLabel.weighty = 1.0;
+                constraintsAboutTitleLabel.insets = new Insets(4, 0, 4, 0);
+                getAboutInfoPanel().add(getAboutTitleLabel(), constraintsAboutTitleLabel);
+
+                GridBagConstraints constraintsAboutDevLabel = new GridBagConstraints();
+                constraintsAboutDevLabel.gridx = 0; constraintsAboutDevLabel.gridy = 1;
+                constraintsAboutDevLabel.fill = GridBagConstraints.HORIZONTAL;
+                constraintsAboutDevLabel.weightx = 1.0;
+                constraintsAboutDevLabel.insets = new Insets(4, 0, 0, 0);
+                getAboutInfoPanel().add(getAboutDevLabel(), constraintsAboutDevLabel);
+
+                GridBagConstraints constraintsAboutContactLabel = new GridBagConstraints();
+                constraintsAboutContactLabel.gridx = 0; constraintsAboutContactLabel.gridy = 2;
+                constraintsAboutContactLabel.fill = GridBagConstraints.HORIZONTAL;
+                constraintsAboutContactLabel.weightx = 1.0;
+                constraintsAboutContactLabel.insets = new Insets(2, 0, 4, 0);
+                getAboutInfoPanel().add(getAboutContactLabel(), constraintsAboutContactLabel);
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iAboutInfoPanel;
+    }
+    /**
+     * Return the AboutMenuItem property value.
+     * @return java.awt.MenuItem
+     */
+    private MenuItem getAboutMenuItem() {
+        if (iAboutMenuItem == null) {
+            try {
+                iAboutMenuItem = new MenuItem();
+                iAboutMenuItem.setLabel("About...");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iAboutMenuItem;
+    }
+    /**
+     * Return the AboutOkButton property value.
+     * @return java.awt.Button
+     */
+    private Button getAboutOkButton() {
+        if (iAboutOkButton == null) {
+            try {
+                iAboutOkButton = new Button();
+                iAboutOkButton.setName("AboutOkButton");
+                iAboutOkButton.setLabel("OK");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iAboutOkButton;
+    }
+    /**
+     * Return the AboutTitleLabel property value.
+     * @return java.awt.Label
+     */
+    private Label getAboutTitleLabel() {
+        if (iAboutTitleLabel == null) {
+            try {
+                iAboutTitleLabel = new Label();
+                iAboutTitleLabel.setName("AboutTitleLabel");
+                iAboutTitleLabel.setFont(new Font("Arial", 1, 12));
+                iAboutTitleLabel.setAlignment(Label.CENTER);
+                iAboutTitleLabel.setText("Ant VisualAge for Java Tool-Integration");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iAboutTitleLabel;
+    }
+    /**
+     * Return the AntMakeMenuBar property value.
+     * @return java.awt.MenuBar
+     */
+    private MenuBar getAntMakeMenuBar() {
+        if (iAntMakeMenuBar == null) {
+            try {
+                iAntMakeMenuBar = new MenuBar();
+                iAntMakeMenuBar.add(getFileMenu());
+                iAntMakeMenuBar.add(getHelpMenu());
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iAntMakeMenuBar;
+    }
+    /**
+     * Return the BrowseButton property value.
+     * @return Button
+     */
+    private Button getBrowseButton() {
+        if (iBrowseButton == null) {
+            try {
+                iBrowseButton = new Button();
+                iBrowseButton.setName("BrowseButton");
+                iBrowseButton.setLabel("...");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iBrowseButton;
+    }
+    /**
+     * Return the BuildButton property value.
+     * @return java.awt.Button
+     */
+    private Button getBuildButton() {
+        if (iBuildButton == null) {
+            try {
+                iBuildButton = new Button();
+                iBuildButton.setName("BuildButton");
+                iBuildButton.setLabel("Execute");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iBuildButton;
+    }
+    /**
+     * Return the BuildFileLabel property value.
+     * @return java.awt.Label
+     */
+    private Label getBuildFileLabel() {
+        if (iBuildFileLabel == null) {
+            try {
+                iBuildFileLabel = new Label();
+                iBuildFileLabel.setName("BuildFileLabel");
+                iBuildFileLabel.setText("Ant-Buildfile:");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iBuildFileLabel;
+    }
+    /**
+     * Return the BuildFileTextField property value.
+     * @return java.awt.TextField
+     */
+    private TextField getBuildFileTextField() {
+        if (iBuildFileTextField == null) {
+            try {
+                iBuildFileTextField = new TextField();
+                iBuildFileTextField.setName("BuildFileTextField");
+                iBuildFileTextField.setBackground(SystemColor.textHighlightText);
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iBuildFileTextField;
+    }
+    /**
+     * Return the BuildInfo property value.
+     * @return org.apache.tools.ant.taskdefs.optional.ide.VAJBuildInfo
+     */
+    private VAJBuildInfo getBuildInfo() {
+        return iBuildInfo;
+    }
+    /**
+     * Return the CloseButton property value.
+     * @return java.awt.Button
+     */
+    private Button getCloseButton() {
+        if (iCloseButton == null) {
+            try {
+                iCloseButton = new Button();
+                iCloseButton.setName("CloseButton");
+                iCloseButton.setLabel("Close");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iCloseButton;
+    }
+    /**
+     * Return the CommandButtonPanel property value.
+     * @return java.awt.Panel
+     */
+    private Panel getCommandButtonPanel() {
+        if (iCommandButtonPanel == null) {
+            try {
+                iCommandButtonPanel = new Panel();
+                iCommandButtonPanel.setName("CommandButtonPanel");
+                iCommandButtonPanel.setLayout(getCommandButtonPanelFlowLayout());
+                iCommandButtonPanel.setBackground(SystemColor.control);
+                iCommandButtonPanel.add(getReloadButton());
+                iCommandButtonPanel.add(getBuildButton());
+                iCommandButtonPanel.add(getStopButton());
+                iCommandButtonPanel.add(getCloseButton());
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iCommandButtonPanel;
+    }
+    /**
+     * Return the CommandButtonPanelFlowLayout property value.
+     * @return java.awt.FlowLayout
+     */
+    private FlowLayout getCommandButtonPanelFlowLayout() {
+        FlowLayout iCommandButtonPanelFlowLayout = null;
+        try {
+            /* Create part */
+            iCommandButtonPanelFlowLayout = new FlowLayout();
+            iCommandButtonPanelFlowLayout.setAlignment(FlowLayout.RIGHT);
+        } catch (Throwable iExc) {
+            handleException(iExc);
+        }
+        return iCommandButtonPanelFlowLayout;
+    }
+    /**
+     * Return the ContentsPane property value.
+     * @return java.awt.Panel
+     */
+    private Panel getContentsPane() {
+        if (iContentsPane == null) {
+            try {
+                iContentsPane = new Panel();
+                iContentsPane.setName("ContentsPane");
+                iContentsPane.setLayout(new BorderLayout());
+                getContentsPane().add(getCommandButtonPanel(), "South");
+                getContentsPane().add(getOptionenPanel(), "Center");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iContentsPane;
+    }
+    /**
+     * Return the FileDialog property value.
+     * @return java.awt.FileDialog
+     */
+    private FileDialog getFileDialog() {
+        if (iFileDialog == null) {
+            try {
+                iFileDialog = new FileDialog(this);
+                iFileDialog.setName("FileDialog");
+                iFileDialog.setLayout(null);
+                centerDialog(iFileDialog);
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iFileDialog;
+    }
+    /**
+     * Return the FileMenu property value.
+     * @return java.awt.Menu
+     */
+    private Menu getFileMenu() {
+        if (iFileMenu == null) {
+            try {
+                iFileMenu = new Menu();
+                iFileMenu.setLabel("File");
+                iFileMenu.add(getSaveMenuItem());
+                iFileMenu.add(getMenuSeparator());
+                iFileMenu.add(getShowLogMenuItem());
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iFileMenu;
+    }
+    /**
+     * Return the HelpMenu property value.
+     * @return java.awt.Menu
+     */
+    private Menu getHelpMenu() {
+        if (iHelpMenu == null) {
+            try {
+                iHelpMenu = new Menu();
+                iHelpMenu.setLabel("Help");
+                iHelpMenu.add(getAboutMenuItem());
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iHelpMenu;
+    }
+    /**
+     * Return the MenuSeparator1 property value.
+     * @return java.awt.MenuItem
+     */
+    private MenuItem getMenuSeparator() {
+        if (iMenuSeparator == null) {
+            try {
+                iMenuSeparator = new MenuItem();
+                iMenuSeparator.setLabel("-");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iMenuSeparator;
+    }
+    /**
+     * Return the MessageClearLogButton property value.
+     * @return java.awt.Button
+     */
+    private Button getMessageClearLogButton() {
+        if (iMessageClearLogButton == null) {
+            try {
+                iMessageClearLogButton = new Button();
+                iMessageClearLogButton.setName("MessageClearLogButton");
+                iMessageClearLogButton.setLabel("Clear Log");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iMessageClearLogButton;
+    }
+    /**
+     * Return the MessageCommandPanel property value.
+     * @return java.awt.Panel
+     */
+    private Panel getMessageCommandPanel() {
+        if (iMessageCommandPanel == null) {
+            try {
+                iMessageCommandPanel = new Panel();
+                iMessageCommandPanel.setName("MessageCommandPanel");
+                iMessageCommandPanel.setLayout(new FlowLayout());
+                getMessageCommandPanel().add(getMessageClearLogButton(),
+                                             getMessageClearLogButton().getName());
+                getMessageCommandPanel().add(getMessageOkButton(),
+                                             getMessageOkButton().getName());
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iMessageCommandPanel;
+    }
+    /**
+     * Return the MessageContentPanel property value.
+     * @return java.awt.Panel
+     */
+    private Panel getMessageContentPanel() {
+        if (iMessageContentPanel == null) {
+            try {
+                iMessageContentPanel = new Panel();
+                iMessageContentPanel.setName("MessageContentPanel");
+                iMessageContentPanel.setLayout(new BorderLayout());
+                iMessageContentPanel.setBackground(SystemColor.control);
+                getMessageContentPanel().add(getMessageTextArea(), "Center");
+                getMessageContentPanel().add(getMessageCommandPanel(), "South");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iMessageContentPanel;
+    }
+    /**
+     * Return the MessageFrame property value.
+     * @return java.awt.Frame
+     */
+    private Frame getMessageFrame() {
+        if (iMessageFrame == null) {
+            try {
+                iMessageFrame = new Frame();
+                iMessageFrame.setName("MessageFrame");
+                iMessageFrame.setLayout(new BorderLayout());
+                iMessageFrame.setBounds(0, 0, 750, 250);
+                iMessageFrame.setTitle("Message Log");
+                iMessageFrame.add(getMessageContentPanel(), "Center");
+                iMessageFrame.setLocation((Toolkit.getDefaultToolkit().getScreenSize().width / 2)
+                        - (iMessageFrame.getSize().width / 2),
+                    (java.awt.Toolkit.getDefaultToolkit().getScreenSize().height / 2));
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iMessageFrame;
+    }
+    /**
+     * Return the MessageOkButton property value.
+     * @return java.awt.Button
+     */
+    private Button getMessageOkButton() {
+        if (iMessageOkButton == null) {
+            try {
+                iMessageOkButton = new Button();
+                iMessageOkButton.setName("MessageOkButton");
+                iMessageOkButton.setLabel("Close");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iMessageOkButton;
+    }
+    /**
+     * Return the MessageOutputLevelChoice property value.
+     * @return java.awt.Choice
+     */
+    private Choice getMessageOutputLevelChoice() {
+        if (iMessageOutputLevelChoice == null) {
+            try {
+                iMessageOutputLevelChoice = new Choice();
+                iMessageOutputLevelChoice.setName("MessageOutputLevelChoice");
+                iMessageOutputLevelChoice.add("Error");
+                iMessageOutputLevelChoice.add("Warning");
+                iMessageOutputLevelChoice.add("Info");
+                iMessageOutputLevelChoice.add("Verbose");
+                iMessageOutputLevelChoice.add("Debug");
+                iMessageOutputLevelChoice.select(2);
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iMessageOutputLevelChoice;
+    }
+    /**
+     * Return the MessageOutputLevelLabel property value.
+     * @return java.awt.Label
+     */
+    private Label getMessageOutputLevelLabel() {
+        if (iMessageOutputLevelLabel == null) {
+            try {
+                iMessageOutputLevelLabel = new Label();
+                iMessageOutputLevelLabel.setName("MessageOutputLevelLabel");
+                iMessageOutputLevelLabel.setText("Message Level:");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iMessageOutputLevelLabel;
+    }
+    /**
+     * Return the MessageTextArea property value.
+     * @return java.awt.TextArea
+     */
+    private TextArea getMessageTextArea() {
+        if (iMessageTextArea == null) {
+            try {
+                iMessageTextArea = new TextArea();
+                iMessageTextArea.setName("MessageTextArea");
+                iMessageTextArea.setFont(new Font("monospaced", 0, 12));
+                iMessageTextArea.setText("");
+                iMessageTextArea.setEditable(false);
+                iMessageTextArea.setEnabled(true);
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iMessageTextArea;
+    }
+    /**
+     * Return the Panel1 property value.
+     * @return java.awt.Panel
+     */
+    private Panel getOptionenPanel() {
+        if (iOptionenPanel == null) {
+            try {
+                iOptionenPanel = new Panel();
+                iOptionenPanel.setName("OptionenPanel");
+                iOptionenPanel.setLayout(new GridBagLayout());
+                iOptionenPanel.setBackground(SystemColor.control);
+
+                GridBagConstraints constraintsProjectLabel = new GridBagConstraints();
+                constraintsProjectLabel.gridx = 0; constraintsProjectLabel.gridy = 0;
+                constraintsProjectLabel.anchor = GridBagConstraints.WEST;
+                constraintsProjectLabel.insets = new Insets(4, 4, 4, 4);
+                getOptionenPanel().add(getProjectLabel(), constraintsProjectLabel);
+
+                GridBagConstraints constraintsBuildFileLabel = new GridBagConstraints();
+                constraintsBuildFileLabel.gridx = 0; constraintsBuildFileLabel.gridy = 1;
+                constraintsBuildFileLabel.anchor = GridBagConstraints.WEST;
+                constraintsBuildFileLabel.insets = new Insets(4, 4, 4, 4);
+                getOptionenPanel().add(getBuildFileLabel(), constraintsBuildFileLabel);
+
+                GridBagConstraints constraintsTargetLabel = new GridBagConstraints();
+                constraintsTargetLabel.gridx = 0; constraintsTargetLabel.gridy = 2;
+                constraintsTargetLabel.anchor = GridBagConstraints.NORTHWEST;
+                constraintsTargetLabel.insets = new Insets(4, 4, 4, 4);
+                getOptionenPanel().add(getTargetLabel(), constraintsTargetLabel);
+
+                GridBagConstraints constraintsProjectText = new GridBagConstraints();
+                constraintsProjectText.gridx = 1; constraintsProjectText.gridy = 0;
+                constraintsProjectText.gridwidth = 2;
+                constraintsProjectText.fill = GridBagConstraints.HORIZONTAL;
+                constraintsProjectText.anchor = GridBagConstraints.WEST;
+                constraintsProjectText.insets = new Insets(4, 4, 4, 4);
+                getOptionenPanel().add(getProjectText(), constraintsProjectText);
+
+                GridBagConstraints constraintsBuildFileTextField = new GridBagConstraints();
+                constraintsBuildFileTextField.gridx = 1; constraintsBuildFileTextField.gridy = 1;
+                constraintsBuildFileTextField.fill = GridBagConstraints.HORIZONTAL;
+                constraintsBuildFileTextField.anchor = GridBagConstraints.WEST;
+                constraintsBuildFileTextField.weightx = 1.0;
+                constraintsBuildFileTextField.insets = new Insets(4, 4, 4, 4);
+                getOptionenPanel().add(getBuildFileTextField(), constraintsBuildFileTextField);
+
+                GridBagConstraints constraintsBrowseButton = new GridBagConstraints();
+                constraintsBrowseButton.gridx = 2; constraintsBrowseButton.gridy = 1;
+                constraintsBrowseButton.insets = new Insets(4, 4, 4, 4);
+                getOptionenPanel().add(getBrowseButton(), constraintsBrowseButton);
+
+                GridBagConstraints constraintsTargetList = new GridBagConstraints();
+                constraintsTargetList.gridx = 1; constraintsTargetList.gridy = 2;
+                constraintsTargetList.gridheight = 2;
+                constraintsTargetList.fill = GridBagConstraints.BOTH;
+                constraintsTargetList.weightx = 1.0;
+                constraintsTargetList.weighty = 1.0;
+                constraintsTargetList.insets = new Insets(4, 4, 4, 4);
+                getOptionenPanel().add(getTargetList(), constraintsTargetList);
+
+                GridBagConstraints constraintsMessageOutputLevelLabel
+                    = new GridBagConstraints();
+                constraintsMessageOutputLevelLabel.gridx = 0;
+                constraintsMessageOutputLevelLabel.gridy = 4;
+                constraintsMessageOutputLevelLabel.anchor = GridBagConstraints.WEST;
+                constraintsMessageOutputLevelLabel.insets = new Insets(4, 4, 4, 4);
+                getOptionenPanel().add(getMessageOutputLevelLabel(),
+                                       constraintsMessageOutputLevelLabel);
+
+                GridBagConstraints constraintsMessageOutputLevelChoice
+                    = new GridBagConstraints();
+                constraintsMessageOutputLevelChoice.gridx = 1;
+                constraintsMessageOutputLevelChoice.gridy = 4;
+                constraintsMessageOutputLevelChoice.fill = GridBagConstraints.HORIZONTAL;
+                constraintsMessageOutputLevelChoice.anchor = GridBagConstraints.WEST;
+                constraintsMessageOutputLevelChoice.weightx = 1.0;
+                constraintsMessageOutputLevelChoice.insets = new Insets(4, 4, 4, 4);
+                getOptionenPanel().add(getMessageOutputLevelChoice(),
+                                       constraintsMessageOutputLevelChoice);
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iOptionenPanel;
+    }
+    /**
+     * Return the ProjectLabel property value.
+     * @return java.awt.Label
+     */
+    private Label getProjectLabel() {
+        if (iProjectLabel == null) {
+            try {
+                iProjectLabel = new Label();
+                iProjectLabel.setName("ProjectLabel");
+                iProjectLabel.setText("Projectname:");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iProjectLabel;
+    }
+    /**
+     * Return the ProjectText property value.
+     * @return java.awt.Label
+     */
+    private Label getProjectText() {
+        if (iProjectText == null) {
+            try {
+                iProjectText = new Label();
+                iProjectText.setName("ProjectText");
+                iProjectText.setText(" ");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iProjectText;
+    }
+    /**
+     * Return the ReloadButton property value.
+     * @return java.awt.Button
+     */
+    private Button getReloadButton() {
+        if (iReloadButton == null) {
+            try {
+                iReloadButton = new Button();
+                iReloadButton.setName("ReloadButton");
+                iReloadButton.setLabel("(Re)Load");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iReloadButton;
+    }
+    /**
+     * Return the SaveMenuItem property value.
+     * @return java.awt.MenuItem
+     */
+    private MenuItem getSaveMenuItem() {
+        if (iSaveMenuItem == null) {
+            try {
+                iSaveMenuItem = new MenuItem();
+                iSaveMenuItem.setLabel("Save BuildInfo To Repository");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iSaveMenuItem;
+    }
+    /**
+     * Return the ShowLogMenuItem property value.
+     * @return java.awt.MenuItem
+     */
+    private MenuItem getShowLogMenuItem() {
+        if (iShowLogMenuItem == null) {
+            try {
+                iShowLogMenuItem = new MenuItem();
+                iShowLogMenuItem.setLabel("Log");
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iShowLogMenuItem;
+    }
+    /**
+     * Return the TargetLabel property value.
+     * @return java.awt.Label
+     */
+    private Label getTargetLabel() {
+        if (iTargetLabel == null) {
+            try {
+                iTargetLabel = new Label();
+                iTargetLabel.setName("TargetLabel");
+                iTargetLabel.setText("Target:");
+                iTargetLabel.setEnabled(true);
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iTargetLabel;
+    }
+    /**
+     * Return the TargetList property value.
+     * @return java.awt.List
+     */
+    private List getTargetList() {
+        if (iTargetList == null) {
+            try {
+                iTargetList = new List();
+                iTargetList.setName("TargetList");
+                iTargetList.setEnabled(true);
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iTargetList;
+    }
+    /**
+     * Called whenever the part throws an exception.
+     * @param exception Throwable
+     */
+    private void handleException(Throwable exception) {
+        // Write exceptions to the log-window
+        String trace = StringUtils.getStackTrace(exception);
+
+        getMessageTextArea().append(lineSeparator + lineSeparator + trace);
+        getMessageFrame().show();
+
+    }
+    /**
+     * Initializes connections
+     * @exception Exception The exception description.
+     */
+    private void initConnections() throws Exception {
+        this.addWindowListener(iEventHandler);
+        getBrowseButton().addActionListener(iEventHandler);
+        getCloseButton().addActionListener(iEventHandler);
+        getBuildButton().addActionListener(iEventHandler);
+        getStopButton().addActionListener(iEventHandler);
+        getSaveMenuItem().addActionListener(iEventHandler);
+        getAboutOkButton().addActionListener(iEventHandler);
+        getAboutMenuItem().addActionListener(iEventHandler);
+        getMessageOkButton().addActionListener(iEventHandler);
+        getMessageClearLogButton().addActionListener(iEventHandler);
+        getMessageOkButton().addActionListener(iEventHandler);
+        getShowLogMenuItem().addActionListener(iEventHandler);
+        getAboutDialog().addWindowListener(iEventHandler);
+        getMessageFrame().addWindowListener(iEventHandler);
+        getReloadButton().addActionListener(iEventHandler);
+        getTargetList().addItemListener(iEventHandler);
+        getMessageOutputLevelChoice().addItemListener(iEventHandler);
+        getBuildFileTextField().addTextListener(iEventHandler);
+        connectProjectNameToLabel();
+        connectBuildFileNameToTextField();
+    }
+    /**
+     * Initialize the class.
+     */
+    private void initialize() {
+        try {
+            setName("AntMake");
+            setMenuBar(getAntMakeMenuBar());
+            setLayout(new java.awt.BorderLayout());
+            setSize(389, 222);
+            setTitle("Ant VisualAge for Java Tool-Integration");
+            add(getContentsPane(), "Center");
+            initConnections();
+        } catch (Throwable iExc) {
+            handleException(iExc);
+        }
+        setLocation((Toolkit.getDefaultToolkit().getScreenSize().width / 2)
+                - (getSize().width / 2),
+            (java.awt.Toolkit.getDefaultToolkit().getScreenSize().height / 2)
+                - (getSize().height));
+        if ((getTargetList().getItemCount() == 0) || (getTargetList().getSelectedIndex() < 0)) {
+            getBuildButton().setEnabled(false);
+        }
+    }
+    /**
+     * Saves the build-informations to repository
+     */
+    private void saveBuildInfo() {
+        try {
+            VAJAntTool.saveBuildData(getBuildInfo());
+        } catch (Throwable exc) {
+            // This Exception occurs when you try to write into a versioned project
+            handleException(exc);
+        }
+        return;
+    }
+    /**
+     * Set the BuildInfo to a new value.
+     * @param newValue org.apache.tools.ant.taskdefs.optional.vaj.VAJBuildInfo
+     */
+    private void setBuildInfo(VAJBuildInfo newValue) {
+        if (iBuildInfo != newValue) {
+            try {
+                /* Stop listening for events from the current object */
+                if (iBuildInfo != null) {
+                    iBuildInfo.removePropertyChangeListener(iEventHandler);
+                }
+                iBuildInfo = newValue;
+
+                /* Listen for events from the new object */
+                if (iBuildInfo != null) {
+                    iBuildInfo.addPropertyChangeListener(iEventHandler);
+                }
+                connectProjectNameToLabel();
+                connectBuildFileNameToTextField();
+
+                // Select the log-level given by BuildInfo
+                getMessageOutputLevelChoice().select(iBuildInfo.getOutputMessageLevel());
+                fillList();
+                // BuildInfo can conly be saved to a VAJ project if tool API
+                // is called via the projects context-menu
+                if ((iBuildInfo.getVAJProjectName() == null)
+                    || (iBuildInfo.getVAJProjectName().equals(""))) {
+                    getSaveMenuItem().setEnabled(false);
+                }
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+    }
+
+    private Button iStopButton = null;
+
+    /**
+     * Return the StopButton property value.
+     * @return java.awt.Button
+     */
+    private Button getStopButton() {
+        if (iStopButton == null) {
+            try {
+                iStopButton = new Button();
+                iStopButton.setName("StopButton");
+                iStopButton.setLabel("Stop");
+                iStopButton.setEnabled(false);
+            } catch (Throwable iExc) {
+                handleException(iExc);
+            }
+        }
+        return iStopButton;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJBuildInfo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJBuildInfo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJBuildInfo.java	(revision 16139)
@@ -0,0 +1,480 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.File;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.Target;
+
+/**
+ * This class wraps the Ant project information needed to
+ * start Ant from Visual Age.
+ * It serves the following purposes:
+ * - acts as model for AntMakeFrame
+ * - converts itself to/from String (to store the information
+ *   as ToolData in the VA repository)
+ * - wraps Project functions for the GUI (get target list,
+ *   execute target)
+ * - manages a seperate thread for Ant project execution
+ *   this allows interrupting a running build from a GUI
+ *
+ */
+
+class VAJBuildInfo implements Runnable {
+    /**
+     * This exception is thrown when a build is interrupted
+     */
+    public static class BuildInterruptedException extends BuildException {
+        public String toString() {
+            return "BUILD INTERRUPTED";
+        }
+    }
+
+    /**
+     * BuildListener which checks for interruption and throws Exception
+     * if build process is interrupted. This class is a wrapper around
+     * a 'real' listener.
+     */
+    private class InterruptedChecker implements BuildListener {
+        // the real listener
+        BuildListener wrappedListener;
+
+        /**
+         * Can only be constructed as wrapper around a real listener
+         * @param listener the real listener
+         */
+        public InterruptedChecker(BuildListener listener) {
+            super();
+            wrappedListener = listener;
+        }
+
+        /**
+         * checks if the thread was interrupted. When an
+         * interrupt occurred, throw an Exception to stop
+         * the execution.
+         */
+        protected void checkInterrupted() {
+            if (buildThread.isInterrupted()) {
+                throw new BuildInterruptedException();
+            }
+        }
+
+        /**
+         *  Fired after the last target has finished. This event
+         *  will still be thrown if an error occurred during the build.
+         */
+        public void buildFinished(BuildEvent event) {
+            wrappedListener.buildFinished(event);
+            checkInterrupted();
+        }
+
+        /**
+         *  Fired before any targets are started.
+         */
+        public void buildStarted(BuildEvent event) {
+            wrappedListener.buildStarted(event);
+            checkInterrupted();
+        }
+
+        /**
+         *  Fired whenever a message is logged.
+         */
+        public void messageLogged(BuildEvent event) {
+            wrappedListener.messageLogged(event);
+            checkInterrupted();
+        }
+
+        /**
+         *  Fired when a target has finished. This event will
+         *  still be thrown if an error occurred during the build.
+         */
+        public void targetFinished(BuildEvent event) {
+            wrappedListener.targetFinished(event);
+            checkInterrupted();
+        }
+
+        /**
+         *  Fired when a target is started.
+         */
+        public void targetStarted(BuildEvent event) {
+            wrappedListener.targetStarted(event);
+            checkInterrupted();
+        }
+
+        /**
+         *  Fired when a task has finished. This event will still
+         *  be throw if an error occurred during the build.
+         */
+        public void taskFinished(BuildEvent event) {
+            wrappedListener.taskFinished(event);
+            checkInterrupted();
+        }
+
+        /**
+         *  Fired when a task is started.
+         */
+        public void taskStarted(BuildEvent event) {
+            wrappedListener.taskStarted(event);
+            checkInterrupted();
+        }
+    }
+
+    // name of the VA project this BuildInfo belongs to
+    private String vajProjectName = "";
+
+    // name of the Ant build file
+    private String buildFileName = "";
+
+    // main targets found in the build file
+    private Vector projectTargets = new Vector();
+
+    // target selected for execution
+    private String target = "";
+
+    // log level
+    private int outputMessageLevel = Project.MSG_INFO;
+
+    // Ant Project created from build file
+    private transient Project project;
+
+    // is true if Project initialization was successful
+    private transient boolean projectInitialized = false;
+
+    // Support for bound properties
+    protected transient PropertyChangeSupport propertyChange;
+
+    // thread for Ant build execution
+    private Thread buildThread;
+
+    // the listener used to log output.
+    private BuildListener projectLogger;
+
+
+    /**
+     * The addPropertyChangeListener method was generated to support the
+     * propertyChange field.
+     */
+    public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
+        getPropertyChange().addPropertyChangeListener(listener);
+    }
+
+    /**
+     * Returns the BuildInfo information as String. The BuildInfo can
+     * be rebuilt from that String by calling parse().
+     * @return String
+     */
+    public String asDataString() {
+        String result = getOutputMessageLevel() + "|" + getBuildFileName()
+            + "|" + getTarget();
+        for (Enumeration e = getProjectTargets().elements();
+              e.hasMoreElements();) {
+            result = result + "|" + e.nextElement();
+        }
+
+        return result;
+    }
+
+    /**
+     * Search for the insert position to keep names a sorted list of Strings
+     * This method has been copied from org.apache.tools.ant.Main
+     */
+    private static int findTargetPosition(Vector names, String name) {
+        int res = names.size();
+        for (int i = 0; i < names.size() && res == names.size(); i++) {
+            if (name.compareTo((String) names.elementAt(i)) < 0) {
+                res = i;
+            }
+        }
+        return res;
+    }
+
+    /**
+     * The firePropertyChange method was generated to support the propertyChange field.
+     */
+    public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
+        getPropertyChange().firePropertyChange(propertyName, oldValue, newValue);
+    }
+
+    /**
+     * Returns the build file name.
+     * @return build file name.
+     */
+    public String getBuildFileName() {
+        return buildFileName;
+    }
+
+    /**
+     * Returns the log level
+     * @return log level.
+     */
+    public int getOutputMessageLevel() {
+        return outputMessageLevel;
+    }
+
+    /**
+     * Returns the Ant project
+     * @return org.apache.tools.ant.Project
+     */
+    private Project getProject() {
+        if (project == null) {
+            project = new Project();
+        }
+        return project;
+    }
+
+    /**
+     * return a list of all targets in the current buildfile
+     */
+    public Vector getProjectTargets() {
+        return projectTargets;
+    }
+
+    /**
+     * Accessor for the propertyChange field.
+     */
+    protected PropertyChangeSupport getPropertyChange() {
+        if (propertyChange == null) {
+            propertyChange = new PropertyChangeSupport(this);
+        }
+        return propertyChange;
+    }
+
+    /**
+     * returns the selected target.
+     */
+    public String getTarget() {
+        return target;
+    }
+
+    /**
+     * returns the VA project name
+     */
+    public String getVAJProjectName() {
+        return vajProjectName;
+    }
+
+    /**
+     * Initializes the Ant project. Assumes that the
+     * project attribute is already set.
+     */
+    private void initProject() {
+        try {
+            project.init();
+            File buildFile = new File(getBuildFileName());
+            project.setUserProperty("ant.file", buildFile.getAbsolutePath());
+            ProjectHelper.configureProject(project, buildFile);
+            setProjectInitialized(true);
+        } catch (RuntimeException exc) {
+            setProjectInitialized(false);
+            throw exc;
+        } catch (Error err) {
+            setProjectInitialized(false);
+            throw err;
+        }
+    }
+
+    /**
+     * Returns true, if the Ant project is initialized.
+     * (i.e., if the buildfile loaded).
+     */
+    public boolean isProjectInitialized() {
+        return projectInitialized;
+    }
+
+    /**
+     * Creates a BuildInfo object from a String
+     * The String must be in the format
+     * outputMessageLevel'|'buildFileName'|'defaultTarget'|'(project target'|')*
+     *
+     * @return org.apache.tools.ant.taskdefs.optional.vaj.BuildInfo
+     * @param data String
+     */
+    public static VAJBuildInfo parse(String data) {
+        VAJBuildInfo result = new VAJBuildInfo();
+
+        try {
+            StringTokenizer tok = new StringTokenizer(data, "|");
+            result.setOutputMessageLevel(tok.nextToken());
+            result.setBuildFileName(tok.nextToken());
+            result.setTarget(tok.nextToken());
+            while (tok.hasMoreTokens()) {
+                result.projectTargets.addElement(tok.nextToken());
+            }
+        } catch (Throwable t) {
+            // if parsing the info fails, just return
+            // an empty VAJBuildInfo
+        }
+        return result;
+    }
+
+    /**
+     * The removePropertyChangeListener method was generated
+     * to support the propertyChange field.
+     */
+    public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
+        getPropertyChange().removePropertyChangeListener(listener);
+    }
+
+    /**
+     * Sets the build file name
+     * @param buildFileName build file name
+     */
+    public void setBuildFileName(String newBuildFileName) {
+        String oldValue = buildFileName;
+        buildFileName = newBuildFileName;
+        setProjectInitialized(false);
+        firePropertyChange("buildFileName", oldValue, buildFileName);
+    }
+
+    /**
+     * Sets the log level (value must be one of the constants in Project)
+     * @param outputMessageLevel log level.
+     */
+    public void setOutputMessageLevel(int newOutputMessageLevel) {
+        int oldValue = outputMessageLevel;
+        outputMessageLevel = newOutputMessageLevel;
+        firePropertyChange("outputMessageLevel",
+                           new Integer(oldValue), new Integer(outputMessageLevel));
+    }
+
+    /**
+     * Sets the log level (value must be one of the constants in Project)
+     * @param outputMessageLevel log level as String.
+     */
+    private void setOutputMessageLevel(String outputMessageLevel) {
+        int level = Integer.parseInt(outputMessageLevel);
+        setOutputMessageLevel(level);
+    }
+
+    /**
+     * sets the initialized flag
+     */
+    private void setProjectInitialized(boolean initialized) {
+        Boolean oldValue = new Boolean(projectInitialized);
+        projectInitialized = initialized;
+        firePropertyChange("projectInitialized", oldValue, new Boolean(projectInitialized));
+    }
+
+    /**
+     * Sets the target to execute when executeBuild is called
+     * @param newTarget build target
+     */
+    public void setTarget(String newTarget) {
+        String oldValue = target;
+        target = newTarget;
+        firePropertyChange("target", oldValue, target);
+    }
+
+    /**
+     * Sets the name of the Visual Age for Java project where
+     * this BuildInfo belongs to
+     * @param newProjectName VAJ project
+     */
+    public void setVAJProjectName(String newVAJProjectName) {
+        String oldValue = vajProjectName;
+        vajProjectName = newVAJProjectName;
+        firePropertyChange("VAJProjectName", oldValue, vajProjectName);
+    }
+
+    /**
+     * reloads the build file and updates the target list
+     */
+    public void updateTargetList() {
+        project = new Project();
+        initProject();
+        projectTargets.removeAllElements();
+        Enumeration ptargets = project.getTargets().elements();
+        while (ptargets.hasMoreElements()) {
+            Target currentTarget = (Target) ptargets.nextElement();
+            if (currentTarget.getDescription() != null) {
+                String targetName = currentTarget.getName();
+                int pos = findTargetPosition(projectTargets, targetName);
+                projectTargets.insertElementAt(targetName, pos);
+            }
+        }
+    }
+
+
+    /**
+     * cancels a build.
+     */
+    public void cancelBuild() {
+        buildThread.interrupt();
+    }
+
+    /**
+     * Executes the target set by setTarget().
+     * @param listener  BuildListener for the output of the build
+     */
+    public void executeProject(BuildListener logger) {
+        Throwable error;
+        projectLogger = logger;
+        try {
+            buildThread = new Thread(this);
+            buildThread.setPriority(Thread.MIN_PRIORITY);
+            buildThread.start();
+        } catch (RuntimeException exc) {
+            error = exc;
+            throw exc;
+        } catch (Error err) {
+            error = err;
+            throw err;
+        }
+    }
+
+    /**
+     * Executes a build. This method is executed by
+     * the Ant execution thread
+     */
+    public void run() {
+        try {
+            InterruptedChecker ic = new InterruptedChecker(projectLogger);
+            BuildEvent e = new BuildEvent(getProject());
+            try {
+                ic.buildStarted(e);
+
+                if (!isProjectInitialized()) {
+                    initProject();
+                }
+
+                project.addBuildListener(ic);
+                project.executeTarget(target);
+
+                ic.buildFinished(e);
+            } catch (Throwable t) {
+                e.setException(t);
+                ic.buildFinished(e);
+            } finally {
+                project.removeBuildListener(ic);
+            }
+        } catch (Throwable t2) {
+            System.out.println("unexpected exception!");
+            t2.printStackTrace();
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJExport.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJExport.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJExport.java	(revision 16139)
@@ -0,0 +1,234 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.PatternSet;
+
+/**
+ * Export packages from the Visual Age for Java workspace.
+ * The packages are specified similar to all other MatchingTasks.
+ * Since the VA Workspace is not file based, this task is simulating
+ * a directory hierarchy for the workspace:
+ * The 'root' contains all project 'dir's, and the projects contain
+ * their respective package 'dir's.
+ * Example:
+ * <blockquote>
+ * &lt;vajexport destdir=&quot;C:/builddir/source&quot;&gt;
+ * &nbsp;&lt;include name=&quot;/MyVAProject/org/foo/subsystem1/**&quot; /&gt;
+ * &nbsp;&lt;exclude name=&quot;/MyVAProject/org/foo/subsystem1/test/**&quot;/&gt;
+ * &lt;/vajexport&gt;
+ * </blockquote>
+ * exports all packages in the project MyVAProject which start with
+ * 'org.foo.subsystem1' except of these starting with
+ * 'org.foo.subsystem1.test'.
+ *
+ * <p>Parameters:
+ * <table border="1" cellpadding="2" cellspacing="0">
+ * <tr>
+ *   <td valign="top"><b>Attribute</b></td>
+ *   <td valign="top"><b>Description</b></td>
+ *   <td align="center" valign="top"><b>Required</b></td>
+ * </tr>
+ * <tr>
+ *   <td valign="top">destdir</td>
+ *   <td valign="top">location to store the exported files</td>
+ *   <td align="center" valign="top">Yes</td>
+ * <tr>
+ *   <td valign="top">exportSources</td>
+ *   <td valign="top">export Java sources, defaults to "yes"</td>
+ *   <td align="center" valign="top">No</td>
+ * </tr>
+ * <tr>
+ *   <td valign="top">exportResources</td>
+ *   <td valign="top">export resource files, defaults to "yes"</td>
+ *   <td align="center" valign="top">No</td>
+ * </tr>
+ * <tr>
+ *   <td valign="top">exportClasses</td>
+ *   <td valign="top">export class files, defaults to "no"</td>
+ *   <td align="center" valign="top">No</td>
+ * </tr>
+ * <tr>
+ *   <td valign="top">exportDebugInfo</td>
+ *   <td valign="top">include debug info in exported class files,
+ *                    defaults to "no"</td>
+ *   <td align="center" valign="top">No</td>
+ * </tr>
+ * <tr>
+ *   <td valign="top">defaultexcludes</td>
+ *   <td valign="top">use default excludes when exporting,
+ *                    defaults to "yes".
+ *                    Default excludes are: IBM&#x2f;**,
+ *                    Java class libraries&#x2f;**, Sun class libraries&#x2f;**,
+ *                    JSP Page Compile Generated Code&#x2f;**, Visual Age*&#x2f;**</td>
+ *   <td align="center" valign="top">No</td>
+ * </tr>
+ * <tr>
+ *   <td valign="top">overwrite</td>
+ *   <td valign="top">overwrite existing files, defaults to "yes"</td>
+ *   <td align="center" valign="top">No</td>
+ * </tr>
+ * <tr>
+ *   <td valign="top">remote</td>
+ *   <td valign="top">remote tool server to run this command against
+ *                    (format: &lt;servername&gt; : &lt;port no&gt;)</td>
+ *   <td align="center" valign="top">No</td>
+ * </tr>
+ * <tr>
+ *   <td valign="top">haltonerror</td>
+ *   <td valign="top">stop the build process if an error occurs,
+ *                    defaults to "yes"</td>
+ *   <td align="center" valign="top">No</td>
+ * </tr>
+ * </table>
+ *
+ */
+
+public class VAJExport extends VAJTask {
+    //set set... method comments for description
+    protected File destDir;
+    protected boolean exportSources = true;
+    protected boolean exportResources = true;
+    protected boolean exportClasses = false;
+    protected boolean exportDebugInfo = false;
+    protected boolean useDefaultExcludes = true;
+    protected boolean overwrite = true;
+
+    protected PatternSet patternSet = new PatternSet();
+
+    /**
+     * add a name entry on the exclude list
+     */
+    public PatternSet.NameEntry createExclude() {
+        return patternSet.createExclude();
+    }
+
+    /**
+     * add a name entry on the include list
+     */
+    public PatternSet.NameEntry createInclude() {
+        return patternSet.createInclude();
+    }
+
+    /**
+     * do the export
+     */
+    public void execute() throws BuildException {
+        // first off, make sure that we've got a destdir
+        if (destDir == null) {
+            throw new BuildException("destdir attribute must be set!");
+        }
+
+        // delegate the export to the VAJUtil object.
+        try {
+            getUtil().exportPackages(destDir,
+                patternSet.getIncludePatterns(getProject()),
+                patternSet.getExcludePatterns(getProject()),
+                exportClasses, exportDebugInfo,
+                exportResources, exportSources,
+                useDefaultExcludes, overwrite);
+        } catch (BuildException ex) {
+            if (haltOnError) {
+                throw ex;
+            } else {
+                log(ex.toString());
+            }
+        }
+    }
+
+    /**
+     * Sets whether default exclusions should be used or not; default true.
+     *
+     * @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
+     *                           should be used, "false"|"off"|"no" when they
+     *                           shouldn't be used.
+     */
+    public void setDefaultexcludes(boolean useDefaultExcludes) {
+        this.useDefaultExcludes = useDefaultExcludes;
+    }
+
+    /**
+     * Set the destination directory into which the selected
+     * items should be exported; required.
+     */
+    public void setDestdir(File destDir) {
+        this.destDir = destDir;
+    }
+
+    /**
+     * Sets the set of exclude patterns. Patterns may be separated by a comma
+     * or a space. Currently only patterns denoting packages are
+     * supported
+     *
+     * @param excludes the string containing the exclude patterns
+     */
+    public void setExcludes(String excludes) {
+        patternSet.setExcludes(excludes);
+    }
+
+    /**
+     * optional flag to export the class files; default false.
+     */
+    public void setExportClasses(boolean doExport) {
+        exportClasses = doExport;
+    }
+
+    /**
+     * optional flag to export the debug info; default false.
+     * debug info
+     */
+    public void setExportDebugInfo(boolean doExport) {
+        exportDebugInfo = doExport;
+    }
+
+    /**
+     * optional flag to export the resource file; default true.
+     */
+    public void setExportResources(boolean doExport) {
+        exportResources = doExport;
+    }
+
+    /**
+     * optional flag to export the Java files; default true.
+     */
+    public void setExportSources(boolean doExport) {
+        exportSources = doExport;
+    }
+
+    /**
+     * Sets the set of include patterns. Patterns may be separated by a comma
+     * or a space. Currently only patterns denoting packages are
+     * supported
+     *
+     * @param includes the string containing the include patterns
+     */
+    public void setIncludes(String includes) {
+        patternSet.setIncludes(includes);
+    }
+
+    /**
+     * if Overwrite is set, files will be overwritten during export
+     */
+    public void setOverwrite(boolean doOverwrite) {
+        overwrite = doOverwrite;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJExportServlet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJExportServlet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJExportServlet.java	(revision 16139)
@@ -0,0 +1,133 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+
+import java.io.File;
+
+/**
+ * A Remote Access to Tools Servlet to extract package
+ * sets from the Workbench to the local file system.
+ * The following table describes the servlet parameters.
+ *
+ * <table border="1">
+ *   <tr>
+ *     <td><strong>Parameter</strong></td>
+ *     <td><strong>Values</strong></td>
+ *     <td><strong>Description</strong></td>
+ *   </tr>
+ *   <tr>
+ *     <td>dir</td>
+ *     <td>Any valid directory name on the server.</td>
+ *     <td>The directory to export the files to on the machine
+ *         where the servlet is being run.  If the directory
+ *         doesn't exist, it will be created.<p>
+ *         Relative paths are relative to
+ *         IBMVJava/ide/tools/com-ibm-ivj-toolserver,
+ *         where IBMVJava is the VisualAge for Java installation
+ *         directory.</td>
+ *   </tr>
+ *   <tr>
+ *     <td>include</td>
+ *     <td>See below.</td>
+ *     <td>The pattern used to indicate which projects and
+ *         packages to export.</td>
+ *   </tr>
+ *   <tr>
+ *     <td>exclude</td>
+ *     <td>See below</td>
+ *     <td>The pattern used to indicate which projects and
+ *         packages <em>not</em> to export.</td>
+ *   </tr>
+ *   <tr>
+ *     <td>cls</td>
+ *     <td>"yes" or "no" (without the quotes)</td>
+ *     <td>Export class files.  Defaults to "no".</td>
+ *   </tr>
+ *   <tr>
+ *     <td>src</td>
+ *     <td>"yes" or "no" (without the quotes)</td>
+ *     <td>Export source files.  Defaults to "yes".</td>
+ *   </tr>
+ *   <tr>
+ *     <td>res</td>
+ *     <td>"yes" or "no" (without the quotes)</td>
+ *     <td>Export resource files associated with the included project(s).
+ *         Defaults to "yes".</td>
+ *   </tr>
+ *   <tr>
+ *     <td>dex</td>
+ *     <td>"yes" or "no" (without the quotes)</td>
+ *     <td>Use the default exclusion patterns.  Defaults to "yes".
+ *         See below for an explanation of default excludes.</td>
+ *   </tr>
+ *   <tr>
+ *     <td>owr</td>
+ *     <td>"yes" or "no" (without the quotes)</td>
+ *     <td>Overwrite any existing files.  Defaults to "yes".</td>
+ *   </tr>
+ * </table>
+ *
+ * <p>The vajexport servlet uses include and exclude parameters to form
+ *    the criteria for selecting packages to export. The parameter is
+ *    broken up into ProjectName/packageNameSegments, where ProjectName
+ *    is what you expect, and packageNameSegments is a partial (or complete)
+ *    package name, separated by forward slashes, rather than periods.
+ *    Each packageNameSegment can have wildcard characters.</p>
+ *
+ * <table border="1">
+ *   <tr>
+ *     <td><strong>Wildcard Characters</strong></td>
+ *     <td><strong>Description</strong></td>
+ *   </tr>
+ *   <tr>
+ *     <td>*</td>
+ *     <td>Match zero or more characters in that segment.</td>
+ *   </tr>
+ *   <tr>
+ *     <td>?</td>
+ *     <td>Match one character in that segment.</td>
+ *   </tr>
+ *   <tr>
+ *     <td>**</td>
+ *     <td>Matches all characters in zero or more segments.</td>
+ *   </tr>
+ * </table>
+ *
+ */
+public class VAJExportServlet extends VAJToolsServlet {
+    // constants for servlet param names
+    public static final String WITH_DEBUG_INFO = "deb";
+    public static final String OVERWRITE_PARAM = "owr";
+
+    /**
+     * Respond to a request to export packages from the Workbench.
+     */
+    protected void executeRequest() {
+        getUtil().exportPackages(
+                         new File(getFirstParamValueString(DIR_PARAM)),
+                         getParamValues(INCLUDE_PARAM),
+                         getParamValues(EXCLUDE_PARAM),
+                         getBooleanParam(CLASSES_PARAM, false),
+                         getBooleanParam(WITH_DEBUG_INFO, false),
+                         getBooleanParam(RESOURCES_PARAM, true),
+                         getBooleanParam(SOURCES_PARAM, true),
+                         getBooleanParam(DEFAULT_EXCLUDES_PARAM, true),
+                         getBooleanParam(OVERWRITE_PARAM, true));
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJImport.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJImport.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJImport.java	(revision 16139)
@@ -0,0 +1,210 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * Import source, class files, and resources to the Visual Age for Java
+ * workspace.
+ * <p>
+ * Example:
+ * <pre>
+ * &lt;vajimport project="MyVAProject"&gt;
+ *   &lt;fileset dir="src"&gt;
+ *     &lt;include name="org/foo/subsystem1/**" /&gt;
+ *     &lt;exclude name="/org/foo/subsystem1/test/**" /&gt;
+ *  &lt;/fileset&gt;
+ * &lt;/vajexport&gt;
+ * </pre>
+ * import all source and resource files from the "src" directory
+ * which start with 'org.foo.subsystem1', except of these starting with
+ * 'org.foo.subsystem1.test' into the project MyVAProject.
+ * </p>
+ * <p>If MyVAProject isn't loaded into the Workspace, a new edition is
+ * created in the repository and automatically loaded into the Workspace.
+ * There has to be at least one nested FileSet element.
+ * </p>
+ * <p>Parameters:
+ * <table border="1" cellpadding="2" cellspacing="0">
+ * <tr>
+ *   <td valign="top"><b>Attribute</b></td>
+ *   <td valign="top"><b>Description</b></td>
+ *   <td align="center" valign="top"><b>Required</b></td>
+ * </tr>
+ * <tr>
+ *   <td valign="top">project</td>
+ *   <td valign="top">the name of the Project to import to</td>
+ *   <td align="center" valign="top">Yes</td>
+ * </tr>
+ * <tr>
+ *   <td valign="top">importSources</td>
+ *   <td valign="top">import Java sources, defaults to "yes"</td>
+ *   <td align="center" valign="top">No</td>
+ * </tr>
+ * <tr>
+ *   <td valign="top">importResources</td>
+ *   <td valign="top">import resource files (anything that doesn't
+ *                    end with .java or .class), defaults to "yes"</td>
+ *   <td align="center" valign="top">No</td>
+ * </tr>
+ * <tr>
+ *   <td valign="top">importClasses</td>
+ *   <td valign="top">import class files, defaults to "no"</td>
+ *   <td align="center" valign="top">No</td>
+ * </tr>
+ * <tr>
+ *   <td valign="top">remote</td>
+ *   <td valign="top">remote tool server to run this command against
+ *                    (format: &lt;servername&gt; : &lt;port no&gt;)</td>
+ *   <td align="center" valign="top">No</td>
+ * </tr>
+ * <tr>
+ *   <td valign="top">haltonerror</td>
+ *   <td valign="top">stop the build process if an error occurs,
+ *                    defaults to "yes"</td>
+ *   <td align="center" valign="top">No</td>
+ * </tr>
+ * </table>
+ *
+ */
+public class VAJImport extends VAJTask {
+    protected Vector filesets = new Vector();
+    protected boolean importSources = true;
+    protected boolean importResources = true;
+    protected boolean importClasses = false;
+    protected String importProject = null;
+    protected boolean useDefaultExcludes = true;
+
+
+    /**
+     * Extended DirectoryScanner that has accessors for the
+     * includes and excludes fields.
+     *
+     * This is kindof a hack to get includes and excludes
+     * from the directory scanner. In order to keep
+     * the URLs short we only want to send the patterns to the
+     * remote tool server and let him figure out the files.
+     *
+     * This replaces the former reflection hack that
+     * didn't compile for old JDKs.
+     *
+     * @see VAJImport#importFileSet(FileSet)
+     */
+    private static class LocalDirectoryScanner extends DirectoryScanner {
+        public String[] getIncludes() { return includes; }
+        public String[] getExcludes() { return excludes; }
+    }
+
+    /**
+     * The VisualAge for Java Project name to import into.
+     */
+    public void setProject(String projectName) {
+        this.importProject = projectName;
+    }
+
+    /**
+     * Adds a set of files (nested fileset attribute).
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * Flag to import .class files; optional, default false.
+     */
+    public void setImportClasses(boolean importClasses) {
+        this.importClasses = importClasses;
+    }
+
+    /**
+     * Import resource files (anything that doesn't end in
+     * .class or .java); optional, default true.
+     */
+    public void setImportResources(boolean importResources) {
+        this.importResources = importResources;
+    }
+
+    /**
+     * Import .java files; optional, default true.
+     */
+    public void setImportSources(boolean importSources) {
+        this.importSources = importSources;
+    }
+
+    /**
+     * Sets whether default exclusions should be used or not.
+     *
+     * @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
+     *                           should be used, "false"|"off"|"no" when they
+     *                           shouldn't be used.
+     */
+    public void setDefaultexcludes(boolean useDefaultExcludes) {
+        this.useDefaultExcludes = useDefaultExcludes;
+    }
+
+    /**
+     * Do the import.
+     */
+    public void execute() throws BuildException {
+        if (filesets.size() == 0) {
+            throw new BuildException("At least one fileset is required!");
+        }
+
+        if (importProject == null || "".equals(importProject)) {
+            throw new BuildException("The VisualAge for Java Project name is required!");
+        }
+
+        try {
+            for (Enumeration e = filesets.elements(); e.hasMoreElements();) {
+                importFileset((FileSet) e.nextElement());
+            }
+        } catch (BuildException ex) {
+            if (haltOnError) {
+                throw ex;
+            } else {
+                log(ex.toString());
+            }
+        }
+    }
+
+    /**
+     * Import all files from the fileset into the Project in the
+     * Workspace.
+     */
+    protected void importFileset(FileSet fileset) {
+        LocalDirectoryScanner ds = new LocalDirectoryScanner();
+        fileset.setupDirectoryScanner(ds, this.getProject());
+        ds.scan();
+        if (ds.getIncludedFiles().length == 0) {
+            return;
+        }
+
+        String[] includes = ds.getIncludes();
+        String[] excludes = ds.getExcludes();
+
+        getUtil().importFiles(importProject, ds.getBasedir(),
+                includes, excludes,
+                importClasses, importResources, importSources,
+                useDefaultExcludes);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJImportServlet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJImportServlet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJImportServlet.java	(revision 16139)
@@ -0,0 +1,65 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+import java.io.File;
+
+
+/**
+ * A Remote Access to Tools Servlet to import a Project
+ * from files into the Repository. The following
+ * table describes the servlet parameters.
+ *
+ * <table>
+ *   <tr>
+ *     <td>Parameter</td>
+ *     <td>Description</td>
+ *   </tr>
+ *   <tr>
+ *     <td>project</td>
+ *     <td>The name of the project where you want the imported
+ *         items to go.
+ *     </td>
+ *   </tr>
+ *   <tr>
+ *     <td>dir</td>
+ *     <td>The directory you want to import from.</td>
+ *   </tr>
+ * </table>
+ *
+ */
+public class VAJImportServlet extends VAJToolsServlet {
+    /**
+     * Respond to a request to import files to the Repository
+     */
+    protected void executeRequest() {
+        getUtil().importFiles(
+                      getFirstParamValueString(PROJECT_NAME_PARAM),
+                      new File(getFirstParamValueString(DIR_PARAM)),
+                      getParamValues(INCLUDE_PARAM),
+                      getParamValues(EXCLUDE_PARAM),
+                      getBooleanParam(CLASSES_PARAM, false),
+                      getBooleanParam(RESOURCES_PARAM, true),
+                      getBooleanParam(SOURCES_PARAM, true),
+                      false);
+        // no default excludes, because they
+        // are already added on client side
+        // getBooleanParam(DEFAULT_EXCLUDES_PARAM, true)
+
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJLoad.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJLoad.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJLoad.java	(revision 16139)
@@ -0,0 +1,92 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Load project versions into the Visual Age for Java workspace.
+ * Each project is identified by its name and a version qualifier.
+ * Allowed qualifiers are:
+ * <ul>
+ * <li>Any valid Visual Age version name</li>
+ * <li>* (loads the latest <b>versioned</b> edition)</li>
+ * <li>** (loads the latest edition, including open editions)</li>
+ * </ul>
+ * Example:
+ * <blockquote>
+ * &lt;vajload&gt;
+ * &nbsp;&lt;project name=&quot;MyVAProject&quot; version=&quot;*&quot;/&gt;
+ * &nbsp;&lt;project name=&quot;Apache Xerces&quot; version=&quot;1.2.0&quot;/&gt;
+ * &nbsp;&lt;project name=&quot;Brand New Stuff&quot; version=&quot;**&quot;/&gt;
+ * &lt;/vajload&gt;
+ * </blockquote>
+ *
+ * <p>Parameters:</p>
+ * <table border="1" cellpadding="2" cellspacing="0">
+ * <tr>
+ *   <td valign="top"><b>Attribute</b></td>
+ *   <td valign="top"><b>Description</b></td>
+ *   <td align="center" valign="top"><b>Required</b></td>
+ * </tr>
+ * <tr>
+ *   <td valign="top">remote</td>
+ *   <td valign="top">remote tool server to run this command against
+ *                    (format: &lt;servername&gt; : &lt;port no&gt;)</td>
+ *   <td align="center" valign="top">No</td>
+ * </tr>
+ * <tr>
+ *   <td valign="top">haltonerror</td>
+ *   <td valign="top">stop the build process if an error occurs,
+ *                      defaults to "yes"</td>
+ *   <td align="center" valign="top">No</td>
+ * </tr>
+ * </table>
+ * </p>
+ *
+ */
+
+public class VAJLoad extends VAJTask {
+    Vector projectDescriptions = new Vector();
+
+    /**
+     * Load specified projects.
+     */
+    public void execute() {
+        try {
+            getUtil().loadProjects(projectDescriptions);
+        } catch (BuildException ex) {
+            if (haltOnError) {
+                throw ex;
+            } else {
+                log(ex.toString());
+            }
+        }
+    }
+
+    /**
+     * Add a project description entry on the project list.
+     * @return a project description
+     */
+    public VAJProjectDescription createVAJProject() {
+        VAJProjectDescription d = new VAJProjectDescription();
+        projectDescriptions.addElement(d);
+        return d;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJLoadProjects.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJLoadProjects.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJLoadProjects.java	(revision 16139)
@@ -0,0 +1,48 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+
+
+
+
+
+
+
+
+
+/**
+ * This is only there for backward compatibility with the default task list
+ * and will be removed soon
+ *
+ * @ant.task ignore="true"
+ */
+
+public class VAJLoadProjects extends VAJLoad {
+}
+
+
+
+
+
+
+
+
+
+
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJLoadServlet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJLoadServlet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJLoadServlet.java	(revision 16139)
@@ -0,0 +1,71 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+import java.util.Vector;
+
+/**
+ * A Remote Access to Tools Servlet to load a Project
+ * from the Repository into the Workbench. The following
+ * table describes the servlet parameters.
+ *
+ * <table>
+ *   <tr>
+ *     <td>Parameter</td>
+ *     <td>Description</td>
+ *   </tr>
+ *   <tr>
+ *     <td>project</td>
+ *     <td>The name of the Project you want to load into
+ *         the Workbench.</td>
+ *   </tr>
+ *   <tr>
+ *     <td>version</td>
+ *     <td>The version of the package you want to load into
+ *         the Workbench.</td>
+ *   </tr>
+ * </table>
+ *
+ */
+public class VAJLoadServlet extends VAJToolsServlet {
+
+    // constants for servlet param names
+    /**
+     * the version param string
+     */
+    public static final String VERSION_PARAM = "version";
+
+    /**
+     * Respond to a request to load a project from the Repository
+     * into the Workbench.
+     */
+    protected void executeRequest() {
+        String[] projectNames = getParamValues(PROJECT_NAME_PARAM);
+        String[] versionNames = getParamValues(VERSION_PARAM);
+
+        Vector projectDescriptions = new Vector(projectNames.length);
+        for (int i = 0; i < projectNames.length && i < versionNames.length; i++) {
+            VAJProjectDescription desc = new VAJProjectDescription();
+            desc.setName(projectNames[i]);
+            desc.setVersion(versionNames[i]);
+            projectDescriptions.addElement(desc);
+        }
+
+        util.loadProjects(projectDescriptions);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJLocalUtil.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJLocalUtil.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJLocalUtil.java	(revision 16139)
@@ -0,0 +1,459 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+import com.ibm.ivj.util.base.ExportCodeSpec;
+import com.ibm.ivj.util.base.ImportCodeSpec;
+import com.ibm.ivj.util.base.IvjException;
+import com.ibm.ivj.util.base.Package;
+import com.ibm.ivj.util.base.Project;
+import com.ibm.ivj.util.base.ProjectEdition;
+import com.ibm.ivj.util.base.ToolEnv;
+import com.ibm.ivj.util.base.Type;
+import com.ibm.ivj.util.base.Workspace;
+import java.io.File;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+
+
+/**
+ * Helper class for VAJ tasks. Holds Workspace singleton and
+ * wraps IvjExceptions into BuildExceptions
+ *
+ */
+abstract class VAJLocalUtil implements VAJUtil {
+    // singleton containing the VAJ workspace
+    private static Workspace workspace;
+
+    /**
+     * Wraps IvjException into a BuildException
+     *
+     * @return org.apache.tools.ant.BuildException
+     * @param errMsg Additional error message
+     * @param e IvjException which is wrapped
+     */
+    static BuildException createBuildException(
+                                               String errMsg, IvjException e) {
+        errMsg = errMsg + "\n" + e.getMessage();
+        String[] errors = e.getErrors();
+        if (errors != null) {
+            for (int i = 0; i < errors.length; i++) {
+                errMsg = errMsg + "\n" + errors[i];
+            }
+        }
+        return new BuildException(errMsg, e);
+    }
+
+    /**
+     * returns the current VAJ workspace.
+     * @return com.ibm.ivj.util.base.Workspace
+     */
+    static Workspace getWorkspace() {
+        if (workspace == null) {
+            workspace = ToolEnv.connectToWorkspace();
+            if (workspace == null) {
+                throw new BuildException(
+                                         "Unable to connect to Workspace! "
+                                         + "Make sure you are running in VisualAge for Java.");
+            }
+        }
+
+        return workspace;
+    }
+
+
+    //-----------------------------------------------------------
+    // export
+    //-----------------------------------------------------------
+
+    /**
+     * export packages
+     */
+    public void exportPackages(File dest,
+                               String[] includePatterns, String[] excludePatterns,
+                               boolean exportClasses, boolean exportDebugInfo,
+                               boolean exportResources, boolean exportSources,
+                               boolean useDefaultExcludes, boolean overwrite) {
+        if (includePatterns == null || includePatterns.length == 0) {
+            log("You must specify at least one include attribute. "
+                + "Not exporting", MSG_ERR);
+        } else {
+            try {
+                VAJWorkspaceScanner scanner = new VAJWorkspaceScanner();
+                scanner.setIncludes(includePatterns);
+                scanner.setExcludes(excludePatterns);
+                if (useDefaultExcludes) {
+                    scanner.addDefaultExcludes();
+                }
+                scanner.scan();
+
+                Package[] packages = scanner.getIncludedPackages();
+
+                log("Exporting " + packages.length + " package(s) to "
+                    + dest, MSG_INFO);
+                for (int i = 0; i < packages.length; i++) {
+                    log("    " + packages[i].getName(), MSG_VERBOSE);
+                }
+
+                ExportCodeSpec exportSpec = new ExportCodeSpec();
+                exportSpec.setPackages(packages);
+                exportSpec.includeJava(exportSources);
+                exportSpec.includeClass(exportClasses);
+                exportSpec.includeResources(exportResources);
+                exportSpec.includeClassDebugInfo(exportDebugInfo);
+                exportSpec.useSubdirectories(true);
+                exportSpec.overwriteFiles(overwrite);
+                exportSpec.setExportDirectory(dest.getAbsolutePath());
+
+                getWorkspace().exportData(exportSpec);
+            } catch (IvjException ex) {
+                throw createBuildException("Exporting failed!", ex);
+            }
+        }
+    }
+
+
+    //-----------------------------------------------------------
+    // load
+    //-----------------------------------------------------------
+
+    /**
+     * Load specified projects.
+     */
+    public void loadProjects(Vector projectDescriptions) {
+        Vector expandedDescs = getExpandedDescriptions(projectDescriptions);
+
+        // output warnings for projects not found
+        for (Enumeration e = projectDescriptions.elements(); e.hasMoreElements();) {
+            VAJProjectDescription d = (VAJProjectDescription) e.nextElement();
+            if (!d.projectFound()) {
+                log("No Projects match the name " + d.getName(), MSG_WARN);
+            }
+        }
+
+        log("Loading " + expandedDescs.size()
+            + " project(s) into workspace", MSG_INFO);
+
+        for (Enumeration e = expandedDescs.elements();
+             e.hasMoreElements();) {
+            VAJProjectDescription d = (VAJProjectDescription) e.nextElement();
+
+            ProjectEdition pe;
+            if (d.getVersion().equals("*")) {
+                pe = findLatestProjectEdition(d.getName(), false);
+            } else if (d.getVersion().equals("**")) {
+                pe = findLatestProjectEdition(d.getName(), true);
+            } else {
+                pe = findProjectEdition(d.getName(), d.getVersion());
+            }
+            try {
+                log("Loading '" + pe.getName() + "', Version '"
+                    + ((pe.getVersionName() != null) ? pe.getVersionName()
+                        : "(" + pe.getVersionStamp() + ")")
+                    + "' into Workspace", MSG_VERBOSE);
+                pe.loadIntoWorkspace();
+            } catch (IvjException ex) {
+                throw createBuildException("Project '" + d.getName()
+                                            + "' could not be loaded.", ex);
+            }
+        }
+    }
+
+    /**
+     * return project descriptions containing full project names instead
+     * of patterns with wildcards.
+     */
+    private Vector getExpandedDescriptions(Vector projectDescs) {
+        Vector expandedDescs = new Vector(projectDescs.size());
+        try {
+            String[] projectNames =
+                getWorkspace().getRepository().getProjectNames();
+            for (int i = 0; i < projectNames.length; i++) {
+                for (Enumeration e = projectDescs.elements();
+                     e.hasMoreElements();) {
+                    VAJProjectDescription d = (VAJProjectDescription) e.nextElement();
+                    String pattern = d.getName();
+                    if (VAJWorkspaceScanner.match(pattern, projectNames[i])) {
+                        d.setProjectFound();
+                        expandedDescs.addElement(new VAJProjectDescription(projectNames[i],
+                            d.getVersion()));
+                        break;
+                    }
+                }
+            }
+        } catch (IvjException e) {
+            throw createBuildException("VA Exception occurred: ", e);
+        }
+
+        return expandedDescs;
+    }
+
+    /**
+     * Finds a specific project edition in the repository.
+     *
+     * @param name project name
+     * @param versionName project version name
+     * @return com.ibm.ivj.util.base.ProjectEdition the specified edition
+     */
+    private ProjectEdition findProjectEdition(
+                                              String name, String versionName) {
+        try {
+            ProjectEdition[] editions = null;
+            editions = getWorkspace().getRepository().getProjectEditions(name);
+
+            if (editions == null) {
+                throw new BuildException("Project " + name + " doesn't exist");
+            }
+
+            ProjectEdition pe = null;
+            for (int i = 0; i < editions.length && pe == null; i++) {
+                if (versionName.equals(editions[i].getVersionName())) {
+                    pe = editions[i];
+                }
+            }
+            if (pe == null) {
+                throw new BuildException("Version " + versionName
+                    + " of Project " + name + " doesn't exist");
+            }
+            return pe;
+
+        } catch (IvjException e) {
+            throw createBuildException("VA Exception occurred: ", e);
+        }
+
+    }
+
+    /**
+     * Finds the latest project edition in the repository.
+     *
+     * @param name project name
+     * @param includeOpenEditions include open/scratch editions in the search?
+     * @return com.ibm.ivj.util.base.ProjectEdition the specified edition
+     */
+    private ProjectEdition findLatestProjectEdition(
+                                              String name,
+                                              boolean includeOpenEditions) {
+        try {
+            ProjectEdition[] editions = null;
+            editions = getWorkspace().getRepository().getProjectEditions(name);
+            if (editions == null) {
+                throw new BuildException("Project " + name + " doesn't exist");
+            }
+
+            // find latest (versioned) project edition by date
+            ProjectEdition pe = null;
+            // Let's hope there are no projects older than the epoch ;-)
+            Date latestStamp = new Date(0);
+            for (int i = 0; i < editions.length; i++) {
+                if (!includeOpenEditions && !editions[i].isVersion()) {
+                    continue;
+                }
+                if (latestStamp.before(editions[i].getVersionStamp())) {
+                    latestStamp = editions[i].getVersionStamp();
+                    pe = editions[i];
+                }
+            }
+
+            if (pe == null) {
+                throw new BuildException("Can't determine latest edition for project " + name);
+            }
+            log("Using version " + ((pe.getVersionName() != null) ? pe.getVersionName()
+                    : "(" + pe.getVersionStamp() + ")")
+                + " of " + pe.getName(), MSG_INFO);
+            return pe;
+        } catch (IvjException e) {
+            throw createBuildException("VA Exception occurred: ", e);
+        }
+
+    }
+
+
+
+    //-----------------------------------------------------------
+    // import
+    //-----------------------------------------------------------
+
+
+    /**
+     * Do the import.
+     */
+    public void importFiles(
+                            String importProject, File srcDir,
+                            String[] includePatterns, String[] excludePatterns,
+                            boolean importClasses, boolean importResources,
+                            boolean importSources, boolean useDefaultExcludes)
+        throws BuildException {
+
+        if (importProject == null || "".equals(importProject)) {
+            throw new BuildException("The VisualAge for Java project "
+                                     + "name is required!");
+        }
+
+        ImportCodeSpec importSpec = new ImportCodeSpec();
+        importSpec.setDefaultProject(getVAJProject(importProject));
+
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(srcDir);
+        ds.setIncludes(includePatterns);
+        ds.setExcludes(excludePatterns);
+        if (useDefaultExcludes) {
+            ds.addDefaultExcludes();
+        }
+        ds.scan();
+
+        Vector classes = new Vector();
+        Vector sources = new Vector();
+        Vector resources = new Vector();
+
+        scanForImport(srcDir, ds.getIncludedFiles(), classes, sources, resources);
+
+        StringBuffer summaryLog = new StringBuffer("Importing ");
+        addFilesToImport(importSpec, importClasses, classes, "Class", summaryLog);
+        addFilesToImport(importSpec, importSources, sources, "Java", summaryLog);
+        addFilesToImport(importSpec, importResources, resources, "Resource", summaryLog);
+        importSpec.setResourcePath(srcDir.getAbsolutePath());
+
+        summaryLog.append(" into the project '");
+        summaryLog.append(importProject);
+        summaryLog.append("'.");
+
+        log(summaryLog.toString(), MSG_INFO);
+
+        try {
+            Type[] importedTypes = getWorkspace().importData(importSpec);
+            if (importedTypes == null) {
+                throw new BuildException("Unable to import into Workspace!");
+            } else {
+                log(importedTypes.length + " types imported", MSG_DEBUG);
+                for (int i = 0; i < importedTypes.length; i++) {
+                    log(importedTypes[i].getPackage().getName()
+                        + "." + importedTypes[i].getName()
+                        + " into " + importedTypes[i].getProject().getName(),
+                        MSG_DEBUG);
+                }
+            }
+        } catch (IvjException ivje) {
+            throw createBuildException("Error while importing into workspace: ",
+                                       ivje);
+        }
+    }
+
+    /**
+     * get a project from the Workspace.
+     */
+    static Project getVAJProject(String importProject) {
+        Project found = null;
+        Project[] currentProjects = getWorkspace().getProjects();
+
+        for (int i = 0; i < currentProjects.length; i++) {
+            Project p = currentProjects[i];
+            if (p.getName().equals(importProject)) {
+                found = p;
+                break;
+            }
+        }
+
+
+        if (found == null) {
+            try {
+                found = getWorkspace().createProject(importProject, true);
+            } catch (IvjException e) {
+                throw createBuildException("Error while creating Project "
+                                           + importProject + ": ", e);
+            }
+        }
+
+        return found;
+    }
+
+
+    /**
+     * Sort the files into classes, sources, and resources.
+     */
+    private void scanForImport(
+                               File dir,
+                               String[] files,
+                               Vector classes,
+                               Vector sources,
+                               Vector resources) {
+        for (int i = 0; i < files.length; i++) {
+            String file = (new File(dir, files[i])).getAbsolutePath();
+            if (file.endsWith(".java") || file.endsWith(".JAVA")) {
+                sources.addElement(file);
+            } else
+                if (file.endsWith(".class") || file.endsWith(".CLASS")) {
+                    classes.addElement(file);
+                } else {
+                    // for resources VA expects the path relative to the resource path
+                    resources.addElement(files[i]);
+                }
+        }
+    }
+
+    /**
+     * Adds files to an import specification. Helper method
+     * for importFiles()
+     *
+     * @param spec       import specification
+     * @param doImport   only add files if doImport is true
+     * @param files      the files to add
+     * @param fileType   type of files (Source/Class/Resource)
+     * @param summaryLog buffer for logging
+     */
+    private void addFilesToImport(ImportCodeSpec spec, boolean doImport,
+                                  Vector files, String fileType,
+                                  StringBuffer summaryLog) {
+
+        if (doImport) {
+            String[] fileArr = new String[files.size()];
+            files.copyInto(fileArr);
+            try {
+                // here it is assumed that fileType is one of the
+                // following strings: // "Java", "Class", "Resource"
+                String methodName = "set" + fileType + "Files";
+                Class[] methodParams = new Class[]{fileArr.getClass()};
+                java.lang.reflect.Method method =
+                    spec.getClass().getDeclaredMethod(methodName, methodParams);
+                method.invoke(spec, new Object[]{fileArr});
+            } catch (Exception e) {
+                throw new BuildException(e);
+            }
+            if (files.size() > 0) {
+                logFiles(files, fileType);
+                summaryLog.append(files.size());
+                summaryLog.append(" " + fileType.toLowerCase() + " file");
+                summaryLog.append(files.size() > 1 ? "s, " : ", ");
+            }
+        }
+    }
+
+    /**
+     * Logs a list of file names to the message log
+     * @param fileNames java.util.Vector file names to be logged
+     * @param type java.lang.String file type
+     */
+    private void logFiles(Vector fileNames, String fileType) {
+        log(fileType + " files found for import:", MSG_VERBOSE);
+        for (Enumeration e = fileNames.elements(); e.hasMoreElements();) {
+            log("    " + e.nextElement(), MSG_VERBOSE);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJProjectDescription.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJProjectDescription.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJProjectDescription.java	(revision 16139)
@@ -0,0 +1,79 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Type class. Holds information about a project edition.
+ */
+public class VAJProjectDescription {
+    private String name;
+    private String version;
+    private boolean projectFound;
+
+    public VAJProjectDescription() {
+    }
+
+    public VAJProjectDescription(String n, String v) {
+        name = n;
+        version = v;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public boolean projectFound() {
+        return projectFound;
+    }
+
+    /**
+     * name of the VAJ project to load into
+     * the workspace; required
+     */
+    public void setName(String newName) {
+        if (newName == null || newName.equals("")) {
+            throw new BuildException("name attribute must be set");
+        }
+        name = newName;
+    }
+
+    /**
+     * name of the requested version; required.
+     */
+    public void setVersion(String newVersion) {
+        if (newVersion == null || newVersion.equals("")) {
+            throw new BuildException("version attribute must be set");
+        }
+        version = newVersion;
+    }
+
+    /**
+     * this may be a helper method, and is being ignored for now
+
+     * @ant.attribute ignore="true"
+     */
+    public void setProjectFound() {
+        projectFound = true;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJRemoteUtil.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJRemoteUtil.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJRemoteUtil.java	(revision 16139)
@@ -0,0 +1,242 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Helper class for VAJ tasks. Holds Workspace singleton and
+ * wraps IvjExceptions into BuildExceptions
+ *
+ */
+class VAJRemoteUtil implements VAJUtil {
+    // calling task
+    Task caller;
+
+    // VAJ remote tool server
+    String remoteServer;
+
+    public VAJRemoteUtil(Task caller, String remote) {
+        this.caller = caller;
+        this.remoteServer = remote;
+    }
+
+    /**
+     * export the array of Packages
+     */
+    public void exportPackages(File destDir,
+                               String[] includePatterns, String[] excludePatterns,
+                               boolean exportClasses, boolean exportDebugInfo,
+                               boolean exportResources, boolean exportSources,
+                               boolean useDefaultExcludes, boolean overwrite) {
+        try {
+            String request = "http://" + remoteServer + "/servlet/vajexport";
+            Vector parameters = new Vector();
+            parameters.addElement(new URLParam(VAJExportServlet.WITH_DEBUG_INFO, exportDebugInfo));
+            parameters.addElement(new URLParam(VAJExportServlet.OVERWRITE_PARAM, overwrite));
+            assembleImportExportParams(parameters, destDir,
+                                       includePatterns, excludePatterns,
+                                       exportClasses, exportResources,
+                                       exportSources, useDefaultExcludes);
+            sendRequest(request, parameters);
+        } catch (Exception ex) {
+            throw new BuildException(ex);
+        }
+    }
+
+    /**
+     * Do the import.
+     */
+    public void importFiles(
+                            String importProject, File srcDir,
+                            String[] includePatterns, String[] excludePatterns,
+                            boolean importClasses, boolean importResources,
+                            boolean importSources, boolean useDefaultExcludes) {
+        try {
+            String request = "http://" + remoteServer + "/servlet/vajimport";
+            Vector parameters = new Vector();
+            parameters.addElement(new
+                                  URLParam(VAJImportServlet.PROJECT_NAME_PARAM, importProject));
+            assembleImportExportParams(parameters, srcDir,
+                                       includePatterns, excludePatterns,
+                                       importClasses, importResources,
+                                       importSources, useDefaultExcludes);
+            sendRequest(request, parameters);
+        } catch (Exception ex) {
+            throw new BuildException(ex);
+        }
+
+    }
+
+    /**
+     * Add parameters common for import and export to vector
+     * Helper method to remove double code.
+     */
+    private void assembleImportExportParams(Vector parameters,
+                                            File dir,
+                                            String[] includePatterns, String[] excludePatterns,
+                                            boolean includeClasses, boolean includeResources,
+                                            boolean includeSources, boolean useDefaultExcludes) {
+        parameters.addElement(new URLParam(VAJToolsServlet.DIR_PARAM, dir.getPath()));
+        parameters.addElement(new URLParam(VAJToolsServlet.CLASSES_PARAM, includeClasses));
+        parameters.addElement(new URLParam(VAJToolsServlet.RESOURCES_PARAM, includeResources));
+        parameters.addElement(new URLParam(VAJToolsServlet.SOURCES_PARAM, includeSources));
+        parameters.addElement(new URLParam(VAJToolsServlet.DEFAULT_EXCLUDES_PARAM, useDefaultExcludes));
+
+        if (includePatterns != null) {
+            for (int i = 0; i < includePatterns.length; i++) {
+                parameters.addElement(new
+                                      URLParam(VAJExportServlet.INCLUDE_PARAM, includePatterns[i]));
+            }
+        }
+        if (excludePatterns != null) {
+            for (int i = 0; i < excludePatterns.length; i++) {
+                parameters.addElement(new
+                                      URLParam(VAJExportServlet.EXCLUDE_PARAM, excludePatterns[i]));
+            }
+        }
+    }
+
+    /**
+     * Load specified projects.
+     */
+    public void loadProjects(Vector projectDescriptions) {
+        try {
+            String request = "http://" + remoteServer + "/servlet/vajload";
+            Vector parameters = new Vector();
+            for (Enumeration e = projectDescriptions.elements(); e.hasMoreElements();) {
+                VAJProjectDescription pd = (VAJProjectDescription) e.nextElement();
+                parameters.addElement(new
+                                      URLParam(VAJLoadServlet.PROJECT_NAME_PARAM, pd.getName()));
+                parameters.addElement(new
+                                      URLParam(VAJLoadServlet.VERSION_PARAM, pd.getVersion())); 
+            }
+            sendRequest(request, parameters);
+        } catch (Exception ex) {
+            throw new BuildException(ex);
+        }
+    }
+
+    /**
+     * logs a message.
+     */
+    public void log(String msg, int level) {
+        caller.log(msg, level);
+    }
+
+    
+    private class URLParam {
+        private String name;
+        private String value;
+        public URLParam(String name, String value) {
+            this.name = name;
+            this.value = value;
+        }
+        public URLParam(String name, boolean value) {
+            this.name = name;
+            this.value = (new Boolean(value)).toString();
+        }
+        public void setValue(String value) { this.value = value; }
+        public void setName(String name) { this.name = name; }
+        public String getName() { return name; }
+        public String getValue() { return value; }
+    }
+    
+    /**
+     * Sends a servlet request.
+     *
+     * The passed URL and parameter list are combined into a
+     * valid URL (with proper URL encoding for the parameters)
+     * and the URL is requested.
+     *
+     * @param request Request URL without trailing characters (no ?)
+     * @param parameters Vector of URLParam objects to append as parameters.
+     */
+    private void sendRequest(String request, Vector parameters) {
+        boolean requestFailed = false;
+        
+        // Build request & URL encode parameters
+        String url = request + "?";
+        for (int i=0; i<parameters.size(); i++) {
+            URLParam p = (URLParam)parameters.elementAt(i);
+            url += p.getName() + "=" + URLEncoder.encode(p.getValue());
+            url += (i==parameters.size()-1)?"":"&";
+        }
+
+
+        try {
+            log("Request: " + url, MSG_DEBUG);
+
+            //must be HTTP connection
+            URL requestUrl = new URL(url);
+            HttpURLConnection connection =
+                (HttpURLConnection) requestUrl.openConnection();
+
+            InputStream is = null;
+            // retry three times
+            for (int i = 0; i < 3; i++) {
+                try {
+                    is = connection.getInputStream();
+                    break;
+                } catch (IOException ex) {
+                    // ignore
+                }
+            }
+            if (is == null) {
+                log("Can't get " + url, MSG_ERR);
+                throw new BuildException("Couldn't execute " + url);
+            }
+
+            // log the response
+            BufferedReader br = new BufferedReader(new InputStreamReader(is));
+            String line = br.readLine();
+            while (line != null) {
+                int level = MSG_ERR;
+                try {
+                    // the first char of each line contains the log level
+                    level = Integer.parseInt(line.substring(0, 1));
+                    if (level == MSG_ERR) {
+                        requestFailed = true;
+                    }
+                } catch (Exception e) {
+                    log("Response line doesn't contain log level!", MSG_ERR);
+                }
+                log(line.substring(2), level);
+                line = br.readLine();
+            }
+
+        } catch (IOException ex) {
+            log("Error sending tool request to VAJ" + ex, MSG_ERR);
+            throw new BuildException("Couldn't execute " + url);
+        }
+        if (requestFailed) {
+            throw new BuildException("VAJ tool request failed");
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJTask.java	(revision 16139)
@@ -0,0 +1,81 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+/**
+ * Super class for all VAJ tasks. Contains common
+ * attributes (remoteServer) and util methods
+ *
+ */
+import org.apache.tools.ant.Task;
+
+
+public class VAJTask extends Task {
+    /**
+     * Adaption of VAJLocalUtil to Task context.
+     */
+    class VAJLocalToolUtil extends VAJLocalUtil {
+        public void log(String msg, int level) {
+            VAJTask.this.log(msg, level);
+        }
+    }
+
+    // server name / port of VAJ remote tool api server
+    protected String remoteServer = null;
+
+    // holds the appropriate VAJUtil implementation
+    private VAJUtil util = null;
+
+    // checks if this task throws BuildException on error
+    protected boolean haltOnError = true;
+
+    /**
+     * returns the VAJUtil implementation
+     */
+    protected VAJUtil getUtil() {
+        if (util == null) {
+            if (remoteServer == null) {
+                util = new VAJLocalToolUtil();
+            } else {
+                util = new VAJRemoteUtil(this, remoteServer);
+            }
+        }
+        return util;
+    }
+
+    /**
+     * Name and port of a remote tool server, optiona.
+     * Format: &lt;servername&gt;:&lt;port no&gt;.
+     * If this attribute is set, the tasks will be executed on the specified tool
+     * server.
+     */
+    public void setRemote(String remoteServer) {
+        this.remoteServer = remoteServer;
+    }
+
+    /**
+    * Flag to control behaviour in case of VAJ errors.
+    * If this attribute is set errors will be ignored
+    * (no BuildException will be thrown) otherwise
+    * VAJ errors will be wrapped into a BuildException and
+    * stop the build.
+    */
+    public void setHaltonerror(boolean newHaltOnError) {
+        haltOnError = newHaltOnError;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJToolsServlet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJToolsServlet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJToolsServlet.java	(revision 16139)
@@ -0,0 +1,182 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+import com.ibm.ivj.toolserver.servletclasses.servlet.ServletException;
+import com.ibm.ivj.toolserver.servletclasses.servlet.http.HttpServlet;
+import com.ibm.ivj.toolserver.servletclasses.servlet.http.HttpServletRequest;
+import com.ibm.ivj.toolserver.servletclasses.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Abstract base class to provide common services for the
+ * VAJ tool API servlets
+ *
+ */
+public abstract class VAJToolsServlet extends HttpServlet {
+    /**
+     * Adaptation of VAJUtil for servlet context.
+     */
+    class VAJLocalServletUtil extends VAJLocalUtil {
+        public void log(String msg, int level) {
+            try {
+                if (msg != null) {
+                    msg = msg.replace('\r', ' ');
+                    int i = 0;
+                    while (i < msg.length()) {
+                        int nlPos = msg.indexOf('\n', i);
+                        if (nlPos == -1) {
+                            nlPos = msg.length();
+                        }
+                        response.getWriter().println(Integer.toString(level)
+                                                     + " " + msg.substring(i, nlPos));
+                        i = nlPos + 1;
+                    }
+                }
+            } catch (IOException e) {
+                throw new BuildException("logging failed. msg was: "
+                                         + e.getMessage());
+            }
+        }
+    }
+
+    // constants for servlet param names
+    public static final String DIR_PARAM = "dir";
+    public static final String INCLUDE_PARAM = "include";
+    public static final String EXCLUDE_PARAM = "exclude";
+    public static final String CLASSES_PARAM = "cls";
+    public static final String SOURCES_PARAM = "src";
+    public static final String RESOURCES_PARAM = "res";
+    public static final String DEFAULT_EXCLUDES_PARAM = "dex";
+    public static final String PROJECT_NAME_PARAM = "project";
+
+
+    // current request
+    HttpServletRequest  request;
+
+    // response to current request
+    HttpServletResponse response;
+
+    // implementation of VAJUtil used by the servlet
+    VAJUtil util;
+
+
+    /**
+     * Execute the request by calling the appropriate
+     * VAJ tool API methods. This method must be implemented
+     * by the concrete servlets
+     */
+    protected abstract void executeRequest();
+
+    /**
+     * Respond to a HTTP request. This method initializes
+     * the servlet and handles errors.
+     * The real work is done in the abstract method executeRequest()
+     */
+    public void doGet(HttpServletRequest req, HttpServletResponse res)
+        throws ServletException, IOException {
+        try {
+            response = res;
+            request = req;
+            initRequest();
+            executeRequest();
+        } catch (BuildException e) {
+            util.log("Error occurred: " + e.getMessage(), VAJUtil.MSG_ERR);
+        } catch (Exception e) {
+            try {
+                if (!(e instanceof BuildException)) {
+                    String trace = StringUtils.getStackTrace(e);
+                    util.log("Program error in " + this.getClass().getName()
+                             + ":\n" + trace, VAJUtil.MSG_ERR);
+                }
+            } catch (Throwable t) {
+                t.printStackTrace();
+            } finally {
+                if (!(e instanceof BuildException)) {
+                    throw new ServletException(e.getMessage());
+                }
+            }
+        }
+    }
+
+    /**
+     * initialize the servlet.
+     */
+    protected void initRequest() throws IOException {
+        response.setContentType("text/ascii");
+        if (util == null) {
+            util = new VAJLocalServletUtil();
+        }
+    }
+
+    /**
+     * Get the VAJUtil implementation
+     */
+    VAJUtil getUtil() {
+        return util;
+    }
+
+    /**
+     * Get the boolean value of a parameter.
+     */
+    protected boolean getBooleanParam(String param) {
+        return getBooleanParam(param, false);
+    }
+
+    /**
+     * Get the boolean value of a parameter, with a default value if
+     * the parameter hasn't been passed to the servlet.
+     */
+    protected boolean getBooleanParam(String param, boolean defaultValue) {
+        String value = getFirstParamValueString(param);
+        if (value != null) {
+            return toBoolean(value);
+        } else {
+            return defaultValue;
+        }
+    }
+
+    /**
+     * Returns the first encountered value for a parameter.
+     */
+    protected String getFirstParamValueString(String param) {
+        String[] paramValuesArray = request.getParameterValues(param);
+        if (paramValuesArray == null) {
+            return null;
+        }
+        return paramValuesArray[0];
+    }
+
+    /**
+     * Returns all values for a parameter.
+     */
+    protected String[] getParamValues(String param) {
+        return request.getParameterValues(param);
+    }
+
+    /**
+     * A utility method to translate the strings "yes", "true", and "ok"
+     * to boolean true, and everything else to false.
+     */
+    protected boolean toBoolean(String string) {
+        String lower = string.toLowerCase();
+        return (lower.equals("yes") || lower.equals("true") || lower.equals("ok"));
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJUtil.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJUtil.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJUtil.java	(revision 16139)
@@ -0,0 +1,64 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+import java.io.File;
+import java.util.Vector;
+
+/**
+ * Helper interface for VAJ tasks. Encapsulates
+ * the interface to the VAJ tool API.
+ *
+ */
+interface VAJUtil {
+    // log levels
+    static final int MSG_DEBUG = 4;
+    static final int MSG_ERR = 0;
+    static final int MSG_INFO = 2;
+    static final int MSG_VERBOSE = 3;
+    static final int MSG_WARN = 1;
+
+    /**
+     * export the array of Packages
+     */
+    void exportPackages(
+        File dest,
+        String[] includePatterns, String[] excludePatterns,
+        boolean exportClasses, boolean exportDebugInfo,
+        boolean exportResources, boolean exportSources,
+        boolean useDefaultExcludes, boolean overwrite);
+
+    /**
+     * Do the import.
+     */
+    void importFiles(
+        String importProject, File srcDir,
+        String[] includePatterns, String[] excludePatterns,
+        boolean importClasses, boolean importResources,
+        boolean importSources, boolean useDefaultExcludes);
+
+    /**
+     * Load specified projects.
+     */
+    void loadProjects(Vector projectDescriptions);
+
+    /**
+     * Logs a message with the specified log level.
+     */
+    void log(String msg, int level);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJWorkspaceScanner.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJWorkspaceScanner.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/VAJWorkspaceScanner.java	(revision 16139)
@@ -0,0 +1,187 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.ide;
+
+import com.ibm.ivj.util.base.IvjException;
+import com.ibm.ivj.util.base.Package;
+import com.ibm.ivj.util.base.Project;
+import java.io.File;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import org.apache.tools.ant.DirectoryScanner;
+
+/**
+ * Class for scanning a Visual Age for Java workspace for packages matching
+ * a certain criteria.
+ * <p>
+ * These criteria consist of a set of include and exclude patterns. With these
+ * patterns, you can select which packages you want to have included, and which
+ * packages you want to have excluded.  You can add patterns to be excluded by
+ * default with the addDefaultExcludes method.  The patters that are excluded
+ * by default include
+ * <ul>
+ *   <li>IBM*\**</li>
+ *   <li>Java class libraries\**</li>
+ *   <li>Sun class libraries*\**</li>
+ *   <li>JSP Page Compile Generated Code\**</li>
+ *   <li>VisualAge*\**</li>
+ * </ul>
+ * <p>
+ * This class works like DirectoryScanner.
+ *
+ * @see org.apache.tools.ant.DirectoryScanner
+ *
+ */
+class VAJWorkspaceScanner extends DirectoryScanner {
+
+    // Patterns that should be excluded by default.
+    private static final String[] DEFAULTEXCLUDES = {
+        "IBM*/**",
+        "Java class libraries/**",
+        "Sun class libraries*/**",
+        "JSP Page Compile Generated Code/**",
+        "VisualAge*/**",
+    };
+
+    // The packages that where found and matched at least
+    // one includes, and matched no excludes.
+    private Vector packagesIncluded = new Vector();
+
+    /**
+     * Adds the array with default exclusions to the current exclusions set.
+     */
+    public void addDefaultExcludes() {
+        int excludesLength = excludes == null ? 0 : excludes.length;
+        String[] newExcludes;
+        newExcludes = new String[excludesLength + DEFAULTEXCLUDES.length];
+        if (excludesLength > 0) {
+            System.arraycopy(excludes, 0, newExcludes, 0, excludesLength);
+        }
+        for (int i = 0; i < DEFAULTEXCLUDES.length; i++) {
+            newExcludes[i + excludesLength] = DEFAULTEXCLUDES[i].
+                replace('/', File.separatorChar).
+                replace('\\', File.separatorChar);
+        }
+        excludes = newExcludes;
+    }
+
+    /**
+     * Finds all Projects specified in include patterns.
+     *
+     * @return the projects
+     */
+    public Vector findMatchingProjects() {
+        Project[] projects = VAJLocalUtil.getWorkspace().getProjects();
+
+        Vector matchingProjects = new Vector();
+
+        boolean allProjectsMatch = false;
+        for (int i = 0; i < projects.length; i++) {
+            Project project = projects[i];
+            for (int j = 0; j < includes.length && !allProjectsMatch; j++) {
+                StringTokenizer tok =
+                    new StringTokenizer(includes[j], File.separator);
+                String projectNamePattern = tok.nextToken();
+                if (projectNamePattern.equals("**")) {
+                    // if an include pattern starts with '**',
+                    // all projects match
+                    allProjectsMatch = true;
+                } else
+                    if (match(projectNamePattern, project.getName())) {
+                        matchingProjects.addElement(project);
+                        break;
+                    }
+            }
+        }
+
+        if (allProjectsMatch) {
+            matchingProjects = new Vector();
+            for (int i = 0; i < projects.length; i++) {
+                matchingProjects.addElement(projects[i]);
+            }
+        }
+
+        return matchingProjects;
+    }
+
+    /**
+     * Get the names of the packages that matched at least one of the include
+     * patterns, and didn't match one of the exclude patterns.
+     *
+     * @return the matching packages
+     */
+    public Package[] getIncludedPackages() {
+        int count = packagesIncluded.size();
+        Package[] packages = new Package[count];
+        for (int i = 0; i < count; i++) {
+            packages[i] = (Package) packagesIncluded.elementAt(i);
+        }
+        return packages;
+    }
+
+    /**
+     * Scans the workspace for packages that match at least one include
+     * pattern, and don't match any exclude patterns.
+     *
+     */
+    public void scan() {
+        if (includes == null) {
+            // No includes supplied, so set it to 'matches all'
+            includes = new String[1];
+            includes[0] = "**";
+        }
+        if (excludes == null) {
+            excludes = new String[0];
+        }
+
+        // only scan projects which are included in at least one include pattern
+        Vector matchingProjects = findMatchingProjects();
+        for (Enumeration e = matchingProjects.elements(); e.hasMoreElements();) {
+            Project project = (Project) e.nextElement();
+            scanProject(project);
+        }
+    }
+
+    /**
+     * Scans a project for packages that match at least one include
+     * pattern, and don't match any exclude patterns.
+     *
+     */
+    public void scanProject(Project project) {
+        try {
+            Package[] packages = project.getPackages();
+            if (packages != null) {
+                for (int i = 0; i < packages.length; i++) {
+                    Package item = packages[i];
+                    // replace '.' by file seperator because the patterns are
+                    // using file seperator syntax (and we can use the match
+                    // methods this way).
+                    String name =
+                        project.getName()
+                        + File.separator
+                        + item.getName().replace('.', File.separatorChar);
+                    if (isIncluded(name) && !isExcluded(name)) {
+                        packagesIncluded.addElement(item);
+                    }
+                }
+            }
+        } catch (IvjException e) {
+            throw VAJLocalUtil.createBuildException("VA Exception occurred: ", e);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/default.ini
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/default.ini	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ide/default.ini	(revision 16139)
@@ -0,0 +1,4 @@
+Name=Ant
+Version=0.1
+Help-Item=Ant Help,doc/VAJAntTool.html
+Menu-Items=Ant Build,org.apache.tools.ant.taskdefs.optional.ide.VAJAntTool,-P;
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/image/Image.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/image/Image.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/image/Image.java	(revision 16139)
@@ -0,0 +1,300 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.image;
+
+import com.sun.media.jai.codec.FileSeekableStream;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.optional.image.Draw;
+import org.apache.tools.ant.types.optional.image.ImageOperation;
+import org.apache.tools.ant.types.optional.image.Rotate;
+import org.apache.tools.ant.types.optional.image.Scale;
+import org.apache.tools.ant.types.optional.image.TransformOperation;
+
+import javax.media.jai.JAI;
+import javax.media.jai.PlanarImage;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Vector;
+
+/**
+ * A MatchingTask which relies on
+ * <A HREF="http://java.sun.com/products/java-media/jai">JAI (Java Advanced Imaging)</A>
+ * to perform image manipulation operations on existing images.  The
+ * operations are represented as ImageOperation DataType objects.
+ * The operations are arranged to conform to the Chaining Model
+ * of JAI.
+ * Check out the
+ * <A HREF="http://java.sun.com/products/java-media/jai/forDevelopers/jai1_0_1guide-unc/">
+ *   JAI Programming Guide</A>
+ *
+ * @see org.apache.tools.ant.types.optional.image.ImageOperation
+ * @see org.apache.tools.ant.types.DataType
+ */
+public class Image extends MatchingTask {
+    protected Vector instructions = new Vector();
+    protected String str_encoding = "JPEG";
+    protected boolean overwrite = false;
+    protected boolean garbage_collect = false;
+    private boolean failonerror = true;
+    protected Vector filesets = new Vector();
+
+
+    protected File srcDir = null;
+    protected File destDir = null;
+
+    /**
+     * Adds a set of files to be deleted.
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * If false, note errors to the output but keep going.
+     * @param failonerror true or false
+     */
+    public void setFailOnError(boolean failonerror) {
+        this.failonerror = failonerror;
+    }
+
+    /**
+     * Set the source dir to find the image files.
+     */
+    public void setSrcdir(File srcDir) {
+        this.srcDir = srcDir;
+    }
+
+    /**
+     * Set the image encoding type.
+     * <A HREF="http://java.sun.com/products/java-media/jai/forDevelopers/jai1_0_1guide-unc/Encode.doc.html#56610">
+     *     See this table in the JAI Programming Guide</A>.
+     */
+    public void setEncoding(String encoding) {
+        str_encoding = encoding;
+    }
+
+    /**
+     *  Sets whether or not to overwrite a file if there is a naming conflict.
+     */
+    public void setOverwrite(boolean overwrite) {
+        this.overwrite = overwrite;
+    }
+
+    /**
+     *  Enables Garbage Collection after each image processed.  Defaults to false.
+     */
+    public void setGc(boolean gc) {
+        garbage_collect = gc;
+    }
+
+
+    /**
+     * Sets the destination directory for manipulated images.
+     * @param destDir The destination directory
+     */
+    public void setDestDir(File destDir) {
+        this.destDir = destDir;
+    }
+
+    /**
+     * Adds an ImageOperation to chain.
+     * @param instr The ImageOperation to append to the chain
+     */
+    public void addImageOperation(ImageOperation instr) {
+        instructions.add(instr);
+    }
+
+    /**
+     * Adds a Rotate ImageOperation to the chain
+     * @param instr The Rotate operation to add to the chain
+     * @see org.apache.tools.ant.types.optional.image.Rotate
+     */
+    public void addRotate(Rotate instr) {
+        instructions.add(instr);
+    }
+
+    /**
+     * Adds a Scale ImageOperation to the chain
+     * @param instr The Scale operation to add to the chain
+     * @see org.apache.tools.ant.types.optional.image.Scale
+     */
+    public void addScale(Scale instr) {
+        instructions.add(instr);
+    }
+
+    /**
+     * Adds a Draw ImageOperation to the chain.  DrawOperation
+     * DataType objects can be nested inside the Draw object
+     * @param instr The Draw operation to add to the chain
+     * @see org.apache.tools.ant.types.optional.image.Draw
+     * @see org.apache.tools.ant.types.optional.image.DrawOperation
+     */
+    public void addDraw(Draw instr) {
+        instructions.add(instr);
+    }
+
+    /**
+     * Executes all the chained ImageOperations on the file
+     * specified.
+     * @param file The file to be processed
+     */
+    public void processFile(File file) {
+        try {
+            log("Processing File: " + file.getAbsolutePath());
+            FileSeekableStream input = new FileSeekableStream(file);
+            PlanarImage image = JAI.create("stream", input);
+            for (int i = 0; i < instructions.size(); i++) {
+                Object instr = instructions.elementAt(i);
+                if (instr instanceof TransformOperation) {
+                    image = ((TransformOperation) instr).executeTransformOperation(image);
+                } else {
+                    log("Not a TransformOperation: " + instr);
+                }
+            }
+            input.close();
+
+            if (str_encoding.toLowerCase().equals("jpg")) {
+                str_encoding = "JPEG";
+            } else if (str_encoding.toLowerCase().equals("tif")) {
+                str_encoding = "TIFF";
+            }
+
+            if (destDir == null) {
+                destDir = srcDir;
+            }
+
+            File new_file = new File(destDir.getAbsolutePath() + File.separator + file.getName());
+
+            if ((overwrite && new_file.exists()) && (!new_file.equals(file))) {
+                new_file.delete();
+            }
+
+            FileOutputStream stream = new FileOutputStream(new_file);
+
+            JAI.create("encode", image, stream, str_encoding.toUpperCase(), null);
+            stream.flush();
+            stream.close();
+
+
+        } catch (IOException err) {
+            if (!failonerror) {
+                log("Error processing file:  " + err);
+            } else {
+                throw new BuildException(err);
+            }
+        } catch (java.lang.RuntimeException rerr) {
+            if (!failonerror) {
+                log("Error processing file:  " + rerr);
+            } else {
+                throw new BuildException(rerr);
+            }
+        }
+
+    }
+
+    /**
+     * Executes the Task
+     */
+    public void execute() throws BuildException {
+
+        validateAttributes();
+
+        try {
+            DirectoryScanner ds = null;
+            String[] files = null;
+            ArrayList filesList = new ArrayList();
+
+
+            // deal with specified srcDir
+            if (srcDir != null) {
+                ds = super.getDirectoryScanner(srcDir);
+
+                files = ds.getIncludedFiles();
+                for (int i = 0; i < files.length; i++) {
+                    filesList.add(new File(srcDir.getAbsolutePath() + File.separator + files[i]));
+                }
+            }
+            // deal with the filesets
+            for (int i = 0; i < filesets.size(); i++) {
+                FileSet fs = (FileSet) filesets.elementAt(i);
+                ds = fs.getDirectoryScanner(getProject());
+                files = ds.getIncludedFiles();
+                File fromDir = fs.getDir(getProject());
+                for (int j = 0; j < files.length; j++) {
+                    filesList.add(new File(fromDir.getAbsolutePath() + File.separator + files[j]));
+                }
+            }
+
+            if (!overwrite) {
+                // remove any files that shouldn't be overwritten.
+                ArrayList filesToRemove = new ArrayList();
+                for (Iterator i = filesList.iterator(); i.hasNext();) {
+                    File f = (File) i.next();
+                    File new_file = new File(destDir.getAbsolutePath()
+                        + File.separator + f.getName());
+                    if (new_file.exists()) {
+                        filesToRemove.add(f);
+                    }
+                }
+                filesList.removeAll(filesToRemove);
+            }
+
+
+            // iterator through all the files and process them.
+            for (Iterator i = filesList.iterator(); i.hasNext();) {
+                File file = (File) i.next();
+
+                processFile(file);
+                if (garbage_collect) {
+                    System.gc();
+                }
+            }
+
+
+        } catch (Exception err) {
+            err.printStackTrace();
+            throw new BuildException(err.getMessage());
+        }
+    }
+
+
+    /**
+     * Ensure we have a consistent and legal set of attributes, and set
+     * any internal flags necessary based on different combinations
+     * of attributes.
+     */
+    protected void validateAttributes() throws BuildException {
+        if (srcDir == null && filesets.size() == 0) {
+            throw new BuildException("Specify at least one source "
+                    + "- a srcDir or a fileset.");
+        }
+
+        if (srcDir == null && destDir == null) {
+            throw new BuildException("Specify the destDir, or the srcDir.");
+        }
+
+
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/AbstractHotDeploymentTool.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/AbstractHotDeploymentTool.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/AbstractHotDeploymentTool.java	(revision 16139)
@@ -0,0 +1,193 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.j2ee;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Path;
+
+/**
+ *  Abstract class to support vendor-specific hot deployment tools.
+ *  This class will validate boilerplate attributes.
+ *
+ *  Subclassing this class for a vendor specific tool involves the
+ *  following.
+ *  <ol><li>Implement the <code>isActionValid()<code> method to insure the
+ *  action supplied as the "action" attribute of ServerDeploy is valid.
+ *  <li>Implement the <code>validateAttributes()</code> method to insure
+ *  all required attributes are supplied, and are in the correct format.
+ *  <li>Add a <code>add&lt;TOOL&gt;</code> method to the ServerDeploy
+ *  class.  This method will be called when Ant encounters a
+ *  <code>add&lt;TOOL&gt;</code> task nested in the
+ *  <code>serverdeploy</code> task.
+ *  <li>Define the <code>deploy</code> method.  This method should perform
+ *  whatever task it takes to hot-deploy the component.  IE: spawn a JVM and
+ *  run class, exec a native executable, run Java code...
+ *
+ *
+ *  @see org.apache.tools.ant.taskdefs.optional.j2ee.HotDeploymentTool
+ *  @see org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy
+ */
+public abstract class AbstractHotDeploymentTool implements HotDeploymentTool {
+    /** The parent task **/
+    private ServerDeploy task;
+
+    /** The classpath passed to the JVM on execution. **/
+    private Path classpath;
+
+    /** The username for the deployment server. **/
+    private String userName;
+
+    /** The password for the deployment server. **/
+    private String password;
+
+    /** The address of the deployment server **/
+    private String server;
+
+    /**
+     *  Add a classpath as a nested element.
+     *  @return A Path object representing the classpath to be used.
+     */
+    public Path createClasspath() {
+        if (classpath == null) {
+            classpath = new Path(task.getProject());
+        }
+        return classpath.createPath();
+    }
+
+    /**
+     *  Determines if the "action" attribute defines a valid action.
+     *  <p>Subclasses should determine if the action passed in is
+     *  supported by the vendor's deployment tool.
+     *  <p>Actions may by "deploy", "delete", etc... It all depends
+     *  on the tool.
+     *  @return true if the "action" attribute is valid, false if not.
+     */
+    protected abstract boolean isActionValid();
+
+    /**
+     *  Validates the passed in attributes.
+     *  Subclasses should chain to this super-method to insure
+     *  validation of boilerplate attributes.
+     *  <p>Only the "action" attribute is required in the
+     *  base class.  Subclasses should check attributes accordingly.
+     *  @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete.
+     */
+    public void validateAttributes() throws BuildException {
+        if (task.getAction() == null) {
+            throw new BuildException("The \"action\" attribute must be set");
+        }
+
+        if (!isActionValid()) {
+            throw new BuildException("Invalid action \"" + task.getAction() + "\" passed");
+        }
+
+        if (classpath == null) {
+            throw new BuildException("The classpath attribute must be set");
+        }
+    }
+
+    /**
+     *  Perform the actual deployment.
+     *  It's up to the subclasses to implement the actual behavior.
+     *  @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete.
+     */
+    public abstract void deploy() throws BuildException;
+
+    /**
+     *  Sets the parent task.
+     *  @param task a ServerDeploy object representing the parent task.
+     *  @ant.attribute ignore="true"
+     */
+    public void setTask(ServerDeploy task) {
+        this.task = task;
+    }
+
+    /**
+     *  Returns the task field, a ServerDeploy object.
+     *  @return An ServerDeploy representing the parent task.
+     */
+    protected ServerDeploy getTask() {
+        return task;
+    }
+
+    /**
+     *  gets the classpath field.
+     *  @return A Path representing the "classpath" attribute.
+     */
+    public Path getClasspath() {
+        return classpath;
+    }
+
+    /**
+     *  The classpath to be passed to the JVM running the tool;
+     *  optional depending upon the tool.
+     *  The classpath may also be supplied as a nested element.
+     *  @param classpath A Path object representing the "classpath" attribute.
+     */
+    public void setClasspath(Path classpath) {
+        this.classpath = classpath;
+    }
+
+    /**
+     *  Returns the userName field.
+     *  @return A String representing the "userName" attribute.
+     */
+    public String getUserName() {
+        return userName;
+    }
+
+    /**
+     *  The user with privileges to deploy applications to the server; optional.
+     *  @param userName A String representing the "userName" attribute.
+     */
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    /**
+     *  Returns the password field.
+     *  @return A String representing the "password" attribute.
+     */
+    public String getPassword() {
+        return password;
+    }
+
+    /**
+     *  The password of the user; optional.
+     *  @param password A String representing the "password" attribute.
+     */
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    /**
+     *  Returns the server field.
+     *  @return A String representing the "server" attribute.
+     */
+    public String getServer() {
+        return server;
+    }
+
+    /**
+     *  The address or URL for the server where the component will be deployed.
+     *  @param server A String representing the "server" attribute.
+     */
+    public void setServer(String server) {
+        this.server = server;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/GenericHotDeploymentTool.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/GenericHotDeploymentTool.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/GenericHotDeploymentTool.java	(revision 16139)
@@ -0,0 +1,135 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.j2ee;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ *  A generic tool for J2EE server hot deployment.
+ *  <p>The simple implementation spawns a JVM with the supplied
+ *  class name, jvm args, and arguments.
+ *
+ *
+ *  @see org.apache.tools.ant.taskdefs.optional.j2ee.HotDeploymentTool
+ *  @see org.apache.tools.ant.taskdefs.optional.j2ee.AbstractHotDeploymentTool
+ *  @see org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy
+ */
+public class GenericHotDeploymentTool extends AbstractHotDeploymentTool {
+    /** A Java task used to run the deployment tool **/
+    private Java java;
+
+    /** The fully qualified class name of the deployment tool **/
+    private String className;
+
+    /** List of valid actions **/
+    private static final String[] VALID_ACTIONS = {ACTION_DEPLOY};
+
+    /**
+     *  Add a nested argument element to hand to the deployment tool; optional.
+     *  @return A Commandline.Argument object representing the
+     *  command line argument being passed when the deployment
+     *  tool is run.  IE: "-user=mark", "-password=venture"...
+     */
+    public Commandline.Argument createArg() {
+        return java.createArg();
+    }
+
+    /**
+     *  Add a nested argment element to hand to the JVM running the
+     *  deployment tool.
+     *  Creates a nested arg element.
+     *  @return A Commandline.Argument object representing the
+     *  JVM command line argument being passed when the deployment
+     *  tool is run.  IE: "-ms64m", "-mx128m"...
+     */
+    public Commandline.Argument createJvmarg() {
+        return java.createJvmarg();
+    }
+
+    /**
+     *  Determines if the "action" attribute defines a valid action.
+     *  <p>Subclasses should determine if the action passed in is
+     *  supported by the vendor's deployment tool.
+     *  For this generic implementation, the only valid action is "deploy"
+     *  @return true if the "action" attribute is valid, false if not.
+     */
+    protected boolean isActionValid() {
+        return (getTask().getAction().equals(VALID_ACTIONS[0]));
+    }
+
+    /**
+     *  Sets the parent task.
+     *  @param task An ServerDeploy object representing the parent task.
+     *  @ant.attribute ignored="true"
+     */
+    public void setTask(ServerDeploy task) {
+        super.setTask(task);
+        java = (Java) task.getProject().createTask("java");
+    }
+
+    /**
+     *  Perform the actual deployment.
+     *  For this generic implementation, a JVM is spawned using the
+     *  supplied classpath, classname, JVM args, and command line arguments.
+     *  @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete.
+     */
+    public void deploy() throws BuildException {
+        java.setClassname(className);
+        java.setClasspath(getClasspath());
+        java.setFork(true);
+        java.setFailonerror(true);
+        java.execute();
+    }
+
+    /**
+     *  Validates the passed in attributes.
+     *  Ensures the className and arguments attribute have been set.
+     *  @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete.
+     */
+    public void validateAttributes() throws BuildException {
+        super.validateAttributes();
+
+        if (className == null) {
+            throw new BuildException("The classname attribute must be set");
+        }
+    }
+
+    /**
+     *  The name of the class to execute to perfom
+     *  deployment; required.
+     *  Example: "com.foobar.tools.deploy.DeployTool"
+     *  @param className The fully qualified class name of the class
+     *  to perform deployment.
+     */
+    public void setClassName(String className) {
+        this.className = className;
+    }
+
+    /**
+     *
+     */
+    public Java getJava() {
+        return java;
+    }
+
+    public String getClassName() {
+        return className;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/HotDeploymentTool.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/HotDeploymentTool.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/HotDeploymentTool.java	(revision 16139)
@@ -0,0 +1,61 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.j2ee;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ *  An interface for vendor-specific "hot" deployment tools.
+ *
+ *
+ *  @see org.apache.tools.ant.taskdefs.optional.j2ee.AbstractHotDeploymentTool
+ *  @see org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy
+ */
+public interface HotDeploymentTool {
+    /** The delete action String **/
+    public static final String ACTION_DELETE = "delete";
+
+    /** The deploy action String **/
+    public static final String ACTION_DEPLOY = "deploy";
+
+    /** The list action String **/
+    public static final String ACTION_LIST = "list";
+
+    /** The undeploy action String **/
+    public static final String ACTION_UNDEPLOY = "undeploy";
+
+    /** The update action String **/
+    public static final String ACTION_UPDATE = "update";
+
+    /**
+     *  Validates the passed in attributes.
+     *  @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete.
+     */
+    public void validateAttributes() throws BuildException;
+
+    /**
+     *  Perform the actual deployment.
+     *  @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete.
+     */
+    public void deploy() throws BuildException;
+
+    /**
+     *  Sets the parent task.
+     *  @param task A ServerDeploy object representing the parent task.
+     */
+    public void setTask(ServerDeploy task);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/JonasHotDeploymentTool.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/JonasHotDeploymentTool.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/JonasHotDeploymentTool.java	(revision 16139)
@@ -0,0 +1,250 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.j2ee;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.types.Path;
+
+/**
+ *  An Ant wrapper task for the weblogic.deploy tool. This is used
+ *  to hot-deploy J2EE applications to a running WebLogic server.
+ *  This is <b>not</b> the same as creating the application
+ *  archive. This task assumes the archive (EAR, JAR, or WAR) file
+ *  has been assembled and is supplied as the "source" attribute.
+ *  <p>
+ *
+ *  In the end, this task assembles the commadline parameters and
+ *  runs the weblogic.deploy tool in a seperate JVM.
+ *
+ *@see org.apache.tools.ant.taskdefs.optional.j2ee.HotDeploymentTool
+ *@see org.apache.tools.ant.taskdefs.optional.j2ee.AbstractHotDeploymentTool
+ *@see org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy
+ */
+public class JonasHotDeploymentTool extends GenericHotDeploymentTool implements HotDeploymentTool {
+
+    /**
+     *  Description of the Field
+     */
+    protected static final String DEFAULT_ORB = "RMI";
+
+    /**
+     *  The classname of the tool to run *
+     */
+    private static final String JONAS_DEPLOY_CLASS_NAME = "org.objectweb.jonas.adm.JonasAdmin";
+
+    /**
+     *  All the valid actions that weblogic.deploy permits *
+     */
+    private static final String[] VALID_ACTIONS
+        = {ACTION_DELETE, ACTION_DEPLOY, ACTION_LIST, ACTION_UNDEPLOY, ACTION_UPDATE};
+
+    /**
+     *  Description of the Field
+     */
+    private File jonasroot;
+
+    /**
+     *  Description of the Field
+     */
+    private String orb = null;
+
+    /**
+     *  Description of the Field
+     */
+    private String davidHost;
+
+    /**
+     *  Description of the Field
+     */
+    private int davidPort;
+
+
+    /**
+     *  Set the host for the David ORB; required if
+     *  ORB==david.
+     *
+     *@param  inValue  The new davidhost value
+     */
+    public void setDavidhost(final String inValue) {
+        davidHost = inValue;
+    }
+
+
+    /**
+     *  Set the port for the David ORB; required if
+     *  ORB==david.
+     *
+     *@param  inValue  The new davidport value
+     */
+    public void setDavidport(final int inValue) {
+        davidPort = inValue;
+    }
+
+
+    /**
+     *  set the jonas root directory (-Dinstall.root=). This
+     *  element is required.
+     *
+     *@param  inValue  The new jonasroot value
+     */
+    public void setJonasroot(final File inValue) {
+        jonasroot = inValue;
+    }
+
+
+    /**
+     *
+     * Choose your ORB : RMI, JEREMIE, DAVID, ...; optional.
+     * If omitted, it defaults
+     * to the one present in classpath. The corresponding JOnAS JAR is
+     * automatically added to the classpath. If your orb is DAVID (RMI/IIOP) you must
+     * specify davidhost and davidport properties.
+     *
+     *@param  inValue  RMI, JEREMIE, DAVID,...
+     */
+    public void setOrb(final String inValue) {
+        orb = inValue;
+    }
+
+
+    /**
+     *  gets the classpath field.
+     *
+     *@return    A Path representing the "classpath" attribute.
+     */
+    public Path getClasspath() {
+
+        Path aClassPath = super.getClasspath();
+
+        if (aClassPath == null) {
+            aClassPath = new Path(getTask().getProject());
+        }
+        if (orb != null) {
+            String aOrbJar = new File(jonasroot, "lib/" + orb + "_jonas.jar").toString();
+            String aConfigDir = new File(jonasroot, "config/").toString();
+            Path aJOnASOrbPath = new Path(aClassPath.getProject(),
+                    aOrbJar + File.pathSeparator + aConfigDir);
+            aClassPath.append(aJOnASOrbPath);
+        }
+        return aClassPath;
+    }
+
+
+    /**
+     *  Validates the passed in attributes. <p>
+     *
+     *  The rules are:
+     *  <ol>
+     *    <li> If action is "deploy" or "update" the "application"
+     *    and "source" attributes must be supplied.
+     *    <li> If action is "delete" or "undeploy" the
+     *    "application" attribute must be supplied.
+     *
+     *@exception  BuildException                       Description
+     *      of Exception
+     */
+    public void validateAttributes() throws BuildException {
+        // super.validateAttributes(); // don't want to call this method
+
+        Java java = getJava();
+
+        String action = getTask().getAction();
+        if (action == null) {
+            throw new BuildException("The \"action\" attribute must be set");
+        }
+
+        if (!isActionValid()) {
+            throw new BuildException("Invalid action \"" + action + "\" passed");
+        }
+
+        if (getClassName() == null) {
+            setClassName(JONAS_DEPLOY_CLASS_NAME);
+        }
+
+        if (jonasroot == null || jonasroot.isDirectory()) {
+            java.createJvmarg().setValue("-Dinstall.root=" + jonasroot);
+            java.createJvmarg().setValue("-Djava.security.policy=" + jonasroot
+                + "/config/java.policy");
+
+            if ("DAVID".equals(orb)) {
+                java.createJvmarg().setValue("-Dorg.omg.CORBA.ORBClass"
+                    + "=org.objectweb.david.libs.binding.orbs.iiop.IIOPORB");
+                java.createJvmarg().setValue("-Dorg.omg.CORBA.ORBSingletonClass="
+                    + "org.objectweb.david.libs.binding.orbs.ORBSingletonClass");
+                java.createJvmarg().setValue("-Djavax.rmi.CORBA.StubClass="
+                    + "org.objectweb.david.libs.stub_factories.rmi.StubDelegate");
+                java.createJvmarg().setValue("-Djavax.rmi.CORBA.PortableRemoteObjectClass="
+                    + "org.objectweb.david.libs.binding.rmi.ORBPortableRemoteObjectDelegate");
+                java.createJvmarg().setValue("-Djavax.rmi.CORBA.UtilClass="
+                    + "org.objectweb.david.libs.helpers.RMIUtilDelegate");
+                java.createJvmarg().setValue("-Ddavid.CosNaming.default_method=0");
+                java.createJvmarg().setValue("-Ddavid.rmi.ValueHandlerClass="
+                    + "com.sun.corba.se.internal.io.ValueHandlerImpl");
+                if (davidHost != null) {
+                    java.createJvmarg().setValue("-Ddavid.CosNaming.default_host="
+                        + davidHost);
+                }
+                if (davidPort != 0) {
+                    java.createJvmarg().setValue("-Ddavid.CosNaming.default_port="
+                        + davidPort);
+                }
+            }
+        }
+
+        if (getServer() != null) {
+            java.createArg().setLine("-n " + getServer());
+        }
+
+        if (action.equals(ACTION_DEPLOY)
+            || action.equals(ACTION_UPDATE)
+            || action.equals("redeploy")) {
+            java.createArg().setLine("-a " + getTask().getSource());
+        } else if (action.equals(ACTION_DELETE) || action.equals(ACTION_UNDEPLOY)) {
+            java.createArg().setLine("-r " + getTask().getSource());
+        } else if (action.equals(ACTION_LIST)) {
+            java.createArg().setValue("-l");
+        }
+    }
+
+
+    /**
+     *  Determines if the action supplied is valid. <p>
+     *
+     *  Valid actions are contained in the static array
+     *  VALID_ACTIONS
+     *
+     *@return    true if the action attribute is valid, false if
+     *      not.
+     */
+    protected boolean isActionValid() {
+        boolean valid = false;
+
+        String action = getTask().getAction();
+
+        for (int i = 0; i < VALID_ACTIONS.length; i++) {
+            if (action.equals(VALID_ACTIONS[i])) {
+                valid = true;
+                break;
+            }
+        }
+
+        return valid;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/ServerDeploy.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/ServerDeploy.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/ServerDeploy.java	(revision 16139)
@@ -0,0 +1,152 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.j2ee;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ *  Controls hot deployment tools for J2EE servers.
+ *
+ *  This class is used as a framework for the creation of vendor specific
+ *  hot deployment tools.
+ *
+ *
+ *  @see org.apache.tools.ant.taskdefs.optional.j2ee.HotDeploymentTool
+ *  @see org.apache.tools.ant.taskdefs.optional.j2ee.AbstractHotDeploymentTool
+ *  @see org.apache.tools.ant.taskdefs.optional.j2ee.GenericHotDeploymentTool
+ *  @see org.apache.tools.ant.taskdefs.optional.j2ee.WebLogicHotDeploymentTool
+ */
+public class ServerDeploy extends Task {
+    /** The action to be performed.  IE: "deploy", "delete", etc... **/
+    private String action;
+
+    /** The source (fully-qualified path) to the component being deployed **/
+    private File source;
+
+    /** The vendor specific tool for deploying the component **/
+    private Vector vendorTools = new Vector();
+
+    ///////////////////////////////////////////////////////////////////////////
+    //
+    // Place vendor specific tool creations here.
+    //
+    ///////////////////////////////////////////////////////////////////////////
+
+    /**
+     *  Creates a generic deployment tool.
+     *  <p>Ant calls this method on creation to handle embedded "generic" elements
+     *  in the ServerDeploy task.
+     *  @param tool An instance of GenericHotDeployment tool, passed in by Ant.
+     */
+    public void addGeneric(GenericHotDeploymentTool tool) {
+        tool.setTask(this);
+        vendorTools.addElement(tool);
+    }
+
+    /**
+     *  Creates a WebLogic deployment tool, for deployment to WebLogic servers.
+     *  <p>Ant calls this method on creation to handle embedded "weblogic" elements
+     *  in the ServerDeploy task.
+     *  @param tool An instance of WebLogicHotDeployment tool, passed in by Ant.
+     */
+    public void addWeblogic(WebLogicHotDeploymentTool tool) {
+        tool.setTask(this);
+        vendorTools.addElement(tool);
+    }
+
+    /**
+     *  Creates a JOnAS deployment tool, for deployment to JOnAS servers.
+     *  <p>Ant calls this method on creation to handle embedded "jonas" elements
+     *  in the ServerDeploy task.
+     *  @param tool An instance of JonasHotDeployment tool, passed in by Ant.
+     */
+    public void addJonas(JonasHotDeploymentTool tool) {
+        tool.setTask(this);
+        vendorTools.addElement(tool);
+    }
+
+
+    ///////////////////////////////////////////////////////////////////////////
+    //
+    // Execute method
+    //
+    ///////////////////////////////////////////////////////////////////////////
+
+    /**
+     *  Execute the task.
+     *  <p>This method calls the deploy() method on each of the vendor-specific tools
+     *  in the <code>vendorTools</code> collection.  This performs the actual
+     *  process of deployment on each tool.
+     *  @exception org.apache.tools.ant.BuildException if the attributes
+     *  are invalid or incomplete, or a failure occurs in the deployment process.
+     */
+    public void execute() throws BuildException {
+        for (Enumeration e = vendorTools.elements();
+             e.hasMoreElements();) {
+            HotDeploymentTool tool = (HotDeploymentTool) e.nextElement();
+            tool.validateAttributes();
+            tool.deploy();
+        }
+    }
+
+    ///////////////////////////////////////////////////////////////////////////
+    //
+    // Set/get methods
+    //
+    ///////////////////////////////////////////////////////////////////////////
+
+    /**
+     *  Returns the action field.
+     *  @return A string representing the "action" attribute.
+     */
+    public String getAction() {
+        return action;
+    }
+
+    /**
+     *  The action to be performed, usually "deploy"; required.
+     *   Some tools support additional actions, such as "delete", "list", "undeploy", "update"...
+     *  @param action A String representing the "action" attribute.
+     */
+    public void setAction(String action) {
+        this.action = action;
+    }
+
+    /**
+     *  Returns the source field (the path/filename of the component to be
+     *  deployed.
+     *  @return A File object representing the "source" attribute.
+     */
+    public File getSource() {
+        return source;
+    }
+
+    /**
+     *  The filename of the component to be deployed; optional
+     *  depending upon the tool and the action.
+     *  @param source String representing the "source" attribute.
+     */
+    public void setSource(File source) {
+        this.source = source;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/WebLogicHotDeploymentTool.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/WebLogicHotDeploymentTool.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/j2ee/WebLogicHotDeploymentTool.java	(revision 16139)
@@ -0,0 +1,240 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.j2ee;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Java;
+
+/**
+ *  An Ant wrapper task for the weblogic.deploy tool.  This is used to
+ *  hot-deploy J2EE applications to a running WebLogic server.
+ *  This is <b>not</b> the same as creating the application archive.
+ *  This task assumes the archive (EAR, JAR, or WAR) file has been
+ *  assembled and is supplied as the "source" attribute.
+ *  <p>In the end, this task assembles the commadline parameters
+ *  and runs the weblogic.deploy tool in a seperate JVM.
+ *
+ *
+ *  @see org.apache.tools.ant.taskdefs.optional.j2ee.HotDeploymentTool
+ *  @see org.apache.tools.ant.taskdefs.optional.j2ee.AbstractHotDeploymentTool
+ *  @see org.apache.tools.ant.taskdefs.optional.j2ee.ServerDeploy
+ */
+public class WebLogicHotDeploymentTool extends AbstractHotDeploymentTool
+                                       implements HotDeploymentTool {
+    /** The classname of the tool to run **/
+    private static final String WEBLOGIC_DEPLOY_CLASS_NAME = "weblogic.deploy";
+
+    /** All the valid actions that weblogic.deploy permits **/
+    private static final String[] VALID_ACTIONS
+        = {ACTION_DELETE, ACTION_DEPLOY, ACTION_LIST, ACTION_UNDEPLOY, ACTION_UPDATE};
+
+    /** Represents the "-debug" flag from weblogic.deploy **/
+    private boolean debug;
+
+    /** The application name that is being deployed **/
+    private String application;
+
+    /** The component name:target(s) for the "-component" argument of weblogic.deploy **/
+    private String component;
+
+    /**
+     *  Perform the actual deployment.
+     *  For this implementation, a JVM is spawned and the weblogic.deploy
+     *  tools is executed.
+     *  @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete.
+     */
+    public void deploy() {
+        Java java = (Java) getTask().getProject().createTask("java");
+        java.setFork(true);
+        java.setFailonerror(true);
+        java.setClasspath(getClasspath());
+
+        java.setClassname(WEBLOGIC_DEPLOY_CLASS_NAME);
+        java.createArg().setLine(getArguments());
+        java.execute();
+    }
+
+    /**
+     *  Validates the passed in attributes.
+     *  <p>The rules are:
+     *  <ol><li>If action is "deploy" or "update" the "application" and "source"
+     *  attributes must be supplied.
+     *  <li>If action is "delete" or "undeploy" the "application" attribute must
+     *  be supplied.
+     *  @exception org.apache.tools.ant.BuildException if the attributes are invalid or incomplete
+     */
+    public void validateAttributes() throws BuildException {
+        super.validateAttributes();
+
+        String action = getTask().getAction();
+
+        // check that the password has been set
+        if ((getPassword() == null))
+            throw new BuildException("The password attribute must be set.");
+
+        // check for missing application on deploy & update
+        if ((action.equals(ACTION_DEPLOY) || action.equals(ACTION_UPDATE))
+            && application == null)
+            throw new BuildException("The application attribute must be set "
+                + "if action = " + action);
+
+        // check for missing source on deploy & update
+        if ((action.equals(ACTION_DEPLOY) || action.equals(ACTION_UPDATE))
+            && getTask().getSource() == null)
+            throw new BuildException("The source attribute must be set if "
+                + "action = " + action);
+
+        // check for missing application on delete & undeploy
+        if ((action.equals(ACTION_DELETE) || action.equals(ACTION_UNDEPLOY))
+            && application == null)
+            throw new BuildException("The application attribute must be set if "
+                + "action = " + action);
+    }
+
+    /**
+     *  Builds the arguments to pass to weblogic.deploy according to the
+     *  supplied action.
+     *  @return A String containing the arguments for the weblogic.deploy tool.
+     */
+    public String getArguments() throws BuildException {
+        String action = getTask().getAction();
+        String args = null;
+
+        if (action.equals(ACTION_DEPLOY) || action.equals(ACTION_UPDATE))
+            args = buildDeployArgs();
+        else if (action.equals(ACTION_DELETE) || action.equals(ACTION_UNDEPLOY))
+            args = buildUndeployArgs();
+        else if (action.equals(ACTION_LIST))
+            args = buildListArgs();
+
+        return args;
+    }
+
+    /**
+     *  Determines if the action supplied is valid.
+     *  <p>Valid actions are contained in the static array VALID_ACTIONS
+     *  @return true if the action attribute is valid, false if not.
+     */
+    protected boolean isActionValid() {
+        boolean valid = false;
+
+        String action = getTask().getAction();
+
+        for (int i = 0; i < VALID_ACTIONS.length; i++) {
+            if (action.equals(VALID_ACTIONS[i])) {
+                valid = true;
+                break;
+            }
+        }
+
+        return valid;
+    }
+
+    /**
+     *  Builds the prefix arguments to pass to weblogic.deploy.
+     *  These arguments are generic across all actions.
+     *  @return A StringBuffer containing the prefix arguments.
+     *  The action-specific build methods will append to this StringBuffer.
+     */
+    protected StringBuffer buildArgsPrefix() {
+        ServerDeploy task = getTask();
+        // constructs the "-url <url> -debug <action> <password>" portion
+        // of the commmand line
+        return new StringBuffer(1024)
+                .append((getServer() != null)
+                    ? "-url " + getServer()
+                    : "")
+                .append(" ")
+                .append(debug ? "-debug " : "")
+                .append((getUserName() != null)
+                    ? "-username " + getUserName()
+                    : "")
+                .append(" ")
+                .append(task.getAction()).append(" ")
+                .append(getPassword()).append(" ");
+    }
+
+    /**
+     *  Builds the arguments to pass to weblogic.deploy for deployment actions
+     *  ("deploy" and "update").
+     *  @return A String containing the full argument string for weblogic.deploy.
+     */
+    protected String buildDeployArgs() {
+        String args = buildArgsPrefix()
+                .append(application).append(" ")
+                .append(getTask().getSource())
+                .toString();
+
+        if (component != null) {
+            args = "-component " + component + " " + args;
+        }
+
+        return args;
+    }
+
+    /**
+     *  Builds the arguments to pass to weblogic.deploy for undeployment actions
+     *  ("undeploy" and "delete").
+     *  @return A String containing the full argument string for weblogic.deploy.
+     */
+    protected String buildUndeployArgs() {
+        return buildArgsPrefix()
+                .append(application).append(" ")
+                .toString();
+    }
+
+    /**
+     *  Builds the arguments to pass to weblogic.deploy for the list action
+     *  @return A String containing the full argument string for weblogic.deploy.
+     */
+    protected String buildListArgs() {
+        return buildArgsPrefix()
+                .toString();
+    }
+
+    /**
+     *  If set to true, additional information will be
+     *  printed during the deployment process; optional.
+     *  @param debug A boolean representing weblogic.deploy "-debug" flag.
+     */
+    public void setDebug(boolean debug) {
+        this.debug = debug;
+    }
+
+    /**
+     *  The name of the application being deployed; required.
+     *  @param application A String representing the application portion of the
+     *  weblogic.deploy command line.
+     */
+    public void setApplication(String application) {
+        this.application = application;
+    }
+
+    /**
+     * the component string for the deployment targets; optional.
+     * It is in the form <code>&lt;component&gt;:&lt;target1&gt;,&lt;target2&gt;...</code>
+     * Where component is the archive name (minus the .jar, .ear, .war
+     * extension).  Targets are the servers where the components will be deployed
+
+     *  @param component A String representing the value of the "-component"
+     *  argument of the weblogic.deploy command line argument.
+     */
+    public void setComponent(String component) {
+        this.component = component;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJDoc.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJDoc.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJDoc.java	(revision 16139)
@@ -0,0 +1,201 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.javacc;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Runs the JJDoc compiler compiler.
+ *
+ */
+public class JJDoc extends Task {
+
+    // keys to optional attributes
+    private static final String OUTPUT_FILE       = "OUTPUT_FILE";
+    private static final String TEXT              = "TEXT";
+    private static final String ONE_TABLE         = "ONE_TABLE";
+
+    private final Hashtable optionalAttrs = new Hashtable();
+
+    private String outputFile = null;
+    private boolean plainText = false;
+
+    private static final String DEFAULT_SUFFIX_HTML = ".html";
+    private static final String DEFAULT_SUFFIX_TEXT = ".txt";
+
+    // required attributes
+    private File target          = null;
+    private File javaccHome      = null;
+
+    private CommandlineJava cmdl = new CommandlineJava();
+
+
+    /**
+     * Sets the TEXT BNF documentation option.
+     */
+    public void setText(boolean plainText) {
+        optionalAttrs.put(TEXT, new Boolean(plainText));
+        this.plainText = plainText;
+    }
+
+    /**
+     * Sets the ONE_TABLE documentation option.
+     */
+    public void setOnetable(boolean oneTable) {
+        optionalAttrs.put(ONE_TABLE, new Boolean(oneTable));
+    }
+
+    /**
+     * The outputfile to write the generated BNF documentation file to.
+     * If not set, the file is written with the same name as
+     * the JavaCC grammar file with a suffix .html or .txt.
+     */
+    public void setOutputfile(String outputFile) {
+        this.outputFile = outputFile;
+    }
+
+    /**
+     * The javacc grammar file to process.
+     */
+    public void setTarget(File target) {
+        this.target = target;
+    }
+
+    /**
+     * The directory containing the JavaCC distribution.
+     */
+    public void setJavacchome(File javaccHome) {
+        this.javaccHome = javaccHome;
+    }
+
+    public JJDoc() {
+        cmdl.setVm(JavaEnvUtils.getJreExecutable("java"));
+    }
+
+    public void execute() throws BuildException {
+
+        // load command line with optional attributes
+        Enumeration iter = optionalAttrs.keys();
+        while (iter.hasMoreElements()) {
+            String name  = (String) iter.nextElement();
+            Object value = optionalAttrs.get(name);
+            cmdl.createArgument()
+                .setValue("-" + name + ":" + value.toString());
+        }
+
+        if (target == null || !target.isFile()) {
+            throw new BuildException("Invalid target: " + target);
+        }
+
+        if (outputFile != null) {
+            cmdl.createArgument() .setValue("-" + OUTPUT_FILE + ":"
+                                            + outputFile.replace('\\', '/'));
+        }
+
+        // use the directory containing the target as the output directory
+        File javaFile = new File(createOutputFileName(target, outputFile,
+                                                      plainText));
+
+        if (javaFile.exists()
+             && target.lastModified() < javaFile.lastModified()) {
+            log("Target is already built - skipping (" + target + ")",
+                Project.MSG_VERBOSE);
+            return;
+        }
+
+        cmdl.createArgument().setValue(target.getAbsolutePath());
+
+        cmdl.setClassname(JavaCC.getMainClass(javaccHome,
+                                              JavaCC.TASKDEF_TYPE_JJDOC));
+
+        final Path classpath = cmdl.createClasspath(getProject());
+        final File javaccJar = JavaCC.getArchiveFile(javaccHome);
+        classpath.createPathElement().setPath(javaccJar.getAbsolutePath());
+        classpath.addJavaRuntime();
+
+        final Commandline.Argument arg = cmdl.createVmArgument();
+        arg.setValue("-mx140M");
+        arg.setValue("-Dinstall.root=" + javaccHome.getAbsolutePath());
+
+        final Execute process =
+            new Execute(new LogStreamHandler(this,
+                                             Project.MSG_INFO,
+                                             Project.MSG_INFO),
+                        null);
+        log(cmdl.describeCommand(), Project.MSG_VERBOSE);
+        process.setCommandline(cmdl.getCommandline());
+
+        try {
+            if (process.execute() != 0) {
+                throw new BuildException("JJDoc failed.");
+            }
+        } catch (IOException e) {
+            throw new BuildException("Failed to launch JJDoc", e);
+        }
+    }
+
+    private String createOutputFileName(File target, String optionalOutputFile,
+                                        boolean plainText) {
+        String suffix = DEFAULT_SUFFIX_HTML;
+        String javaccFile = target.getAbsolutePath().replace('\\', '/');
+
+        if (plainText) {
+            suffix = DEFAULT_SUFFIX_TEXT;
+        }
+
+        if ((optionalOutputFile == null) || optionalOutputFile.equals("")) {
+            int filePos = javaccFile.lastIndexOf("/");
+
+            if (filePos >= 0) {
+                javaccFile = javaccFile.substring(filePos + 1);
+            }
+
+            int suffixPos = javaccFile.lastIndexOf('.');
+
+            if (suffixPos == -1) {
+                optionalOutputFile = javaccFile + suffix;
+            } else {
+                String currentSuffix = javaccFile.substring(suffixPos);
+
+                if (currentSuffix.equals(suffix)) {
+                    optionalOutputFile = javaccFile + suffix;
+                } else {
+                    optionalOutputFile = javaccFile.substring(0, suffixPos)
+                        + suffix;
+                }
+            }
+        } else {
+            optionalOutputFile = optionalOutputFile.replace('\\', '/');
+        }
+
+        return (getProject().getBaseDir() + "/" + optionalOutputFile)
+            .replace('\\', '/');
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJTree.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJTree.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JJTree.java	(revision 16139)
@@ -0,0 +1,387 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.javacc;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Runs the JJTree compiler compiler.
+ *
+ */
+public class JJTree extends Task {
+
+    // keys to optional attributes
+    private static final String OUTPUT_FILE       = "OUTPUT_FILE";
+    private static final String BUILD_NODE_FILES  = "BUILD_NODE_FILES";
+    private static final String MULTI             = "MULTI";
+    private static final String NODE_DEFAULT_VOID = "NODE_DEFAULT_VOID";
+    private static final String NODE_FACTORY      = "NODE_FACTORY";
+    private static final String NODE_SCOPE_HOOK   = "NODE_SCOPE_HOOK";
+    private static final String NODE_USES_PARSER  = "NODE_USES_PARSER";
+    private static final String STATIC            = "STATIC";
+    private static final String VISITOR           = "VISITOR";
+
+    private static final String NODE_PACKAGE      = "NODE_PACKAGE";
+    private static final String VISITOR_EXCEPTION = "VISITOR_EXCEPTION";
+    private static final String NODE_PREFIX       = "NODE_PREFIX";
+
+    private final Hashtable optionalAttrs = new Hashtable();
+
+    private String outputFile = null;
+
+    private static final String DEFAULT_SUFFIX = ".jj";
+
+    // required attributes
+    private File outputDirectory = null;
+    private File target          = null;
+    private File javaccHome      = null;
+
+    private CommandlineJava cmdl = new CommandlineJava();
+
+
+    /**
+     * Sets the BUILD_NODE_FILES grammar option.
+     */
+    public void setBuildnodefiles(boolean buildNodeFiles) {
+        optionalAttrs.put(BUILD_NODE_FILES, new Boolean(buildNodeFiles));
+    }
+
+    /**
+     * Sets the MULTI grammar option.
+     */
+    public void setMulti(boolean multi) {
+        optionalAttrs.put(MULTI, new Boolean(multi));
+    }
+
+    /**
+     * Sets the NODE_DEFAULT_VOID grammar option.
+     */
+    public void setNodedefaultvoid(boolean nodeDefaultVoid) {
+        optionalAttrs.put(NODE_DEFAULT_VOID, new Boolean(nodeDefaultVoid));
+    }
+
+    /**
+     * Sets the NODE_FACTORY grammar option.
+     */
+    public void setNodefactory(boolean nodeFactory) {
+        optionalAttrs.put(NODE_FACTORY, new Boolean(nodeFactory));
+    }
+
+    /**
+     * Sets the NODE_SCOPE_HOOK grammar option.
+     */
+    public void setNodescopehook(boolean nodeScopeHook) {
+        optionalAttrs.put(NODE_SCOPE_HOOK, new Boolean(nodeScopeHook));
+    }
+
+    /**
+     * Sets the NODE_USES_PARSER grammar option.
+     */
+    public void setNodeusesparser(boolean nodeUsesParser) {
+        optionalAttrs.put(NODE_USES_PARSER, new Boolean(nodeUsesParser));
+    }
+
+    /**
+     * Sets the STATIC grammar option.
+     */
+    public void setStatic(boolean staticParser) {
+        optionalAttrs.put(STATIC, new Boolean(staticParser));
+    }
+
+    /**
+     * Sets the VISITOR grammar option.
+     */
+    public void setVisitor(boolean visitor) {
+        optionalAttrs.put(VISITOR, new Boolean(visitor));
+    }
+
+    /**
+     * Sets the NODE_PACKAGE grammar option.
+     */
+    public void setNodepackage(String nodePackage) {
+        optionalAttrs.put(NODE_PACKAGE, new String(nodePackage));
+    }
+
+    /**
+     * Sets the VISITOR_EXCEPTION grammar option.
+     */
+    public void setVisitorException(String visitorException) {
+        optionalAttrs.put(VISITOR_EXCEPTION, new String(visitorException));
+    }
+
+    /**
+     * Sets the NODE_PREFIX grammar option.
+     */
+    public void setNodeprefix(String nodePrefix) {
+        optionalAttrs.put(NODE_PREFIX, new String(nodePrefix));
+    }
+
+    /**
+     * The directory to write the generated JavaCC grammar and node files to.
+     * If not set, the files are written to the directory
+     * containing the grammar file.
+     */
+    public void setOutputdirectory(File outputDirectory) {
+        this.outputDirectory = outputDirectory;
+    }
+
+    /**
+     * The outputfile to write the generated JavaCC grammar file to.
+     * If not set, the file is written with the same name as
+     * the JJTree grammar file with a suffix .jj.
+     */
+    public void setOutputfile(String outputFile) {
+        this.outputFile = outputFile;
+    }
+
+    /**
+     * The jjtree grammar file to process.
+     */
+    public void setTarget(File target) {
+        this.target = target;
+    }
+
+    /**
+     * The directory containing the JavaCC distribution.
+     */
+    public void setJavacchome(File javaccHome) {
+        this.javaccHome = javaccHome;
+    }
+
+    public JJTree() {
+        cmdl.setVm(JavaEnvUtils.getJreExecutable("java"));
+    }
+
+    public void execute() throws BuildException {
+
+        // load command line with optional attributes
+        Enumeration iter = optionalAttrs.keys();
+        while (iter.hasMoreElements()) {
+            String name  = (String) iter.nextElement();
+            Object value = optionalAttrs.get(name);
+            cmdl.createArgument().setValue("-" + name + ":" + value.toString());
+        }
+
+        if (target == null || !target.isFile()) {
+            throw new BuildException("Invalid target: " + target);
+        }
+
+        File javaFile = null;
+
+        // use the directory containing the target as the output directory
+        if (outputDirectory == null) {
+            // convert backslashes to slashes, otherwise jjtree will
+            // put this as comments and this seems to confuse javacc
+            cmdl.createArgument().setValue("-OUTPUT_DIRECTORY:"
+                                           + getDefaultOutputDirectory());
+
+            javaFile = new File(createOutputFileName(target, outputFile,
+                                                     null));
+        } else {
+            if (!outputDirectory.isDirectory()) {
+                throw new BuildException("'outputdirectory' " + outputDirectory
+                                         + " is not a directory.");
+            }
+
+            // convert backslashes to slashes, otherwise jjtree will
+            // put this as comments and this seems to confuse javacc
+            cmdl.createArgument().setValue("-OUTPUT_DIRECTORY:"
+                                           + outputDirectory.getAbsolutePath()
+                                             .replace('\\', '/'));
+
+            javaFile = new File(createOutputFileName(target, outputFile,
+                                                     outputDirectory
+                                                     .getPath()));
+        }
+
+        if (javaFile.exists()
+            && target.lastModified() < javaFile.lastModified()) {
+            log("Target is already built - skipping (" + target + ")",
+                Project.MSG_VERBOSE);
+            return;
+        }
+
+        if (outputFile != null) {
+            cmdl.createArgument().setValue("-" + OUTPUT_FILE + ":"
+                                           + outputFile.replace('\\', '/'));
+        }
+
+        cmdl.createArgument().setValue(target.getAbsolutePath());
+
+        cmdl.setClassname(JavaCC.getMainClass(javaccHome,
+                                              JavaCC.TASKDEF_TYPE_JJTREE));
+
+        final Path classpath = cmdl.createClasspath(getProject());
+        final File javaccJar = JavaCC.getArchiveFile(javaccHome);
+        classpath.createPathElement().setPath(javaccJar.getAbsolutePath());
+        classpath.addJavaRuntime();
+
+        final Commandline.Argument arg = cmdl.createVmArgument();
+        arg.setValue("-mx140M");
+        arg.setValue("-Dinstall.root=" + javaccHome.getAbsolutePath());
+
+        final Execute process =
+            new Execute(new LogStreamHandler(this,
+                                             Project.MSG_INFO,
+                                             Project.MSG_INFO),
+                        null);
+        log(cmdl.describeCommand(), Project.MSG_VERBOSE);
+        process.setCommandline(cmdl.getCommandline());
+
+        try {
+            if (process.execute() != 0) {
+                throw new BuildException("JJTree failed.");
+            }
+        } catch (IOException e) {
+            throw new BuildException("Failed to launch JJTree", e);
+        }
+    }
+
+    private String createOutputFileName(File target, String optionalOutputFile,
+                                        String outputDirectory) {
+        optionalOutputFile = validateOutputFile(optionalOutputFile,
+                                                outputDirectory);
+        String jjtreeFile = target.getAbsolutePath().replace('\\', '/');
+
+        if ((optionalOutputFile == null) || optionalOutputFile.equals("")) {
+            int filePos = jjtreeFile.lastIndexOf("/");
+
+            if (filePos >= 0) {
+                jjtreeFile = jjtreeFile.substring(filePos + 1);
+            }
+
+            int suffixPos = jjtreeFile.lastIndexOf('.');
+
+            if (suffixPos == -1) {
+                optionalOutputFile = jjtreeFile + DEFAULT_SUFFIX;
+            } else {
+                String currentSuffix = jjtreeFile.substring(suffixPos);
+
+                if (currentSuffix.equals(DEFAULT_SUFFIX)) {
+                    optionalOutputFile = jjtreeFile + DEFAULT_SUFFIX;
+                } else {
+                    optionalOutputFile = jjtreeFile.substring(0, suffixPos)
+                        + DEFAULT_SUFFIX;
+                }
+            }
+        }
+
+        if ((outputDirectory == null) || outputDirectory.equals("")) {
+            outputDirectory = getDefaultOutputDirectory();
+        }
+
+        return (outputDirectory + "/" + optionalOutputFile).replace('\\', '/');
+    }
+
+ /*
+  * Not used anymore
+    private boolean isAbsolute(String fileName) {
+        return (fileName.startsWith("/") || (new File(fileName).isAbsolute()));
+    }
+*/
+    /**
+     * When running JJTree from an Ant taskdesk the -OUTPUT_DIRECTORY must
+     * always be set. But when -OUTPUT_DIRECTORY is set, -OUTPUT_FILE is
+     * handled as if relative of this -OUTPUT_DIRECTORY. Thus when the
+     * -OUTPUT_FILE is absolute or contains a drive letter we have a problem.
+     *
+     * @param outputFile
+     * @param outputDirectory
+     * @return
+     * @throws BuildException
+     */
+    private String validateOutputFile(String outputFile,
+                                      String outputDirectory)
+        throws BuildException {
+        if (outputFile == null) {
+            return null;
+        }
+
+        if ((outputDirectory == null)
+            && (outputFile.startsWith("/") || outputFile.startsWith("\\"))) {
+            String relativeOutputFile = makeOutputFileRelative(outputFile);
+            setOutputfile(relativeOutputFile);
+
+            return relativeOutputFile;
+        }
+
+        String root = getRoot(new File(outputFile)).getAbsolutePath();
+
+        if ((root.length() > 1)
+            && outputFile.startsWith(root.substring(0, root.length() - 1))) {
+            throw new BuildException("Drive letter in 'outputfile' not "
+                                     + "supported: " + outputFile);
+        }
+
+        return outputFile;
+    }
+
+    private String makeOutputFileRelative(String outputFile) {
+        StringBuffer relativePath = new StringBuffer();
+        String defaultOutputDirectory = getDefaultOutputDirectory();
+        int nextPos = defaultOutputDirectory.indexOf('/');
+        int startPos = nextPos + 1;
+
+        while (startPos > -1 && startPos < defaultOutputDirectory.length()) {
+            relativePath.append("/..");
+            nextPos = defaultOutputDirectory.indexOf('/', startPos);
+
+            if (nextPos == -1) {
+                startPos = nextPos;
+            } else {
+                startPos = nextPos + 1;
+            }
+        }
+
+        relativePath.append(outputFile);
+
+        return relativePath.toString();
+    }
+
+    private String getDefaultOutputDirectory() {
+        return getProject().getBaseDir().getAbsolutePath().replace('\\', '/');
+    }
+
+    /**
+     * Determine root directory for a given file.
+     *
+     * @param file
+     * @return file's root directory
+     */
+    private File getRoot(File file) {
+        File root = file.getAbsoluteFile();
+
+        while (root.getParent() != null) {
+            root = root.getParentFile();
+        }
+
+        return root;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JavaCC.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JavaCC.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javacc/JavaCC.java	(revision 16139)
@@ -0,0 +1,501 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.javacc;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.zip.ZipFile;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * JavaCC compiler compiler task.
+ *
+ */
+public class JavaCC extends Task {
+
+    // keys to optional attributes
+    private static final String LOOKAHEAD              = "LOOKAHEAD";
+    private static final String CHOICE_AMBIGUITY_CHECK = "CHOICE_AMBIGUITY_CHECK";
+    private static final String OTHER_AMBIGUITY_CHECK  = "OTHER_AMBIGUITY_CHECK";
+
+    private static final String STATIC                 = "STATIC";
+    private static final String DEBUG_PARSER           = "DEBUG_PARSER";
+    private static final String DEBUG_LOOKAHEAD        = "DEBUG_LOOKAHEAD";
+    private static final String DEBUG_TOKEN_MANAGER    = "DEBUG_TOKEN_MANAGER";
+    private static final String OPTIMIZE_TOKEN_MANAGER = "OPTIMIZE_TOKEN_MANAGER";
+    private static final String ERROR_REPORTING        = "ERROR_REPORTING";
+    private static final String JAVA_UNICODE_ESCAPE    = "JAVA_UNICODE_ESCAPE";
+    private static final String UNICODE_INPUT          = "UNICODE_INPUT";
+    private static final String IGNORE_CASE            = "IGNORE_CASE";
+    private static final String COMMON_TOKEN_ACTION    = "COMMON_TOKEN_ACTION";
+    private static final String USER_TOKEN_MANAGER     = "USER_TOKEN_MANAGER";
+    private static final String USER_CHAR_STREAM       = "USER_CHAR_STREAM";
+    private static final String BUILD_PARSER           = "BUILD_PARSER";
+    private static final String BUILD_TOKEN_MANAGER    = "BUILD_TOKEN_MANAGER";
+    private static final String SANITY_CHECK           = "SANITY_CHECK";
+    private static final String FORCE_LA_CHECK         = "FORCE_LA_CHECK";
+    private static final String CACHE_TOKENS           = "CACHE_TOKENS";
+    private static final String KEEP_LINE_COLUMN       = "KEEP_LINE_COLUMN";
+
+    private final Hashtable optionalAttrs = new Hashtable();
+
+    // required attributes
+    private File outputDirectory = null;
+    private File target          = null;
+    private File javaccHome      = null;
+
+    private CommandlineJava cmdl = new CommandlineJava();
+
+    protected static final int TASKDEF_TYPE_JAVACC = 1;
+    protected static final int TASKDEF_TYPE_JJTREE = 2;
+    protected static final int TASKDEF_TYPE_JJDOC = 3;
+
+    protected static final String[] ARCHIVE_LOCATIONS =
+        new String[] {
+            "JavaCC.zip",
+            "bin/lib/JavaCC.zip",
+            "bin/lib/javacc.jar",
+            "javacc.jar", // used by jpackage for JavaCC 3.x
+        };
+
+    protected static final int[] ARCHIVE_LOCATIONS_VS_MAJOR_VERSION =
+        new int[] {
+            1,
+            2,
+            3,
+            3,
+        };
+
+    protected static final String COM_PACKAGE = "COM.sun.labs.";
+    protected static final String COM_JAVACC_CLASS = "javacc.Main";
+    protected static final String COM_JJTREE_CLASS = "jjtree.Main";
+    protected static final String COM_JJDOC_CLASS = "jjdoc.JJDocMain";
+
+    protected static final String ORG_PACKAGE_3_0 = "org.netbeans.javacc.";
+    protected static final String ORG_PACKAGE_3_1 = "org.javacc.";
+    protected static final String ORG_JAVACC_CLASS = "parser.Main";
+    protected static final String ORG_JJTREE_CLASS = COM_JJTREE_CLASS;
+    protected static final String ORG_JJDOC_CLASS = COM_JJDOC_CLASS;
+
+    /**
+     * Sets the LOOKAHEAD grammar option.
+     */
+    public void setLookahead(int lookahead) {
+        optionalAttrs.put(LOOKAHEAD, new Integer(lookahead));
+    }
+
+    /**
+     * Sets the CHOICE_AMBIGUITY_CHECK grammar option.
+     */
+    public void setChoiceambiguitycheck(int choiceAmbiguityCheck) {
+        optionalAttrs.put(CHOICE_AMBIGUITY_CHECK, new Integer(choiceAmbiguityCheck));
+    }
+
+    /**
+     * Sets the OTHER_AMBIGUITY_CHECK grammar option.
+     */
+    public void setOtherambiguityCheck(int otherAmbiguityCheck) {
+        optionalAttrs.put(OTHER_AMBIGUITY_CHECK, new Integer(otherAmbiguityCheck));
+    }
+
+    /**
+     * Sets the STATIC grammar option.
+     */
+    public void setStatic(boolean staticParser) {
+        optionalAttrs.put(STATIC, new Boolean(staticParser));
+    }
+
+    /**
+     * Sets the DEBUG_PARSER grammar option.
+     */
+    public void setDebugparser(boolean debugParser) {
+        optionalAttrs.put(DEBUG_PARSER, new Boolean(debugParser));
+    }
+
+    /**
+     * Sets the DEBUG_LOOKAHEAD grammar option.
+     */
+    public void setDebuglookahead(boolean debugLookahead) {
+        optionalAttrs.put(DEBUG_LOOKAHEAD, new Boolean(debugLookahead));
+    }
+
+    /**
+     * Sets the DEBUG_TOKEN_MANAGER grammar option.
+     */
+    public void setDebugtokenmanager(boolean debugTokenManager) {
+        optionalAttrs.put(DEBUG_TOKEN_MANAGER, new Boolean(debugTokenManager));
+    }
+
+    /**
+     * Sets the OPTIMIZE_TOKEN_MANAGER grammar option.
+     */
+    public void setOptimizetokenmanager(boolean optimizeTokenManager) {
+        optionalAttrs.put(OPTIMIZE_TOKEN_MANAGER, new Boolean(optimizeTokenManager));
+    }
+
+    /**
+     * Sets the ERROR_REPORTING grammar option.
+     */
+    public void setErrorreporting(boolean errorReporting) {
+        optionalAttrs.put(ERROR_REPORTING, new Boolean(errorReporting));
+    }
+
+    /**
+     * Sets the JAVA_UNICODE_ESCAPE grammar option.
+     */
+    public void setJavaunicodeescape(boolean javaUnicodeEscape) {
+        optionalAttrs.put(JAVA_UNICODE_ESCAPE, new Boolean(javaUnicodeEscape));
+    }
+
+    /**
+     * Sets the UNICODE_INPUT grammar option.
+     */
+    public void setUnicodeinput(boolean unicodeInput) {
+        optionalAttrs.put(UNICODE_INPUT, new Boolean(unicodeInput));
+    }
+
+    /**
+     * Sets the IGNORE_CASE grammar option.
+     */
+    public void setIgnorecase(boolean ignoreCase) {
+        optionalAttrs.put(IGNORE_CASE, new Boolean(ignoreCase));
+    }
+
+    /**
+     * Sets the COMMON_TOKEN_ACTION grammar option.
+     */
+    public void setCommontokenaction(boolean commonTokenAction) {
+        optionalAttrs.put(COMMON_TOKEN_ACTION, new Boolean(commonTokenAction));
+    }
+
+    /**
+     * Sets the USER_TOKEN_MANAGER grammar option.
+     */
+    public void setUsertokenmanager(boolean userTokenManager) {
+        optionalAttrs.put(USER_TOKEN_MANAGER, new Boolean(userTokenManager));
+    }
+
+    /**
+     * Sets the USER_CHAR_STREAM grammar option.
+     */
+    public void setUsercharstream(boolean userCharStream) {
+        optionalAttrs.put(USER_CHAR_STREAM, new Boolean(userCharStream));
+    }
+
+    /**
+     * Sets the BUILD_PARSER grammar option.
+     */
+    public void setBuildparser(boolean buildParser) {
+        optionalAttrs.put(BUILD_PARSER, new Boolean(buildParser));
+    }
+
+    /**
+     * Sets the BUILD_TOKEN_MANAGER grammar option.
+     */
+    public void setBuildtokenmanager(boolean buildTokenManager) {
+        optionalAttrs.put(BUILD_TOKEN_MANAGER, new Boolean(buildTokenManager));
+    }
+
+    /**
+     * Sets the SANITY_CHECK grammar option.
+     */
+    public void setSanitycheck(boolean sanityCheck) {
+        optionalAttrs.put(SANITY_CHECK, new Boolean(sanityCheck));
+    }
+
+    /**
+     * Sets the FORCE_LA_CHECK grammar option.
+     */
+    public void setForcelacheck(boolean forceLACheck) {
+        optionalAttrs.put(FORCE_LA_CHECK, new Boolean(forceLACheck));
+    }
+
+    /**
+     * Sets the CACHE_TOKENS grammar option.
+     */
+    public void setCachetokens(boolean cacheTokens) {
+        optionalAttrs.put(CACHE_TOKENS, new Boolean(cacheTokens));
+    }
+
+    /**
+     * Sets the KEEP_LINE_COLUMN grammar option.
+     */
+    public void setKeeplinecolumn(boolean keepLineColumn) {
+        optionalAttrs.put(KEEP_LINE_COLUMN, new Boolean(keepLineColumn));
+    }
+
+    /**
+     * The directory to write the generated files to.
+     * If not set, the files are written to the directory
+     * containing the grammar file.
+     */
+    public void setOutputdirectory(File outputDirectory) {
+        this.outputDirectory = outputDirectory;
+    }
+
+    /**
+     * The grammar file to process.
+     */
+    public void setTarget(File target) {
+        this.target = target;
+    }
+
+    /**
+     * The directory containing the JavaCC distribution.
+     */
+    public void setJavacchome(File javaccHome) {
+        this.javaccHome = javaccHome;
+    }
+
+    public JavaCC() {
+        cmdl.setVm(JavaEnvUtils.getJreExecutable("java"));
+    }
+
+    public void execute() throws BuildException {
+
+        // load command line with optional attributes
+        Enumeration iter = optionalAttrs.keys();
+        while (iter.hasMoreElements()) {
+            String name  = (String) iter.nextElement();
+            Object value = optionalAttrs.get(name);
+            cmdl.createArgument().setValue("-" + name + ":" + value.toString());
+        }
+
+        // check the target is a file
+        if (target == null || !target.isFile()) {
+            throw new BuildException("Invalid target: " + target);
+        }
+
+        // use the directory containing the target as the output directory
+        if (outputDirectory == null) {
+            outputDirectory = new File(target.getParent());
+        } else if (!outputDirectory.isDirectory()) {
+            throw new BuildException("Outputdir not a directory.");
+        }
+        cmdl.createArgument().setValue("-OUTPUT_DIRECTORY:"
+                                       + outputDirectory.getAbsolutePath());
+
+        // determine if the generated java file is up-to-date
+        final File javaFile = getOutputJavaFile(outputDirectory, target);
+        if (javaFile.exists() && target.lastModified() < javaFile.lastModified()) {
+            log("Target is already built - skipping (" + target + ")", Project.MSG_VERBOSE);
+            return;
+        }
+        cmdl.createArgument().setValue(target.getAbsolutePath());
+
+        cmdl.setClassname(JavaCC.getMainClass(javaccHome,
+                                              JavaCC.TASKDEF_TYPE_JAVACC));
+
+        final Path classpath = cmdl.createClasspath(getProject());
+        final File javaccJar = JavaCC.getArchiveFile(javaccHome);
+        classpath.createPathElement().setPath(javaccJar.getAbsolutePath());
+        classpath.addJavaRuntime();
+
+        final Commandline.Argument arg = cmdl.createVmArgument();
+        arg.setValue("-mx140M");
+        arg.setValue("-Dinstall.root=" + javaccHome.getAbsolutePath());
+
+        Execute.runCommand(this, cmdl.getCommandline());
+    }
+
+    /**
+     * Helper method to retrieve the path used to store the JavaCC.zip
+     * or javacc.jar which is different from versions.
+     *
+     * @param home the javacc home path directory.
+     * @throws BuildException thrown if the home directory is invalid
+     * or if the archive could not be found despite attempts to do so.
+     * @return the file object pointing to the JavaCC archive.
+     */
+    protected static File getArchiveFile(File home) throws BuildException {
+        return new File(home,
+                        ARCHIVE_LOCATIONS[getArchiveLocationIndex(home)]);
+    }
+
+    /**
+     * Helper method to retrieve main class which is different from versions.
+     * @param home the javacc home path directory.
+     * @param type the taskdef.
+     * @throws BuildException thrown if the home directory is invalid
+     * or if the archive could not be found despite attempts to do so.
+     * @return the main class for the taskdef.
+     */
+    protected static String getMainClass(File home, int type)
+        throws BuildException {
+
+        int majorVersion = getMajorVersionNumber(home);
+        String packagePrefix = null;
+        String mainClass = null;
+
+        switch (majorVersion) {
+        case 1:
+        case 2:
+            packagePrefix = COM_PACKAGE;
+
+            switch (type) {
+            case TASKDEF_TYPE_JAVACC:
+                mainClass = COM_JAVACC_CLASS;
+
+                break;
+
+            case TASKDEF_TYPE_JJTREE:
+                mainClass = COM_JJTREE_CLASS;
+
+                break;
+
+            case TASKDEF_TYPE_JJDOC:
+                mainClass = COM_JJDOC_CLASS;
+
+                break;
+            }
+
+            break;
+
+        case 3:
+            /*
+             * This is where the fun starts, JavaCC 3.0 uses
+             * org.netbeans.javacc, 3.1 uses org.javacc - I wonder
+             * which version is going to use net.java.javacc.
+             *
+             * Look into to the archive to pick up the best
+             * package.
+             */
+            ZipFile zf = null;
+            try {
+                zf = new ZipFile(getArchiveFile(home));
+                if (zf.getEntry(ORG_PACKAGE_3_0.replace('.', '/')) != null) {
+                    packagePrefix = ORG_PACKAGE_3_0;
+                } else {
+                    packagePrefix = ORG_PACKAGE_3_1;
+                }
+            } catch (IOException e) {
+                throw new BuildException("Error reading javacc.jar", e);
+            } finally {
+                if (zf != null) {
+                    try {
+                        zf.close();
+                    } catch (IOException e) {
+                        throw new BuildException(e);
+                    }
+                }
+            }
+
+            switch (type) {
+            case TASKDEF_TYPE_JAVACC:
+                mainClass = ORG_JAVACC_CLASS;
+
+                break;
+
+            case TASKDEF_TYPE_JJTREE:
+                mainClass = ORG_JJTREE_CLASS;
+
+                break;
+
+            case TASKDEF_TYPE_JJDOC:
+                mainClass = ORG_JJDOC_CLASS;
+
+                break;
+            }
+
+            break;
+        }
+
+        return packagePrefix + mainClass;
+    }
+
+    /**
+     * Helper method to determine the archive location index.
+     *
+     * @param home the javacc home path directory.
+     * @throws BuildException thrown if the home directory is invalid
+     * or if the archive could not be found despite attempts to do so.
+     * @return the archive location index
+     */
+    private static int getArchiveLocationIndex(File home)
+        throws BuildException {
+
+        if (home == null || !home.isDirectory()) {
+            throw new BuildException("JavaCC home must be a valid directory.");
+        }
+
+        for (int i = 0; i < ARCHIVE_LOCATIONS.length; i++) {
+            File f = new File(home, ARCHIVE_LOCATIONS[i]);
+
+            if (f.exists()) {
+                return i;
+            }
+        }
+
+        throw new BuildException("Could not find a path to JavaCC.zip "
+                                 + "or javacc.jar from '" + home + "'.");
+    }
+
+    /**
+     * Helper method to determine the major version number of JavaCC.
+     *
+     * @param home the javacc home path directory.
+     * @throws BuildException thrown if the home directory is invalid
+     * or if the archive could not be found despite attempts to do so.
+     * @return a the major version number
+     */
+    protected static int getMajorVersionNumber(File home)
+        throws BuildException {
+
+        return
+            ARCHIVE_LOCATIONS_VS_MAJOR_VERSION[getArchiveLocationIndex(home)];
+    }
+
+    /**
+     * Determines the output Java file to be generated by the given grammar
+     * file.
+     *
+     */
+    private File getOutputJavaFile(File outputdir, File srcfile) {
+        String path = srcfile.getPath();
+
+        // Extract file's base-name
+        int startBasename = path.lastIndexOf(File.separator);
+        if (startBasename != -1) {
+            path = path.substring(startBasename + 1);
+        }
+
+        // Replace the file's extension with '.java'
+        int startExtn = path.lastIndexOf('.');
+        if (startExtn != -1) {
+            path = path.substring(0, startExtn) + ".java";
+        } else {
+            path += ".java";
+        }
+
+        // Change the directory
+        if (outputdir != null) {
+            path = outputdir + File.separator + path;
+        }
+
+        return new File(path);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapter.java	(revision 16139)
@@ -0,0 +1,34 @@
+/*
+ * Copyright  2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.javah;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.optional.Javah;
+
+/**
+ * Interface for different backend implementations of the Javah task.
+ *
+ * @since Ant 1.6.3
+ */
+public interface JavahAdapter {
+    /**
+     * Performs the actual compilation.
+     *
+     * @since Ant 1.6.3
+     */
+    boolean compile(Javah javah) throws BuildException;
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapterFactory.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapterFactory.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javah/JavahAdapterFactory.java	(revision 16139)
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.javah;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Creates the JavahAdapter based on the user choice and
+ * potentially the VM vendor.
+ *
+ * @since Ant 1.6.3
+ */
+public class JavahAdapterFactory {
+
+    /**
+     * Determines the default choice of adapter based on the VM
+     * vendor.
+     *
+     * @return the default choice of adapter based on the VM
+     * vendor
+     */
+    public static String getDefault() {
+        if (JavaEnvUtils.isKaffe()) {
+            return Kaffeh.IMPLEMENTATION_NAME;
+        }
+        return SunJavah.IMPLEMENTATION_NAME;
+    }
+
+    /**
+     * Creates the JavahAdapter based on the user choice and
+     * potentially the VM vendor.
+     *
+     * @param choice the user choice (if any).
+     * @param log a ProjectComponent instance used to access Ant's
+     * logging system.
+     * @return The adapter to use.
+     */
+    public static JavahAdapter getAdapter(String choice,
+                                          ProjectComponent log)
+        throws BuildException {
+        if ((JavaEnvUtils.isKaffe() && choice == null)
+            || Kaffeh.IMPLEMENTATION_NAME.equals(choice)) {
+            return new Kaffeh();
+        } else if (SunJavah.IMPLEMENTATION_NAME.equals(choice)) {
+            return new SunJavah();
+        } else if (choice != null) {
+            return resolveClassName(choice);
+        }
+
+        // This default has been good enough until Ant 1.6.3, so stick
+        // with it
+        return new SunJavah();
+    }
+
+    /**
+     * Tries to resolve the given classname into a native2ascii adapter.
+     * Throws a fit if it can't.
+     *
+     * @param className The fully qualified classname to be created.
+     * @throws BuildException This is the fit that is thrown if className
+     * isn't an instance of JavahAdapter.
+     */
+    private static JavahAdapter resolveClassName(String className)
+        throws BuildException {
+        try {
+            Class c = Class.forName(className);
+            Object o = c.newInstance();
+            return (JavahAdapter) o;
+        } catch (ClassNotFoundException cnfe) {
+            throw new BuildException("Can't load " + className, cnfe);
+        } catch (ClassCastException cce) {
+            throw new BuildException(className 
+                                     + " is not a Javah adapter", cce);
+        } catch (Throwable t) {
+            // for all other possibilities
+            throw new BuildException(className + " caused an interesting "
+                                     + "exception.", t);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javah/Kaffeh.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javah/Kaffeh.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javah/Kaffeh.java	(revision 16139)
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.javah;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.optional.Javah;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Adapter to the native kaffeh compiler.
+ *
+ * @since Ant 1.6.3
+ */
+public class Kaffeh implements JavahAdapter {
+
+    public static final String IMPLEMENTATION_NAME = "kaffeh";
+
+    /**
+     * Performs the actual compilation.
+     *
+     * @since Ant 1.6.3
+     */
+    public boolean compile(Javah javah) throws BuildException {
+        Commandline cmd = setupKaffehCommand(javah);
+        try {
+            Execute.runCommand(javah, cmd.getCommandline());
+            return true;
+        } catch (BuildException e) {
+            if (e.getMessage().indexOf("failed with return code") == -1) {
+                throw e;
+            }
+        }
+        return false;
+    }
+
+    private Commandline setupKaffehCommand(Javah javah) {
+        Commandline cmd = new Commandline();
+        cmd.setExecutable(JavaEnvUtils.getJdkExecutable("kaffeh"));
+
+        if (javah.getDestdir() != null) {
+            cmd.createArgument().setValue("-d");
+            cmd.createArgument().setFile(javah.getDestdir());
+        }
+
+        if (javah.getOutputfile() != null) {
+            cmd.createArgument().setValue("-o");
+            cmd.createArgument().setFile(javah.getOutputfile());
+        }
+
+        Path cp = new Path(javah.getProject());
+        if (javah.getBootclasspath() != null) {
+            cp.append(javah.getBootclasspath());
+        }
+        if (javah.getClasspath() != null) {
+            cp.append(javah.getClasspath());
+        }
+        if (cp.size() > 0) {
+            cmd.createArgument().setValue("-classpath");
+            cmd.createArgument().setPath(cp);
+        }
+
+        if (!javah.getOld()) {
+            cmd.createArgument().setValue("-jni");
+        }
+
+        cmd.addArguments(javah.getCurrentArgs());
+
+        javah.logAndAddFiles(cmd);
+        return cmd;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javah/SunJavah.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javah/SunJavah.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/javah/SunJavah.java	(revision 16139)
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.javah;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.launch.Locator;
+import org.apache.tools.ant.taskdefs.ExecuteJava;
+import org.apache.tools.ant.taskdefs.optional.Javah;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+
+/**
+ * Adapter to com.sun.tools.javah.oldjavah.Main or com.sun.tools.javah.Main.
+ *
+ * @since Ant 1.6.3
+ */
+public class SunJavah implements JavahAdapter {
+
+    public static final String IMPLEMENTATION_NAME = "sun";
+
+    /**
+     * Performs the actual compilation.
+     *
+     * @since Ant 1.6.3
+     */
+    public boolean compile(Javah javah) throws BuildException {
+        Commandline cmd = setupJavahCommand(javah);
+        ExecuteJava ej = new ExecuteJava();
+        Class c = null;
+        try {
+            try {
+                // first search for the "old" javah class in 1.4.2 tools.jar
+                c = Class.forName("com.sun.tools.javah.oldjavah.Main");
+            } catch (ClassNotFoundException cnfe) {
+                // assume older than 1.4.2 tools.jar
+                c = Class.forName("com.sun.tools.javah.Main");
+            }
+        } catch (ClassNotFoundException ex) {
+            throw new BuildException("Can't load javah", ex, 
+                                     javah.getLocation());
+        }
+        cmd.setExecutable(c.getName());
+        ej.setJavaCommand(cmd);
+        File f = Locator.getClassSource(c);
+        if (f != null) {
+            ej.setClasspath(new Path(javah.getProject(), f.getPath()));
+        }
+        return ej.fork(javah) == 0;
+    }
+
+    private Commandline setupJavahCommand(Javah javah) {
+        Commandline cmd = new Commandline();
+
+        if (javah.getDestdir() != null) {
+            cmd.createArgument().setValue("-d");
+            cmd.createArgument().setFile(javah.getDestdir());
+        }
+
+        if (javah.getOutputfile() != null) {
+            cmd.createArgument().setValue("-o");
+            cmd.createArgument().setFile(javah.getOutputfile());
+        }
+
+        if (javah.getClasspath() != null) {
+            cmd.createArgument().setValue("-classpath");
+            cmd.createArgument().setPath(javah.getClasspath());
+        }
+
+        if (javah.getVerbose()) {
+            cmd.createArgument().setValue("-verbose");
+        }
+        if (javah.getOld()) {
+            cmd.createArgument().setValue("-old");
+        }
+        if (javah.getForce()) {
+            cmd.createArgument().setValue("-force");
+        }
+        if (javah.getStubs() && !javah.getOld()) {
+            throw new BuildException("stubs only available in old mode.", 
+                                     javah.getLocation());
+        }
+
+        if (javah.getStubs()) {
+            cmd.createArgument().setValue("-stubs");
+        }
+        Path bcp = new Path(javah.getProject());
+        if (javah.getBootclasspath() != null) {
+            bcp.append(javah.getBootclasspath());
+        }
+        if (bcp.size() > 0) {
+            cmd.createArgument().setValue("-bootclasspath");
+            cmd.createArgument().setPath(bcp);
+        }
+
+        cmd.addArguments(javah.getCurrentArgs());
+
+        javah.logAndAddFiles(cmd);
+        return cmd;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jdepend/JDependTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jdepend/JDependTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jdepend/JDependTask.java	(revision 16139)
@@ -0,0 +1,685 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.jdepend;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.Vector;
+import java.util.Enumeration;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteWatchdog;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.PatternSet;
+import org.apache.tools.ant.types.Reference;
+import org.apache.tools.ant.util.LoaderUtils;
+
+/**
+ * Runs JDepend tests.
+ *
+ * <p>JDepend is a tool to generate design quality metrics for each Java package.
+ * It has been initially created by Mike Clark. JDepend can be found at <a
+ * href="http://www.clarkware.com/software/JDepend.html">http://www.clarkware.com/software/JDepend.html</a>.
+ *
+ * The current implementation spawn a new Java VM.
+ *
+ */
+public class JDependTask extends Task {
+    //private CommandlineJava commandline = new CommandlineJava();
+
+    // required attributes
+    private Path sourcesPath; // Deprecated!
+    private Path classesPath; // Use this going forward
+
+    // optional attributes
+    private File outputFile;
+    private File dir;
+    private Path compileClasspath;
+    private boolean haltonerror = false;
+    private boolean fork = false;
+    private Long timeout = null;
+
+    private String jvm = null;
+    private String format = "text";
+    private PatternSet defaultPatterns = new PatternSet();
+
+    private static Constructor packageFilterC;
+    private static Method setFilter;
+
+    private boolean includeRuntime = false;
+    private Path runtimeClasses = null;
+
+    static {
+        try {
+            Class packageFilter =
+                Class.forName("jdepend.framework.PackageFilter");
+            packageFilterC =
+                packageFilter.getConstructor(new Class[] {java.util.Collection.class});
+            setFilter =
+                jdepend.textui.JDepend.class.getDeclaredMethod("setFilter",
+                                                               new Class[] {packageFilter});
+        } catch (Throwable t) {
+            if (setFilter == null) {
+                packageFilterC = null;
+            }
+        }
+    }
+
+    /**
+     * If true,
+     *  include jdepend.jar in the forked VM.
+     *
+     * @param b include ant run time yes or no
+     * @since Ant 1.6
+     */
+    public void setIncluderuntime(boolean b) {
+        includeRuntime = b;
+    }
+
+    /**
+     * Set the timeout value (in milliseconds).
+     *
+     * <p>If the operation is running for more than this value, the jdepend
+     * will be canceled. (works only when in 'fork' mode).</p>
+     * @param value the maximum time (in milliseconds) allowed before
+     * declaring the test as 'timed-out'
+     * @see #setFork(boolean)
+     */
+    public void setTimeout(Long value) {
+        timeout = value;
+    }
+
+    /**
+     * @return the timeout value
+     */
+    public Long getTimeout() {
+        return timeout;
+    }
+
+    /**
+     * The output file name.
+     *
+     * @param outputFile the output file name
+     */
+    public void setOutputFile(File outputFile) {
+        this.outputFile = outputFile;
+    }
+
+    /**
+     * @return the output file name
+     */
+    public File getOutputFile() {
+        return outputFile;
+    }
+
+    /**
+     * Whether or not to halt on failure. Default: false.
+     * @param haltonerror the value to set
+     */
+    public void setHaltonerror(boolean haltonerror) {
+        this.haltonerror = haltonerror;
+    }
+
+    /**
+     * @return the value of the haltonerror attribute
+     */
+    public boolean getHaltonerror() {
+        return haltonerror;
+    }
+
+    /**
+     * If true, forks into a new JVM. Default: false.
+     *
+     * @param   value   <tt>true</tt> if a JVM should be forked,
+     *                  otherwise <tt>false<tt>
+     */
+    public void setFork(boolean value) {
+        fork = value;
+    }
+
+    /**
+     * @return the value of the fork attribute
+     */
+    public boolean getFork() {
+        return fork;
+    }
+
+    /**
+     * The command used to invoke a forked Java Virtual Machine.
+     *
+     * Default is <tt>java</tt>. Ignored if no JVM is forked.
+     * @param   value   the new VM to use instead of <tt>java</tt>
+     * @see #setFork(boolean)
+     */
+    public void setJvm(String value) {
+        jvm = value;
+
+    }
+
+    /**
+     * Adds a path to source code to analyze.
+     * @return a source path
+     * @deprecated
+     */
+    public Path createSourcespath() {
+        if (sourcesPath == null) {
+            sourcesPath = new Path(getProject());
+        }
+        return sourcesPath.createPath();
+    }
+
+    /**
+     * Gets the sourcepath.
+     * @return the sources path
+     * @deprecated
+     *
+     */
+    public Path getSourcespath() {
+        return sourcesPath;
+    }
+
+    /**
+     * Adds a path to class code to analyze.
+     * @return a classes path
+     */
+    public Path createClassespath() {
+        if (classesPath == null) {
+            classesPath = new Path(getProject());
+        }
+        return classesPath.createPath();
+    }
+
+    /**
+     * Gets the classespath.
+     * @return the classes path
+     */
+    public Path getClassespath() {
+        return classesPath;
+    }
+
+    /**
+     * The directory to invoke the VM in. Ignored if no JVM is forked.
+     * @param   dir     the directory to invoke the JVM from.
+     * @see #setFork(boolean)
+     */
+    public void setDir(File dir) {
+        this.dir = dir;
+    }
+
+    /**
+     * @return the dir attribute
+     */
+    public File getDir() {
+        return dir;
+    }
+
+    /**
+     * Set the classpath to be used for this compilation.
+     * @param classpath a class path to be used
+     */
+    public void setClasspath(Path classpath) {
+        if (compileClasspath == null) {
+            compileClasspath = classpath;
+        } else {
+            compileClasspath.append(classpath);
+        }
+    }
+
+    /**
+     * Gets the classpath to be used for this compilation.
+     * @return the class path used for compilation
+     */
+    public Path getClasspath() {
+        return compileClasspath;
+    }
+
+    /**
+     * Adds a path to the classpath.
+     * @return a classpath
+     */
+    public Path createClasspath() {
+        if (compileClasspath == null) {
+            compileClasspath = new Path(getProject());
+        }
+        return compileClasspath.createPath();
+    }
+
+    /**
+     * Create a new JVM argument. Ignored if no JVM is forked.
+     * @param commandline the commandline to create the argument on
+     * @return  create a new JVM argument so that any argument can
+     *          be passed to the JVM.
+     * @see #setFork(boolean)
+     */
+    public Commandline.Argument createJvmarg(CommandlineJava commandline) {
+        return commandline.createVmArgument();
+    }
+
+    /**
+     * Adds a reference to a classpath defined elsewhere.
+     * @param r a classpath reference
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+
+    /**
+     * add a name entry on the exclude list
+     * @return a pattern for the excludes
+     */
+    public PatternSet.NameEntry createExclude() {
+        return defaultPatterns.createExclude();
+    }
+
+    /**
+     * @return the excludes patterns
+     */
+    public PatternSet getExcludes() {
+        return defaultPatterns;
+    }
+
+    /**
+     * The format to write the output in, "xml" or "text".
+     *
+     * @param ea xml or text
+     */
+    public void setFormat(FormatAttribute ea) {
+        format = ea.getValue();
+    }
+
+    /**
+     * A class for the enumerated attribute format,
+     * values are xml and text.
+     * @see EnumeratedAttribute
+     */
+    public static class FormatAttribute extends EnumeratedAttribute {
+        private String [] formats = new String[]{"xml", "text"};
+
+        /**
+         * @return the enumerated values
+         */
+        public String[] getValues() {
+            return formats;
+        }
+    }
+
+    /**
+     * No problems with this test.
+     */
+    private static final int SUCCESS = 0;
+    /**
+     * An error occurred.
+     */
+    private static final int ERRORS = 1;
+
+    /**
+     * Search for the given resource and add the directory or archive
+     * that contains it to the classpath.
+     *
+     * <p>Doesn't work for archives in JDK 1.1 as the URL returned by
+     * getResource doesn't contain the name of the archive.</p>
+     *
+     * @param resource resource that one wants to lookup
+     * @since Ant 1.6
+     */
+    private void addClasspathEntry(String resource) {
+        /*
+         * pre Ant 1.6 this method used to call getClass().getResource
+         * while Ant 1.6 will call ClassLoader.getResource().
+         *
+         * The difference is that Class.getResource expects a leading
+         * slash for "absolute" resources and will strip it before
+         * delegating to ClassLoader.getResource - so we now have to
+         * emulate Class's behavior.
+         */
+        if (resource.startsWith("/")) {
+            resource = resource.substring(1);
+        } else {
+            resource = "org/apache/tools/ant/taskdefs/optional/jdepend/"
+                + resource;
+        }
+
+        File f = LoaderUtils.getResourceSource(getClass().getClassLoader(),
+                                               resource);
+        if (f != null) {
+            log("Found " + f.getAbsolutePath(), Project.MSG_DEBUG);
+            runtimeClasses.createPath().setLocation(f);
+        } else {
+            log("Couldn\'t find " + resource, Project.MSG_DEBUG);
+        }
+    }
+
+    /**
+     * execute the task
+     *
+     * @exception BuildException if an error occurs
+     */
+    public void execute() throws BuildException {
+
+        CommandlineJava commandline = new CommandlineJava();
+
+        if ("text".equals(format)) {
+            commandline.setClassname("jdepend.textui.JDepend");
+        } else
+            if ("xml".equals(format)) {
+                commandline.setClassname("jdepend.xmlui.JDepend");
+            }
+
+        if (jvm != null) {
+            commandline.setVm(jvm);
+        }
+        if (getSourcespath() == null && getClassespath() == null) {
+            throw new BuildException("Missing classespath required argument");
+        } else if (getClassespath() == null) {
+            String msg =
+                "sourcespath is deprecated in JDepend >= 2.5 "
+                + "- please convert to classespath";
+            log(msg);
+        }
+
+        // execute the test and get the return code
+        int exitValue = JDependTask.ERRORS;
+        boolean wasKilled = false;
+        if (!getFork()) {
+            exitValue = executeInVM(commandline);
+        } else {
+            ExecuteWatchdog watchdog = createWatchdog();
+            exitValue = executeAsForked(commandline, watchdog);
+            // null watchdog means no timeout, you'd better not check with null
+            if (watchdog != null) {
+                wasKilled = watchdog.killedProcess();
+            }
+        }
+
+        // if there is an error/failure and that it should halt, stop
+        // everything otherwise just log a statement
+        boolean errorOccurred = exitValue == JDependTask.ERRORS || wasKilled;
+
+        if (errorOccurred) {
+            String errorMessage = "JDepend FAILED"
+                + (wasKilled ? " - Timed out" : "");
+
+            if  (getHaltonerror()) {
+                throw new BuildException(errorMessage, getLocation());
+            } else {
+                log(errorMessage, Project.MSG_ERR);
+            }
+        }
+    }
+
+    // this comment extract from JUnit Task may also apply here
+    // "in VM is not very nice since it could probably hang the
+    // whole build. IMHO this method should be avoided and it would be best
+    // to remove it in future versions. TBD. (SBa)"
+
+    /**
+     * Execute inside VM.
+     *
+     * @param commandline the command line
+     * @return the return value of the mvm
+     * @exception BuildException if an error occurs
+     */
+    public int executeInVM(CommandlineJava commandline) throws BuildException {
+        jdepend.textui.JDepend jdepend;
+
+        if ("xml".equals(format)) {
+            jdepend = new jdepend.xmlui.JDepend();
+        } else {
+            jdepend = new jdepend.textui.JDepend();
+        }
+
+        FileWriter fw = null;
+        if (getOutputFile() != null) {
+            try {
+                fw = new FileWriter(getOutputFile().getPath());
+            } catch (IOException e) {
+                String msg = "JDepend Failed when creating the output file: "
+                    + e.getMessage();
+                log(msg);
+                throw new BuildException(msg);
+            }
+            jdepend.setWriter(new PrintWriter(fw));
+            log("Output to be stored in " + getOutputFile().getPath());
+        }
+
+
+        try {
+            if (getClassespath() != null) {
+                // This is the new, better way - use classespath instead
+                // of sourcespath.  The code is currently the same - you
+                // need class files in a directory to use this - jar files
+                // coming soon....
+                String[] classesPath = getClassespath().list();
+                for (int i = 0; i < classesPath.length; i++) {
+                    File f = new File(classesPath[i]);
+                    // not necessary as JDepend would fail, but why loose
+                    // some time?
+                    if (!f.exists() || !f.isDirectory()) {
+                        String msg = "\""
+                            + f.getPath()
+                            + "\" does not represent a valid"
+                            + " directory. JDepend would fail.";
+                        log(msg);
+                        throw new BuildException(msg);
+                    }
+                    try {
+                        jdepend.addDirectory(f.getPath());
+                    } catch (IOException e) {
+                        String msg =
+                            "JDepend Failed when adding a class directory: "
+                            + e.getMessage();
+                        log(msg);
+                        throw new BuildException(msg);
+                    }
+                }
+
+            } else if (getSourcespath() != null) {
+
+                // This is the old way and is deprecated - classespath is
+                // the right way to do this and is above
+                String[] sourcesPath = getSourcespath().list();
+                for (int i = 0; i < sourcesPath.length; i++) {
+                    File f = new File(sourcesPath[i]);
+
+                    // not necessary as JDepend would fail, but why loose
+                    // some time?
+                    if (!f.exists() || !f.isDirectory()) {
+                        String msg = "\""
+                            + f.getPath()
+                            + "\" does not represent a valid"
+                            + " directory. JDepend would fail.";
+                        log(msg);
+                        throw new BuildException(msg);
+                    }
+                    try {
+                        jdepend.addDirectory(f.getPath());
+                    } catch (IOException e) {
+                        String msg =
+                            "JDepend Failed when adding a source directory: "
+                            + e.getMessage();
+                        log(msg);
+                        throw new BuildException(msg);
+                    }
+                }
+            }
+
+            // This bit turns <exclude> child tags into patters to ignore
+            String[] patterns = defaultPatterns.getExcludePatterns(getProject());
+            if (patterns != null && patterns.length > 0) {
+                if (setFilter != null) {
+                    Vector v = new Vector();
+                    for (int i = 0; i < patterns.length; i++) {
+                        v.addElement(patterns[i]);
+                    }
+                    try {
+                        Object o = packageFilterC.newInstance(new Object[] {v});
+                        setFilter.invoke(jdepend, new Object[] {o});
+                    } catch (Throwable e) {
+                        log("excludes will be ignored as JDepend doesn't like me: "
+                            + e.getMessage(), Project.MSG_WARN);
+                    }
+                } else {
+                    log("Sorry, your version of JDepend doesn't support excludes",
+                        Project.MSG_WARN);
+                }
+            }
+
+            jdepend.analyze();
+        } finally {
+            if (fw != null) {
+                try {
+                    fw.close();
+                } catch (Throwable t) {
+                    // Ignore
+                }
+            }
+        }
+        return SUCCESS;
+    }
+
+
+    /**
+     * Execute the task by forking a new JVM. The command will block until
+     * it finishes. To know if the process was destroyed or not, use the
+     * <tt>killedProcess()</tt> method of the watchdog class.
+     * @param commandline the commandline for forked jvm
+     * @param  watchdog   the watchdog in charge of cancelling the test if it
+     * exceeds a certain amount of time. Can be <tt>null</tt>.
+     * @return the result of running the jdepend
+     * @throws BuildException in case of error
+     */
+    // JL: comment extracted from JUnitTask (and slightly modified)
+    public int executeAsForked(CommandlineJava commandline,
+                               ExecuteWatchdog watchdog) throws BuildException {
+        runtimeClasses = new Path(getProject());
+        addClasspathEntry("/jdepend/textui/JDepend.class");
+
+        // if not set, auto-create the ClassPath from the project
+        createClasspath();
+
+        // not sure whether this test is needed but cost nothing to put.
+        // hope it will be reviewed by anybody competent
+        if (getClasspath().toString().length() > 0) {
+            createJvmarg(commandline).setValue("-classpath");
+            createJvmarg(commandline).setValue(getClasspath().toString());
+        }
+
+        if (includeRuntime) {
+            Vector v = Execute.getProcEnvironment();
+            Enumeration e = v.elements();
+            while (e.hasMoreElements()) {
+                String s = (String) e.nextElement();
+                if (s.startsWith("CLASSPATH=")) {
+                    commandline.createClasspath(getProject()).createPath()
+                        .append(new Path(getProject(),
+                                         s.substring("CLASSPATH=".length()
+                                                     )));
+                }
+            }
+            log("Implicitly adding " + runtimeClasses + " to CLASSPATH",
+                Project.MSG_VERBOSE);
+            commandline.createClasspath(getProject()).createPath()
+                .append(runtimeClasses);
+        }
+
+        if (getOutputFile() != null) {
+            // having a space between the file and its path causes commandline
+            // to add quotes around the argument thus making JDepend not taking
+            // it into account. Thus we split it in two
+            commandline.createArgument().setValue("-file");
+            commandline.createArgument().setValue(outputFile.getPath());
+            // we have to find a cleaner way to put this output
+        }
+
+        if (getSourcespath() != null) {
+            // This is deprecated - use classespath in the future
+            String[] sourcesPath = getSourcespath().list();
+            for (int i = 0; i < sourcesPath.length; i++) {
+                File f = new File(sourcesPath[i]);
+
+                // not necessary as JDepend would fail, but why loose
+                // some time?
+                if (!f.exists() || !f.isDirectory()) {
+                    throw new BuildException("\"" + f.getPath()
+                                             + "\" does not represent a valid"
+                                             + " directory. JDepend would"
+                                             + " fail.");
+                }
+                commandline.createArgument().setValue(f.getPath());
+            }
+        }
+
+        if (getClassespath() != null) {
+            // This is the new way - use classespath - code is the
+            // same for now
+            String[] classesPath = getClassespath().list();
+            for (int i = 0; i < classesPath.length; i++) {
+                File f = new File(classesPath[i]);
+                // not necessary as JDepend would fail, but why loose
+                // some time?
+                if (!f.exists() || !f.isDirectory()) {
+                    throw new BuildException("\"" + f.getPath()
+                                             + "\" does not represent a valid"
+                                             + " directory. JDepend would"
+                                             + " fail.");
+                }
+                commandline.createArgument().setValue(f.getPath());
+            }
+        }
+
+        Execute execute = new Execute(new LogStreamHandler(this,
+            Project.MSG_INFO, Project.MSG_WARN), watchdog);
+        execute.setCommandline(commandline.getCommandline());
+        if (getDir() != null) {
+            execute.setWorkingDirectory(getDir());
+            execute.setAntRun(getProject());
+        }
+
+        if (getOutputFile() != null) {
+            log("Output to be stored in " + getOutputFile().getPath());
+        }
+        log(commandline.describeCommand(), Project.MSG_VERBOSE);
+        try {
+            return execute.execute();
+        } catch (IOException e) {
+            throw new BuildException("Process fork failed.", e, getLocation());
+        }
+    }
+
+    /**
+     * @return <tt>null</tt> if there is a timeout value, otherwise the
+     * watchdog instance.
+     * @throws BuildException in case of error
+     */
+    protected ExecuteWatchdog createWatchdog() throws BuildException {
+        if (getTimeout() == null) {
+            return null;
+        }
+        return new ExecuteWatchdog(getTimeout().longValue());
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.java	(revision 16139)
@@ -0,0 +1,122 @@
+/*
+ * Copyright  2000,2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.jlink;
+
+import java.io.DataInput;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Reads just enough of a class file to determine the class' full name.
+ *
+ * <p>Extremely minimal constant pool implementation, mainly to support extracting
+ * strings from a class file.
+ */
+class ConstantPool {
+
+    static final
+        byte UTF8 = 1, UNUSED = 2, INTEGER = 3, FLOAT = 4, LONG = 5, DOUBLE = 6,
+        CLASS = 7, STRING = 8, FIELDREF = 9, METHODREF = 10,
+        INTERFACEMETHODREF = 11, NAMEANDTYPE = 12;
+
+    byte[] types;
+
+    Object[] values;
+
+    ConstantPool(DataInput data) throws IOException {
+        super();
+
+        int count = data.readUnsignedShort();
+        types = new byte [ count ];
+        values = new Object [ count ];
+        // read in all constant pool entries.
+        for (int i = 1; i < count; i++) {
+            byte type = data.readByte();
+            types[i] = type;
+            switch (type) {
+            case UTF8 :
+                values[i] = data.readUTF();
+                break;
+
+            case UNUSED :
+                break;
+
+            case INTEGER :
+                values[i] = new Integer(data.readInt());
+                break;
+
+            case FLOAT :
+                values[i] = new Float(data.readFloat());
+                break;
+
+            case LONG :
+                values[i] = new Long(data.readLong());
+                ++i;
+                break;
+
+            case DOUBLE :
+                values[i] = new Double(data.readDouble());
+                ++i;
+                break;
+
+            case CLASS :
+            case STRING :
+                values[i] = new Integer(data.readUnsignedShort());
+                break;
+
+            case FIELDREF :
+            case METHODREF :
+            case INTERFACEMETHODREF :
+            case NAMEANDTYPE :
+                values[i] = new Integer(data.readInt());
+                break;
+            }
+        }
+    }
+}
+
+/**
+ * Provides a quick and dirty way to determine the true name of a class
+ * given just an InputStream. Reads in just enough to perform this
+ * minimal task only.
+ */
+public class ClassNameReader extends Object {
+
+    public static String getClassName(InputStream input) throws IOException {
+        DataInputStream data = new DataInputStream(input);
+        // verify this is a valid class file.
+        int cookie = data.readInt();
+        if (cookie != 0xCAFEBABE) {
+            return null;
+        }
+        /* int version = */ data.readInt();
+        // read the constant pool.
+        ConstantPool constants = new ConstantPool(data);
+        Object[] values = constants.values;
+        // read access flags and class index.
+        /* int accessFlags = */ data.readUnsignedShort();
+        int classIndex = data.readUnsignedShort();
+        Integer stringIndex = (Integer) values[classIndex];
+        String className = (String) values[stringIndex.intValue()];
+        return className;
+    }
+
+
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java	(revision 16139)
@@ -0,0 +1,174 @@
+/*
+ * Copyright  2000,2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.jlink;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * This class defines objects that can link together various jar and
+ * zip files.
+ *
+ * <p>It is basically a wrapper for the jlink code written originally
+ * by <a href="mailto:beard@netscape.com">Patrick Beard</a>.  The
+ * classes org.apache.tools.ant.taskdefs.optional.jlink.Jlink and
+ * org.apache.tools.ant.taskdefs.optional.jlink.ClassNameReader
+ * support this class.</p>
+ *
+ * <p>For example:
+ * <code>
+ * <pre>
+ * &lt;jlink compress=&quot;false&quot; outfile=&quot;out.jar&quot;/&gt;
+ *   &lt;mergefiles&gt;
+ *     &lt;pathelement path=&quot;${build.dir}/mergefoo.jar&quot;/&gt;
+ *     &lt;pathelement path=&quot;${build.dir}/mergebar.jar&quot;/&gt;
+ *   &lt;/mergefiles&gt;
+ *   &lt;addfiles&gt;
+ *     &lt;pathelement path=&quot;${build.dir}/mac.jar&quot;/&gt;
+ *     &lt;pathelement path=&quot;${build.dir}/pc.zip&quot;/&gt;
+ *   &lt;/addfiles&gt;
+ * &lt;/jlink&gt;
+ * </pre>
+ * </code>
+ *
+ * @ant.task ignore="true"
+ */
+public class JlinkTask extends MatchingTask {
+
+    /**
+     * The output file for this run of jlink. Usually a jar or zip file.
+     */
+    public  void setOutfile(File outfile) {
+        this.outfile = outfile;
+    }
+
+    /**
+     * Establishes the object that contains the files to
+     * be merged into the output.
+     */
+    public  Path createMergefiles() {
+        if (this.mergefiles == null) {
+            this.mergefiles = new Path(getProject());
+        }
+        return this.mergefiles.createPath();
+    }
+
+    /**
+     * Sets the files to be merged into the output.
+     */
+    public  void setMergefiles(Path mergefiles) {
+        if (this.mergefiles == null) {
+            this.mergefiles = mergefiles;
+        } else {
+            this.mergefiles.append(mergefiles);
+        }
+    }
+
+    /**
+     * Establishes the object that contains the files to
+     * be added to the output.
+     */
+    public  Path createAddfiles() {
+        if (this.addfiles == null) {
+            this.addfiles = new Path(getProject());
+        }
+        return this.addfiles.createPath();
+    }
+
+    /**
+     * Sets the files to be added into the output.
+     */
+    public  void setAddfiles(Path addfiles) {
+        if (this.addfiles == null) {
+            this.addfiles = addfiles;
+        } else {
+            this.addfiles.append(addfiles);
+        }
+    }
+
+    /**
+     * Defines whether or not the output should be compacted.
+     */
+    public  void setCompress(boolean compress) {
+        this.compress = compress;
+    }
+
+    /**
+     * Does the adding and merging.
+     */
+    public  void execute() throws BuildException {
+        //Be sure everything has been set.
+        if (outfile == null) {
+            throw new BuildException("outfile attribute is required! "
+                + "Please set.");
+        }
+        if (!haveAddFiles() && !haveMergeFiles()) {
+            throw new BuildException("addfiles or mergefiles required! "
+                + "Please set.");
+        }
+        log("linking:     " + outfile.getPath());
+        log("compression: " + compress, Project.MSG_VERBOSE);
+        jlink linker = new jlink();
+        linker.setOutfile(outfile.getPath());
+        linker.setCompression(compress);
+        if (haveMergeFiles()) {
+            log("merge files: " + mergefiles.toString(), Project.MSG_VERBOSE);
+            linker.addMergeFiles(mergefiles.list());
+        }
+        if (haveAddFiles()) {
+            log("add files: " + addfiles.toString(), Project.MSG_VERBOSE);
+            linker.addAddFiles(addfiles.list());
+        }
+        try  {
+            linker.link();
+        } catch (Exception ex) {
+            throw new BuildException(ex, getLocation());
+        }
+    }
+
+    private boolean haveAddFiles() {
+        return haveEntries(addfiles);
+    }
+
+    private boolean haveMergeFiles() {
+        return haveEntries(mergefiles);
+    }
+
+    private boolean haveEntries(Path p) {
+        if (p == null) {
+            return false;
+        }
+        if (p.size() > 0) {
+            return true;
+        }
+        return false;
+    }
+
+    private  File outfile = null;
+
+    private  Path mergefiles = null;
+
+    private  Path addfiles = null;
+
+    private  boolean compress = false;
+
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java	(revision 16139)
@@ -0,0 +1,426 @@
+/*
+ * Copyright  2000,2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/**
+ * jlink.java links together multiple .jar files Original code by Patrick
+ * Beard. Modifications to work with ANT by Matthew Kuperus Heun.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.jlink;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+import java.util.zip.ZipOutputStream;
+
+public class jlink extends Object {
+
+    /** The file that will be created by this instance of jlink.  */
+    public void setOutfile(String outfile) {
+        if (outfile == null) {
+            return;
+        }
+        this.outfile = outfile;
+    }
+
+
+    /** Adds a file to be merged into the output.  */
+    public void addMergeFile(String mergefile) {
+        if (mergefile == null) {
+            return;
+        }
+        mergefiles.addElement(mergefile);
+    }
+
+
+    /** Adds a file to be added into the output.  */
+    public void addAddFile(String addfile) {
+        if (addfile == null) {
+            return;
+        }
+        addfiles.addElement(addfile);
+    }
+
+
+    /** Adds several files to be merged into the output.  */
+    public void addMergeFiles(String[] mergefiles) {
+        if (mergefiles == null) {
+            return;
+        }
+        for (int i = 0; i < mergefiles.length; i++) {
+            addMergeFile(mergefiles[i]);
+        }
+    }
+
+
+    /** Adds several file to be added into the output.  */
+    public void addAddFiles(String[] addfiles) {
+        if (addfiles == null) {
+            return;
+        }
+        for (int i = 0; i < addfiles.length; i++) {
+            addAddFile(addfiles[i]);
+        }
+    }
+
+
+    /** Determines whether output will be compressed.  */
+    public void setCompression(boolean compress) {
+        this.compression = compress;
+    }
+
+
+    /**
+     * Performs the linking of files. Addfiles are added to the output as-is.
+     * For example, a jar file is added to the output as a jar file. However,
+     * mergefiles are first examined for their type. If it is a jar or zip
+     * file, the contents will be extracted from the mergefile and entered
+     * into the output. If a zip or jar file is encountered in a subdirectory
+     * it will be added, not merged. If a directory is encountered, it becomes
+     * the root entry of all the files below it. Thus, you can provide
+     * multiple, disjoint directories, as addfiles: they will all be added in
+     * a rational manner to outfile.
+     */
+    public void link() throws Exception {
+        ZipOutputStream output = new ZipOutputStream(new FileOutputStream(outfile));
+
+        if (compression) {
+            output.setMethod(ZipOutputStream.DEFLATED);
+            output.setLevel(Deflater.DEFAULT_COMPRESSION);
+        } else {
+            output.setMethod(ZipOutputStream.STORED);
+        }
+
+        Enumeration merges = mergefiles.elements();
+
+        while (merges.hasMoreElements()) {
+            String path = (String) merges.nextElement();
+            File f = new File(path);
+
+            if (f.getName().endsWith(".jar") || f.getName().endsWith(".zip")) {
+                //Do the merge
+                mergeZipJarContents(output, f);
+            } else {
+                //Add this file to the addfiles Vector and add it
+                //later at the top level of the output file.
+                addAddFile(path);
+            }
+        }
+
+        Enumeration adds = addfiles.elements();
+
+        while (adds.hasMoreElements()) {
+            String name = (String) adds.nextElement();
+            File f = new File(name);
+
+            if (f.isDirectory()) {
+                //System.out.println("in jlink: adding directory contents of " + f.getPath());
+                addDirContents(output, f, f.getName() + '/', compression);
+            } else {
+                addFile(output, f, "", compression);
+            }
+        }
+        if (output != null) {
+            try {
+                output.close();
+            } catch (IOException ioe) {
+            }
+        }
+    }
+
+
+    public static void main(String[] args) {
+        // jlink output input1 ... inputN
+        if (args.length < 2) {
+            System.out.println("usage: jlink output input1 ... inputN");
+            System.exit(1);
+        }
+        jlink linker = new jlink();
+
+        linker.setOutfile(args[0]);
+        // To maintain compatibility with the command-line version,
+        // we will only add files to be merged.
+        for (int i = 1; i < args.length; i++) {
+            linker.addMergeFile(args[i]);
+        }
+        try {
+            linker.link();
+        } catch (Exception ex) {
+            System.err.print(ex.getMessage());
+        }
+    }
+
+
+    /*
+     * Actually performs the merging of f into the output.
+     * f should be a zip or jar file.
+     */
+    private void mergeZipJarContents(ZipOutputStream output, File f) throws IOException {
+        //Check to see that the file with name "name" exists.
+        if (!f.exists()) {
+            return;
+        }
+        ZipFile zipf = new ZipFile(f);
+        Enumeration entries = zipf.entries();
+
+        while (entries.hasMoreElements()) {
+            ZipEntry inputEntry = (ZipEntry) entries.nextElement();
+            //Ignore manifest entries.  They're bound to cause conflicts between
+            //files that are being merged.  User should supply their own
+            //manifest file when doing the merge.
+            String inputEntryName = inputEntry.getName();
+            int index = inputEntryName.indexOf("META-INF");
+
+            if (index < 0) {
+                //META-INF not found in the name of the entry. Go ahead and process it.
+                try {
+                    output.putNextEntry(processEntry(zipf, inputEntry));
+                } catch (ZipException ex) {
+                    //If we get here, it could be because we are trying to put a
+                    //directory entry that already exists.
+                    //For example, we're trying to write "com", but a previous
+                    //entry from another mergefile was called "com".
+                    //In that case, just ignore the error and go on to the
+                    //next entry.
+                    String mess = ex.getMessage();
+
+                    if (mess.indexOf("duplicate") >= 0) {
+                        //It was the duplicate entry.
+                        continue;
+                    } else {
+                        // I hate to admit it, but we don't know what happened
+                        // here.  Throw the Exception.
+                        throw ex;
+                    }
+                }
+
+                InputStream in = zipf.getInputStream(inputEntry);
+                int len = buffer.length;
+                int count = -1;
+
+                while ((count = in.read(buffer, 0, len)) > 0) {
+                    output.write(buffer, 0, count);
+                }
+                in.close();
+                output.closeEntry();
+            }
+        }
+        zipf.close();
+    }
+
+
+    /*
+     * Adds contents of a directory to the output.
+     */
+    private void addDirContents(ZipOutputStream output, File dir, String prefix,
+                                boolean compress) throws IOException {
+        String[] contents = dir.list();
+
+        for (int i = 0; i < contents.length; ++i) {
+            String name = contents[i];
+            File file = new File(dir, name);
+
+            if (file.isDirectory()) {
+                addDirContents(output, file, prefix + name + '/', compress);
+            } else {
+                addFile(output, file, prefix, compress);
+            }
+        }
+    }
+
+
+    /*
+     * Gets the name of an entry in the file.  This is the real name
+     * which for a class is the name of the package with the class
+     * name appended.
+     */
+    private String getEntryName(File file, String prefix) {
+        String name = file.getName();
+
+        if (!name.endsWith(".class")) {
+            // see if the file is in fact a .class file, and determine its actual name.
+            InputStream input = null;
+            try {
+                input = new FileInputStream(file);
+                String className = ClassNameReader.getClassName(input);
+
+                if (className != null) {
+                    return className.replace('.', '/') + ".class";
+                }
+            } catch (IOException ioe) {
+            } finally {
+                if (input != null) {
+                    try {
+                        input.close();
+                    } catch (IOException e) {
+                    }
+                }
+            }
+        }
+        System.out.println("From " + file.getPath() + " and prefix " + prefix
+                           + ", creating entry " + prefix + name);
+        return (prefix + name);
+    }
+
+
+    /*
+     * Adds a file to the output stream.
+     */
+    private void addFile(ZipOutputStream output, File file, String prefix,
+                         boolean compress) throws IOException {
+        //Make sure file exists
+        if (!file.exists()) {
+            return;
+        }
+        ZipEntry entry = new ZipEntry(getEntryName(file, prefix));
+
+        entry.setTime(file.lastModified());
+        entry.setSize(file.length());
+        if (!compress) {
+            entry.setCrc(calcChecksum(file));
+        }
+        FileInputStream input = new FileInputStream(file);
+
+        addToOutputStream(output, input, entry);
+    }
+
+
+    /*
+     * A convenience method that several other methods might call.
+     */
+    private void addToOutputStream(ZipOutputStream output, InputStream input,
+                                   ZipEntry ze) throws IOException {
+        try {
+            output.putNextEntry(ze);
+        } catch (ZipException zipEx) {
+            //This entry already exists. So, go with the first one.
+            input.close();
+            return;
+        }
+
+        int numBytes = -1;
+
+        while ((numBytes = input.read(buffer)) > 0) {
+            output.write(buffer, 0, numBytes);
+        }
+        output.closeEntry();
+        input.close();
+    }
+
+
+    /*
+     * A method that does the work on a given entry in a mergefile.
+     * The big deal is to set the right parameters in the ZipEntry
+     * on the output stream.
+     */
+    private ZipEntry processEntry(ZipFile zip, ZipEntry inputEntry) throws IOException {
+        /*
+          First, some notes.
+          On MRJ 2.2.2, getting the size, compressed size, and CRC32 from the
+          ZipInputStream does not work for compressed (deflated) files.  Those calls return -1.
+          For uncompressed (stored) files, those calls do work.
+          However, using ZipFile.getEntries() works for both compressed and
+          uncompressed files.
+
+          Now, from some simple testing I did, it seems that the value of CRC-32 is
+          independent of the compression setting. So, it should be easy to pass this
+          information on to the output entry.
+        */
+        String name = inputEntry.getName();
+
+        if (!(inputEntry.isDirectory() || name.endsWith(".class"))) {
+            try {
+                InputStream input = zip.getInputStream(zip.getEntry(name));
+                String className = ClassNameReader.getClassName(input);
+
+                input.close();
+                if (className != null) {
+                    name = className.replace('.', '/') + ".class";
+                }
+            } catch (IOException ioe) {
+            }
+        }
+        ZipEntry outputEntry = new ZipEntry(name);
+
+        outputEntry.setTime(inputEntry.getTime());
+        outputEntry.setExtra(inputEntry.getExtra());
+        outputEntry.setComment(inputEntry.getComment());
+        outputEntry.setTime(inputEntry.getTime());
+        if (compression) {
+            outputEntry.setMethod(ZipEntry.DEFLATED);
+            //Note, don't need to specify size or crc for compressed files.
+        } else {
+            outputEntry.setMethod(ZipEntry.STORED);
+            outputEntry.setCrc(inputEntry.getCrc());
+            outputEntry.setSize(inputEntry.getSize());
+        }
+        return outputEntry;
+    }
+
+
+    /*
+     * Necessary in the case where you add a entry that
+     * is not compressed.
+     */
+    private long calcChecksum(File f) throws IOException {
+        BufferedInputStream in = new BufferedInputStream(new FileInputStream(f));
+
+        return calcChecksum(in);
+    }
+
+
+    /*
+     * Necessary in the case where you add a entry that
+     * is not compressed.
+     */
+    private long calcChecksum(InputStream in) throws IOException {
+        CRC32 crc = new CRC32();
+        int len = buffer.length;
+        int count = -1;
+        int haveRead = 0;
+
+        while ((count = in.read(buffer, 0, len)) > 0) {
+            haveRead += count;
+            crc.update(buffer, 0, count);
+        }
+        in.close();
+        return crc.getValue();
+    }
+
+
+    private String outfile = null;
+
+    private Vector mergefiles = new Vector(10);
+
+    private Vector addfiles = new Vector(10);
+
+    private boolean compression = false;
+
+    byte[] buffer = new byte[8192];
+
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/Jasper41Mangler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/Jasper41Mangler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/Jasper41Mangler.java	(revision 16139)
@@ -0,0 +1,89 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.jsp;
+
+import java.io.File;
+
+/**
+ * this class implements the name mangling rules of the jasper in tomcat4.1.x
+ * which is likely to remain for some time
+ * @see org.apache.jasper.JspCompilationContext
+ */
+public class Jasper41Mangler implements JspMangler {
+
+
+    /**
+     * map from a jsp file to a java filename; does not do packages
+     *
+     * @param jspFile file
+     * @return java filename
+     */
+    public String mapJspToJavaName(File jspFile) {
+        String jspUri = jspFile.getAbsolutePath();
+        int start = jspUri.lastIndexOf(File.separatorChar) + 1;
+        int end = jspUri.length();
+        StringBuffer modifiedClassName;
+        modifiedClassName = new StringBuffer(jspUri.length() - start);
+        if (!Character.isJavaIdentifierStart(jspUri.charAt(start))
+            || jspUri.charAt(start) == '_') {
+            // If the first char is not a start of Java identifier or is _
+            // prepend a '_'.
+            modifiedClassName.append('_');
+        }
+        for (int i = start; i < end; i++) {
+            char ch = jspUri.charAt(i);
+            if (Character.isJavaIdentifierPart(ch)) {
+                modifiedClassName.append(ch);
+            } else if (ch == '.') {
+                modifiedClassName.append('_');
+            } else {
+                modifiedClassName.append(mangleChar(ch));
+            }
+        }
+        return modifiedClassName.toString();
+    }
+
+    /**
+     * Mangle the specified character to create a legal Java class name.
+     */
+    private static final String mangleChar(char ch) {
+
+        String s = Integer.toHexString(ch);
+        int nzeros = 5 - s.length();
+        char[] result = new char[6];
+        result[0] = '_';
+        for (int i = 1; i <= nzeros; i++) {
+            result[i] = '0';
+        }
+        for (int i = nzeros + 1, j = 0; i < 6; i++, j++) {
+            result[i] = s.charAt(j);
+        }
+        return new String(result);
+    }
+
+
+    /**
+     * taking in the substring representing the path relative to the source dir
+     * return a new string representing the destination path
+     * @todo
+     */
+    public String mapPath(String path) {
+        return null;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspC.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspC.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspC.java	(revision 16139)
@@ -0,0 +1,614 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.jsp;
+
+import java.io.File;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.taskdefs.optional.jsp.compilers.JspCompilerAdapter;
+import org.apache.tools.ant.taskdefs.optional.jsp.compilers.JspCompilerAdapterFactory;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Runs a JSP compiler.
+ *
+ * <p> This task takes the given jsp files and compiles them into java
+ * files. It is then up to the user to compile the java files into classes.
+ *
+ * <p> The task requires the srcdir and destdir attributes to be
+ * set. This Task is a MatchingTask, so the files to be compiled can be
+ * specified using includes/excludes attributes or nested include/exclude
+ * elements. Optional attributes are verbose (set the verbosity level passed
+ * to jasper), package (name of the destination package for generated java
+ * classes and classpath (the classpath to use when running the jsp
+ * compiler).
+ * <p> This task supports the nested elements classpath (A Path) and
+ * classpathref (A Reference) which can be used in preference to the
+ * attribute classpath, if the jsp compiler is not already in the ant
+ * classpath.
+ *
+ * <p><h4>Usage</h4>
+ * <pre>
+ * &lt;jspc srcdir="${basedir}/src/war"
+ *       destdir="${basedir}/gensrc"
+ *       package="com.i3sp.jsp"
+ *       verbose="9"&gt;
+ *   &lt;include name="**\/*.jsp" /&gt;
+ * &lt;/jspc&gt;
+ * </pre>
+ *
+ * @since 1.5
+ */
+public class JspC extends MatchingTask {
+    private Path classpath;
+    private Path compilerClasspath;
+    private Path src;
+    private File destDir;
+    private String packageName ;
+    /** name of the compiler to use */
+    private String compilerName = "jasper";
+
+    /**
+     *  -ieplugin &lt;clsid&gt; Java Plugin classid for Internet Explorer
+     */
+    private String iepluginid ;
+    private boolean mapped ;
+    private int verbose = 0;
+    protected Vector compileList = new Vector();
+    Vector javaFiles = new Vector();
+
+    /**
+     *  flag to control action on execution trouble
+     */
+    protected boolean failOnError = true;
+
+    /**
+     *  -uriroot &lt;dir&gt; The root directory that uri files should be resolved
+     *  against,
+     */
+    private File uriroot;
+
+    /**
+     *  -webinc &lt;file&gt; Creates partial servlet mappings for the -webapp option
+     */
+    private File webinc;
+
+    /**
+     *  -webxml &lt;file&gt; Creates a complete web.xml when using the -webapp option.
+     */
+
+    private File webxml;
+
+    /**
+     *  web apps
+     */
+    protected WebAppParameter webApp;
+
+
+
+    private static final String FAIL_MSG
+        = "Compile failed, messages should have been provided.";
+
+    /**
+     * Set the path for source JSP files.
+     */
+    public void setSrcDir(Path srcDir) {
+        if (src == null) {
+            src = srcDir;
+        } else {
+            src.append(srcDir);
+        }
+    }
+    public Path getSrcDir() {
+        return src;
+    }
+
+    /**
+     * Set the destination directory into which the JSP source
+     * files should be compiled.
+     */
+    public void setDestdir(File destDir) {
+        this.destDir = destDir;
+    }
+    public File getDestdir() {
+        return destDir;
+    }
+
+    /**
+     * Set the name of the package the compiled jsp files should be in.
+     */
+    public void setPackage(String pkg) {
+        this.packageName = pkg;
+    }
+
+    public String getPackage() {
+        return packageName;
+    }
+
+    /**
+     * Set the verbose level of the compiler
+     */
+    public void setVerbose(int i) {
+        verbose = i;
+    }
+    public int getVerbose() {
+        return verbose;
+    }
+
+    /**
+     * Whether or not the build should halt if compilation fails.
+     * Defaults to <code>true</code>.
+     */
+    public void setFailonerror(boolean fail) {
+        failOnError = fail;
+    }
+    /**
+     * Gets the failonerror flag.
+     */
+    public boolean getFailonerror() {
+        return failOnError;
+    }
+
+    public String getIeplugin() {
+        return iepluginid;
+    }
+    /**
+     * Java Plugin CLASSID for Internet Explorer
+     */
+    public void setIeplugin(String iepluginid) {
+        this.iepluginid = iepluginid;
+    }
+
+    /**
+     * If true, generate separate write() calls for each HTML line
+     * in the JSP.
+     * @return mapping status
+     */
+    public boolean isMapped() {
+        return mapped;
+    }
+
+    /**
+     * If true, generate separate write() calls for each HTML line
+     * in the JSP.
+     */
+    public void setMapped(boolean mapped) {
+        this.mapped = mapped;
+    }
+
+    /**
+     * The URI context of relative URI references in the JSP pages.
+     * If it does not exist then it is derived from the location
+     * of the file relative to the declared or derived value of uriroot.
+     *
+     * @param  uribase  The new Uribase value
+     */
+    public void setUribase(File uribase) {
+        log("Uribase is currently an unused parameter", Project.MSG_WARN);
+    }
+
+    public File getUribase() {
+        return uriroot;
+    }
+
+    /**
+     *  The root directory that uri files should be resolved
+     *  against. (Default is the directory jspc is invoked from)
+     *
+     * @param  uriroot  The new Uribase value
+     */
+    public void setUriroot(File uriroot) {
+        this.uriroot = uriroot;
+    }
+
+    public File getUriroot() {
+        return uriroot;
+    }
+
+
+    /**
+     * Set the classpath to be used for this compilation.
+     */
+    public void setClasspath(Path cp) {
+        if (classpath == null) {
+            classpath = cp;
+        } else {
+            classpath.append(cp);
+        }
+    }
+
+    /**
+     * Adds a path to the classpath.
+     */
+    public Path createClasspath() {
+        if (classpath == null) {
+            classpath = new Path(getProject());
+        }
+        return classpath.createPath();
+    }
+
+    /**
+     * Adds a reference to a classpath defined elsewhere
+     */
+    public void setClasspathRef(Reference r) {
+        createClasspath().setRefid(r);
+    }
+    public Path getClasspath() {
+        return classpath;
+    }
+
+    /**
+     * Set the classpath to be used to find this compiler adapter
+     */
+    public void setCompilerclasspath(Path cp) {
+        if (compilerClasspath == null) {
+            compilerClasspath = cp;
+        } else {
+            compilerClasspath.append(cp);
+        }
+    }
+
+    /**
+     * get the classpath used to find the compiler adapter
+     */
+    public Path getCompilerclasspath() {
+        return compilerClasspath;
+    }
+
+    /**
+     * Support nested compiler classpath, used to locate compiler adapter
+     */
+    public Path createCompilerclasspath() {
+        if (compilerClasspath == null) {
+            compilerClasspath = new Path(getProject());
+        }
+        return compilerClasspath.createPath();
+    }
+
+    /**
+     *  Filename for web.xml.
+     *
+     * @param  webxml  The new Webxml value
+     */
+    public void setWebxml(File webxml) {
+        this.webxml = webxml;
+    }
+
+    /**
+     * Filename for web.xml.
+     * @return The filename for web.xml.
+     */
+    public File getWebxml() {
+        return this.webxml;
+    }
+
+    /**
+     *  output filename for the fraction of web.xml that lists
+     *  servlets.
+     * @param  webinc  The new Webinc value
+     */
+    public void setWebinc(File webinc) {
+        this.webinc = webinc;
+    }
+
+    public File getWebinc() {
+        return this.webinc;
+    }
+
+    /**
+     * Adds a single webapp.
+     *
+     * @param  webappParam  add a web app parameter
+     */
+    public void addWebApp(WebAppParameter webappParam)
+        throws BuildException {
+        //demand create vector of filesets
+        if (webApp == null) {
+            webApp = webappParam;
+        } else {
+            throw new BuildException("Only one webapp can be specified");
+        }
+    }
+
+    public WebAppParameter getWebApp() {
+        return webApp;
+    }
+
+    /**
+     * Class name of a JSP compiler adapter.
+     */
+    public void setCompiler(String compiler) {
+        this.compilerName = compiler;
+    }
+
+    /**
+    * get the list of files to compile
+    */
+    public Vector getCompileList() {
+        return compileList;
+    }
+
+    /**
+     * execute by building up a list of files that
+     * have changed and hand them off to a jsp compiler
+     */
+    public void execute()
+        throws BuildException {
+
+        // make sure that we've got a destdir
+        if (destDir == null) {
+            throw new BuildException("destdir attribute must be set!",
+                                     getLocation());
+        }
+
+        if (!destDir.isDirectory()) {
+            throw new BuildException("destination directory \"" + destDir
+                    + "\" does not exist or is not a directory", getLocation());
+        }
+
+        File dest = getActualDestDir();
+
+        //bind to a compiler
+        JspCompilerAdapter compiler =
+            JspCompilerAdapterFactory.getCompiler(compilerName, this,
+                getProject().createClassLoader(compilerClasspath));
+
+        //if we are a webapp, hand off to the compiler, which had better handle it
+        if (webApp != null) {
+            doCompilation(compiler);
+            return;
+        }
+
+        // make sure that we've got a srcdir
+        if (src == null) {
+            throw new BuildException("srcdir attribute must be set!",
+                                     getLocation());
+        }
+        String [] list = src.list();
+        if (list.length == 0) {
+            throw new BuildException("srcdir attribute must be set!",
+                    getLocation());
+        }
+
+
+        // if the compiler does its own dependency stuff, we just call it right now
+        if (compiler.implementsOwnDependencyChecking()) {
+            doCompilation(compiler);
+            return;
+        }
+
+        //the remainder of this method is only for compilers that need their dependency work done
+        JspMangler mangler = compiler.createMangler();
+
+        // scan source directories and dest directory to build up both copy
+        // lists and compile lists
+        resetFileLists();
+        int filecount = 0;
+        for (int i = 0; i < list.length; i++) {
+            File srcDir = getProject().resolveFile(list[i]);
+            if (!srcDir.exists()) {
+                throw new BuildException("srcdir \"" + srcDir.getPath()
+                    + "\" does not exist!", getLocation());
+            }
+            DirectoryScanner ds = this.getDirectoryScanner(srcDir);
+            String[] files = ds.getIncludedFiles();
+            filecount = files.length;
+            scanDir(srcDir, dest, mangler, files);
+        }
+
+        // compile the source files
+
+        log("compiling " + compileList.size() + " files", Project.MSG_VERBOSE);
+
+        if (compileList.size() > 0) {
+
+            log("Compiling " + compileList.size() + " source file"
+                + (compileList.size() == 1 ? "" : "s")
+                + " to "
+                + dest);
+            doCompilation(compiler);
+
+        } else {
+            if (filecount == 0) {
+                log("there were no files to compile", Project.MSG_INFO);
+            } else {
+                log("all files are up to date", Project.MSG_VERBOSE);
+            }
+        }
+    }
+
+    /**
+     * calculate where the files will end up:
+     * this is destDir or it id destDir + the package name
+     */
+    private File getActualDestDir() {
+        File dest = null;
+        if (packageName == null) {
+            dest = destDir;
+        } else {
+            String path = destDir.getPath() + File.separatorChar
+                + packageName.replace('.', File.separatorChar);
+            dest = new File(path);
+        }
+        return dest;
+    }
+
+    /**
+     * do the compile
+     */
+    private void doCompilation(JspCompilerAdapter compiler)
+            throws BuildException {
+        // now we need to populate the compiler adapter
+        compiler.setJspc(this);
+
+        // finally, lets execute the compiler!!
+        if (!compiler.execute()) {
+            if (failOnError) {
+                throw new BuildException(FAIL_MSG, getLocation());
+            } else {
+                log(FAIL_MSG, Project.MSG_ERR);
+            }
+        }
+    }
+
+    /**
+     * Clear the list of files to be compiled and copied..
+     */
+    protected void resetFileLists() {
+        compileList.removeAllElements();
+    }
+
+    /**
+     * Scans the directory looking for source files to be compiled.
+     * The results are returned in the class variable compileList
+     */
+    protected void scanDir(File srcDir, File dest, JspMangler mangler, String files[]) {
+
+        long now = (new Date()).getTime();
+
+        for (int i = 0; i < files.length; i++) {
+            String filename = files[i];
+            File srcFile = new File(srcDir, filename);
+            File javaFile = mapToJavaFile(mangler, srcFile, srcDir, dest);
+            if (javaFile == null) {
+                continue;
+            }
+
+            if (srcFile.lastModified() > now) {
+                log("Warning: file modified in the future: " + filename,
+                        Project.MSG_WARN);
+            }
+            boolean shouldCompile = false;
+            shouldCompile = isCompileNeeded(srcFile, javaFile);
+            if (shouldCompile) {
+               compileList.addElement(srcFile.getAbsolutePath());
+               javaFiles.addElement(javaFile);
+            }
+        }
+    }
+
+    /**
+     * Test whether or not compilation is needed. A return value of
+     * <code>true<code> means yes, <code>false</code> means
+     * our tests do not indicate this, but as the TLDs are
+     * not used for dependency checking this is not guaranteed.
+     * The current tests are
+     * <ol>
+     * <li>no dest file
+     * <li>dest file out of date w.r.t source
+     * <li>dest file zero bytes long
+     * </ol>
+     * @param srcFile JSP source file
+     * @param javaFile JSP dest file
+     * @return true if a compile is definately needed.
+     *
+     */
+    private boolean isCompileNeeded(File srcFile, File javaFile) {
+        boolean shouldCompile = false;
+        if (!javaFile.exists()) {
+            shouldCompile = true;
+            log("Compiling " + srcFile.getPath()
+                + " because java file " + javaFile.getPath()
+                + " does not exist", Project.MSG_VERBOSE);
+            } else {
+                if (srcFile.lastModified() > javaFile.lastModified()) {
+                    shouldCompile = true;
+                    log("Compiling " + srcFile.getPath()
+                        + " because it is out of date with respect to "
+                        + javaFile.getPath(),
+                        Project.MSG_VERBOSE);
+                } else {
+                    if (javaFile.length() == 0) {
+                        shouldCompile = true;
+                        log("Compiling " + srcFile.getPath()
+                            + " because java file " + javaFile.getPath()
+                            + " is empty", Project.MSG_VERBOSE);
+                    }
+                }
+        }
+        return shouldCompile;
+    }
+
+
+    /**
+     * get a filename from our jsp file
+     * @todo support packages and subdirs
+     */
+    protected File mapToJavaFile(JspMangler mangler, File srcFile, File srcDir, File dest) {
+        if (!srcFile.getName().endsWith(".jsp")) {
+            return null;
+        }
+        String javaFileName = mangler.mapJspToJavaName(srcFile);
+//        String srcFileDir=srcFile.getParent();
+        return new File(dest, javaFileName);
+    }
+
+    /**
+     * delete any java output files that are empty
+     * this is to get around a little defect in jasper: when it
+     * fails, it leaves incomplete files around.
+     */
+    public void deleteEmptyJavaFiles() {
+        if (javaFiles != null) {
+            Enumeration e = javaFiles.elements();
+            while (e.hasMoreElements()) {
+                File file = (File) e.nextElement();
+                if (file.exists() && file.length() == 0) {
+                    log("deleting empty output file " + file);
+                    file.delete();
+                }
+            }
+        }
+    }
+
+    /**
+     * static inner class used as a parameter element
+     */
+    public static class WebAppParameter {
+
+        /**
+         * the sole option
+         */
+        private File directory;
+
+        /**
+         * query current directory
+         */
+
+        public File getDirectory() {
+            return directory;
+        }
+
+        /**
+         * set directory; alternate syntax
+         */
+        public void setBaseDir(File directory) {
+            this.directory = directory;
+        }
+    //end inner class
+    }
+
+
+//end class
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspMangler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspMangler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspMangler.java	(revision 16139)
@@ -0,0 +1,44 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.jsp;
+
+import java.io.File;
+
+/**
+ * This is an interface to the Mangler service that jspc needs to map
+ * JSP file names to java files.
+ * Note the complete lack of correlation
+ * with Jasper's mangler interface.
+ */
+public interface JspMangler {
+
+
+    /**
+     * map from a jsp file to a java filename; does not do packages
+     *
+     * @param jspFile file
+     * @return java filename
+     */
+    String mapJspToJavaName(File jspFile);
+
+    /**
+     * taking in the substring representing the path relative to the source dir
+     * return a new string representing the destination path
+     */
+    String mapPath(String path);
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspNameMangler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspNameMangler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/JspNameMangler.java	(revision 16139)
@@ -0,0 +1,153 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.jsp;
+import java.io.File;
+
+/**
+ * This is a class derived from the Jasper code
+ * (org.apache.jasper.compiler.CommandLineCompiler) to map from a JSP filename
+ * to a valid Java classname.
+ *
+ */
+public class JspNameMangler implements JspMangler {
+
+    /**
+     * this is the list of keywords which can not be used as classnames
+     */
+    public static final String[] keywords = {
+            "assert",
+            "abstract", "boolean", "break", "byte",
+            "case", "catch", "char", "class",
+            "const", "continue", "default", "do",
+            "double", "else", "extends", "final",
+            "finally", "float", "for", "goto",
+            "if", "implements", "import",
+            "instanceof", "int", "interface",
+            "long", "native", "new", "package",
+            "private", "protected", "public",
+            "return", "short", "static", "super",
+            "switch", "synchronized", "this",
+            "throw", "throws", "transient",
+            "try", "void", "volatile", "while"
+            };
+
+
+    /**
+     * map from a jsp file to a java filename; does not do packages
+     *
+     * @param jspFile file
+     * @return java filename
+     */
+    public String mapJspToJavaName(File jspFile) {
+        return mapJspToBaseName(jspFile) + ".java";
+    }
+
+
+    /**
+     * map from a jsp file to a base name; does not deal with extensions
+     *
+     * @param jspFile jspFile file
+     * @return exensionless potentially remapped name
+     */
+    private String mapJspToBaseName(File jspFile) {
+        String className;
+        className = stripExtension(jspFile);
+
+        // since we don't mangle extensions like the servlet does,
+        // we need to check for keywords as class names
+        for (int i = 0; i < keywords.length; ++i) {
+            if (className.equals(keywords[i])) {
+                className += "%";
+                break;
+            }
+        }
+
+        // Fix for invalid characters. If you think of more add to the list.
+        StringBuffer modifiedClassName = new StringBuffer(className.length());
+        // first char is more restrictive than the rest
+        char firstChar = className.charAt(0);
+        if (Character.isJavaIdentifierStart(firstChar)) {
+            modifiedClassName.append(firstChar);
+        } else {
+            modifiedClassName.append(mangleChar(firstChar));
+        }
+        // this is the rest
+        for (int i = 1; i < className.length(); i++) {
+            char subChar = className.charAt(i);
+            if (Character.isJavaIdentifierPart(subChar)) {
+                modifiedClassName.append(subChar);
+            } else {
+                modifiedClassName.append(mangleChar(subChar));
+            }
+        }
+        return modifiedClassName.toString();
+    }
+
+
+    /**
+     * get short filename from file
+     *
+     * @param jspFile file in
+     * @return file without any jsp extension
+     */
+    private String stripExtension(File jspFile) {
+        String className;
+        String filename = jspFile.getName();
+        if (filename.endsWith(".jsp")) {
+            className = filename.substring(0, filename.length() - 4);
+        } else {
+            className = filename;
+        }
+        return className;
+    }
+
+
+    /**
+     * definition of the char escaping algorithm
+     *
+     * @param ch char to mangle
+     * @return mangled string; 5 digit hex value
+     */
+    private static final String mangleChar(char ch) {
+
+        if (ch == File.separatorChar) {
+            ch = '/';
+        }
+        String s = Integer.toHexString(ch);
+        int nzeros = 5 - s.length();
+        char[] result = new char[6];
+        result[0] = '_';
+        for (int i = 1; i <= nzeros; ++i) {
+            result[i] = '0';
+        }
+        int resultIndex = 0;
+        for (int i = nzeros + 1; i < 6; ++i) {
+            result[i] = s.charAt(resultIndex++);
+        }
+        return new String(result);
+    }
+
+    /**
+     * taking in the substring representing the path relative to the source dir
+     * return a new string representing the destination path
+     * not supported, as jasper in tomcat4.0 doesnt either
+     */
+    public String mapPath(String path) {
+        return null;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/WLJspc.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/WLJspc.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/WLJspc.java	(revision 16139)
@@ -0,0 +1,313 @@
+/*
+ * Copyright  2000,2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.jsp;
+
+//apache/ant imports
+import java.io.File;
+import java.util.Date;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.taskdefs.MatchingTask;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Precompiles JSP's using WebLogic's JSP compiler (weblogic.jspc).
+ *
+ *
+ * Tested only on Weblogic 4.5.1 - NT4.0 and Solaris 5.7
+ *
+ * required attributes
+ *      src : root of source tree for JSP, ie, the document root for your weblogic server
+ *      dest : root of destination directory, what you have set as
+ *             WorkingDir in the weblogic properties
+ *      package : start package name under which your JSP's would be compiled
+ *
+ * other attributes
+ *     classpath
+ *
+ * A classpath should be set which contains the weblogic classes as well as all
+ * application classes referenced by the JSP. The system classpath is also
+ * appended when the jspc is called, so you may choose to put everything in
+ * the classpath while calling Ant. However, since presumably the JSP's will
+ * reference classes being build by Ant, it would be better to explicitly add
+ * the classpath in the task
+ *
+ * The task checks timestamps on the JSP's and the generated classes, and compiles
+ * only those files that have changed.
+ *
+ * It follows the weblogic naming convention of putting classes in
+ *  <b> _dirName/_fileName.class for dirname/fileName.jsp   </b>
+ *
+ * Limitation: It compiles the files thru the Classic compiler only.
+ * Limitation: Since it is my experience that weblogic jspc throws out of
+ *             memory error on being given too many files at one go, it is
+ *             called multiple times with one jsp file each.
+ *
+ * <pre>
+ * example
+ * &lt;target name="jspcompile" depends="compile"&gt;
+ *   &lt;wljspc src="c:\\weblogic\\myserver\\public_html"
+ *           dest="c:\\weblogic\\myserver\\serverclasses" package="myapp.jsp"&gt;
+ *   &lt;classpath&gt;
+ *          &lt;pathelement location="${weblogic.classpath}" /&gt;
+ *           &lt;pathelement path="${compile.dest}" /&gt;
+ *      &lt;/classpath&gt;
+ *
+ *   &lt;/wljspc&gt;
+ * &lt;/target&gt;
+ * </pre>
+ *
+ */
+
+public class WLJspc extends MatchingTask {
+    //TODO Test on other versions of weblogic
+    //TODO add more attributes to the task, to take care of all jspc options
+    //TODO Test on Unix
+
+    /** root of compiled files tree */
+    private File destinationDirectory;
+
+    /** root of source files tree */
+    private File sourceDirectory;
+
+    /** package under which resultant classes will reside */
+    private String destinationPackage;
+
+    /** classpath used to compile the jsp files. */
+    private Path compileClasspath;
+
+    //private String compilerPath; //fully qualified name for the compiler executable
+
+    private String pathToPackage = "";
+    private Vector filesToDo = new Vector();
+
+    public void execute() throws BuildException {
+        if (!destinationDirectory.isDirectory()) {
+            throw new BuildException("destination directory "
+                + destinationDirectory.getPath() + " is not valid");
+        }
+
+        if (!sourceDirectory.isDirectory()) {
+            throw new BuildException("src directory "
+                + sourceDirectory.getPath() + " is not valid");
+        }
+
+        if (destinationPackage == null) {
+            throw new BuildException("package attribute must be present.",
+                                     getLocation());
+        }
+
+
+        pathToPackage
+            = this.destinationPackage.replace('.', File.separatorChar);
+        // get all the files in the sourceDirectory
+        DirectoryScanner ds = super.getDirectoryScanner(sourceDirectory);
+
+        //use the systemclasspath as well, to include the ant jar
+        if (compileClasspath == null) {
+            compileClasspath = new Path(getProject());
+        }
+
+        compileClasspath = compileClasspath.concatSystemClasspath();
+        String[] files = ds.getIncludedFiles();
+
+        //Weblogic.jspc calls System.exit() ... have to fork
+        // Therefore, takes loads of time
+        // Can pass directories at a time (*.jsp) but easily runs out of
+        // memory on hefty dirs (even on  a Sun)
+        Java helperTask = (Java) getProject().createTask("java");
+        helperTask.setFork(true);
+        helperTask.setClassname("weblogic.jspc");
+        helperTask.setTaskName(getTaskName());
+        String[] args = new String[12];
+
+        File jspFile = null;
+        String parents = "";
+        int j = 0;
+        //XXX  this array stuff is a remnant of prev trials.. gotta remove.
+        args[j++] = "-d";
+        args[j++] = destinationDirectory.getAbsolutePath().trim();
+        args[j++] = "-docroot";
+        args[j++] = sourceDirectory.getAbsolutePath().trim();
+        args[j++] = "-keepgenerated";  //TODO: Parameterise ??
+        //Call compiler as class... dont want to fork again
+        //Use classic compiler -- can be parameterised?
+        args[j++] =  "-compilerclass";
+        args[j++] = "sun.tools.javac.Main";
+        //Weblogic jspc does not seem to work unless u explicitly set this...
+        // Does not take the classpath from the env....
+        // Am i missing something about the Java task??
+        args[j++] = "-classpath";
+        args[j++] = compileClasspath.toString();
+
+        this.scanDir(files);
+        log("Compiling " + filesToDo.size() + " JSP files");
+
+        for (int i = 0; i < filesToDo.size(); i++) {
+            //XXX
+            // All this to get package according to weblogic standards
+            // Can be written better... this is too hacky!
+            // Careful.. similar code in scanDir , but slightly different!!
+            String filename = (String) filesToDo.elementAt(i);
+            jspFile = new File(filename);
+            args[j] = "-package";
+            parents = jspFile.getParent();
+            if ((parents != null)  && (!("").equals(parents))) {
+                parents =  this.replaceString(parents, File.separator, "_.");
+                args[j + 1] = destinationPackage + "." + "_" + parents;
+            } else {
+                args[j + 1] = destinationPackage;
+            }
+
+
+            args[j + 2] =  sourceDirectory + File.separator + filename;
+            helperTask.clearArgs();
+
+            for (int x = 0; x < j + 3; x++) {
+                helperTask.createArg().setValue(args[x]);
+            }
+
+            helperTask.setClasspath(compileClasspath);
+            if (helperTask.executeJava() != 0) {
+                log(filename + " failed to compile", Project.MSG_WARN);
+            }
+        }
+    }
+
+
+
+    /**
+     * Set the classpath to be used for this compilation.
+     *
+     */
+    public void setClasspath(Path classpath) {
+        if (compileClasspath == null) {
+            compileClasspath = classpath;
+        } else {
+            compileClasspath.append(classpath);
+        }
+    }
+
+    /**
+     * Maybe creates a nested classpath element.
+     */
+    public Path createClasspath() {
+        if (compileClasspath == null) {
+            compileClasspath = new Path(getProject());
+        }
+        return compileClasspath;
+    }
+
+    /**
+     * Set the directory containing the source jsp's
+     *
+     *
+     * @param dirName the directory containg the source jsp's
+     */
+    public void setSrc(File dirName) {
+
+        sourceDirectory = dirName;
+    }
+
+     /**
+     * Set the directory containing the source jsp's
+     *
+     *
+     * @param dirName the directory containg the source jsp's
+     */
+    public void setDest(File dirName) {
+
+        destinationDirectory = dirName;
+    }
+
+    /**
+     * Set the package under which the compiled classes go
+     *
+     * @param packageName the package name for the clases
+     */
+    public void setPackage(String packageName) {
+
+        destinationPackage = packageName;
+    }
+
+
+
+    protected void scanDir(String files[]) {
+
+        long now = (new Date()).getTime();
+        File jspFile = null;
+        String parents = null;
+        String pack = "";
+        for (int i = 0; i < files.length; i++) {
+            File srcFile = new File(this.sourceDirectory, files[i]);
+            //XXX
+            // All this to convert source to destination directory according
+            // to weblogic standards Can be written better... this is too hacky!
+            jspFile = new File(files[i]);
+            parents = jspFile.getParent();
+
+            if ((parents != null)  && (!("").equals(parents))) {
+                parents =  this.replaceString(parents, File.separator, "_/");
+                pack = pathToPackage + File.separator + "_" + parents;
+            } else {
+                pack = pathToPackage;
+            }
+
+            String filePath = pack + File.separator + "_";
+            int startingIndex = files[i].lastIndexOf(File.separator) != -1
+                    ? files[i].lastIndexOf(File.separator) + 1 : 0;
+            int endingIndex = files[i].indexOf(".jsp");
+            if (endingIndex == -1) {
+                log("Skipping " + files[i] + ". Not a JSP",
+                    Project.MSG_VERBOSE);
+                continue;
+            }
+
+            filePath += files[i].substring(startingIndex, endingIndex);
+            filePath += ".class";
+            File classFile = new File(this.destinationDirectory, filePath);
+
+            if (srcFile.lastModified() > now) {
+                log("Warning: file modified in the future: "
+                    + files[i], Project.MSG_WARN);
+            }
+            if (srcFile.lastModified() > classFile.lastModified()) {
+                filesToDo.addElement(files[i]);
+                log("Recompiling File " + files[i], Project.MSG_VERBOSE);
+            }
+        }
+    }
+
+
+    protected String replaceString(String inpString, String escapeChars,
+                                   String replaceChars) {
+        String localString = "";
+        int numTokens = 0;
+        StringTokenizer st = new StringTokenizer(inpString, escapeChars, true);
+        numTokens = st.countTokens();
+        for (int i = 0; i < numTokens; i++) {
+            String test = st.nextToken();
+            test = (test.equals(escapeChars) ? replaceChars : test);
+            localString += test;
+        }
+        return localString;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/DefaultJspCompilerAdapter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/DefaultJspCompilerAdapter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/DefaultJspCompilerAdapter.java	(revision 16139)
@@ -0,0 +1,141 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.jsp.compilers;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.optional.jsp.JspC;
+import org.apache.tools.ant.types.CommandlineJava;
+
+/**
+ * This is the default implementation for the JspCompilerAdapter interface.
+ * This is currently very light on the ground since only one compiler type is
+ * supported.
+ *
+ */
+public abstract class DefaultJspCompilerAdapter
+    implements JspCompilerAdapter {
+
+    private static String lSep = System.getProperty("line.separator");
+
+    /**
+     * Logs the compilation parameters, adds the files to compile and logs the
+     * &quot;niceSourceList&quot;
+     */
+    protected void logAndAddFilesToCompile(JspC jspc,
+                                           Vector compileList,
+                                           CommandlineJava cmd) {
+        jspc.log("Compilation " + cmd.describeJavaCommand(),
+                 Project.MSG_VERBOSE);
+
+        StringBuffer niceSourceList = new StringBuffer("File");
+        if (compileList.size() != 1) {
+            niceSourceList.append("s");
+        }
+        niceSourceList.append(" to be compiled:");
+
+        niceSourceList.append(lSep);
+
+        Enumeration e = compileList.elements();
+        while (e.hasMoreElements()) {
+            String arg = (String) e.nextElement();
+            cmd.createArgument().setValue(arg);
+            niceSourceList.append("    " + arg + lSep);
+        }
+
+        jspc.log(niceSourceList.toString(), Project.MSG_VERBOSE);
+    }
+
+    /**
+     * our owner
+     */
+    protected JspC owner;
+
+    /**
+     * set the owner
+     */
+    public void setJspc(JspC owner) {
+        this.owner = owner;
+    }
+
+    /** get the owner
+     * @return the owner; should never be null
+     */
+    public JspC getJspc() {
+        return owner;
+    }
+
+
+    /**
+     *  add an argument oneple to the argument list, if the value aint null
+     *
+     * @param  argument  The argument
+     */
+    protected void addArg(CommandlineJava cmd, String argument) {
+        if (argument != null && argument.length() != 0) {
+           cmd.createArgument().setValue(argument);
+        }
+    }
+
+
+    /**
+     *  add an argument tuple to the argument list, if the value aint null
+     *
+     * @param  argument  The argument
+     * @param  value     the parameter
+     */
+    protected void addArg(CommandlineJava cmd, String argument, String value) {
+        if (value != null) {
+            cmd.createArgument().setValue(argument);
+            cmd.createArgument().setValue(value);
+        }
+    }
+
+    /**
+     *  add an argument tuple to the arg list, if the file parameter aint null
+     *
+     * @param  argument  The argument
+     * @param  file     the parameter
+     */
+    protected void addArg(CommandlineJava cmd, String argument, File file) {
+        if (file != null) {
+            cmd.createArgument().setValue(argument);
+            cmd.createArgument().setFile(file);
+        }
+    }
+
+    /**
+     * ask if compiler can sort out its own dependencies
+     * @return true if the compiler wants to do its own
+     * depends
+     */
+    public boolean implementsOwnDependencyChecking() {
+        return false;
+    }
+
+    /**
+     * get our project
+     * @return owner project data
+     */
+    public Project getProject() {
+        return getJspc().getProject();
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JasperC.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JasperC.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JasperC.java	(revision 16139)
@@ -0,0 +1,171 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.jsp.compilers;
+
+import java.io.File;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Java;
+import org.apache.tools.ant.taskdefs.optional.jsp.JspC;
+import org.apache.tools.ant.taskdefs.optional.jsp.JspMangler;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * The implementation of the jasper compiler.
+ * This is a cut-and-paste of the original Jspc task.
+ *
+ * @since ant1.5
+ */
+public class JasperC extends DefaultJspCompilerAdapter {
+
+
+    /**
+     * what produces java classes from .jsp files
+     */
+    JspMangler mangler;
+
+    public JasperC(JspMangler mangler) {
+        this.mangler = mangler;
+    }
+
+    /**
+     * our execute method
+     */
+    public boolean execute()
+        throws BuildException {
+        getJspc().log("Using jasper compiler", Project.MSG_VERBOSE);
+        CommandlineJava cmd = setupJasperCommand();
+
+        try {
+            // Create an instance of the compiler, redirecting output to
+            // the project log
+            Java java = (Java) (getProject().createTask("java"));
+            Path p = getClasspath();
+            if (getJspc().getClasspath() != null) {
+                getProject().log("using user supplied classpath: " + p, 
+                                 Project.MSG_DEBUG);
+            } else {
+                getProject().log("using system classpath: " + p,
+                                 Project.MSG_DEBUG);
+            }
+            java.setClasspath(p);
+            java.setDir(getProject().getBaseDir());
+            java.setClassname("org.apache.jasper.JspC");
+            //this is really irritating; we need a way to set stuff
+            String args[] = cmd.getJavaCommand().getArguments();
+            for (int i = 0; i < args.length; i++) {
+                java.createArg().setValue(args[i]);
+            }
+            java.setFailonerror(getJspc().getFailonerror());
+            //we are forking here to be sure that if JspC calls
+            //System.exit() it doesn't halt the build
+            java.setFork(true);
+            java.setTaskName("jasperc");
+            java.execute();
+            return true;
+        } catch (Exception ex) {
+            if (ex instanceof BuildException) {
+                throw (BuildException) ex;
+            } else {
+                throw new BuildException("Error running jsp compiler: ",
+                                         ex, getJspc().getLocation());
+            }
+        } finally {
+            getJspc().deleteEmptyJavaFiles();
+        }
+    }
+
+
+
+    /**
+     * build up a command line
+     * @return a command line for jasper
+     */
+    private CommandlineJava setupJasperCommand() {
+        CommandlineJava cmd = new CommandlineJava();
+        JspC jspc = getJspc();
+        addArg(cmd, "-d", jspc.getDestdir());
+        addArg(cmd, "-p", jspc.getPackage());
+
+        if (!isTomcat5x()) {
+            addArg(cmd, "-v" + jspc.getVerbose());
+        } else {
+            getProject().log("this task doesn't support Tomcat 5.x properly, "
+                             + "please use the Tomcat provided jspc task "
+                             + "instead");
+        }
+        
+        addArg(cmd, "-uriroot", jspc.getUriroot());
+        addArg(cmd, "-uribase", jspc.getUribase());
+        addArg(cmd, "-ieplugin", jspc.getIeplugin());
+        addArg(cmd, "-webinc", jspc.getWebinc());
+        addArg(cmd, "-webxml", jspc.getWebxml());
+        addArg(cmd, "-die9");
+
+        if (jspc.isMapped()) {
+            addArg(cmd, "-mapped");
+        }
+        if (jspc.getWebApp() != null) {
+            File dir = jspc.getWebApp().getDirectory();
+            addArg(cmd, "-webapp", dir);
+        }
+        logAndAddFilesToCompile(getJspc(), getJspc().getCompileList(), cmd);
+        return cmd;
+    }
+
+    /**
+     * @return an instance of the mangler this compiler uses
+     */
+
+    public JspMangler createMangler() {
+        return mangler;
+    }
+
+    /**
+     * @since Ant 1.6.2
+     */
+    private Path getClasspath() {
+        Path p = getJspc().getClasspath();
+        if (p == null) {
+            p = new Path(getProject());
+            return p.concatSystemClasspath("only");
+        } else {
+            return p.concatSystemClasspath("ignore");
+        }
+    }
+
+    /**
+     * @since Ant 1.6.2
+     */
+    private boolean isTomcat5x() {
+        AntClassLoader l = null;
+        try {
+            l = getProject().createClassLoader(getClasspath());
+            l.loadClass("org.apache.jasper.tagplugins.jstl.If");
+            return true;
+        } catch (ClassNotFoundException e) {
+            return false;
+        } finally {
+            if (l != null) {
+                l.cleanup();
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapter.java	(revision 16139)
@@ -0,0 +1,61 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.jsp.compilers;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.optional.jsp.JspC;
+import org.apache.tools.ant.taskdefs.optional.jsp.JspMangler;
+
+/**
+ * The interface that all jsp compiler adapters must adher to.
+ *
+ * <p>A compiler adapter is an adapter that interprets the jspc's
+ * parameters in preperation to be passed off to the compier this
+ * adapter represents.  As all the necessary values are stored in the
+ * Jspc task itself, the only thing all adapters need is the jsp
+ * task, the execute command and a parameterless constructor (for
+ * reflection).</p>
+ *
+ */
+
+public interface JspCompilerAdapter {
+
+    /**
+     * Sets the compiler attributes, which are stored in the Jspc task.
+     */
+    void setJspc(JspC attributes);
+
+    /**
+     * Executes the task.
+     *
+     * @return has the compilation been successful
+     */
+    boolean execute() throws BuildException;
+
+    /**
+     * @return an instance of the mangler this compiler uses
+     */
+
+    JspMangler createMangler();
+
+    /**
+     * ask if compiler can sort out its own dependencies
+     * @return true if the compiler wants to do its own
+     * depends
+     */
+    boolean implementsOwnDependencyChecking();
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapterFactory.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapterFactory.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/jsp/compilers/JspCompilerAdapterFactory.java	(revision 16139)
@@ -0,0 +1,118 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.jsp.compilers;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.optional.jsp.JspNameMangler;
+import org.apache.tools.ant.taskdefs.optional.jsp.Jasper41Mangler;
+
+
+/**
+ * Creates the necessary compiler adapter, given basic criteria.
+ *
+ */
+public class JspCompilerAdapterFactory {
+
+    /** This is a singleton -- can't create instances!! */
+    private JspCompilerAdapterFactory() {
+    }
+
+    /**
+     * Based on the parameter passed in, this method creates the necessary
+     * factory desired.
+     *
+     * The current mapping for compiler names are as follows:
+     * <ul><li>jasper = jasper compiler (the default)
+     * <li><i>a fully quallified classname</i> = the name of a jsp compiler
+     * adapter
+     * </ul>
+     *
+     * @param compilerType either the name of the desired compiler, or the
+     * full classname of the compiler's adapter.
+     * @param task a task to log through.
+     * @throws BuildException if the compiler type could not be resolved into
+     * a compiler adapter.
+     */
+    public static JspCompilerAdapter getCompiler(String compilerType, Task task)
+        throws BuildException {
+        return getCompiler(compilerType, task,
+                           task.getProject().createClassLoader(null));
+    }
+
+    /**
+     * Based on the parameter passed in, this method creates the necessary
+     * factory desired.
+     *
+     * The current mapping for compiler names are as follows:
+     * <ul><li>jasper = jasper compiler (the default)
+     * <li><i>a fully quallified classname</i> = the name of a jsp compiler
+     * adapter
+     * </ul>
+     *
+     * @param compilerType either the name of the desired compiler, or the
+     * full classname of the compiler's adapter.
+     * @param task a task to log through.
+     * @param loader AntClassLoader with which the compiler should be loaded
+     * @throws BuildException if the compiler type could not be resolved into
+     * a compiler adapter.
+     */
+    public static JspCompilerAdapter getCompiler(String compilerType, Task task,
+                                                 AntClassLoader loader)
+        throws BuildException {
+
+        if (compilerType.equalsIgnoreCase("jasper")) {
+            //tomcat4.0 gets the old mangler
+            return new JasperC(new JspNameMangler());
+        }
+        if (compilerType.equalsIgnoreCase("jasper41")) {
+            //tomcat4.1 gets the new one
+            return new JasperC(new Jasper41Mangler());
+        }
+        return resolveClassName(compilerType, loader);
+    }
+
+    /**
+     * Tries to resolve the given classname into a compiler adapter.
+     * Throws a fit if it can't.
+     *
+     * @param className The fully qualified classname to be created.
+     * @param classloader Classloader with which to load the class
+     * @throws BuildException This is the fit that is thrown if className
+     * isn't an instance of JspCompilerAdapter.
+     */
+    private static JspCompilerAdapter resolveClassName(String className,
+                                                       AntClassLoader classloader)
+        throws BuildException {
+        try {
+            Class c = classloader.findClass(className);
+            Object o = c.newInstance();
+            return (JspCompilerAdapter) o;
+        } catch (ClassNotFoundException cnfe) {
+            throw new BuildException(className + " can\'t be found.", cnfe);
+        } catch (ClassCastException cce) {
+            throw new BuildException(className + " isn\'t the classname of "
+                                     + "a compiler adapter.", cce);
+        } catch (Throwable t) {
+            // for all other possibilities
+            throw new BuildException(className + " caused an interesting "
+                                     + "exception.", t);
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/AggregateTransformer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/AggregateTransformer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/AggregateTransformer.java	(revision 16139)
@@ -0,0 +1,192 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.JAXPUtils;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.w3c.dom.Document;
+
+/**
+ * Transform a JUnit xml report.
+ * The default transformation generates an html report in either framed or non-framed
+ * style. The non-framed style is convenient to have a concise report via mail, the
+ * framed report is much more convenient if you want to browse into different
+ * packages or testcases since it is a Javadoc like report.
+ *
+ */
+public class AggregateTransformer {
+
+    public static final String FRAMES = "frames";
+
+    public static final String NOFRAMES = "noframes";
+
+    public static class Format extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[]{FRAMES, NOFRAMES};
+        }
+    }
+
+    /** Task */
+    protected Task task;
+
+    /** the xml document to process */
+    protected Document document;
+
+    /** the style directory. XSLs should be read from here if necessary */
+    protected File styleDir;
+
+    /** the destination directory, this is the root from where html should be generated */
+    protected File toDir;
+
+    /** the format to use for the report. Must be <tt>FRAMES</tt> or <tt>NOFRAMES</tt> */
+    protected String format = FRAMES;
+
+    /** XML Parser factory */
+    private static DocumentBuilderFactory privateDBFactory;
+
+    /** XML Parser factory accessible to subclasses */
+    protected static DocumentBuilderFactory dbfactory;
+
+    static {
+       privateDBFactory = DocumentBuilderFactory.newInstance();
+       dbfactory = privateDBFactory;
+    }
+
+    public AggregateTransformer(Task task) {
+        this.task = task;
+    }
+
+    /**
+     * Get the Document Builder Factory
+     *
+     * @return the DocumentBuilderFactory instance in use
+     */
+    protected static DocumentBuilderFactory getDocumentBuilderFactory() {
+        return privateDBFactory;
+    }
+
+    public void setFormat(Format format) {
+        this.format = format.getValue();
+    }
+
+    public void setXmlDocument(Document doc) {
+        this.document = doc;
+    }
+
+    /**
+     * Set the xml file to be processed. This is a helper if you want
+     * to set the file directly. Much more for testing purposes.
+     * @param xmlfile xml file to be processed
+     */
+    protected void setXmlfile(File xmlfile) throws BuildException {
+        try {
+            DocumentBuilder builder = privateDBFactory.newDocumentBuilder();
+            InputStream in = new FileInputStream(xmlfile);
+            try {
+                Document doc = builder.parse(in);
+                setXmlDocument(doc);
+            } finally {
+                in.close();
+            }
+        } catch (Exception e) {
+            throw new BuildException("Error while parsing document: " + xmlfile, e);
+        }
+    }
+
+    /**
+     * set the style directory. It is optional and will override the
+     * default xsl used.
+     * @param styledir  the directory containing the xsl files if the user
+     * would like to override with its own style.
+     */
+    public void setStyledir(File styledir) {
+        this.styleDir = styledir;
+    }
+
+    /** set the destination directory */
+    public void setTodir(File todir) {
+        this.toDir = todir;
+    }
+
+    /** set the extension of the output files */
+    public void setExtension(String ext) {
+        task.log("extension is not used anymore", Project.MSG_WARN);
+    }
+
+    public void transform() throws BuildException {
+        checkOptions();
+        final long t0 = System.currentTimeMillis();
+        XalanExecutor executor = XalanExecutor.newInstance(this);
+        try {
+            executor.execute();
+        } catch (Exception e) {
+            throw new BuildException("Errors while applying transformations: "
+                + e.getMessage(), e);
+        }
+        final long dt = System.currentTimeMillis() - t0;
+        task.log("Transform time: " + dt + "ms");
+    }
+
+    /** check for invalid options */
+    protected void checkOptions() throws BuildException {
+        // set the destination directory relative from the project if needed.
+        if (toDir == null) {
+            toDir = task.getProject().resolveFile(".");
+        } else if (!toDir.isAbsolute()) {
+            toDir = task.getProject().resolveFile(toDir.getPath());
+        }
+    }
+
+    /**
+     * Get the systemid of the appropriate stylesheet based on its
+     * name and styledir. If no styledir is defined it will load
+     * it as a java resource in the xsl child package, otherwise it
+     * will get it from the given directory.
+     * @throws IOException thrown if the requested stylesheet does
+     * not exist.
+     */
+    protected String getStylesheetSystemId() throws IOException {
+        String xslname = "junit-frames.xsl";
+        if (NOFRAMES.equals(format)) {
+            xslname = "junit-noframes.xsl";
+        }
+        if (styleDir == null) {
+            URL url = getClass().getResource("xsl/" + xslname);
+            if (url == null) {
+                throw new FileNotFoundException("Could not find jar resource " + xslname);
+            }
+            return url.toExternalForm();
+        }
+        File file = new File(styleDir, xslname);
+        if (!file.exists()) {
+            throw new FileNotFoundException("Could not find file '" + file + "'");
+        }
+        return JAXPUtils.getSystemId(file);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/BaseTest.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/BaseTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/BaseTest.java	(revision 16139)
@@ -0,0 +1,118 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.File;
+import java.util.Vector;
+
+/**
+ * Baseclass for BatchTest and JUnitTest.
+ *
+ */
+public abstract class BaseTest {
+    protected boolean haltOnError = false;
+    protected boolean haltOnFail = false;
+    protected boolean filtertrace = true;
+    protected boolean fork = false;
+    protected String ifProperty = null;
+    protected String unlessProperty = null;
+    protected Vector formatters = new Vector();
+    /** destination directory */
+    protected File destDir = null;
+
+    protected String failureProperty;
+    protected String errorProperty;
+
+    public void setFiltertrace(boolean value) {
+        filtertrace = value;
+    }
+
+    public boolean getFiltertrace() {
+        return filtertrace;
+    }
+
+    public void setFork(boolean value) {
+        fork = value;
+    }
+
+    public boolean getFork() {
+        return fork;
+    }
+
+    public void setHaltonerror(boolean value) {
+        haltOnError = value;
+    }
+
+    public void setHaltonfailure(boolean value) {
+        haltOnFail = value;
+    }
+
+    public boolean getHaltonerror() {
+        return haltOnError;
+    }
+
+    public boolean getHaltonfailure() {
+        return haltOnFail;
+    }
+
+    public void setIf(String propertyName) {
+        ifProperty = propertyName;
+    }
+
+    public void setUnless(String propertyName) {
+        unlessProperty = propertyName;
+    }
+
+    public void addFormatter(FormatterElement elem) {
+        formatters.addElement(elem);
+    }
+
+    /**
+     * Sets the destination directory.
+     */
+    public void setTodir(File destDir) {
+        this.destDir = destDir;
+    }
+
+    /**
+     * @return the destination directory as an absolute path if it exists
+     *         otherwise return <tt>null</tt>
+     */
+    public String getTodir() {
+        if (destDir != null) {
+            return destDir.getAbsolutePath();
+        }
+        return null;
+    }
+
+    public java.lang.String getFailureProperty() {
+        return failureProperty;
+    }
+
+    public void setFailureProperty(String failureProperty) {
+        this.failureProperty = failureProperty;
+    }
+
+    public java.lang.String getErrorProperty() {
+        return errorProperty;
+    }
+
+    public void setErrorProperty(String errorProperty) {
+        this.errorProperty = errorProperty;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/BatchTest.java	(revision 16139)
@@ -0,0 +1,174 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * <p> Create then run <code>JUnitTest</code>'s based on the list of files
+ *     given by the fileset attribute.
+ *
+ * <p> Every <code>.java</code> or <code>.class</code> file in the fileset is
+ * assumed to be a testcase.
+ * A <code>JUnitTest</code> is created for each of these named classes with
+ * basic setup inherited from the parent <code>BatchTest</code>.
+ *
+ * @see JUnitTest
+ */
+public final class BatchTest extends BaseTest {
+
+    /** the reference to the project */
+    private Project project;
+
+    /** the list of filesets containing the testcase filename rules */
+    private Vector filesets = new Vector();
+
+    /**
+     * create a new batchtest instance
+     * @param project     the project it depends on.
+     */
+    public BatchTest(Project project) {
+        this.project = project;
+    }
+
+    /**
+     * Add a new fileset instance to this batchtest. Whatever the fileset is,
+     * only filename that are <tt>.java</tt> or <tt>.class</tt> will be
+     * considered as 'candidates'.
+     * @param     fs the new fileset containing the rules to get the testcases.
+     */
+    public void addFileSet(FileSet fs) {
+        filesets.addElement(fs);
+    }
+
+    /**
+     * Return all <tt>JUnitTest</tt> instances obtain by applying the fileset rules.
+     * @return  an enumeration of all elements of this batchtest that are
+     * a <tt>JUnitTest</tt> instance.
+     */
+    public final Enumeration elements() {
+        JUnitTest[] tests = createAllJUnitTest();
+        return Enumerations.fromArray(tests);
+    }
+
+    /**
+     * Convenient method to merge the <tt>JUnitTest</tt>s of this batchtest
+     * to a <tt>Vector</tt>.
+     * @param v the vector to which should be added all individual tests of this
+     * batch test.
+     */
+    final void addTestsTo(Vector v) {
+        JUnitTest[] tests = createAllJUnitTest();
+        v.ensureCapacity(v.size() + tests.length);
+        for (int i = 0; i < tests.length; i++) {
+            v.addElement(tests[i]);
+        }
+    }
+
+    /**
+     * Create all <tt>JUnitTest</tt>s based on the filesets. Each instance
+     * is configured to match this instance properties.
+     * @return the array of all <tt>JUnitTest</tt>s that belongs to this batch.
+     */
+    private JUnitTest[] createAllJUnitTest() {
+        String[] filenames = getFilenames();
+        JUnitTest[] tests = new JUnitTest[filenames.length];
+        for (int i = 0; i < tests.length; i++) {
+            String classname = javaToClass(filenames[i]);
+            tests[i] = createJUnitTest(classname);
+        }
+        return tests;
+    }
+
+    /**
+     * Iterate over all filesets and return the filename of all files
+     * that end with <tt>.java</tt> or <tt>.class</tt>. This is to avoid
+     * wrapping a <tt>JUnitTest</tt> over an xml file for example. A Testcase
+     * is obviously a java file (compiled or not).
+     * @return an array of filenames without their extension. As they should
+     * normally be taken from their root, filenames should match their fully
+     * qualified class name (If it is not the case it will fail when running the test).
+     * For the class <tt>org/apache/Whatever.class</tt> it will return <tt>org/apache/Whatever</tt>.
+     */
+    private String[] getFilenames() {
+        Vector v = new Vector();
+        final int size = this.filesets.size();
+        for (int j = 0; j < size; j++) {
+            FileSet fs = (FileSet) filesets.elementAt(j);
+            DirectoryScanner ds = fs.getDirectoryScanner(project);
+            ds.scan();
+            String[] f = ds.getIncludedFiles();
+            for (int k = 0; k < f.length; k++) {
+                String pathname = f[k];
+                if (pathname.endsWith(".java")) {
+                    v.addElement(pathname.substring(0, pathname.length() - ".java".length()));
+                } else if (pathname.endsWith(".class")) {
+                    v.addElement(pathname.substring(0, pathname.length() - ".class".length()));
+                }
+            }
+        }
+
+        String[] files = new String[v.size()];
+        v.copyInto(files);
+        return files;
+    }
+
+    /**
+     * Convenient method to convert a pathname without extension to a
+     * fully qualified classname. For example <tt>org/apache/Whatever</tt> will
+     * be converted to <tt>org.apache.Whatever</tt>
+     * @param filename the filename to "convert" to a classname.
+     * @return the classname matching the filename.
+     */
+    public static final String javaToClass(String filename) {
+        return filename.replace(File.separatorChar, '.');
+    }
+
+    /**
+     * Create a <tt>JUnitTest</tt> that has the same property as this
+     * <tt>BatchTest</tt> instance.
+     * @param classname the name of the class that should be run as a
+     * <tt>JUnitTest</tt>. It must be a fully qualified name.
+     * @return the <tt>JUnitTest</tt> over the given classname.
+     */
+    private JUnitTest createJUnitTest(String classname) {
+        JUnitTest test = new JUnitTest();
+        test.setName(classname);
+        test.setHaltonerror(this.haltOnError);
+        test.setHaltonfailure(this.haltOnFail);
+        test.setFiltertrace(this.filtertrace);
+        test.setFork(this.fork);
+        test.setIf(this.ifProperty);
+        test.setUnless(this.unlessProperty);
+        test.setTodir(this.destDir);
+        test.setFailureProperty(failureProperty);
+        test.setErrorProperty(errorProperty);
+        Enumeration list = this.formatters.elements();
+        while (list.hasMoreElements()) {
+            test.addFormatter((FormatterElement) list.nextElement());
+        }
+        return test;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/BriefJUnitResultFormatter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/BriefJUnitResultFormatter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/BriefJUnitResultFormatter.java	(revision 16139)
@@ -0,0 +1,244 @@
+/*
+ * Copyright  2001-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.NumberFormat;
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Prints plain text output of the test to a specified Writer.
+ * Inspired by the PlainJUnitResultFormatter.
+ *
+ *
+ * @see FormatterElement
+ * @see PlainJUnitResultFormatter
+ */
+public class BriefJUnitResultFormatter implements JUnitResultFormatter {
+
+    /**
+     * Where to write the log to.
+     */
+    private OutputStream out;
+
+    /**
+     * Used for writing the results.
+     */
+    private PrintWriter output;
+
+    /**
+     * Used as part of formatting the results.
+     */
+    private StringWriter results;
+
+    /**
+     * Used for writing formatted results to.
+     */
+    private PrintWriter resultWriter;
+
+    /**
+     * Formatter for timings.
+     */
+    private NumberFormat numberFormat = NumberFormat.getInstance();
+
+    /**
+     * Output suite has written to System.out
+     */
+    private String systemOutput = null;
+
+    /**
+     * Output suite has written to System.err
+     */
+    private String systemError = null;
+
+    /**
+     * Constructor for BriefJUnitResultFormatter.
+     */
+    public BriefJUnitResultFormatter() {
+        results = new StringWriter();
+        resultWriter = new PrintWriter(results);
+    }
+
+    /**
+     * Sets the stream the formatter is supposed to write its results to.
+     * @param out the output stream to write to
+     */
+    public void setOutput(OutputStream out) {
+        this.out = out;
+        output = new PrintWriter(out);
+    }
+
+    /**
+     * @see JUnitResultFormatter#setSystemOutput(String)
+     */
+    public void setSystemOutput(String out) {
+        systemOutput = out;
+    }
+
+    /**
+     * @see JUnitResultFormatter#setSystemError(String)
+     */
+    public void setSystemError(String err) {
+        systemError = err;
+    }
+
+
+    /**
+     * The whole testsuite started.
+     */
+    public void startTestSuite(JUnitTest suite) throws BuildException {
+    }
+
+    /**
+     * The whole testsuite ended.
+     */
+    public void endTestSuite(JUnitTest suite) throws BuildException {
+        String newLine = System.getProperty("line.separator");
+        StringBuffer sb = new StringBuffer("Testsuite: ");
+        sb.append(suite.getName());
+        sb.append(newLine);
+        sb.append("Tests run: ");
+        sb.append(suite.runCount());
+        sb.append(", Failures: ");
+        sb.append(suite.failureCount());
+        sb.append(", Errors: ");
+        sb.append(suite.errorCount());
+        sb.append(", Time elapsed: ");
+        sb.append(numberFormat.format(suite.getRunTime() / 1000.0));
+        sb.append(" sec");
+        sb.append(newLine);
+        sb.append(newLine);
+
+        // append the err and output streams to the log
+        if (systemOutput != null && systemOutput.length() > 0) {
+            sb.append("------------- Standard Output ---------------")
+                    .append(newLine)
+                    .append(systemOutput)
+                    .append("------------- ---------------- ---------------")
+                    .append(newLine);
+        }
+
+        if (systemError != null && systemError.length() > 0) {
+            sb.append("------------- Standard Error -----------------")
+                    .append(newLine)
+                    .append(systemError)
+                    .append("------------- ---------------- ---------------")
+                    .append(newLine);
+        }
+
+        if (output != null) {
+            try {
+                output.write(sb.toString());
+                resultWriter.close();
+                output.write(results.toString());
+                output.flush();
+            } finally {
+                if (out != System.out && out != System.err) {
+                    try {
+                        out.close();
+                    } catch (IOException e) {
+                        // ignore
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * A test started.
+     * @param test a test
+     */
+    public void startTest(Test test) {
+    }
+
+    /**
+     * A test ended.
+     * @param test a test
+     */
+    public void endTest(Test test) {
+    }
+
+    /**
+     * Interface TestListener for JUnit &lt;= 3.4.
+     *
+     * <p>A Test failed.
+     * @param test a test
+     * @param t    the exception thrown by the test
+     */
+    public void addFailure(Test test, Throwable t) {
+        formatError("\tFAILED", test, t);
+    }
+
+    /**
+     * Interface TestListener for JUnit &gt; 3.4.
+     *
+     * <p>A Test failed.
+     * @param test a test
+     * @param t    the assertion failed by the test
+     */
+    public void addFailure(Test test, AssertionFailedError t) {
+        addFailure(test, (Throwable) t);
+    }
+
+    /**
+     * A test caused an error.
+     * @param test  a test
+     * @param error the error thrown by the test
+     */
+    public void addError(Test test, Throwable error) {
+        formatError("\tCaused an ERROR", test, error);
+    }
+
+    /**
+     * Format the test for printing..
+     * @param test a test
+     * @return the formatted testname
+     */
+    protected String formatTest(Test test) {
+        if (test == null) {
+            return "Null Test: ";
+        } else {
+            return "Testcase: " + test.toString() + ":";
+        }
+    }
+
+    /**
+     * Format an error and print it.
+     * @param type the type of error
+     * @param test the test that failed
+     * @param error the exception that the test threw
+     */
+    protected synchronized void formatError(String type, Test test,
+                                            Throwable error) {
+        if (test != null) {
+            endTest(test);
+        }
+
+        resultWriter.println(formatTest(test) + type);
+        resultWriter.println(error.getMessage());
+        String strace = JUnitTestRunner.getFilteredTrace(error);
+        resultWriter.println(strace);
+        resultWriter.println();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/DOMUtil.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/DOMUtil.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/DOMUtil.java	(revision 16139)
@@ -0,0 +1,213 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.util.Vector;
+import org.w3c.dom.Attr;
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Comment;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.ProcessingInstruction;
+import org.w3c.dom.Text;
+
+/**
+ * Some utilities that might be useful when manipulating DOM trees.
+ *
+ */
+public final class DOMUtil {
+
+    /** unused constructor */
+    private DOMUtil() {
+    }
+
+    /**
+     * Filter interface to be applied when iterating over a DOM tree.
+     * Just think of it like a <tt>FileFilter</tt> clone.
+     */
+    public interface NodeFilter {
+        /**
+         * @param       node    the node to check for acceptance.
+         * @return      <tt>true</tt> if the node is accepted by this filter,
+         *                      otherwise <tt>false</tt>
+         */
+        boolean accept(Node node);
+    }
+
+    /**
+     * list a set of node that match a specific filter. The list can be made
+     * recursively or not.
+     * @param   parent  the parent node to search from
+     * @param   filter  the filter that children should match.
+     * @param   recurse <tt>true</tt> if you want the list to be made recursively
+     *                  otherwise <tt>false</tt>.
+     */
+    public static NodeList listChildNodes(Node parent, NodeFilter filter, boolean recurse) {
+        NodeListImpl matches = new NodeListImpl();
+        NodeList children = parent.getChildNodes();
+        if (children != null) {
+            final int len = children.getLength();
+            for (int i = 0; i < len; i++) {
+                Node child = children.item(i);
+                if (filter.accept(child)) {
+                    matches.addElement(child);
+                }
+                if (recurse) {
+                    NodeList recmatches = listChildNodes(child, filter, recurse);
+                    final int reclength = matches.getLength();
+                    for (int j = 0; j < reclength; j++) {
+                        matches.addElement(recmatches.item(i));
+                    }
+                }
+            }
+        }
+        return matches;
+    }
+
+    /** custom implementation of a nodelist */
+    public static class NodeListImpl extends Vector implements NodeList {
+        public int getLength() {
+            return size();
+        }
+        public Node item(int i) {
+            try {
+                return (Node) elementAt(i);
+            } catch (ArrayIndexOutOfBoundsException e) {
+                return null; // conforming to NodeList interface
+            }
+        }
+    }
+
+    /**
+     * return the attribute value of an element.
+     * @param node the node to get the attribute from.
+     * @param name the name of the attribute we are looking for the value.
+     * @return the value of the requested attribute or <tt>null</tt> if the
+     *         attribute was not found or if <tt>node</tt> is not an <tt>Element</tt>.
+     */
+    public static String getNodeAttribute(Node node, String name) {
+        if (node instanceof Element) {
+            Element element = (Element) node;
+            return element.getAttribute(name);
+        }
+        return null;
+    }
+
+
+    /**
+     * Iterate over the children of a given node and return the first node
+     * that has a specific name.
+     * @param   parent  the node to search child from. Can be <tt>null</tt>.
+     * @param   tagname the child name we are looking for. Cannot be <tt>null</tt>.
+     * @return  the first child that matches the given name or <tt>null</tt> if
+     *                  the parent is <tt>null</tt> or if a child does not match the
+     *                  given name.
+     */
+    public static Element getChildByTagName (Node parent, String tagname) {
+        if (parent == null) {
+            return null;
+        }
+        NodeList childList = parent.getChildNodes();
+        final int len = childList.getLength();
+        for (int i = 0; i < len; i++) {
+            Node child = childList.item(i);
+            if (child != null && child.getNodeType() == Node.ELEMENT_NODE
+                && child.getNodeName().equals(tagname)) {
+                return (Element) child;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Simple tree walker that will clone recursively a node. This is to
+     * avoid using parser-specific API such as Sun's <tt>changeNodeOwner</tt>
+     * when we are dealing with DOM L1 implementations since <tt>cloneNode(boolean)</tt>
+     * will not change the owner document.
+     * <tt>changeNodeOwner</tt> is much faster and avoid the costly cloning process.
+     * <tt>importNode</tt> is in the DOM L2 interface.
+     * @param   parent  the node parent to which we should do the import to.
+     * @param   child   the node to clone recursively. Its clone will be
+     *              appended to <tt>parent</tt>.
+     * @return  the cloned node that is appended to <tt>parent</tt>
+     */
+    public static final Node importNode(Node parent, Node child) {
+        Node copy = null;
+        final Document doc = parent.getOwnerDocument();
+
+        switch (child.getNodeType()) {
+        case Node.CDATA_SECTION_NODE:
+            copy = doc.createCDATASection(((CDATASection) child).getData());
+            break;
+        case Node.COMMENT_NODE:
+            copy = doc.createComment(((Comment) child).getData());
+            break;
+        case Node.DOCUMENT_FRAGMENT_NODE:
+            copy = doc.createDocumentFragment();
+            break;
+        case Node.ELEMENT_NODE:
+            final Element elem = doc.createElement(((Element) child).getTagName());
+            copy = elem;
+            final NamedNodeMap attributes = child.getAttributes();
+            if (attributes != null) {
+                final int size = attributes.getLength();
+                for (int i = 0; i < size; i++) {
+                    final Attr attr = (Attr) attributes.item(i);
+                    elem.setAttribute(attr.getName(), attr.getValue());
+                }
+            }
+            break;
+        case Node.ENTITY_REFERENCE_NODE:
+            copy = doc.createEntityReference(child.getNodeName());
+            break;
+        case Node.PROCESSING_INSTRUCTION_NODE:
+            final ProcessingInstruction pi = (ProcessingInstruction) child;
+            copy = doc.createProcessingInstruction(pi.getTarget(), pi.getData());
+            break;
+        case Node.TEXT_NODE:
+            copy = doc.createTextNode(((Text) child).getData());
+            break;
+        default:
+            // this should never happen
+            throw new IllegalStateException("Invalid node type: " + child.getNodeType());
+        }
+
+        // okay we have a copy of the child, now the child becomes the parent
+        // and we are iterating recursively over its children.
+        try {
+            final NodeList children = child.getChildNodes();
+            if (children != null) {
+                final int size = children.getLength();
+                for (int i = 0; i < size; i++) {
+                    final Node newChild = children.item(i);
+                    if (newChild != null) {
+                        importNode(copy, newChild);
+                    }
+                }
+            }
+        } catch (DOMException ignored) {
+        }
+
+        // bingo append it. (this should normally not be done here)
+        parent.appendChild(copy);
+        return copy;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/Enumerations.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/Enumerations.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/Enumerations.java	(revision 16139)
@@ -0,0 +1,176 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+/**
+ * A couple of methods related to enumerations that might be useful.
+ * This class should probably disappear once the required JDK is set to 1.2
+ * instead of 1.1.
+ *
+ */
+public final class Enumerations {
+
+        private Enumerations() {
+        }
+
+        /**
+         * creates an enumeration from an array of objects.
+         * @param       array   the array of object to enumerate.
+         * @return the enumeration over the array of objects.
+         */
+        public static Enumeration fromArray(Object[] array) {
+                return new ArrayEnumeration(array);
+        }
+
+        /**
+        * creates an enumeration from an array of enumeration. The created enumeration
+        * will sequentially enumerate over all elements of each enumeration and skip
+        * <tt>null</tt> enumeration elements in the array.
+        * @param        enums   the array of enumerations.
+        * @return the enumeration over the array of enumerations.
+         */
+        public static Enumeration fromCompound(Enumeration[] enums) {
+                return new CompoundEnumeration(enums);
+        }
+
+}
+
+
+/**
+ * Convenient enumeration over an array of objects.
+ */
+class ArrayEnumeration implements Enumeration {
+
+        /** object array */
+        private Object[] array;
+
+        /** current index */
+        private int pos;
+
+        /**
+         * Initialize a new enumeration that wraps an array.
+         * @param       array   the array of object to enumerate.
+         */
+        public ArrayEnumeration(Object[] array) {
+                this.array = array;
+                this.pos = 0;
+        }
+        /**
+         * Tests if this enumeration contains more elements.
+         *
+         * @return  <code>true</code> if and only if this enumeration object
+         *           contains at least one more element to provide;
+         *          <code>false</code> otherwise.
+         */
+        public boolean hasMoreElements() {
+                return (pos < array.length);
+        }
+
+        /**
+         * Returns the next element of this enumeration if this enumeration
+         * object has at least one more element to provide.
+         *
+         * @return     the next element of this enumeration.
+         * @throws  NoSuchElementException  if no more elements exist.
+         */
+        public Object nextElement() throws NoSuchElementException {
+                if (hasMoreElements()) {
+                        Object o = array[pos];
+                        pos++;
+                        return o;
+                }
+                throw new NoSuchElementException();
+        }
+}
+/**
+ * Convenient enumeration over an array of enumeration. For example:
+ * <pre>
+ * Enumeration e1 = v1.elements();
+ * while (e1.hasMoreElements()) {
+ *    // do something
+ * }
+ * Enumeration e2 = v2.elements();
+ * while (e2.hasMoreElements()) {
+ *    // do the same thing
+ * }
+ * </pre>
+ * can be written as:
+ * <pre>
+ * Enumeration[] enums = { v1.elements(), v2.elements() };
+ * Enumeration e = Enumerations.fromCompound(enums);
+ * while (e.hasMoreElements()) {
+ *    // do something
+ * }
+ * </pre>
+ * Note that the enumeration will skip null elements in the array. The following is
+ * thus possible:
+ * <pre>
+ * Enumeration[] enums = { v1.elements(), null, v2.elements() }; // a null enumeration in the array
+ * Enumeration e = Enumerations.fromCompound(enums);
+ * while (e.hasMoreElements()) {
+ *    // do something
+ * }
+ * </pre>
+ */
+ class CompoundEnumeration implements Enumeration {
+
+        /** enumeration array */
+        private Enumeration[] enumArray;
+
+        /** index in the enums array */
+        private int index = 0;
+
+    public CompoundEnumeration(Enumeration[] enumarray) {
+                this.enumArray = enumarray;
+    }
+
+        /**
+         * Tests if this enumeration contains more elements.
+         *
+         * @return  <code>true</code> if and only if this enumeration object
+         *           contains at least one more element to provide;
+         *          <code>false</code> otherwise.
+         */
+    public boolean hasMoreElements() {
+                while (index < enumArray.length) {
+                        if (enumArray[index] != null && enumArray[index].hasMoreElements()) {
+                                return true;
+                        }
+                        index++;
+                }
+                return false;
+    }
+
+        /**
+         * Returns the next element of this enumeration if this enumeration
+         * object has at least one more element to provide.
+         *
+         * @return     the next element of this enumeration.
+         * @throws  NoSuchElementException  if no more elements exist.
+         */
+    public Object nextElement() throws NoSuchElementException {
+                if (hasMoreElements()) {
+                        return enumArray[index].nextElement();
+                }
+                throw new NoSuchElementException();
+    }
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/FormatterElement.java	(revision 16139)
@@ -0,0 +1,253 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+/**
+ * <p> A wrapper for the implementations of <code>JUnitResultFormatter</code>.
+ * In particular, used as a nested <code>&lt;formatter&gt;</code> element in
+ * a <code>&lt;junit&gt;</code> task.
+ * <p> For example,
+ * <code><pre>
+ *       &lt;junit printsummary="no" haltonfailure="yes" fork="false"&gt;
+ *           &lt;formatter type="plain" usefile="false" /&gt;
+ *           &lt;test name="org.apache.ecs.InternationalCharTest" /&gt;
+ *       &lt;/junit&gt;</pre></code>
+ * adds a <code>plain</code> type implementation
+ * (<code>PlainJUnitResultFormatter</code>) to display the results of the test.
+ *
+ * <p> Either the <code>type</code> or the <code>classname</code> attribute
+ * must be set.
+ *
+ * @see JUnitTask
+ * @see XMLJUnitResultFormatter
+ * @see BriefJUnitResultFormatter
+ * @see PlainJUnitResultFormatter
+ * @see JUnitResultFormatter
+ */
+public class FormatterElement {
+
+    private String classname;
+    private String extension;
+    private OutputStream out = System.out;
+    private File outFile;
+    private boolean useFile = true;
+    private String ifProperty;
+    private String unlessProperty;
+
+    public static final String XML_FORMATTER_CLASS_NAME =
+        "org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter";
+    public static final String BRIEF_FORMATTER_CLASS_NAME =
+        "org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter";
+    public static final String PLAIN_FORMATTER_CLASS_NAME =
+        "org.apache.tools.ant.taskdefs.optional.junit.PlainJUnitResultFormatter";
+
+    /**
+     * <p> Quick way to use a standard formatter.
+     *
+     * <p> At the moment, there are three supported standard formatters.
+     * <ul>
+     * <li> The <code>xml</code> type uses a <code>XMLJUnitResultFormatter</code>.
+     * <li> The <code>brief</code> type uses a <code>BriefJUnitResultFormatter</code>.
+     * <li> The <code>plain</code> type (the default) uses a <code>PlainJUnitResultFormatter</code>.
+     * </ul>
+     *
+     * <p> Sets <code>classname</code> attribute - so you can't use that
+     * attribute if you use this one.
+     */
+    public void setType(TypeAttribute type) {
+        if ("xml".equals(type.getValue())) {
+            setClassname(XML_FORMATTER_CLASS_NAME);
+        } else {
+            if ("brief".equals(type.getValue())) {
+                setClassname(BRIEF_FORMATTER_CLASS_NAME);
+            } else { // must be plain, ensured by TypeAttribute
+                setClassname(PLAIN_FORMATTER_CLASS_NAME);
+            }
+        }
+    }
+
+    /**
+     * <p> Set name of class to be used as the formatter.
+     *
+     * <p> This class must implement <code>JUnitResultFormatter</code>
+     */
+    public void setClassname(String classname) {
+        this.classname = classname;
+        if (XML_FORMATTER_CLASS_NAME.equals(classname)) {
+           setExtension(".xml");
+        } else if (PLAIN_FORMATTER_CLASS_NAME.equals(classname)) {
+           setExtension(".txt");
+        } else if (BRIEF_FORMATTER_CLASS_NAME.equals(classname)) {
+           setExtension(".txt");
+        }
+    }
+
+    /**
+     * Get name of class to be used as the formatter.
+     */
+    public String getClassname() {
+        return classname;
+    }
+
+    public void setExtension(String ext) {
+        this.extension = ext;
+    }
+
+    public String getExtension() {
+        return extension;
+    }
+
+    /**
+     * <p> Set the file which the formatte should log to.
+     *
+     * <p> Note that logging to file must be enabled .
+     */
+    void setOutfile(File out) {
+        this.outFile = out;
+    }
+
+    /**
+     * <p> Set output stream for formatter to use.
+     *
+     * <p> Defaults to standard out.
+     */
+    public void setOutput(OutputStream out) {
+        this.out = out;
+    }
+
+    /**
+     * Set whether the formatter should log to file.
+     */
+    public void setUseFile(boolean useFile) {
+        this.useFile = useFile;
+    }
+
+    /**
+     * Get whether the formatter should log to file.
+     */
+    boolean getUseFile() {
+        return useFile;
+    }
+
+    /**
+     * Set whether this formatter should be used.  It will be
+     * used if the property has been set, otherwise it won't.
+     * @param ifProperty name of property
+     */
+    public void setIf(String ifProperty) {
+        this.ifProperty = ifProperty;
+    }
+
+    /**
+     * Set whether this formatter should NOT be used. It
+     * will not be used if the property has been set, orthwise it
+     * will be used.
+     * @param unlessProperty name of property
+     */
+    public void setUnless(String unlessProperty) {
+        this.unlessProperty = unlessProperty;
+    }
+
+    /**
+     * Ensures that the selector passes the conditions placed
+     * on it with <code>if</code> and <code>unless</code> properties.
+     */
+    public boolean shouldUse(Task t) {
+        if (ifProperty != null && t.getProject().getProperty(ifProperty) == null) {
+            return false;
+        } else if (unlessProperty != null
+                    && t.getProject().getProperty(unlessProperty) != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    /**
+     * @since Ant 1.2
+     */
+    JUnitResultFormatter createFormatter() throws BuildException {
+        return createFormatter(null);
+    }
+
+    /**
+     * @since Ant 1.6
+     */
+    JUnitResultFormatter createFormatter(ClassLoader loader)
+        throws BuildException {
+
+        if (classname == null) {
+            throw new BuildException("you must specify type or classname");
+        }
+
+        Class f = null;
+        try {
+            if (loader == null) {
+                f = Class.forName(classname);
+            } else {
+                f = Class.forName(classname, true, loader);
+            }
+        } catch (ClassNotFoundException e) {
+            throw new BuildException(e);
+        }
+
+        Object o = null;
+        try {
+            o = f.newInstance();
+        } catch (InstantiationException e) {
+            throw new BuildException(e);
+        } catch (IllegalAccessException e) {
+            throw new BuildException(e);
+        }
+
+        if (!(o instanceof JUnitResultFormatter)) {
+            throw new BuildException(classname
+                + " is not a JUnitResultFormatter");
+        }
+
+        JUnitResultFormatter r = (JUnitResultFormatter) o;
+
+        if (useFile && outFile != null) {
+            try {
+                out = new FileOutputStream(outFile);
+            } catch (java.io.IOException e) {
+                throw new BuildException(e);
+            }
+        }
+        r.setOutput(out);
+        return r;
+    }
+
+    /**
+     * <p> Enumerated attribute with the values "plain", "xml" and "brief".
+     *
+     * <p> Use to enumerate options for <code>type</code> attribute.
+     */
+    public static class TypeAttribute extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[] {"plain", "xml", "brief"};
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitResultFormatter.java	(revision 16139)
@@ -0,0 +1,54 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.OutputStream;
+import junit.framework.TestListener;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * This Interface describes classes that format the results of a JUnit
+ * testrun.
+ *
+ */
+public interface JUnitResultFormatter extends TestListener {
+    /**
+     * The whole testsuite started.
+     */
+    void startTestSuite(JUnitTest suite) throws BuildException;
+
+    /**
+     * The whole testsuite ended.
+     */
+    void endTestSuite(JUnitTest suite) throws BuildException;
+
+    /**
+     * Sets the stream the formatter is supposed to write its results to.
+     */
+    void setOutput(OutputStream out);
+
+    /**
+     * This is what the test has written to System.out
+     */
+    void setSystemOutput(String out);
+
+    /**
+     * This is what the test has written to System.err
+     */
+    void setSystemError(String err);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTask.java	(revision 16139)
@@ -0,0 +1,1483 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Vector;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteWatchdog;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Assertions;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Environment;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Permissions;
+import org.apache.tools.ant.types.PropertySet;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.LoaderUtils;
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+/**
+ * Runs JUnit tests.
+ *
+ * <p> JUnit is a framework to create unit test. It has been initially
+ * created by Erich Gamma and Kent Beck.  JUnit can be found at <a
+ * href="http://www.junit.org">http://www.junit.org</a>.
+ *
+ * <p> <code>JUnitTask</code> can run a single specific
+ * <code>JUnitTest</code> using the <code>test</code> element.</p>
+ * For example, the following target <code><pre>
+ *   &lt;target name="test-int-chars" depends="jar-test"&gt;
+ *       &lt;echo message="testing international characters"/&gt;
+ *       &lt;junit printsummary="no" haltonfailure="yes" fork="false"&gt;
+ *           &lt;classpath refid="classpath"/&gt;
+ *           &lt;formatter type="plain" usefile="false" /&gt;
+ *           &lt;test name="org.apache.ecs.InternationalCharTest" /&gt;
+ *       &lt;/junit&gt;
+ *   &lt;/target&gt;
+ * </pre></code>
+ * <p>runs a single junit test
+ * (<code>org.apache.ecs.InternationalCharTest</code>) in the current
+ * VM using the path with id <code>classpath</code> as classpath and
+ * presents the results formatted using the standard
+ * <code>plain</code> formatter on the command line.</p>
+ *
+ * <p> This task can also run batches of tests.  The
+ * <code>batchtest</code> element creates a <code>BatchTest</code>
+ * based on a fileset.  This allows, for example, all classes found in
+ * directory to be run as testcases.</p>
+ *
+ * <p>For example,</p><code><pre>
+ * &lt;target name="run-tests" depends="dump-info,compile-tests" if="junit.present"&gt;
+ *   &lt;junit printsummary="no" haltonfailure="yes" fork="${junit.fork}"&gt;
+ *     &lt;jvmarg value="-classic"/&gt;
+ *     &lt;classpath refid="tests-classpath"/&gt;
+ *     &lt;sysproperty key="build.tests" value="${build.tests}"/&gt;
+ *     &lt;formatter type="brief" usefile="false" /&gt;
+ *     &lt;batchtest&gt;
+ *       &lt;fileset dir="${tests.dir}"&gt;
+ *         &lt;include name="**&#047;*Test*" /&gt;
+ *       &lt;/fileset&gt;
+ *     &lt;/batchtest&gt;
+ *   &lt;/junit&gt;
+ * &lt;/target&gt;
+ * </pre></code>
+ * <p>this target finds any classes with a <code>test</code> directory
+ * anywhere in their path (under the top <code>${tests.dir}</code>, of
+ * course) and creates <code>JUnitTest</code>'s for each one.</p>
+ *
+ * <p> Of course, <code>&lt;junit&gt;</code> and
+ * <code>&lt;batch&gt;</code> elements can be combined for more
+ * complex tests. For an example, see the ant <code>build.xml</code>
+ * target <code>run-tests</code> (the second example is an edited
+ * version).</p>
+ *
+ * <p> To spawn a new Java VM to prevent interferences between
+ * different testcases, you need to enable <code>fork</code>.  A
+ * number of attributes and elements allow you to set up how this JVM
+ * runs.
+ *
+ *
+ * @since Ant 1.2
+ *
+ * @see JUnitTest
+ * @see BatchTest
+ */
+public class JUnitTask extends Task {
+
+    private CommandlineJava commandline;
+    private Vector tests = new Vector();
+    private Vector batchTests = new Vector();
+    private Vector formatters = new Vector();
+    private File dir = null;
+
+    private Integer timeout = null;
+    private boolean summary = false;
+    private boolean reloading = true;
+    private String summaryValue = "";
+    private JUnitTestRunner runner = null;
+
+    private boolean newEnvironment = false;
+    private Environment env = new Environment();
+
+    private boolean includeAntRuntime = true;
+    private Path antRuntimeClasses = null;
+
+    private boolean showOutput = false;
+    private File tmpDir;
+    private AntClassLoader classLoader = null;
+    private Permissions perm = null;
+    private ForkMode forkMode = new ForkMode("perTest");
+
+    private static final int STRING_BUFFER_SIZE = 128;
+
+    /**
+     * If true, force ant to re-classload all classes for each JUnit TestCase
+     *
+     * @param value force class reloading for each test case
+     */
+    public void setReloading(boolean value) {
+        reloading = value;
+    }
+
+    /**
+     * If true, smartly filter the stack frames of
+     * JUnit errors and failures before reporting them.
+     *
+     * <p>This property is applied on all BatchTest (batchtest) and
+     * JUnitTest (test) however it can possibly be overridden by their
+     * own properties.</p>
+     * @param value <tt>false</tt> if it should not filter, otherwise
+     * <tt>true<tt>
+     *
+     * @since Ant 1.5
+     */
+    public void setFiltertrace(boolean value) {
+        Enumeration e = allTests();
+        while (e.hasMoreElements()) {
+            BaseTest test = (BaseTest) e.nextElement();
+            test.setFiltertrace(value);
+        }
+    }
+
+    /**
+     * If true, stop the build process when there is an error in a test.
+     * This property is applied on all BatchTest (batchtest) and JUnitTest
+     * (test) however it can possibly be overridden by their own
+     * properties.
+     * @param value <tt>true</tt> if it should halt, otherwise
+     * <tt>false</tt>
+     *
+     * @since Ant 1.2
+     */
+    public void setHaltonerror(boolean value) {
+        Enumeration e = allTests();
+        while (e.hasMoreElements()) {
+            BaseTest test = (BaseTest) e.nextElement();
+            test.setHaltonerror(value);
+        }
+    }
+
+    /**
+     * Property to set to "true" if there is a error in a test.
+     *
+     * <p>This property is applied on all BatchTest (batchtest) and
+     * JUnitTest (test), however, it can possibly be overriden by
+     * their own properties.</p>
+     * @param propertyName the name of the property to set in the
+     * event of an error.
+     *
+     * @since Ant 1.4
+     */
+    public void setErrorProperty(String propertyName) {
+        Enumeration e = allTests();
+        while (e.hasMoreElements()) {
+            BaseTest test = (BaseTest) e.nextElement();
+            test.setErrorProperty(propertyName);
+        }
+    }
+
+    /**
+     * If true, stop the build process if a test fails
+     * (errors are considered failures as well).
+     * This property is applied on all BatchTest (batchtest) and
+     * JUnitTest (test) however it can possibly be overridden by their
+     * own properties.
+     * @param value <tt>true</tt> if it should halt, otherwise
+     * <tt>false</tt>
+     *
+     * @since Ant 1.2
+     */
+    public void setHaltonfailure(boolean value) {
+        Enumeration e = allTests();
+        while (e.hasMoreElements()) {
+            BaseTest test = (BaseTest) e.nextElement();
+            test.setHaltonfailure(value);
+        }
+    }
+
+    /**
+     * Property to set to "true" if there is a failure in a test.
+     *
+     * <p>This property is applied on all BatchTest (batchtest) and
+     * JUnitTest (test), however, it can possibly be overriden by
+     * their own properties.</p>
+     * @param propertyName the name of the property to set in the
+     * event of an failure.
+     *
+     * @since Ant 1.4
+     */
+    public void setFailureProperty(String propertyName) {
+        Enumeration e = allTests();
+        while (e.hasMoreElements()) {
+            BaseTest test = (BaseTest) e.nextElement();
+            test.setFailureProperty(propertyName);
+        }
+    }
+
+    /**
+     * If true, JVM should be forked for each test.
+     *
+     * <p>It avoids interference between testcases and possibly avoids
+     * hanging the build.  this property is applied on all BatchTest
+     * (batchtest) and JUnitTest (test) however it can possibly be
+     * overridden by their own properties.</p>
+     * @param value <tt>true</tt> if a JVM should be forked, otherwise
+     * <tt>false</tt>
+     * @see #setTimeout
+     *
+     * @since Ant 1.2
+     */
+    public void setFork(boolean value) {
+        Enumeration e = allTests();
+        while (e.hasMoreElements()) {
+            BaseTest test = (BaseTest) e.nextElement();
+            test.setFork(value);
+        }
+    }
+
+    /**
+     * Set the behavior when {@link #setFork fork} fork has been enabled.
+     *
+     * <p>Possible values are "once", "perTest" and "perBatch".  If
+     * set to "once", only a single Java VM will be forked for all
+     * tests, with "perTest" (the default) each test will run in a
+     * fresh Java VM and "perBatch" will run all tests from the same
+     * &lt;batchtest&gt; in the same Java VM.</p>
+     *
+     * <p>This attribute will be ignored if tests run in the same VM
+     * as Ant.</p>
+     *
+     * <p>Only tests with the same configuration of haltonerror,
+     * haltonfailure, errorproperty, failureproperty and filtertrace
+     * can share a forked Java VM, so even if you set the value to
+     * "once", Ant may need to fork mutliple VMs.</p>
+     *
+     * @since Ant 1.6.2
+     */
+    public void setForkMode(ForkMode mode) {
+        this.forkMode = mode;
+    }
+
+    /**
+     * If true, print one-line statistics for each test, or "withOutAndErr"
+     * to also show standard output and error.
+     *
+     * Can take the values on, off, and withOutAndErr.
+     * @param value <tt>true</tt> to print a summary,
+     * <tt>withOutAndErr</tt> to include the test&apos;s output as
+     * well, <tt>false</tt> otherwise.
+     * @see SummaryJUnitResultFormatter
+     *
+     * @since Ant 1.2
+     */
+    public void setPrintsummary(SummaryAttribute value) {
+        summaryValue = value.getValue();
+        summary = value.asBoolean();
+    }
+
+    /**
+     * Print summary enumeration values.
+     */
+    public static class SummaryAttribute extends EnumeratedAttribute {
+        /**
+         * list the possible values
+         * @return  array of allowed values
+         */
+        public String[] getValues() {
+            return new String[] {"true", "yes", "false", "no",
+                                 "on", "off", "withOutAndErr"};
+        }
+
+        /**
+         * gives the boolean equivalent of the authorized values
+         * @return boolean equivalent of the value
+         */
+        public boolean asBoolean() {
+            String value = getValue();
+            return "true".equals(value)
+                || "on".equals(value)
+                || "yes".equals(value)
+                || "withOutAndErr".equals(value);
+        }
+    }
+
+    /**
+     * Set the timeout value (in milliseconds).
+     *
+     * <p>If the test is running for more than this value, the test
+     * will be canceled. (works only when in 'fork' mode).</p>
+     * @param value the maximum time (in milliseconds) allowed before
+     * declaring the test as 'timed-out'
+     * @see #setFork(boolean)
+     *
+     * @since Ant 1.2
+     */
+    public void setTimeout(Integer value) {
+        timeout = value;
+    }
+
+    /**
+     * Set the maximum memory to be used by all forked JVMs.
+     * @param   max     the value as defined by <tt>-mx</tt> or <tt>-Xmx</tt>
+     *                  in the java command line options.
+     *
+     * @since Ant 1.2
+     */
+    public void setMaxmemory(String max) {
+        getCommandline().setMaxmemory(max);
+    }
+
+    /**
+     * The command used to invoke the Java Virtual Machine,
+     * default is 'java'. The command is resolved by
+     * java.lang.Runtime.exec(). Ignored if fork is disabled.
+     *
+     * @param   value   the new VM to use instead of <tt>java</tt>
+     * @see #setFork(boolean)
+     *
+     * @since Ant 1.2
+     */
+    public void setJvm(String value) {
+        getCommandline().setVm(value);
+    }
+
+    /**
+     * Adds a JVM argument; ignored if not forking.
+     *
+     * @return create a new JVM argument so that any argument can be
+     * passed to the JVM.
+     * @see #setFork(boolean)
+     *
+     * @since Ant 1.2
+     */
+    public Commandline.Argument createJvmarg() {
+        return getCommandline().createVmArgument();
+    }
+
+    /**
+     * The directory to invoke the VM in. Ignored if no JVM is forked.
+     * @param   dir     the directory to invoke the JVM from.
+     * @see #setFork(boolean)
+     *
+     * @since Ant 1.2
+     */
+    public void setDir(File dir) {
+        this.dir = dir;
+    }
+
+    /**
+     * Adds a system property that tests can access.
+     * This might be useful to tranfer Ant properties to the
+     * testcases when JVM forking is not enabled.
+     *
+     * @since Ant 1.3
+     * @deprecated since ant 1.6
+     * @param sysp environment variable to add
+     */
+    public void addSysproperty(Environment.Variable sysp) {
+
+        getCommandline().addSysproperty(sysp);
+    }
+
+    /**
+     * Adds a system property that tests can access.
+     * This might be useful to tranfer Ant properties to the
+     * testcases when JVM forking is not enabled.
+     * @param sysp new environment variable to add
+     * @since Ant 1.6
+     */
+    public void addConfiguredSysproperty(Environment.Variable sysp) {
+        // get a build exception if there is a missing key or value
+        // see bugzilla report 21684
+        String testString = sysp.getContent();
+        getProject().log("sysproperty added : " + testString, Project.MSG_DEBUG);
+        getCommandline().addSysproperty(sysp);
+    }
+
+    /**
+     * Adds a set of properties that will be used as system properties
+     * that tests can access.
+     *
+     * This might be useful to tranfer Ant properties to the
+     * testcases when JVM forking is not enabled.
+     *
+     * @param sysp set of properties to be added
+     * @since Ant 1.6
+     */
+    public void addSyspropertyset(PropertySet sysp) {
+        getCommandline().addSyspropertyset(sysp);
+    }
+
+    /**
+     * Adds path to classpath used for tests.
+     *
+     * @return reference to the classpath in the embedded java command line
+     * @since Ant 1.2
+     */
+    public Path createClasspath() {
+        return getCommandline().createClasspath(getProject()).createPath();
+    }
+
+    /**
+     * Adds a path to the bootclasspath.
+     * @return reference to the bootclasspath in the embedded java command line
+     * @since Ant 1.6
+     */
+    public Path createBootclasspath() {
+        return getCommandline().createBootclasspath(getProject()).createPath();
+    }
+
+    /**
+     * Adds an environment variable; used when forking.
+     *
+     * <p>Will be ignored if we are not forking a new VM.</p>
+     * @param var environment variable to be added
+     * @since Ant 1.5
+     */
+    public void addEnv(Environment.Variable var) {
+        env.addVariable(var);
+    }
+
+    /**
+     * If true, use a new environment when forked.
+     *
+     * <p>Will be ignored if we are not forking a new VM.</p>
+     *
+     * @param newenv boolean indicating if setting a new environment is wished
+     * @since Ant 1.5
+     */
+    public void setNewenvironment(boolean newenv) {
+        newEnvironment = newenv;
+    }
+
+    /**
+     * Add a new single testcase.
+     * @param   test    a new single testcase
+     * @see JUnitTest
+     *
+     * @since Ant 1.2
+     */
+    public void addTest(JUnitTest test) {
+        tests.addElement(test);
+    }
+
+    /**
+     * Adds a set of tests based on pattern matching.
+     *
+     * @return  a new instance of a batch test.
+     * @see BatchTest
+     *
+     * @since Ant 1.2
+     */
+    public BatchTest createBatchTest() {
+        BatchTest test = new BatchTest(getProject());
+        batchTests.addElement(test);
+        return test;
+    }
+
+    /**
+     * Add a new formatter to all tests of this task.
+     *
+     * @param fe formatter element
+     * @since Ant 1.2
+     */
+    public void addFormatter(FormatterElement fe) {
+        formatters.addElement(fe);
+    }
+
+    /**
+     * If true, include ant.jar, optional.jar and junit.jar in the forked VM.
+     *
+     * @param b include ant run time yes or no
+     * @since Ant 1.5
+     */
+    public void setIncludeantruntime(boolean b) {
+        includeAntRuntime = b;
+    }
+
+    /**
+     * If true, send any output generated by tests to Ant's logging system
+     * as well as to the formatters.
+     * By default only the formatters receive the output.
+     *
+     * <p>Output will always be passed to the formatters and not by
+     * shown by default.  This option should for example be set for
+     * tests that are interactive and prompt the user to do
+     * something.</p>
+     *
+     * @param showOutput if true, send output to Ant's logging system too
+     * @since Ant 1.5
+     */
+    public void setShowOutput(boolean showOutput) {
+        this.showOutput = showOutput;
+    }
+
+    /**
+     * Assertions to enable in this program (if fork=true)
+     * @since Ant 1.6
+     * @param asserts assertion set
+     */
+    public void addAssertions(Assertions asserts) {
+        if (getCommandline().getAssertions() != null) {
+            throw new BuildException("Only one assertion declaration is allowed");
+        }
+        getCommandline().setAssertions(asserts);
+    }
+
+    /**
+     * Sets the permissions for the application run inside the same JVM.
+     * @since Ant 1.6
+     * @return .
+     */
+    public Permissions createPermissions() {
+        if (perm == null) {
+            perm = new Permissions();
+        }
+        return perm;
+    }
+
+    /**
+     * Creates a new JUnitRunner and enables fork of a new Java VM.
+     *
+     * @throws Exception under ??? circumstances
+     * @since Ant 1.2
+     */
+    public JUnitTask() throws Exception {
+        getCommandline()
+            .setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner");
+    }
+
+    /**
+     * Where Ant should place temporary files.
+     *
+     * @param tmpDir location where temporary files should go to
+     * @since Ant 1.6
+     */
+    public void setTempdir(File tmpDir) {
+        if (tmpDir!=null) {
+            if (!tmpDir.exists() || !tmpDir.isDirectory()) {
+                throw new BuildException(tmpDir.toString()
+                                         +" is not a valid temp directory");
+            }
+        }
+        this.tmpDir = tmpDir;
+    }
+
+    /**
+     * Adds the jars or directories containing Ant, this task and
+     * JUnit to the classpath - this should make the forked JVM work
+     * without having to specify them directly.
+     *
+     * @since Ant 1.4
+     */
+    public void init() {
+        antRuntimeClasses = new Path(getProject());
+        addClasspathEntry("/junit/framework/TestCase.class");
+        addClasspathEntry("/org/apache/tools/ant/launch/AntMain.class");
+        addClasspathEntry("/org/apache/tools/ant/Task.class");
+        addClasspathEntry("/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.class");
+    }
+
+    /**
+     * Runs the testcase.
+     *
+     * @throws BuildException in case of test failures or errors
+     * @since Ant 1.2
+     */
+    public void execute() throws BuildException {
+        List testLists = new ArrayList();
+
+        boolean forkPerTest = forkMode.getValue().equals(ForkMode.PER_TEST);
+        if (forkPerTest || forkMode.getValue().equals(ForkMode.ONCE)) {
+            testLists.addAll(executeOrQueue(getIndividualTests(),
+                                            forkPerTest));
+        } else { /* forkMode.getValue().equals(ForkMode.PER_BATCH) */
+            final int count = batchTests.size();
+            for (int i = 0; i < count; i++) {
+                BatchTest batchtest = (BatchTest) batchTests.elementAt(i);
+                testLists.addAll(executeOrQueue(batchtest.elements(), false));
+            }
+            testLists.addAll(executeOrQueue(tests.elements(), forkPerTest));
+        }
+
+        try {
+            Iterator iter = testLists.iterator();
+            while (iter.hasNext()) {
+                List l = (List) iter.next();
+                if (l.size() == 1) {
+                    execute((JUnitTest) l.get(0));
+                } else {
+                    execute(l);
+                }            
+            }
+        } finally {
+            if (classLoader != null && reloading) {
+                classLoader.cleanup();
+            }
+            classLoader = null;
+        }
+    }
+
+    /**
+     * Run the tests.
+     * @param arg one JunitTest
+     * @throws BuildException in case of test failures or errors
+     */
+    protected void execute(JUnitTest arg) throws BuildException {
+        JUnitTest test = (JUnitTest) arg.clone();
+        // set the default values if not specified
+        //@todo should be moved to the test class instead.
+        if (test.getTodir() == null) {
+            test.setTodir(getProject().resolveFile("."));
+        }
+
+        if (test.getOutfile() == null) {
+            test.setOutfile("TEST-" + test.getName());
+        }
+
+        // execute the test and get the return code
+        int exitValue = JUnitTestRunner.ERRORS;
+        boolean wasKilled = false;
+        if (!test.getFork()) {
+            exitValue = executeInVM(test);
+        } else {
+            ExecuteWatchdog watchdog = createWatchdog();
+            exitValue = executeAsForked(test, watchdog, null);
+            // null watchdog means no timeout, you'd better not check with null
+            if (watchdog != null) {
+                wasKilled = watchdog.killedProcess();
+            }
+        }
+        actOnTestResult(exitValue, wasKilled, test, "Test " + test.getName());
+    }
+
+    /**
+     * Execute a list of tests in a single forked Java VM.
+     */
+    protected void execute(List tests) throws BuildException {
+        JUnitTest test = null;
+        // Create a temporary file to pass the test cases to run to 
+        // the runner (one test case per line)
+        File casesFile = createTempPropertiesFile("junittestcases");
+        PrintWriter writer = null;
+        try {
+            writer = 
+                new PrintWriter(new BufferedWriter(new FileWriter(casesFile)));
+            Iterator iter = tests.iterator();
+            while (iter.hasNext()) {
+                test = (JUnitTest) iter.next();
+                writer.print(test.getName()); 
+                if (test.getTodir() == null) {
+                    writer.print("," + getProject().resolveFile("."));
+                } else {
+                    writer.print("," + test.getTodir());
+                }
+
+                if (test.getOutfile() == null) {
+                    writer.println("," + "TEST-" + test.getName());
+                } else {
+                    writer.println("," + test.getOutfile());
+                }
+            }
+            writer.flush();
+            writer.close();
+            writer = null;
+
+            // execute the test and get the return code
+            int exitValue = JUnitTestRunner.ERRORS;
+            boolean wasKilled = false;
+            ExecuteWatchdog watchdog = createWatchdog();
+            exitValue = executeAsForked(test, watchdog, casesFile);
+            // null watchdog means no timeout, you'd better not check
+            // with null
+            if (watchdog != null) {
+                wasKilled = watchdog.killedProcess();
+            }
+            actOnTestResult(exitValue, wasKilled, test, "Tests");
+        } catch(IOException e) {
+            log(e.toString(), Project.MSG_ERR);
+            throw new BuildException(e);
+        } finally {
+            if (writer != null) {
+                writer.close();
+            }
+            
+            try {
+                casesFile.delete();
+            } catch (Exception e) {
+                log(e.toString(), Project.MSG_ERR);
+            }
+        }
+    }
+
+    /**
+     * Execute a testcase by forking a new JVM. The command will block until
+     * it finishes. To know if the process was destroyed or not, use the
+     * <tt>killedProcess()</tt> method of the watchdog class.
+     * @param  test       the testcase to execute.
+     * @param  watchdog   the watchdog in charge of cancelling the test if it
+     * exceeds a certain amount of time. Can be <tt>null</tt>, in this case
+     * the test could probably hang forever.
+     * @throws BuildException in case of error creating a temporary property file,
+     * or if the junit process can not be forked
+     */
+    private int executeAsForked(JUnitTest test, ExecuteWatchdog watchdog, 
+                                File casesFile)
+        throws BuildException {
+
+        if (perm != null) {
+            log("Permissions ignored when running in forked mode!",
+                Project.MSG_WARN);
+        }
+
+        CommandlineJava cmd = null;
+        try {
+            cmd = (CommandlineJava) getCommandline().clone();
+        } catch (CloneNotSupportedException e) {
+            throw new BuildException(e);
+        }
+
+        cmd.setClassname("org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner");
+        if (casesFile == null) {
+            cmd.createArgument().setValue(test.getName());
+        } else {
+            log("Running multiple tests in the same VM", Project.MSG_VERBOSE);
+            cmd.createArgument().setValue("testsfile=" + casesFile);
+        }
+        
+        cmd.createArgument().setValue("filtertrace=" + test.getFiltertrace());
+        cmd.createArgument().setValue("haltOnError=" + test.getHaltonerror());
+        cmd.createArgument().setValue("haltOnFailure="
+                                      + test.getHaltonfailure());
+        if (includeAntRuntime) {
+            Vector v = Execute.getProcEnvironment();
+            Enumeration e = v.elements();
+            while (e.hasMoreElements()) {
+                String s = (String) e.nextElement();
+                if (s.startsWith("CLASSPATH=")) {
+                    cmd.createClasspath(getProject()).createPath()
+                        .append(new Path(getProject(),
+                                         s.substring(10 // "CLASSPATH=".length()
+                                                     )));
+                }
+            }
+            log("Implicitly adding " + antRuntimeClasses + " to CLASSPATH",
+                Project.MSG_VERBOSE);
+            cmd.createClasspath(getProject()).createPath()
+                .append(antRuntimeClasses);
+        }
+
+        if (summary) {
+            log("Running " + test.getName(), Project.MSG_INFO);
+            cmd.createArgument()
+                .setValue("formatter"
+                          + "=org.apache.tools.ant.taskdefs.optional.junit.SummaryJUnitResultFormatter");
+        }
+
+        cmd.createArgument().setValue("showoutput="
+                                      + String.valueOf(showOutput));
+
+        StringBuffer formatterArg = new StringBuffer(STRING_BUFFER_SIZE);
+        final FormatterElement[] feArray = mergeFormatters(test);
+        for (int i = 0; i < feArray.length; i++) {
+            FormatterElement fe = feArray[i];
+            if (fe.shouldUse(this)) {
+                formatterArg.append("formatter=");
+                formatterArg.append(fe.getClassname());
+                File outFile = getOutput(fe, test);
+                if (outFile != null) {
+                    formatterArg.append(",");
+                    formatterArg.append(outFile);
+                }
+                cmd.createArgument().setValue(formatterArg.toString());
+                formatterArg = new StringBuffer();
+            }
+        }
+
+
+        File propsFile = createTempPropertiesFile("junit");
+        cmd.createArgument().setValue("propsfile="
+                                      + propsFile.getAbsolutePath());
+        Hashtable p = getProject().getProperties();
+        Properties props = new Properties();
+        for (Enumeration e = p.keys(); e.hasMoreElements();) {
+            Object key = e.nextElement();
+            props.put(key, p.get(key));
+        }
+        try {
+            FileOutputStream outstream = new FileOutputStream(propsFile);
+            props.store(outstream, "Ant JUnitTask generated properties file");
+            outstream.close();
+        } catch (java.io.IOException e) {
+            propsFile.delete();
+            throw new BuildException("Error creating temporary properties "
+                                     + "file.", e, getLocation());
+        }
+
+        Execute execute = new Execute(new LogStreamHandler(this,
+                                                           Project.MSG_INFO,
+                                                           Project.MSG_WARN),
+                                      watchdog);
+        execute.setCommandline(cmd.getCommandline());
+        execute.setAntRun(getProject());
+        if (dir != null) {
+            execute.setWorkingDirectory(dir);
+        }
+
+        String[] environment = env.getVariables();
+        if (environment != null) {
+            for (int i = 0; i < environment.length; i++) {
+                log("Setting environment variable: " + environment[i],
+                    Project.MSG_VERBOSE);
+            }
+        }
+        execute.setNewenvironment(newEnvironment);
+        execute.setEnvironment(environment);
+
+        log(cmd.describeCommand(), Project.MSG_VERBOSE);
+        int retVal;
+        try {
+            retVal = execute.execute();
+        } catch (IOException e) {
+            throw new BuildException("Process fork failed.", e, getLocation());
+        } finally {
+            if (watchdog != null && watchdog.killedProcess()) {
+                logTimeout(feArray, test);
+            }
+
+            if (!propsFile.delete()) {
+                throw new BuildException("Could not delete temporary "
+                                         + "properties file.");
+            }
+        }
+
+        return retVal;
+    }
+
+    /**
+     * Create a temporary file to pass the properties to a new process.
+     * Will auto-delete on (graceful) exit.
+     * The file will be in the project basedir unless tmpDir declares
+     * something else.
+     * @param prefix
+     * @return
+     */
+    private File createTempPropertiesFile(String prefix) {
+        File propsFile =
+            FileUtils.newFileUtils().createTempFile(prefix, ".properties",
+                                                    tmpDir != null ? tmpDir : getProject().getBaseDir());
+        propsFile.deleteOnExit();
+        return propsFile;
+    }
+
+
+    /**
+     * Pass output sent to System.out to the TestRunner so it can
+     * collect ot for the formatters.
+     *
+     * @param output output coming from System.out
+     * @since Ant 1.5
+     */
+    protected void handleOutput(String output) {
+        if (runner != null) {
+            runner.handleOutput(output);
+            if (showOutput) {
+                super.handleOutput(output);
+            }
+        } else {
+            super.handleOutput(output);
+        }
+    }
+
+    /**
+     * @see Task#handleInput(byte[], int, int)
+     *
+     * @since Ant 1.6
+     */
+    protected int handleInput(byte[] buffer, int offset, int length)
+        throws IOException {
+        if (runner != null) {
+            return runner.handleInput(buffer, offset, length);
+        } else {
+            return super.handleInput(buffer, offset, length);
+        }
+    }
+
+
+    /**
+     * Pass output sent to System.out to the TestRunner so it can
+     * collect ot for the formatters.
+     *
+     * @param output output coming from System.out
+     * @since Ant 1.5.2
+     */
+    protected void handleFlush(String output) {
+        if (runner != null) {
+            runner.handleFlush(output);
+            if (showOutput) {
+                super.handleFlush(output);
+            }
+        } else {
+            super.handleFlush(output);
+        }
+    }
+
+    /**
+     * Pass output sent to System.err to the TestRunner so it can
+     * collect it for the formatters.
+     *
+     * @param output output coming from System.err
+     * @since Ant 1.5
+     */
+    public void handleErrorOutput(String output) {
+        if (runner != null) {
+            runner.handleErrorOutput(output);
+            if (showOutput) {
+                super.handleErrorOutput(output);
+            }
+        } else {
+            super.handleErrorOutput(output);
+        }
+    }
+
+
+    /**
+     * Pass output sent to System.err to the TestRunner so it can
+     * collect it for the formatters.
+     *
+     * @param output coming from System.err
+     * @since Ant 1.5.2
+     */
+    public void handleErrorFlush(String output) {
+        if (runner != null) {
+            runner.handleErrorFlush(output);
+            if (showOutput) {
+                super.handleErrorFlush(output);
+            }
+        } else {
+            super.handleErrorFlush(output);
+        }
+    }
+
+    // in VM is not very nice since it could probably hang the
+    // whole build. IMHO this method should be avoided and it would be best
+    // to remove it in future versions. TBD. (SBa)
+
+    /**
+     * Execute inside VM.
+     * @param arg one JUnitTest
+     * @throws BuildException under unspecified circumstances
+     */
+    private int executeInVM(JUnitTest arg) throws BuildException {
+        JUnitTest test = (JUnitTest) arg.clone();
+        test.setProperties(getProject().getProperties());
+        if (dir != null) {
+            log("dir attribute ignored if running in the same VM",
+                Project.MSG_WARN);
+        }
+
+        if (newEnvironment || null != env.getVariables()) {
+            log("Changes to environment variables are ignored if running in "
+                + "the same VM.", Project.MSG_WARN);
+        }
+
+        if (getCommandline().getBootclasspath() != null) {
+            log("bootclasspath is ignored if running in the same VM.",
+                Project.MSG_WARN);
+        }
+
+        CommandlineJava.SysProperties sysProperties =
+            getCommandline().getSystemProperties();
+        if (sysProperties != null) {
+            sysProperties.setSystem();
+        }
+
+        try {
+            log("Using System properties " + System.getProperties(),
+                Project.MSG_VERBOSE);
+            createClassLoader();
+            if (classLoader != null) {
+                classLoader.setThreadContextLoader();
+            }
+            runner = new JUnitTestRunner(test, test.getHaltonerror(),
+                                         test.getFiltertrace(),
+                                         test.getHaltonfailure(), classLoader);
+            if (summary) {
+                log("Running " + test.getName(), Project.MSG_INFO);
+
+                SummaryJUnitResultFormatter f =
+                    new SummaryJUnitResultFormatter();
+                f.setWithOutAndErr("withoutanderr"
+                                   .equalsIgnoreCase(summaryValue));
+                f.setOutput(getDefaultOutput());
+                runner.addFormatter(f);
+            }
+
+            runner.setPermissions(perm);
+
+            final FormatterElement[] feArray = mergeFormatters(test);
+            for (int i = 0; i < feArray.length; i++) {
+                FormatterElement fe = feArray[i];
+                if (fe.shouldUse(this)) {
+                    File outFile = getOutput(fe, test);
+                    if (outFile != null) {
+                        fe.setOutfile(outFile);
+                    } else {
+                        fe.setOutput(getDefaultOutput());
+                    }
+                    runner.addFormatter(fe.createFormatter(classLoader));
+                }
+            }
+
+            runner.run();
+            return runner.getRetCode();
+        } finally {
+            if (sysProperties != null) {
+                sysProperties.restoreSystem();
+            }
+            if (classLoader != null) {
+                classLoader.resetThreadContextLoader();
+                if (!reloading) {
+                    classLoader.cleanup();
+                }
+            }
+        }
+    }
+
+    /**
+     * @return <tt>null</tt> if there is a timeout value, otherwise the
+     * watchdog instance.
+     *
+     * @throws BuildException under unspecified circumstances
+     * @since Ant 1.2
+     */
+    protected ExecuteWatchdog createWatchdog() throws BuildException {
+        if (timeout == null) {
+            return null;
+        }
+        return new ExecuteWatchdog((long) timeout.intValue());
+    }
+
+    /**
+     * Get the default output for a formatter.
+     *
+     * @return default output stream for a formatter
+     * @since Ant 1.3
+     */
+    protected OutputStream getDefaultOutput() {
+        return new LogOutputStream(this, Project.MSG_INFO);
+    }
+
+    /**
+     * Merge all individual tests from the batchtest with all individual tests
+     * and return an enumeration over all <tt>JUnitTest</tt>.
+     *
+     * @return enumeration over individual tests
+     * @since Ant 1.3
+     */
+    protected Enumeration getIndividualTests() {
+        final int count = batchTests.size();
+        final Enumeration[] enums = new Enumeration[ count + 1];
+        for (int i = 0; i < count; i++) {
+            BatchTest batchtest = (BatchTest) batchTests.elementAt(i);
+            enums[i] = batchtest.elements();
+        }
+        enums[enums.length - 1] = tests.elements();
+        return Enumerations.fromCompound(enums);
+    }
+
+    /**
+     * return an enumeration listing each test, then each batchtest
+     * @return enumeration
+     * @since Ant 1.3
+     */
+    protected Enumeration allTests() {
+        Enumeration[] enums = {tests.elements(), batchTests.elements()};
+        return Enumerations.fromCompound(enums);
+    }
+
+    /**
+     * @param test junit test
+     * @return array of FormatterElement
+     * @since Ant 1.3
+     */
+    private FormatterElement[] mergeFormatters(JUnitTest test) {
+        Vector feVector = (Vector) formatters.clone();
+        test.addFormattersTo(feVector);
+        FormatterElement[] feArray = new FormatterElement[feVector.size()];
+        feVector.copyInto(feArray);
+        return feArray;
+    }
+
+    /**
+     * If the formatter sends output to a file, return that file.
+     * null otherwise.
+     * @param fe  formatter element
+     * @param test one JUnit test
+     * @return file reference
+     * @since Ant 1.3
+     */
+    protected File getOutput(FormatterElement fe, JUnitTest test) {
+        if (fe.getUseFile()) {
+            String base = test.getOutfile();
+            if (base == null) {
+                base = JUnitTestRunner.IGNORED_FILE_NAME;
+            }
+            String filename = base + fe.getExtension();
+            File destFile = new File(test.getTodir(), filename);
+            String absFilename = destFile.getAbsolutePath();
+            return getProject().resolveFile(absFilename);
+        }
+        return null;
+    }
+
+    /**
+     * Search for the given resource and add the directory or archive
+     * that contains it to the classpath.
+     *
+     * <p>Doesn't work for archives in JDK 1.1 as the URL returned by
+     * getResource doesn't contain the name of the archive.</p>
+     *
+     * @param resource resource that one wants to lookup
+     * @since Ant 1.4
+     */
+    protected void addClasspathEntry(String resource) {
+        /*
+         * pre Ant 1.6 this method used to call getClass().getResource
+         * while Ant 1.6 will call ClassLoader.getResource().
+         *
+         * The difference is that Class.getResource expects a leading
+         * slash for "absolute" resources and will strip it before
+         * delegating to ClassLoader.getResource - so we now have to
+         * emulate Class's behavior.
+         */
+        if (resource.startsWith("/")) {
+            resource = resource.substring(1);
+        } else {
+            resource = "org/apache/tools/ant/taskdefs/optional/junit/"
+                + resource;
+        }
+
+        File f = LoaderUtils.getResourceSource(getClass().getClassLoader(),
+                                               resource);
+        if (f != null) {
+            log("Found " + f.getAbsolutePath(), Project.MSG_DEBUG);
+            antRuntimeClasses.createPath().setLocation(f);
+        } else {
+            log("Couldn\'t find " + resource, Project.MSG_DEBUG);
+        }
+    }
+
+    /**
+     * Take care that some output is produced in report files if the
+     * watchdog kills the test.
+     *
+     * @since Ant 1.5.2
+     */
+
+    private void logTimeout(FormatterElement[] feArray, JUnitTest test) {
+        createClassLoader();
+        test.setCounts(1, 0, 1);
+        test.setProperties(getProject().getProperties());
+        for (int i = 0; i < feArray.length; i++) {
+            FormatterElement fe = feArray[i];
+            File outFile = getOutput(fe, test);
+            JUnitResultFormatter formatter = fe.createFormatter(classLoader);
+            if (outFile != null && formatter != null) {
+                try {
+                    OutputStream out = new FileOutputStream(outFile);
+                    addTimeout(test, formatter, out);
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+        if (summary) {
+            SummaryJUnitResultFormatter f = new SummaryJUnitResultFormatter();
+            f.setWithOutAndErr("withoutanderr".equalsIgnoreCase(summaryValue));
+            addTimeout(test, f, getDefaultOutput());
+        }
+    }
+
+    /**
+     * Adds the actual timeout to the formatter.
+     * Only used from the logTimeout method.
+     * @since Ant 1.6
+     */
+    private void addTimeout(JUnitTest test, JUnitResultFormatter formatter,
+                            OutputStream out) {
+        formatter.setOutput(out);
+        formatter.startTestSuite(test);
+
+        //the trick to integrating test output to the formatter, is to
+        //create a special test class that asserts a timout occurred,
+        //and tell the formatter that it raised.  
+        Test t = new Test() {
+                public int countTestCases() { return 1; }
+                public void run(TestResult r) {
+                    throw new AssertionFailedError("Timeout occurred");
+                }
+            };
+        formatter.startTest(t);
+        formatter.addError(t, new AssertionFailedError("Timeout occurred"));
+        formatter.endTestSuite(test);
+    }
+
+    /**
+     * Creates and configures an AntClassLoader instance from the
+     * nested classpath element.
+     *
+     * @since Ant 1.6
+     */
+    private void createClassLoader() {
+        Path userClasspath = getCommandline().getClasspath();
+        if (userClasspath != null) {
+            if (reloading || classLoader == null) {
+                Path classpath = (Path) userClasspath.clone();
+                if (includeAntRuntime) {
+                    log("Implicitly adding " + antRuntimeClasses
+                        + " to CLASSPATH", Project.MSG_VERBOSE);
+                    classpath.append(antRuntimeClasses);
+                }
+                classLoader = getProject().createClassLoader(classpath);
+                if (getClass().getClassLoader() != null
+                    && getClass().getClassLoader() != Project.class.getClassLoader()) {
+                    classLoader.setParent(getClass().getClassLoader());
+                }
+                classLoader.setParentFirst(false);
+                classLoader.addJavaLibraries();
+                log("Using CLASSPATH " + classLoader.getClasspath(), 
+                    Project.MSG_VERBOSE);
+                // make sure the test will be accepted as a TestCase
+                classLoader.addSystemPackageRoot("junit");
+                // will cause trouble in JDK 1.1 if omitted
+                classLoader.addSystemPackageRoot("org.apache.tools.ant");
+            }
+        }
+    }
+
+    /**
+     * @since Ant 1.6.2
+     */
+    protected CommandlineJava getCommandline() {
+        if (commandline == null) {
+            commandline = new CommandlineJava();
+        }
+        return commandline;
+    }
+
+    /**
+     * Forked test support
+     * @since Ant 1.6.2
+     */
+    private final class ForkedTestConfiguration {
+        private boolean filterTrace;
+        private boolean haltOnError;
+        private boolean haltOnFailure;
+        private String errorProperty;
+        private String failureProperty;
+
+        /**
+         * constructor for forked test configuration
+         * @param filterTrace
+         * @param haltOnError
+         * @param haltOnFailure
+         * @param errorProperty
+         * @param failureProperty
+         */
+        ForkedTestConfiguration(boolean filterTrace, boolean haltOnError,
+                                boolean haltOnFailure, String errorProperty,
+                                String failureProperty) {
+            this.filterTrace = filterTrace;
+            this.haltOnError = haltOnError;
+            this.haltOnFailure = haltOnFailure;
+            this.errorProperty = errorProperty;
+            this.failureProperty = failureProperty;
+        }
+
+        /**
+         * configure from a test; sets member variables to attributes of the test
+         * @param test
+         */
+        ForkedTestConfiguration(JUnitTest test) {
+            this(test.getFiltertrace(),
+                 test.getHaltonerror(),
+                 test.getHaltonfailure(),
+                 test.getErrorProperty(),
+                 test.getFailureProperty());
+        }
+
+        /**
+         * equality test checks all the member variables
+         * @param other
+         * @return true if everything is equal
+         */
+        public boolean equals(Object other) {
+            if (other == null 
+                || other.getClass() != ForkedTestConfiguration.class) {
+                return false;
+            }
+            ForkedTestConfiguration o = (ForkedTestConfiguration) other;
+            return filterTrace == o.filterTrace 
+                && haltOnError == o.haltOnError
+                && haltOnFailure == o.haltOnFailure
+                && ((errorProperty == null && o.errorProperty == null)
+                    || 
+                    (errorProperty != null 
+                     && errorProperty.equals(o.errorProperty)))
+                && ((failureProperty == null && o.failureProperty == null)
+                    || 
+                    (failureProperty != null 
+                     && failureProperty.equals(o.failureProperty)));
+        }
+
+        /**
+         * hashcode is based only on the boolean members, and returns a value
+         * in the range 0-7.
+         * @return
+         */
+        public int hashCode() {
+            return (filterTrace ? 1 : 0) 
+                + (haltOnError ? 2 : 0)
+                + (haltOnFailure ? 4 : 0);
+        }
+    }
+
+    /**
+     * These are the different forking options
+     * @since 1.6.2
+     */
+    public static final class ForkMode extends EnumeratedAttribute {
+
+        /**
+         * fork once only
+         */
+        public static final String ONCE = "once";
+        /**
+         * fork once per test class
+         */
+        public static final String PER_TEST = "perTest";
+        /**
+         * fork once per batch of tests
+         */
+        public static final String PER_BATCH = "perBatch";
+
+        public ForkMode() {
+            super();
+        }
+
+        public ForkMode(String value) {
+            super();
+            setValue(value);
+        }
+
+        public String[] getValues() {
+            return new String[] {ONCE, PER_TEST, PER_BATCH};
+        }
+    }
+
+    /**
+     * Executes all tests that don't need to be forked (or all tests
+     * if the runIndividual argument is true.  Returns a collection of
+     * lists of tests that share the same VM configuration and haven't
+     * been executed yet.
+     *
+     * @since 1.6.2
+     */
+    protected Collection executeOrQueue(Enumeration testList,
+                                        boolean runIndividual) {
+        Map testConfigurations = new HashMap();
+        while (testList.hasMoreElements()) {
+            JUnitTest test = (JUnitTest) testList.nextElement();
+            if (test.shouldRun(getProject())) {
+                if (runIndividual || !test.getFork()) {
+                    execute(test);
+                } else {
+                    ForkedTestConfiguration c =
+                        new ForkedTestConfiguration(test);
+                    List l = (List) testConfigurations.get(c);
+                    if (l == null) {
+                        l = new ArrayList();
+                        testConfigurations.put(c, l);
+                    }
+                    l.add(test);
+                }
+            }
+        }
+        return testConfigurations.values();
+    }
+
+    /**
+     * Logs information about failed tests, potentially stops
+     * processing (by throwing a BuildException) if a failure/error
+     * occured or sets a property.
+     *
+     * @since Ant 1.6.2
+     */
+    protected void actOnTestResult(int exitValue, boolean wasKilled,
+                                   JUnitTest test, String name) {
+        // if there is an error/failure and that it should halt, stop
+        // everything otherwise just log a statement
+        boolean errorOccurredHere =
+            exitValue == JUnitTestRunner.ERRORS || wasKilled;
+        boolean failureOccurredHere =
+            exitValue != JUnitTestRunner.SUCCESS || wasKilled;
+        if (errorOccurredHere || failureOccurredHere) {
+            if ((errorOccurredHere && test.getHaltonerror())
+                || (failureOccurredHere && test.getHaltonfailure())) {
+                throw new BuildException(name + " failed"
+                                         + (wasKilled ? " (timeout)" : ""), getLocation());
+            } else {
+                log(name + " FAILED"
+                    + (wasKilled ? " (timeout)" : ""), Project.MSG_ERR);
+                if (errorOccurredHere && test.getErrorProperty() != null) {
+                    getProject().setNewProperty(test.getErrorProperty(), "true");
+                }
+                if (failureOccurredHere && test.getFailureProperty() != null) {
+                    getProject().setNewProperty(test.getFailureProperty(), "true");
+                }
+            }
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTest.java	(revision 16139)
@@ -0,0 +1,178 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Vector;
+import org.apache.tools.ant.Project;
+
+/**
+ * <p> Run a single JUnit test.
+ *
+ * <p> The JUnit test is actually run by {@link JUnitTestRunner}.
+ * So read the doc comments for that class :)
+ *
+ * @since Ant 1.2
+ *
+ * @see JUnitTask
+ * @see JUnitTestRunner
+ */
+public class JUnitTest extends BaseTest implements Cloneable {
+
+    /** the name of the test case */
+    private String name = null;
+
+    /** the name of the result file */
+    private String outfile = null;
+
+    // @todo this is duplicating TestResult information. Only the time is not
+    // part of the result. So we'd better derive a new class from TestResult
+    // and deal with it. (SB)
+    private long runs, failures, errors;
+    private long runTime;
+
+    // Snapshot of the system properties
+    private Properties props = null;
+
+    public JUnitTest() {
+    }
+
+    public JUnitTest(String name) {
+        this.name  = name;
+    }
+
+    public JUnitTest(String name, boolean haltOnError, boolean haltOnFailure,
+                     boolean filtertrace) {
+        this.name  = name;
+        this.haltOnError = haltOnError;
+        this.haltOnFail = haltOnFailure;
+        this.filtertrace = filtertrace;
+    }
+
+    /**
+     * Set the name of the test class.
+     */
+    public void setName(String value) {
+        name = value;
+    }
+
+    /**
+     * Set the name of the output file.
+     */
+    public void setOutfile(String value) {
+        outfile = value;
+    }
+
+    /**
+     * Get the name of the test class.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Get the name of the output file
+     *
+     * @return the name of the output file.
+     */
+    public String getOutfile() {
+        return outfile;
+    }
+
+    public void setCounts(long runs, long failures, long errors) {
+        this.runs = runs;
+        this.failures = failures;
+        this.errors = errors;
+    }
+
+    public void setRunTime(long runTime) {
+        this.runTime = runTime;
+    }
+
+    public long runCount() {
+        return runs;
+    }
+
+    public long failureCount() {
+        return failures;
+    }
+
+    public long errorCount() {
+        return errors;
+    }
+
+    public long getRunTime() {
+        return runTime;
+    }
+
+    public Properties getProperties() {
+        return props;
+    }
+
+    public void setProperties(Hashtable p) {
+        props = new Properties();
+        for (Enumeration e = p.keys(); e.hasMoreElements();) {
+            Object key = e.nextElement();
+            props.put(key, p.get(key));
+        }
+    }
+
+    public boolean shouldRun(Project p) {
+        if (ifProperty != null && p.getProperty(ifProperty) == null) {
+            return false;
+        } else if (unlessProperty != null
+                    && p.getProperty(unlessProperty) != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    public FormatterElement[] getFormatters() {
+        FormatterElement[] fes = new FormatterElement[formatters.size()];
+        formatters.copyInto(fes);
+        return fes;
+    }
+
+    /**
+     * Convenient method to add formatters to a vector
+     */
+    void addFormattersTo(Vector v) {
+        final int count = formatters.size();
+        for (int i = 0; i < count; i++) {
+            v.addElement(formatters.elementAt(i));
+        }
+    }
+
+    /**
+     * @since Ant 1.5
+     */
+    public Object clone() {
+        try {
+            JUnitTest t = (JUnitTest) super.clone();
+            t.props = props == null ? null : (Properties) props.clone();
+            t.formatters = (Vector) formatters.clone();
+            return t;
+        } catch (CloneNotSupportedException e) {
+            // plain impossible
+            return this;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunner.java	(revision 16139)
@@ -0,0 +1,675 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestListener;
+import junit.framework.TestResult;
+import junit.framework.TestSuite;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Permissions;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.ant.util.TeeOutputStream;
+
+/**
+ * Simple Testrunner for JUnit that runs all tests of a testsuite.
+ *
+ * <p>This TestRunner expects a name of a TestCase class as its
+ * argument. If this class provides a static suite() method it will be
+ * called and the resulting Test will be run. So, the signature should be
+ * <pre><code>
+ *     public static junit.framework.Test suite()
+ * </code></pre>
+ *
+ * <p> If no such method exists, all public methods starting with
+ * "test" and taking no argument will be run.
+ *
+ * <p> Summary output is generated at the end.
+ *
+ * @since Ant 1.2
+ */
+
+public class JUnitTestRunner implements TestListener {
+
+    /**
+     * No problems with this test.
+     */
+    public static final int SUCCESS = 0;
+
+    /**
+     * Some tests failed.
+     */
+    public static final int FAILURES = 1;
+
+    /**
+     * An error occurred.
+     */
+    public static final int ERRORS = 2;
+
+    /**
+     * Used in formatter arguments as a placeholder for the basename
+     * of the output file (which gets replaced by a test specific
+     * output file name later).
+     *
+     * @since Ant 1.6.3
+     */
+    public static final String IGNORED_FILE_NAME = "IGNORETHIS";
+
+    /**
+     * Holds the registered formatters.
+     */
+    private Vector formatters = new Vector();
+
+    /**
+     * Collects TestResults.
+     */
+    private TestResult res;
+
+    /**
+     * Do we filter junit.*.* stack frames out of failure and error exceptions.
+     */
+    private static boolean filtertrace = true;
+
+    /**
+     * Do we send output to System.out/.err in addition to the formatters?
+     */
+    private boolean showOutput = false;
+
+    /**
+     * The permissions set for the test to run.
+     */
+    private Permissions perm = null;
+
+    private static final String[] DEFAULT_TRACE_FILTERS = new String[] {
+                "junit.framework.TestCase",
+                "junit.framework.TestResult",
+                "junit.framework.TestSuite",
+                "junit.framework.Assert.", // don't filter AssertionFailure
+                "junit.swingui.TestRunner",
+                "junit.awtui.TestRunner",
+                "junit.textui.TestRunner",
+                "java.lang.reflect.Method.invoke(",
+                "sun.reflect.",
+                "org.apache.tools.ant."
+        };
+
+
+    /**
+     * Do we stop on errors.
+     */
+    private boolean haltOnError = false;
+
+    /**
+     * Do we stop on test failures.
+     */
+    private boolean haltOnFailure = false;
+
+    /**
+     * Returncode
+     */
+    private int retCode = SUCCESS;
+
+    /**
+     * The TestSuite we are currently running.
+     */
+    private JUnitTest junitTest;
+
+    /** output written during the test */
+    private PrintStream systemError;
+
+    /** Error output during the test */
+    private PrintStream systemOut;
+
+    /** is this runner running in forked mode? */
+    private boolean forked = false;
+
+    /** Running more than one test suite? */
+    private static boolean multipleTests = false;
+
+    /** ClassLoader passed in in non-forked mode. */
+    private ClassLoader loader;
+
+    /**
+     * Constructor for fork=true or when the user hasn't specified a
+     * classpath.
+     */
+    public JUnitTestRunner(JUnitTest test, boolean haltOnError,
+                           boolean filtertrace, boolean haltOnFailure) {
+        this(test, haltOnError, filtertrace, haltOnFailure, false);
+    }
+
+    /**
+     * Constructor for fork=true or when the user hasn't specified a
+     * classpath.
+     */
+    public JUnitTestRunner(JUnitTest test, boolean haltOnError,
+                           boolean filtertrace, boolean haltOnFailure,
+                           boolean showOutput) {
+        this(test, haltOnError, filtertrace, haltOnFailure, showOutput, null);
+    }
+
+    /**
+     * Constructor to use when the user has specified a classpath.
+     */
+    public JUnitTestRunner(JUnitTest test, boolean haltOnError,
+                           boolean filtertrace, boolean haltOnFailure,
+                           ClassLoader loader) {
+        this(test, haltOnError, filtertrace, haltOnFailure, false, loader);
+    }
+
+    /**
+     * Constructor to use when the user has specified a classpath.
+     */
+    public JUnitTestRunner(JUnitTest test, boolean haltOnError,
+                           boolean filtertrace, boolean haltOnFailure,
+                           boolean showOutput, ClassLoader loader) {
+        this.filtertrace = filtertrace;
+        this.junitTest = test;
+        this.haltOnError = haltOnError;
+        this.haltOnFailure = haltOnFailure;
+        this.showOutput = showOutput;
+        this.loader = loader;
+    }
+
+    public void run() {
+        res = new TestResult();
+        res.addListener(this);
+        for (int i = 0; i < formatters.size(); i++) {
+            res.addListener((TestListener) formatters.elementAt(i));
+        }
+
+        ByteArrayOutputStream errStrm = new ByteArrayOutputStream();
+        systemError = new PrintStream(errStrm);
+
+        ByteArrayOutputStream outStrm = new ByteArrayOutputStream();
+        systemOut = new PrintStream(outStrm);
+
+        PrintStream savedErr = null;
+        PrintStream savedOut = null;
+
+        if (forked) {
+            savedOut = System.out;
+            savedErr = System.err;
+            if (!showOutput) {
+                System.setOut(systemOut);
+                System.setErr(systemError);
+            } else {
+                System.setOut(new PrintStream(
+                                       new TeeOutputStream(savedOut, systemOut)
+                                       )
+                              );
+                System.setErr(new PrintStream(
+                                       new TeeOutputStream(savedErr,
+                                                           systemError)
+                                       )
+                              );
+            }
+            perm = null;
+        } else {
+            if (perm != null) {
+                perm.setSecurityManager();
+            }
+        }
+
+        Test suite = null;
+        Throwable exception = null;
+
+        try {
+
+            try {
+                Class testClass = null;
+                if (loader == null) {
+                    testClass = Class.forName(junitTest.getName());
+                } else {
+                    testClass = Class.forName(junitTest.getName(), true,
+                                              loader);
+                }
+
+                Method suiteMethod = null;
+                try {
+                    // check if there is a suite method
+                    suiteMethod = testClass.getMethod("suite", new Class[0]);
+                } catch (NoSuchMethodException e) {
+                    // no appropriate suite method found. We don't report any
+                    // error here since it might be perfectly normal.
+                }
+                if (suiteMethod != null) {
+                    // if there is a suite method available, then try
+                    // to extract the suite from it. If there is an error
+                    // here it will be caught below and reported.
+                    suite = (Test) suiteMethod.invoke(null, new Class[0]);
+                } else {
+                    // try to extract a test suite automatically this
+                    // will generate warnings if the class is no
+                    // suitable Test
+                    suite = new TestSuite(testClass);
+                }
+
+            } catch (Throwable e) {
+                retCode = ERRORS;
+                exception = e;
+            }
+
+            long start = System.currentTimeMillis();
+
+            fireStartTestSuite();
+            if (exception != null) { // had an exception constructing suite
+                for (int i = 0; i < formatters.size(); i++) {
+                    ((TestListener) formatters.elementAt(i))
+                        .addError(null, exception);
+                }
+                junitTest.setCounts(1, 0, 1);
+                junitTest.setRunTime(0);
+            } else {
+                try {
+                    suite.run(res);
+                } finally {
+                    junitTest.setCounts(res.runCount(), res.failureCount(),
+                                        res.errorCount());
+                    junitTest.setRunTime(System.currentTimeMillis() - start);
+                }
+            }
+        } finally {
+            if (perm != null) {
+                perm.restoreSecurityManager();
+            }
+            if (savedOut != null) {
+                System.setOut(savedOut);
+            }
+            if (savedErr != null) {
+                System.setErr(savedErr);
+            }
+
+            systemError.close();
+            systemError = null;
+            systemOut.close();
+            systemOut = null;
+            sendOutAndErr(new String(outStrm.toByteArray()),
+                          new String(errStrm.toByteArray()));
+        }
+        fireEndTestSuite();
+
+        if (retCode != SUCCESS || res.errorCount() != 0) {
+            retCode = ERRORS;
+        } else if (res.failureCount() != 0) {
+            retCode = FAILURES;
+        }
+    }
+
+    /**
+     * Returns what System.exit() would return in the standalone version.
+     *
+     * @return 2 if errors occurred, 1 if tests failed else 0.
+     */
+    public int getRetCode() {
+        return retCode;
+    }
+
+    /**
+     * Interface TestListener.
+     *
+     * <p>A new Test is started.
+     */
+    public void startTest(Test t) {
+    }
+
+    /**
+     * Interface TestListener.
+     *
+     * <p>A Test is finished.
+     */
+    public void endTest(Test test) {
+    }
+
+    /**
+     * Interface TestListener for JUnit &lt;= 3.4.
+     *
+     * <p>A Test failed.
+     */
+    public void addFailure(Test test, Throwable t) {
+        if (haltOnFailure) {
+            res.stop();
+        }
+    }
+
+    /**
+     * Interface TestListener for JUnit &gt; 3.4.
+     *
+     * <p>A Test failed.
+     */
+    public void addFailure(Test test, AssertionFailedError t) {
+        addFailure(test, (Throwable) t);
+    }
+
+    /**
+     * Interface TestListener.
+     *
+     * <p>An error occurred while running the test.
+     */
+    public void addError(Test test, Throwable t) {
+        if (haltOnError) {
+            res.stop();
+        }
+    }
+
+    /**
+     * Permissions for the test run.
+     * @since Ant 1.6
+     * @param permissions
+     */
+    public void setPermissions(Permissions permissions) {
+        perm = permissions;
+    }
+
+    protected void handleOutput(String output) {
+        if (systemOut != null) {
+            systemOut.print(output);
+        }
+    }
+
+    /**
+     * @see org.apache.tools.ant.Task#handleInput(byte[], int, int)
+     *
+     * @since Ant 1.6
+     */
+    protected int handleInput(byte[] buffer, int offset, int length)
+        throws IOException {
+        return -1;
+    }
+
+    protected void handleErrorOutput(String output) {
+        if (systemError != null) {
+            systemError.print(output);
+        }
+    }
+
+    protected void handleFlush(String output) {
+        if (systemOut != null) {
+            systemOut.print(output);
+        }
+    }
+
+    protected void handleErrorFlush(String output) {
+        if (systemError != null) {
+            systemError.print(output);
+        }
+    }
+
+    private void sendOutAndErr(String out, String err) {
+        for (int i = 0; i < formatters.size(); i++) {
+            JUnitResultFormatter formatter =
+                ((JUnitResultFormatter) formatters.elementAt(i));
+
+            formatter.setSystemOutput(out);
+            formatter.setSystemError(err);
+        }
+    }
+
+    private void fireStartTestSuite() {
+        for (int i = 0; i < formatters.size(); i++) {
+            ((JUnitResultFormatter) formatters.elementAt(i))
+                .startTestSuite(junitTest);
+        }
+    }
+
+    private void fireEndTestSuite() {
+        for (int i = 0; i < formatters.size(); i++) {
+            ((JUnitResultFormatter) formatters.elementAt(i))
+                .endTestSuite(junitTest);
+        }
+    }
+
+    public void addFormatter(JUnitResultFormatter f) {
+        formatters.addElement(f);
+    }
+
+    /**
+     * Entry point for standalone (forked) mode.
+     *
+     * Parameters: testcaseclassname plus parameters in the format
+     * key=value, none of which is required.
+     *
+     * <table cols="4" border="1">
+     * <tr><th>key</th><th>description</th><th>default value</th></tr>
+     *
+     * <tr><td>haltOnError</td><td>halt test on
+     * errors?</td><td>false</td></tr>
+     *
+     * <tr><td>haltOnFailure</td><td>halt test on
+     * failures?</td><td>false</td></tr>
+     *
+     * <tr><td>formatter</td><td>A JUnitResultFormatter given as
+     * classname,filename. If filename is ommitted, System.out is
+     * assumed.</td><td>none</td></tr>
+     *
+     * <tr><td>showoutput</td><td>send output to System.err/.out as
+     * well as to the formatters?</td><td>false</td></tr>
+     *
+     * </table>
+     */
+    public static void main(String[] args) throws IOException {
+        boolean haltError = false;
+        boolean haltFail = false;
+        boolean stackfilter = true;
+        Properties props = new Properties();
+        boolean showOut = false;
+
+        if (args.length == 0) {
+            System.err.println("required argument TestClassName missing");
+            System.exit(ERRORS);
+        }
+
+        if (args[0].startsWith("testsfile=")) {
+            multipleTests = true;
+            args[0] = args[0].substring(10 /* "testsfile=".length() */);
+        }
+
+        for (int i = 1; i < args.length; i++) {
+            if (args[i].startsWith("haltOnError=")) {
+                haltError = Project.toBoolean(args[i].substring(12));
+            } else if (args[i].startsWith("haltOnFailure=")) {
+                haltFail = Project.toBoolean(args[i].substring(14));
+            } else if (args[i].startsWith("filtertrace=")) {
+                stackfilter = Project.toBoolean(args[i].substring(12));
+            } else if (args[i].startsWith("formatter=")) {
+                try {
+                    createAndStoreFormatter(args[i].substring(10));
+                } catch (BuildException be) {
+                    System.err.println(be.getMessage());
+                    System.exit(ERRORS);
+                }
+            } else if (args[i].startsWith("propsfile=")) {
+                FileInputStream in = new FileInputStream(args[i]
+                                                         .substring(10));
+                props.load(in);
+                in.close();
+            } else if (args[i].startsWith("showoutput=")) {
+                showOut = Project.toBoolean(args[i].substring(11));
+            }
+        }
+
+        // Add/overlay system properties on the properties from the Ant project
+        Hashtable p = System.getProperties();
+        for (Enumeration e = p.keys(); e.hasMoreElements();) {
+            Object key = e.nextElement();
+            props.put(key, p.get(key));
+        }
+
+        int returnCode = SUCCESS;
+        if (multipleTests) {
+            try {
+                java.io.BufferedReader reader =
+                    new java.io.BufferedReader(new java.io.FileReader(args[0]));
+                String testCaseName;
+                int code = 0;
+                boolean errorOccured = false;
+                boolean failureOccured = false;
+                String line = null;
+                while ((line = reader.readLine()) != null) {
+                    StringTokenizer st = new StringTokenizer(line, ",");
+                    testCaseName = st.nextToken();
+                    JUnitTest t = new JUnitTest(testCaseName);
+                    t.setTodir(new File(st.nextToken()));
+                    t.setOutfile(st.nextToken());
+                    code = launch(t, haltError, stackfilter, haltFail,
+                                  showOut, props);
+                    errorOccured = (code == ERRORS);
+                    failureOccured = (code != SUCCESS);
+                    if (errorOccured || failureOccured ) {
+                        if ((errorOccured && haltError)
+                            || (failureOccured && haltFail)) {
+                            System.exit(code);
+                        } else {
+                            if (code > returnCode) {
+                                returnCode = code;
+                            }
+                            System.out.println("TEST " + t.getName()
+                                               + " FAILED");
+                        }
+                    }
+                }
+            } catch(IOException e) {
+                e.printStackTrace();
+            }
+        } else {
+            returnCode = launch(new JUnitTest(args[0]), haltError,
+                                stackfilter, haltFail, showOut, props);
+        }
+
+        System.exit(returnCode);
+    }
+
+    private static Vector fromCmdLine = new Vector();
+
+    private static void transferFormatters(JUnitTestRunner runner,
+                                           JUnitTest test) {
+        for (int i = 0; i < fromCmdLine.size(); i++) {
+            FormatterElement fe = (FormatterElement) fromCmdLine.elementAt(i);
+            if (multipleTests && fe.getUseFile()) {
+                File destFile =
+                    new File(test.getTodir(),
+                             test.getOutfile() + fe.getExtension());
+                fe.setOutfile(destFile);
+            }
+            runner.addFormatter(fe.createFormatter());
+        }
+    }
+
+    /**
+     * Line format is: formatter=<classname>(,<pathname>)?
+     */
+    private static void createAndStoreFormatter(String line)
+        throws BuildException {
+        FormatterElement fe = new FormatterElement();
+        int pos = line.indexOf(',');
+        if (pos == -1) {
+            fe.setClassname(line);
+            fe.setUseFile(false);
+        } else {
+            fe.setClassname(line.substring(0, pos));
+            fe.setUseFile(true);
+            if (!multipleTests) {
+                fe.setOutfile(new File(line.substring(pos + 1)));
+            } else {
+                int fName = line.indexOf(IGNORED_FILE_NAME);
+                if (fName > -1) {
+                    fe.setExtension(line
+                                    .substring(fName
+                                               + IGNORED_FILE_NAME.length()));
+                }
+            }
+        }
+        fromCmdLine.addElement(fe);
+    }
+
+    /**
+     * Returns a filtered stack trace.
+     * This is ripped out of junit.runner.BaseTestRunner.
+     */
+    public static String getFilteredTrace(Throwable t) {
+        String trace = StringUtils.getStackTrace(t);
+        return JUnitTestRunner.filterStack(trace);
+    }
+
+    /**
+     * Filters stack frames from internal JUnit and Ant classes
+     */
+    public static String filterStack(String stack) {
+        if (!filtertrace) {
+            return stack;
+        }
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        StringReader sr = new StringReader(stack);
+        BufferedReader br = new BufferedReader(sr);
+
+        String line;
+        try {
+            while ((line = br.readLine()) != null) {
+                if (!filterLine(line)) {
+                    pw.println(line);
+                }
+            }
+        } catch (Exception IOException) {
+            return stack; // return the stack unfiltered
+        }
+        return sw.toString();
+    }
+
+    private static boolean filterLine(String line) {
+        for (int i = 0; i < DEFAULT_TRACE_FILTERS.length; i++) {
+            if (line.indexOf(DEFAULT_TRACE_FILTERS[i]) > 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * @since Ant 1.6.2
+     */
+    private static int launch(JUnitTest t, boolean haltError,
+                              boolean stackfilter, boolean haltFail,
+                              boolean showOut, Properties props) {
+        t.setProperties(props);
+        JUnitTestRunner runner =
+            new JUnitTestRunner(t, haltError, stackfilter, haltFail, showOut);
+        runner.forked = true;
+        transferFormatters(runner, t);
+
+        runner.run();
+        return runner.getRetCode();
+     }
+} // JUnitTestRunner
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelper.java	(revision 16139)
@@ -0,0 +1,83 @@
+/*
+ * Copyright  2001-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.lang.reflect.Method;
+import junit.framework.Test;
+import junit.framework.TestCase;
+
+/**
+ * Work around for some changes to the public JUnit API between
+ * different JUnit releases.
+ *
+ */
+public class JUnitVersionHelper {
+
+    private static Method testCaseName = null;
+    static {
+        try {
+            testCaseName = TestCase.class.getMethod("getName", new Class[0]);
+        } catch (NoSuchMethodException e) {
+            // pre JUnit 3.7
+            try {
+                testCaseName = TestCase.class.getMethod("name", new Class[0]);
+            } catch (NoSuchMethodException e2) {
+                // ignore
+            }
+        }
+    }
+
+    /**
+     * JUnit 3.7 introduces TestCase.getName() and subsequent versions
+     * of JUnit remove the old name() method.  This method provides
+     * access to the name of a TestCase via reflection that is
+     * supposed to work with version before and after JUnit 3.7.
+     *
+     * <p>since Ant 1.5.1 this method will invoke &quot;<code>public
+     * String getName()</code>&quot; on any implementation of Test if
+     * it exists.</p>
+     */
+    public static String getTestCaseName(Test t) {
+        if (t instanceof TestCase && testCaseName != null) {
+            try {
+                return (String) testCaseName.invoke(t, new Object[0]);
+            } catch (Throwable e) {
+                // ignore
+            }
+        } else {
+            try {
+                Method getNameMethod = null;
+                try {
+                    getNameMethod =
+                        t.getClass().getMethod("getName", new Class [0]);
+                } catch (NoSuchMethodException e) {
+                    getNameMethod = t.getClass().getMethod("name",
+                                                           new Class [0]);
+                }
+                if (getNameMethod != null
+                    && getNameMethod.getReturnType() == String.class) {
+                    return (String) getNameMethod.invoke(t, new Object[0]);
+                }
+            } catch (Throwable e) {
+                // ignore
+            }
+        }
+        return "unknown";
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/PlainJUnitResultFormatter.java	(revision 16139)
@@ -0,0 +1,223 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.NumberFormat;
+import java.util.Hashtable;
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Prints plain text output of the test to a specified Writer.
+ *
+ */
+
+public class PlainJUnitResultFormatter implements JUnitResultFormatter {
+
+    /**
+     * Formatter for timings.
+     */
+    private NumberFormat nf = NumberFormat.getInstance();
+    /**
+     * Timing helper.
+     */
+    private Hashtable testStarts = new Hashtable();
+    /**
+     * Where to write the log to.
+     */
+    private OutputStream out;
+    /**
+     * Helper to store intermediate output.
+     */
+    private StringWriter inner;
+    /**
+     * Convenience layer on top of {@link #inner inner}.
+     */
+    private PrintWriter wri;
+    /**
+     * Suppress endTest if testcase failed.
+     */
+    private Hashtable failed = new Hashtable();
+
+    private String systemOutput = null;
+    private String systemError = null;
+
+    public PlainJUnitResultFormatter() {
+        inner = new StringWriter();
+        wri = new PrintWriter(inner);
+    }
+
+    public void setOutput(OutputStream out) {
+        this.out = out;
+    }
+
+    public void setSystemOutput(String out) {
+        systemOutput = out;
+    }
+
+    public void setSystemError(String err) {
+        systemError = err;
+    }
+
+    /**
+     * Empty.
+     */
+    public void startTestSuite(JUnitTest suite) {
+    }
+
+    /**
+     * The whole testsuite ended.
+     */
+    public void endTestSuite(JUnitTest suite) throws BuildException {
+        String newLine = System.getProperty("line.separator");
+        StringBuffer sb = new StringBuffer("Testsuite: ");
+        sb.append(suite.getName());
+        sb.append(newLine);
+        sb.append("Tests run: ");
+        sb.append(suite.runCount());
+        sb.append(", Failures: ");
+        sb.append(suite.failureCount());
+        sb.append(", Errors: ");
+        sb.append(suite.errorCount());
+        sb.append(", Time elapsed: ");
+        sb.append(nf.format(suite.getRunTime() / 1000.0));
+        sb.append(" sec");
+        sb.append(newLine);
+
+        // append the err and output streams to the log
+        if (systemOutput != null && systemOutput.length() > 0) {
+            sb.append("------------- Standard Output ---------------")
+                .append(newLine)
+                .append(systemOutput)
+                .append("------------- ---------------- ---------------")
+                .append(newLine);
+        }
+
+        if (systemError != null && systemError.length() > 0) {
+            sb.append("------------- Standard Error -----------------")
+                .append(newLine)
+                .append(systemError)
+                .append("------------- ---------------- ---------------")
+                .append(newLine);
+        }
+
+        sb.append(newLine);
+
+        if (out != null) {
+            try {
+                out.write(sb.toString().getBytes());
+                wri.close();
+                out.write(inner.toString().getBytes());
+                out.flush();
+            } catch (IOException ioex) {
+                throw new BuildException("Unable to write output", ioex);
+            } finally {
+                if (out != System.out && out != System.err) {
+                    try {
+                        out.close();
+                    } catch (IOException e) {
+                        // ignore
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Interface TestListener.
+     *
+     * <p>A new Test is started.
+     */
+    public void startTest(Test t) {
+        testStarts.put(t, new Long(System.currentTimeMillis()));
+        failed.put(t, Boolean.FALSE);
+    }
+
+    /**
+     * Interface TestListener.
+     *
+     * <p>A Test is finished.
+     */
+    public void endTest(Test test) {
+        if (Boolean.TRUE.equals(failed.get(test))) {
+            return;
+        }
+        synchronized (wri) {
+            wri.print("Testcase: "
+                      + JUnitVersionHelper.getTestCaseName(test));
+            Long l = (Long) testStarts.get(test);
+            double seconds = 0;
+            // can be null if an error occurred in setUp
+            if (l != null) {
+                seconds =
+                    (System.currentTimeMillis() - l.longValue()) / 1000.0;
+            }
+
+            wri.println(" took " + nf.format(seconds) + " sec");
+        }
+    }
+
+    /**
+     * Interface TestListener for JUnit &lt;= 3.4.
+     *
+     * <p>A Test failed.
+     */
+    public void addFailure(Test test, Throwable t) {
+        formatError("\tFAILED", test, t);
+    }
+
+    /**
+     * Interface TestListener for JUnit &gt; 3.4.
+     *
+     * <p>A Test failed.
+     */
+    public void addFailure(Test test, AssertionFailedError t) {
+        addFailure(test, (Throwable) t);
+    }
+
+    /**
+     * Interface TestListener.
+     *
+     * <p>An error occurred while running the test.
+     */
+    public void addError(Test test, Throwable t) {
+        formatError("\tCaused an ERROR", test, t);
+    }
+
+    private void formatError(String type, Test test, Throwable t) {
+        synchronized (wri) {
+            if (test != null) {
+                endTest(test);
+                failed.put(test, Boolean.TRUE);
+            }
+
+            wri.println(type);
+            wri.println(t.getMessage());
+            String strace = JUnitTestRunner.getFilteredTrace(t);
+            wri.print(strace);
+            wri.println("");
+        }
+    }
+
+} // PlainJUnitResultFormatter
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/SummaryJUnitResultFormatter.java	(revision 16139)
@@ -0,0 +1,149 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.text.NumberFormat;
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Prints short summary output of the test to Ant's logging system.
+ *
+ */
+
+public class SummaryJUnitResultFormatter implements JUnitResultFormatter {
+
+    /**
+     * Formatter for timings.
+     */
+    private NumberFormat nf = NumberFormat.getInstance();
+    /**
+     * OutputStream to write to.
+     */
+    private OutputStream out;
+
+    private boolean withOutAndErr = false;
+    private String systemOutput = null;
+    private String systemError = null;
+
+    /**
+     * Empty
+     */
+    public SummaryJUnitResultFormatter() {
+    }
+    /**
+     * Empty
+     */
+    public void startTestSuite(JUnitTest suite) {
+    }
+    /**
+     * Empty
+     */
+    public void startTest(Test t) {
+    }
+    /**
+     * Empty
+     */
+    public void endTest(Test test) {
+    }
+    /**
+     * Empty
+     */
+    public void addFailure(Test test, Throwable t) {
+    }
+    /**
+     * Interface TestListener for JUnit &gt; 3.4.
+     *
+     * <p>A Test failed.
+     */
+    public void addFailure(Test test, AssertionFailedError t) {
+        addFailure(test, (Throwable) t);
+    }
+    /**
+     * Empty
+     */
+    public void addError(Test test, Throwable t) {
+    }
+
+    public void setOutput(OutputStream out) {
+        this.out = out;
+    }
+
+    public void setSystemOutput(String out) {
+        systemOutput = out;
+    }
+
+    public void setSystemError(String err) {
+        systemError = err;
+    }
+
+    /**
+     * Should the output to System.out and System.err be written to
+     * the summary.
+     */
+    public void setWithOutAndErr(boolean value) {
+        withOutAndErr = value;
+    }
+
+    /**
+     * The whole testsuite ended.
+     */
+    public void endTestSuite(JUnitTest suite) throws BuildException {
+        String newLine = System.getProperty("line.separator");
+        StringBuffer sb = new StringBuffer("Tests run: ");
+        sb.append(suite.runCount());
+        sb.append(", Failures: ");
+        sb.append(suite.failureCount());
+        sb.append(", Errors: ");
+        sb.append(suite.errorCount());
+        sb.append(", Time elapsed: ");
+        sb.append(nf.format(suite.getRunTime() / 1000.0));
+        sb.append(" sec");
+        sb.append(newLine);
+
+        if (withOutAndErr) {
+            if (systemOutput != null && systemOutput.length() > 0) {
+                sb.append("Output:").append(newLine).append(systemOutput)
+                    .append(newLine);
+            }
+
+            if (systemError != null && systemError.length() > 0) {
+                sb.append("Error: ").append(newLine).append(systemError)
+                    .append(newLine);
+            }
+        }
+
+        try {
+            out.write(sb.toString().getBytes());
+            out.flush();
+        } catch (IOException ioex) {
+            throw new BuildException("Unable to write summary output", ioex);
+        } finally {
+            if (out != System.out && out != System.err) {
+                try {
+                    out.close();
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLConstants.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLConstants.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLConstants.java	(revision 16139)
@@ -0,0 +1,91 @@
+/*
+ * Copyright  2001,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+/**
+ * <p> Interface groups XML constants.
+ * Interface that groups all constants used throughout the <tt>XML</tt>
+ * documents that are generated by the <tt>XMLJUnitResultFormatter</tt>
+ * As of now the DTD is:
+ * <code><pre>
+ * <----------------- @todo describe DTDs ---------------------->
+ *
+ * </pre></code>
+ * @see XMLJUnitResultFormatter
+ * @see XMLResultAggregator
+ */
+public interface XMLConstants {
+    /** the testsuites element for the aggregate document */
+    String TESTSUITES = "testsuites";
+
+    /** the testsuite element */
+    String TESTSUITE = "testsuite";
+
+    /** the testcase element */
+    String TESTCASE = "testcase";
+
+    /** the error element */
+    String ERROR = "error";
+
+    /** the failure element */
+    String FAILURE = "failure";
+
+    /** the system-err element */
+    String SYSTEM_ERR = "system-err";
+
+    /** the system-out element */
+    String SYSTEM_OUT = "system-out";
+
+    /** package attribute for the aggregate document */
+    String ATTR_PACKAGE = "package";
+
+    /** name attribute for property, testcase and testsuite elements */
+    String ATTR_NAME = "name";
+
+    /** time attribute for testcase and testsuite elements */
+    String ATTR_TIME = "time";
+
+    /** errors attribute for testsuite elements */
+    String ATTR_ERRORS = "errors";
+
+    /** failures attribute for testsuite elements */
+    String ATTR_FAILURES = "failures";
+
+    /** tests attribute for testsuite elements */
+    String ATTR_TESTS = "tests";
+
+    /** type attribute for failure and error elements */
+    String ATTR_TYPE = "type";
+
+    /** message attribute for failure elements */
+    String ATTR_MESSAGE = "message";
+
+    /** the properties element */
+    String PROPERTIES = "properties";
+
+    /** the property element */
+    String PROPERTY = "property";
+
+    /** value attribute for property elements */
+    String ATTR_VALUE = "value";
+
+    /** classname attribute for testcase elements */
+    String ATTR_CLASSNAME = "classname";
+
+    /** id attribute */
+    String ATTR_ID = "id";
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLJUnitResultFormatter.java	(revision 16139)
@@ -0,0 +1,258 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Text;
+
+
+/**
+ * Prints XML output of the test to a specified Writer.
+ *
+ *
+ * @see FormatterElement
+ */
+
+public class XMLJUnitResultFormatter implements JUnitResultFormatter, XMLConstants {
+
+    /** constant for unnnamed testsuites/cases */
+    private static final String UNKNOWN = "unknown";
+
+    private static DocumentBuilder getDocumentBuilder() {
+        try {
+            return DocumentBuilderFactory.newInstance().newDocumentBuilder();
+        } catch (Exception exc) {
+            throw new ExceptionInInitializerError(exc);
+        }
+    }
+
+    /**
+     * The XML document.
+     */
+    private Document doc;
+    /**
+     * The wrapper for the whole testsuite.
+     */
+    private Element rootElement;
+    /**
+     * Element for the current test.
+     */
+    private Hashtable testElements = new Hashtable();
+    /**
+     * tests that failed.
+     */
+    private Hashtable failedTests = new Hashtable();
+    /**
+     * Timing helper.
+     */
+    private Hashtable testStarts = new Hashtable();
+    /**
+     * Where to write the log to.
+     */
+    private OutputStream out;
+
+    public XMLJUnitResultFormatter() {
+    }
+
+    public void setOutput(OutputStream out) {
+        this.out = out;
+    }
+
+    public void setSystemOutput(String out) {
+        formatOutput(SYSTEM_OUT, out);
+    }
+
+    public void setSystemError(String out) {
+        formatOutput(SYSTEM_ERR, out);
+    }
+
+    /**
+     * The whole testsuite started.
+     */
+    public void startTestSuite(JUnitTest suite) {
+        doc = getDocumentBuilder().newDocument();
+        rootElement = doc.createElement(TESTSUITE);
+        String n = suite.getName();
+        rootElement.setAttribute(ATTR_NAME, n == null ? UNKNOWN : n);
+
+        // Output properties
+        Element propsElement = doc.createElement(PROPERTIES);
+        rootElement.appendChild(propsElement);
+        Properties props = suite.getProperties();
+        if (props != null) {
+            Enumeration e = props.propertyNames();
+            while (e.hasMoreElements()) {
+                String name = (String) e.nextElement();
+                Element propElement = doc.createElement(PROPERTY);
+                propElement.setAttribute(ATTR_NAME, name);
+                propElement.setAttribute(ATTR_VALUE, props.getProperty(name));
+                propsElement.appendChild(propElement);
+            }
+        }
+    }
+
+    /**
+     * The whole testsuite ended.
+     */
+    public void endTestSuite(JUnitTest suite) throws BuildException {
+        rootElement.setAttribute(ATTR_TESTS, "" + suite.runCount());
+        rootElement.setAttribute(ATTR_FAILURES, "" + suite.failureCount());
+        rootElement.setAttribute(ATTR_ERRORS, "" + suite.errorCount());
+        rootElement.setAttribute(ATTR_TIME, "" + (suite.getRunTime() / 1000.0));
+        if (out != null) {
+            Writer wri = null;
+            try {
+                wri = new BufferedWriter(new OutputStreamWriter(out, "UTF8"));
+                wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
+                (new DOMElementWriter()).write(rootElement, wri, 0, "  ");
+                wri.flush();
+            } catch (IOException exc) {
+                throw new BuildException("Unable to write log file", exc);
+            } finally {
+                if (out != System.out && out != System.err) {
+                    if (wri != null) {
+                        try {
+                            wri.close();
+                        } catch (IOException e) {
+                            // ignore
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Interface TestListener.
+     *
+     * <p>A new Test is started.
+     */
+    public void startTest(Test t) {
+        testStarts.put(t, new Long(System.currentTimeMillis()));
+    }
+
+    /**
+     * Interface TestListener.
+     *
+     * <p>A Test is finished.
+     */
+    public void endTest(Test test) {
+        // Fix for bug #5637 - if a junit.extensions.TestSetup is
+        // used and throws an exception during setUp then startTest
+        // would never have been called
+        if (!testStarts.containsKey(test)) {
+            startTest(test);
+        }
+
+        Element currentTest = null;
+        if (!failedTests.containsKey(test)) {
+            currentTest = doc.createElement(TESTCASE);
+            String n = JUnitVersionHelper.getTestCaseName(test);
+            currentTest.setAttribute(ATTR_NAME,
+                                     n == null ? UNKNOWN : n);
+            // a TestSuite can contain Tests from multiple classes,
+            // even tests with the same name - disambiguate them.
+            currentTest.setAttribute(ATTR_CLASSNAME,
+                                     test.getClass().getName());
+            rootElement.appendChild(currentTest);
+            testElements.put(test, currentTest);
+        } else {
+            currentTest = (Element) testElements.get(test);
+        }
+
+        Long l = (Long) testStarts.get(test);
+        currentTest.setAttribute(ATTR_TIME,
+            "" + ((System.currentTimeMillis() - l.longValue()) / 1000.0));
+    }
+
+    /**
+     * Interface TestListener for JUnit &lt;= 3.4.
+     *
+     * <p>A Test failed.
+     */
+    public void addFailure(Test test, Throwable t) {
+        formatError(FAILURE, test, t);
+    }
+
+    /**
+     * Interface TestListener for JUnit &gt; 3.4.
+     *
+     * <p>A Test failed.
+     */
+    public void addFailure(Test test, AssertionFailedError t) {
+        addFailure(test, (Throwable) t);
+    }
+
+    /**
+     * Interface TestListener.
+     *
+     * <p>An error occurred while running the test.
+     */
+    public void addError(Test test, Throwable t) {
+        formatError(ERROR, test, t);
+    }
+
+    private void formatError(String type, Test test, Throwable t) {
+        if (test != null) {
+            endTest(test);
+            failedTests.put(test, test);
+        }
+
+        Element nested = doc.createElement(type);
+        Element currentTest = null;
+        if (test != null) {
+            currentTest = (Element) testElements.get(test);
+        } else {
+            currentTest = rootElement;
+        }
+
+        currentTest.appendChild(nested);
+
+        String message = t.getMessage();
+        if (message != null && message.length() > 0) {
+            nested.setAttribute(ATTR_MESSAGE, t.getMessage());
+        }
+        nested.setAttribute(ATTR_TYPE, t.getClass().getName());
+
+        String strace = JUnitTestRunner.getFilteredTrace(t);
+        Text trace = doc.createTextNode(strace);
+        nested.appendChild(trace);
+    }
+
+    private void formatOutput(String type, String output) {
+        Element nested = doc.createElement(type);
+        rootElement.appendChild(nested);
+        nested.appendChild(doc.createCDATASection(output));
+    }
+
+} // XMLJUnitResultFormatter
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLResultAggregator.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLResultAggregator.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/XMLResultAggregator.java	(revision 16139)
@@ -0,0 +1,309 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import java.util.Vector;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.apache.tools.ant.util.StringUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+
+/**
+ * Aggregates all &lt;junit&gt; XML formatter testsuite data under
+ * a specific directory and transforms the results via XSLT.
+ * It is not particulary clean but
+ * should be helpful while I am thinking about another technique.
+ *
+ * <p> The main problem is due to the fact that a JVM can be forked for a testcase
+ * thus making it impossible to aggregate all testcases since the listener is
+ * (obviously) in the forked JVM. A solution could be to write a
+ * TestListener that will receive events from the TestRunner via sockets. This
+ * is IMHO the simplest way to do it to avoid this file hacking thing.
+ *
+ *
+ * @ant.task name="junitreport" category="testing"
+ */
+public class XMLResultAggregator extends Task implements XMLConstants {
+
+    /** the list of all filesets, that should contains the xml to aggregate */
+    protected Vector filesets = new Vector();
+
+    /** the name of the result file */
+    protected String toFile;
+
+    /** the directory to write the file to */
+    protected File toDir;
+
+    protected Vector transformers = new Vector();
+
+    /** The default directory: <tt>&#046;</tt>. It is resolved from the project directory */
+    public static final String DEFAULT_DIR = ".";
+
+    /** the default file name: <tt>TESTS-TestSuites.xml</tt> */
+    public static final String DEFAULT_FILENAME = "TESTS-TestSuites.xml";
+
+    /** the current generated id */
+    protected int generatedId = 0;
+
+    /**
+     * Generate a report based on the document created by the merge.
+     * @return the report
+     */
+    public AggregateTransformer createReport() {
+        AggregateTransformer transformer = new AggregateTransformer(this);
+        transformers.addElement(transformer);
+        return transformer;
+    }
+
+    /**
+     * Set the name of the aggregegated results file. It must be relative
+     * from the <tt>todir</tt> attribute. If not set it will use {@link #DEFAULT_FILENAME}
+     * @param  value   the name of the file.
+     * @see #setTodir(File)
+     */
+    public void setTofile(String value) {
+        toFile = value;
+    }
+
+    /**
+     * Set the destination directory where the results should be written. If not
+     * set if will use {@link #DEFAULT_DIR}. When given a relative directory
+     * it will resolve it from the project directory.
+     * @param value    the directory where to write the results, absolute or
+     * relative.
+     */
+    public void setTodir(File value) {
+        toDir = value;
+    }
+
+    /**
+     * Add a new fileset containing the XML results to aggregate
+     * @param    fs      the new fileset of xml results.
+     */
+    public void addFileSet(FileSet fs) {
+        filesets.addElement(fs);
+    }
+
+    /**
+     * Aggregate all testsuites into a single document and write it to the
+     * specified directory and file.
+     * @throws  BuildException  thrown if there is a serious error while writing
+     *          the document.
+     */
+    public void execute() throws BuildException {
+        Element rootElement = createDocument();
+        File destFile = getDestinationFile();
+        // write the document
+        try {
+            writeDOMTree(rootElement.getOwnerDocument(), destFile);
+        } catch (IOException e) {
+            throw new BuildException("Unable to write test aggregate to '" + destFile + "'", e);
+        }
+        // apply transformation
+        Enumeration e = transformers.elements();
+        while (e.hasMoreElements()) {
+            AggregateTransformer transformer =
+                (AggregateTransformer) e.nextElement();
+            transformer.setXmlDocument(rootElement.getOwnerDocument());
+            transformer.transform();
+        }
+    }
+
+    /**
+     * Get the full destination file where to write the result. It is made of
+     * the <tt>todir</tt> and <tt>tofile</tt> attributes.
+     * @return the destination file where should be written the result file.
+     */
+    protected File getDestinationFile() {
+        if (toFile == null) {
+            toFile = DEFAULT_FILENAME;
+        }
+        if (toDir == null) {
+            toDir = getProject().resolveFile(DEFAULT_DIR);
+        }
+        return new File(toDir, toFile);
+    }
+
+    /**
+     * Get all <code>.xml</code> files in the fileset.
+     *
+     * @return all files in the fileset that end with a '.xml'.
+     */
+    protected File[] getFiles() {
+        Vector v = new Vector();
+        final int size = filesets.size();
+        for (int i = 0; i < size; i++) {
+            FileSet fs = (FileSet) filesets.elementAt(i);
+            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+            ds.scan();
+            String[] f = ds.getIncludedFiles();
+            for (int j = 0; j < f.length; j++) {
+                String pathname = f[j];
+                if (pathname.endsWith(".xml")) {
+                    File file = new File(ds.getBasedir(), pathname);
+                    file = getProject().resolveFile(file.getPath());
+                    v.addElement(file);
+                }
+            }
+        }
+
+        File[] files = new File[v.size()];
+        v.copyInto(files);
+        return files;
+    }
+
+    //----- from now, the methods are all related to DOM tree manipulation
+
+    /**
+     * Write the DOM tree to a file.
+     * @param doc the XML document to dump to disk.
+     * @param file the filename to write the document to. Should obviouslly be a .xml file.
+     * @throws IOException thrown if there is an error while writing the content.
+     */
+    protected void writeDOMTree(Document doc, File file) throws IOException {
+        OutputStream out = null;
+        PrintWriter wri = null;
+        try {
+            out = new BufferedOutputStream(new FileOutputStream(file));
+            wri = new PrintWriter(new OutputStreamWriter(out, "UTF8"));
+            wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
+            (new DOMElementWriter()).write(doc.getDocumentElement(), wri, 0, "  ");
+            wri.flush();
+            // writers do not throw exceptions, so check for them.
+            if (wri.checkError()) {
+                throw new IOException("Error while writing DOM content");
+            }
+        } finally {
+            if (wri != null) {
+                wri.close();
+                out = null;
+            }
+            if (out != null) {
+                out.close();
+            }
+        }
+    }
+
+    /**
+     * <p> Create a DOM tree.
+     * Has 'testsuites' as firstchild and aggregates all
+     * testsuite results that exists in the base directory.
+     * @return  the root element of DOM tree that aggregates all testsuites.
+     */
+    protected Element createDocument() {
+        // create the dom tree
+        DocumentBuilder builder = getDocumentBuilder();
+        Document doc = builder.newDocument();
+        Element rootElement = doc.createElement(TESTSUITES);
+        doc.appendChild(rootElement);
+
+        generatedId = 0;
+
+        // get all files and add them to the document
+        File[] files = getFiles();
+        for (int i = 0; i < files.length; i++) {
+            try {
+                log("Parsing file: '" + files[i] + "'", Project.MSG_VERBOSE);
+                //XXX there seems to be a bug in xerces 1.3.0 that doesn't like file object
+                // will investigate later. It does not use the given directory but
+                // the vm dir instead ? Works fine with crimson.
+                Document testsuiteDoc
+                    = builder.parse("file:///" + files[i].getAbsolutePath());
+                Element elem = testsuiteDoc.getDocumentElement();
+                // make sure that this is REALLY a testsuite.
+                if (TESTSUITE.equals(elem.getNodeName())) {
+                    addTestSuite(rootElement, elem);
+                    generatedId++;
+                } else {
+                    // issue a warning.
+                    log("the file " + files[i]
+                        + " is not a valid testsuite XML document",
+                        Project.MSG_WARN);
+                }
+            } catch (SAXException e) {
+                // a testcase might have failed and write a zero-length document,
+                // It has already failed, but hey.... mm. just put a warning
+                log("The file " + files[i] + " is not a valid XML document. "
+                    + "It is possibly corrupted.", Project.MSG_WARN);
+                log(StringUtils.getStackTrace(e), Project.MSG_DEBUG);
+            } catch (IOException e) {
+                log("Error while accessing file " + files[i] + ": "
+                    + e.getMessage(), Project.MSG_ERR);
+            }
+        }
+        return rootElement;
+    }
+
+    /**
+     * <p> Add a new testsuite node to the document.
+     * The main difference is that it
+     * split the previous fully qualified name into a package and a name.
+     * <p> For example: <tt>org.apache.Whatever</tt> will be split into
+     * <tt>org.apache</tt> and <tt>Whatever</tt>.
+     * @param root the root element to which the <tt>testsuite</tt> node should
+     *        be appended.
+     * @param testsuite the element to append to the given root. It will slightly
+     *        modify the original node to change the name attribute and add
+     *        a package one.
+     */
+    protected void addTestSuite(Element root, Element testsuite) {
+        String fullclassname = testsuite.getAttribute(ATTR_NAME);
+        int pos = fullclassname.lastIndexOf('.');
+
+        // a missing . might imply no package at all. Don't get fooled.
+        String pkgName = (pos == -1) ? "" : fullclassname.substring(0, pos);
+        String classname = (pos == -1) ? fullclassname : fullclassname.substring(pos + 1);
+        Element copy = (Element) DOMUtil.importNode(root, testsuite);
+
+        // modify the name attribute and set the package
+        copy.setAttribute(ATTR_NAME, classname);
+        copy.setAttribute(ATTR_PACKAGE, pkgName);
+        copy.setAttribute(ATTR_ID, Integer.toString(generatedId));
+    }
+
+    /**
+     * Create a new document builder. Will issue an <tt>ExceptionInitializerError</tt>
+     * if something is going wrong. It is fatal anyway.
+     * @todo factorize this somewhere else. It is duplicated code.
+     * @return a new document builder to create a DOM
+     */
+    private static DocumentBuilder getDocumentBuilder() {
+        try {
+            return DocumentBuilderFactory.newInstance().newDocumentBuilder();
+        } catch (Exception exc) {
+            throw new ExceptionInInitializerError(exc);
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/Xalan1Executor.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/Xalan1Executor.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/Xalan1Executor.java	(revision 16139)
@@ -0,0 +1,81 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.OutputStream;
+import org.apache.xalan.xslt.XSLTInputSource;
+import org.apache.xalan.xslt.XSLTProcessor;
+import org.apache.xalan.xslt.XSLTProcessorFactory;
+import org.apache.xalan.xslt.XSLTResultTarget;
+import org.apache.tools.ant.BuildException;
+import org.xml.sax.SAXException;
+
+/**
+ * Xalan 1 executor. It will need a lot of things in the classpath:
+ * xerces for the serialization, xalan and bsf for the extension.
+ * @todo do everything via reflection to avoid compile problems ?
+ *
+ * @ant.task ignore="true"
+ */
+public class Xalan1Executor extends XalanExecutor {
+
+    private static final String xsltP = "org.apache.xalan.xslt.XSLTProcessor";
+
+    XSLTProcessor processor = null;
+    public Xalan1Executor() {
+        try {
+            processor = XSLTProcessorFactory.getProcessor();
+        } catch (SAXException e) {
+            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
+        }
+    }
+    protected String getImplementation() {
+        return processor.getClass().getName();
+    }
+
+    protected String getProcVersion(String classNameImpl) 
+        throws BuildException {
+        try {
+            // xalan 1
+            if (classNameImpl.equals(xsltP)){
+                return getXalanVersion(xsltP + "Version");
+            }
+            throw new BuildException("Could not find a valid processor version"
+                                     + " implementation from " 
+                                     + classNameImpl);
+        } catch (ClassNotFoundException e){
+            throw new BuildException("Could not find processor version "
+                                     + "implementation", e);
+        }
+    }
+
+    void execute() throws Exception {
+        // need to quote otherwise it breaks because of "extra illegal tokens"
+        processor.setStylesheetParam("output.dir", "'" 
+                                     + caller.toDir.getAbsolutePath() + "'");
+        XSLTInputSource xml_src = new XSLTInputSource(caller.document);
+        String system_id = caller.getStylesheetSystemId();
+        XSLTInputSource xsl_src = new XSLTInputSource(system_id);
+        OutputStream os = getOutputStream();
+        try {
+            XSLTResultTarget target = new XSLTResultTarget(os);
+            processor.process(xml_src, xsl_src, target);
+        } finally {
+            os.close();
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/Xalan2Executor.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/Xalan2Executor.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/Xalan2Executor.java	(revision 16139)
@@ -0,0 +1,90 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.OutputStream;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import javax.xml.transform.stream.StreamSource;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Xalan executor via JAXP. Nothing special must exists in the classpath
+ * besides of course, a parser, jaxp and xalan.
+ *
+ * @ant.task ignore="true"
+ */
+public class Xalan2Executor extends XalanExecutor {
+
+    private static final String aPack = "org.apache.xalan.";
+    private static final String sPack = "com.sun.org.apache.xalan.";
+
+    private TransformerFactory tfactory = TransformerFactory.newInstance();
+
+    protected String getImplementation() throws BuildException {
+        return tfactory.getClass().getName();
+    }
+
+    protected String getProcVersion(String classNameImpl) 
+        throws BuildException {
+        try {
+            // xalan 2
+            if (classNameImpl.equals(aPack + "processor.TransformerFactoryImpl") 
+                ||
+                classNameImpl.equals(aPack + "xslt.XSLTProcessorFactory")) {
+                return getXalanVersion(aPack + "processor.XSLProcessorVersion");
+            }
+            // xalan xsltc
+            if (classNameImpl.equals(aPack 
+                                     + "xsltc.trax.TransformerFactoryImpl")){
+                return getXSLTCVersion(aPack +"xsltc.ProcessorVersion");
+            }
+            // jdk 1.5 xsltc
+            if (classNameImpl
+                .equals(sPack + "internal.xsltc.trax.TransformerFactoryImpl")){
+                return getXSLTCVersion(sPack 
+                                       + "internal.xsltc.ProcessorVersion");
+            }
+            throw new BuildException("Could not find a valid processor version"
+                                     + " implementation from " 
+                                     + classNameImpl);
+        } catch (ClassNotFoundException e){
+            throw new BuildException("Could not find processor version "
+                                     + "implementation", e);
+        }
+    }
+
+    void execute() throws Exception {
+        String system_id = caller.getStylesheetSystemId();
+        Source xsl_src = new StreamSource(system_id);
+        Transformer tformer = tfactory.newTransformer(xsl_src);
+        Source xml_src = new DOMSource(caller.document);
+        OutputStream os = getOutputStream();
+        try {
+            tformer.setParameter("output.dir", caller.toDir.getAbsolutePath());
+            Result result = new StreamResult(os);
+            tformer.transform(xml_src, result);
+        } finally {
+            os.close();
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/XalanExecutor.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/XalanExecutor.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/junit/XalanExecutor.java	(revision 16139)
@@ -0,0 +1,138 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Field;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Command class that encapsulate specific behavior for each
+ * Xalan version. The right executor will be instantiated at
+ * runtime via class lookup. For instance, it will check first
+ * for Xalan2/XSLTC, then for Xalan1.
+ */
+abstract class XalanExecutor {
+    private static final String pack = 
+        "org.apache.tools.ant.taskdefs.optional.junit.";
+
+    /** the transformer caller */
+    protected AggregateTransformer caller;
+
+    /** set the caller for this object. */
+    private final void setCaller(AggregateTransformer caller) {
+        this.caller = caller;
+    }
+
+    /** get the appropriate stream based on the format (frames/noframes) */
+    protected final OutputStream getOutputStream() throws IOException {
+        if (AggregateTransformer.FRAMES.equals(caller.format)) {
+            // dummy output for the framed report
+            // it's all done by extension...
+            return new ByteArrayOutputStream();
+        } else {
+            return new BufferedOutputStream(new FileOutputStream(new File(caller.toDir, "junit-noframes.html")));
+        }
+    }
+
+    /** override to perform transformation */
+    abstract void execute() throws Exception;
+
+    /**
+     * Create a valid Xalan executor. It checks first if Xalan2 is
+     * present, if not it checks for xalan1. If none is available, it
+     * fails.
+     * @param caller object containing the transformation information.
+     * @throws BuildException thrown if it could not find a valid xalan
+     * executor.
+     */
+    static XalanExecutor newInstance(AggregateTransformer caller) 
+        throws BuildException {
+        XalanExecutor executor = null;
+        try {
+            Class clazz = Class.forName(pack + "Xalan2Executor");
+            executor = (XalanExecutor)clazz.newInstance();
+        } catch (Exception xsltcApacheMissing){
+            caller.task.log(xsltcApacheMissing.toString());
+            try {
+                Class clazz = Class.forName(pack + "Xalan1Executor");
+                executor = (XalanExecutor) clazz.newInstance();
+            } catch (Exception xalan1Missing){
+                caller.task.log(xalan1Missing.toString());
+                throw new BuildException("Could not find xstlc nor xalan2 nor "
+                                         + "xalan1 in the classpath. Check "
+                                         + "http://xml.apache.org/xalan-j");
+            }
+        }
+        String classNameImpl = executor.getImplementation();
+        String version = executor.getProcVersion(classNameImpl);
+        caller.task.log("Using " + version, Project.MSG_VERBOSE);
+        executor.setCaller(caller);
+        return executor;
+    }
+
+    /**
+     * This methods should return the classname implementation of the
+     * underlying xslt processor
+     * @return the classname of the implementation, for example:
+     * org.apache.xalan.processor.TransformerFactoryImpl
+     * @see #getProcVersion(String)
+     */
+    protected abstract String getImplementation();
+
+    /**
+     * Try to discover the xslt processor version based on the
+     * className. There is nothing carved in stone and it can change
+     * anytime, so this is just for the sake of giving additional
+     * information if we can find it.
+     * @param classNameImpl the classname of the underlying xslt processor
+     * @return a string representing the implementation version.
+     * @throws BuildException
+     */
+    protected abstract String getProcVersion(String classNameImpl) 
+        throws BuildException;
+
+    /** a bit simplistic but xsltc data are conveniently private non final */
+    protected final String getXSLTCVersion(String procVersionClassName) 
+        throws ClassNotFoundException {
+        // there's a convenient xsltc class version but data are
+        // private so use package information
+        Class procVersion = Class.forName(procVersionClassName);
+        Package pkg = procVersion.getPackage();
+        return pkg.getName() + " " + pkg.getImplementationTitle() 
+            + " " + pkg.getImplementationVersion();
+    }
+
+    /** pretty useful data (Xalan version information) to display. */
+    protected final String getXalanVersion(String procVersionClassName) 
+        throws ClassNotFoundException {
+        Class procVersion = Class.forName(procVersionClassName);
+        String pkg = procVersion.getPackage().getName();
+        try {
+            Field f = procVersion.getField("S_VERSION");
+            return pkg + " " + f.get(null).toString();
+        } catch (Exception e) {
+            return pkg + " ?.?";
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/AbstractMetamataTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/AbstractMetamataTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/AbstractMetamataTask.java	(revision 16139)
@@ -0,0 +1,348 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.metamata;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Somewhat abstract framework to be used for other metama 2.0 tasks.
+ * This should include, audit, metrics, cover and mparse.
+ *
+ * For more information, visit the website at
+ * <a href="http://www.metamata.com">www.metamata.com</a>
+ *
+ */
+public abstract class AbstractMetamataTask extends Task {
+
+    /**
+     * The user classpath to be provided. It matches the -classpath of the
+     * command line. The classpath must includes both the <tt>.class</tt> and the
+     * <tt>.java</tt> files for accurate audit.
+     */
+    protected Path classPath = null;
+
+    /** the path to the source file */
+    protected Path sourcePath = null;
+
+    /**
+     * Metamata home directory. It will be passed as a <tt>metamata.home</tt> property
+     * and should normally matches the environment property <tt>META_HOME</tt>
+     * set by the Metamata installer.
+     */
+    protected File metamataHome = null;
+
+    /** the command line used to run MAudit */
+    protected CommandlineJava cmdl = new CommandlineJava();
+
+    /** the set of files to be audited */
+    protected Vector fileSets = new Vector();
+
+    /** the options file where are stored the command line options */
+    protected File optionsFile = null;
+
+    // this is used to keep track of which files were included. It will
+    // be set when calling scanFileSets();
+    protected Hashtable includedFiles = null;
+
+    public AbstractMetamataTask() {
+    }
+
+    /** initialize the task with the classname of the task to run */
+    protected AbstractMetamataTask(String className) {
+        cmdl.setVm(JavaEnvUtils.getJreExecutable("java"));
+        cmdl.setClassname(className);
+    }
+
+    /**
+     * the metamata.home property to run all tasks.
+     * @ant.attribute ignore="true"
+     */
+    public void setHome(final File value) {
+        this.metamataHome = value;
+    }
+
+    /**
+     * The home directory containing the Metamata distribution; required
+     */
+    public void setMetamatahome(final File value) {
+        setHome(value);
+    }
+
+    /**
+     * Sets the class path (also source path unless one explicitly set).
+     * Overrides METAPATH/CLASSPATH environment variables.
+     */
+    public Path createClasspath() {
+        if (classPath == null) {
+            classPath = new Path(getProject());
+        }
+        return classPath;
+    }
+
+    /**
+     * Sets the source path.
+     * Overrides the SOURCEPATH environment variable.
+     */
+    public Path createSourcepath() {
+        if (sourcePath == null) {
+            sourcePath = new Path(getProject());
+        }
+        return sourcePath;
+    }
+
+    /**
+     * Additional optional parameters to pass to the JVM.
+     * You can avoid using the  <code>&lt;jvmarg&gt;</code> by adding these empty
+     * entries to <code>metamata.properties</code> located at <code>${metamata.home}/bin</code>
+     *
+     * <pre>metamata.classpath=
+     * metamata.sourcepath=
+     * metamata.baseclasspath=
+     * </pre>
+     */
+    public Commandline.Argument createJvmarg() {
+        return cmdl.createVmArgument();
+    }
+
+    /**
+     * Set the maximum memory for the JVM; optional.
+     * -mx or -Xmx depending on VM version
+     */
+    public void setMaxmemory(String max) {
+        cmdl.setMaxmemory(max);
+    }
+
+
+    /**
+     * The java files or directory to audit.
+     * Whatever the filter is, only the files that end
+     * with .java will be included for processing.
+     * Note that the base directory used for the fileset
+     * MUST be the root of the source files otherwise package names
+     * deduced from the file path will be incorrect.
+     */
+    public void addFileSet(FileSet fs) {
+        fileSets.addElement(fs);
+    }
+
+    /** execute the command line */
+    public void execute() throws BuildException {
+        try {
+            setUp();
+            ExecuteStreamHandler handler = createStreamHandler();
+            execute0(handler);
+        } finally {
+            cleanUp();
+        }
+    }
+
+    /** check the options and build the command line */
+    protected void setUp() throws BuildException {
+        checkOptions();
+
+        // set the classpath as the jar file
+        File jar = getMetamataJar(metamataHome);
+        final Path classPath = cmdl.createClasspath(getProject());
+        classPath.createPathElement().setLocation(jar);
+
+        // set the metamata.home property
+        final Commandline.Argument vmArgs = cmdl.createVmArgument();
+        vmArgs.setValue("-Dmetamata.home=" + metamataHome.getAbsolutePath());
+
+        // retrieve all the files we want to scan
+        includedFiles = scanSources(new Hashtable());
+        //String[] entries = sourcePath.list();
+        //includedFiles = scanSources(new Hashtable(), entries);
+        log(includedFiles.size() + " files added for audit", Project.MSG_VERBOSE);
+
+        // write all the options to a temp file and use it ro run the process
+        Vector options = getOptions();
+        optionsFile = createTmpFile();
+        generateOptionsFile(optionsFile, options);
+        Commandline.Argument args = cmdl.createArgument();
+        args.setLine("-arguments " + optionsFile.getAbsolutePath());
+    }
+
+    /**
+     * create a stream handler that will be used to get the output since
+     * metamata tools do not report with convenient files such as XML.
+     */
+    protected abstract ExecuteStreamHandler createStreamHandler();
+
+
+    /** execute the process with a specific handler */
+    protected void execute0(ExecuteStreamHandler handler) throws BuildException {
+        final Execute process = new Execute(handler);
+        log(cmdl.describeCommand(), Project.MSG_VERBOSE);
+        process.setCommandline(cmdl.getCommandline());
+        try {
+            if (process.execute() != 0) {
+                throw new BuildException("Metamata task failed.");
+            }
+        } catch (IOException e) {
+            throw new BuildException("Failed to launch Metamata task", e);
+        }
+    }
+
+    /** clean up all the mess that we did with temporary objects */
+    protected void cleanUp() {
+        if (optionsFile != null) {
+            optionsFile.delete();
+            optionsFile = null;
+        }
+    }
+
+    /** return the location of the jar file used to run */
+    protected final File getMetamataJar(File home) {
+        return new File(home, "lib/metamata.jar");
+    }
+
+    /** validate options set */
+    protected void checkOptions() throws BuildException {
+        // do some validation first
+        if (metamataHome == null || !metamataHome.exists()) {
+            throw new BuildException("'home' must point to Metamata home directory.");
+        }
+        File jar = getMetamataJar(metamataHome);
+        if (!jar.exists()) {
+            throw new BuildException(jar + " does not exist. Check your metamata installation.");
+        }
+    }
+
+    /** return all options of the command line as string elements */
+    protected abstract Vector getOptions();
+
+
+    protected void generateOptionsFile(File tofile, Vector options) throws BuildException {
+        FileWriter fw = null;
+        try {
+            fw = new FileWriter(tofile);
+            PrintWriter pw = new PrintWriter(fw);
+            final int size = options.size();
+            for (int i = 0; i < size; i++) {
+                pw.println(options.elementAt(i));
+            }
+            pw.flush();
+        } catch (IOException e) {
+            throw new BuildException("Error while writing options file " + tofile, e);
+        } finally {
+            if (fw != null) {
+                try {
+                    fw.close();
+                } catch (IOException ignored) {
+                }
+            }
+        }
+    }
+
+
+    protected Hashtable getFileMapping() {
+        return includedFiles;
+    }
+
+    /**
+     * convenient method for JDK 1.1. Will copy all elements from src to dest
+     */
+    protected static final void addAllVector(Vector dest, Enumeration files) {
+        while (files.hasMoreElements()) {
+            dest.addElement(files.nextElement());
+        }
+    }
+
+    protected final File createTmpFile() {
+        File tmpFile = FileUtils.newFileUtils()
+            .createTempFile("metamata", ".tmp", getProject().getBaseDir());
+        tmpFile.deleteOnExit();
+        return tmpFile;
+    }
+
+    /**
+     * @return the list of .java files (as their absolute path) that should
+     *         be audited.
+     */
+
+    protected Hashtable scanSources(Hashtable map) {
+        Hashtable files = new Hashtable();
+        for (int i = 0; i < fileSets.size(); i++) {
+            FileSet fs = (FileSet) fileSets.elementAt(i);
+            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+            ds.scan();
+            String[] f = ds.getIncludedFiles();
+            log(i + ") Adding " + f.length + " files from directory "
+                + ds.getBasedir(), Project.MSG_VERBOSE);
+            for (int j = 0; j < f.length; j++) {
+                String pathname = f[j];
+                if (pathname.endsWith(".java")) {
+                    File file = new File(ds.getBasedir(), pathname);
+//                  file = project.resolveFile(file.getAbsolutePath());
+                    String classname = pathname.substring(0, pathname.length() - ".java".length());
+                    classname = classname.replace(File.separatorChar, '.');
+                    files.put(file.getAbsolutePath(), classname); // it's a java file, add it.
+                }
+            }
+        }
+        return files;
+    }
+
+    protected Hashtable scanSources(final Hashtable mapping, final String[] entries) {
+        final Vector javaFiles = new Vector(512);
+        for (int i = 0; i < entries.length; i++) {
+            final File f = new File(entries[i]);
+            if (f.isDirectory()) {
+                DirectoryScanner ds = new DirectoryScanner();
+                ds.setBasedir(f);
+                ds.setIncludes(new String[]{"**/*.java"});
+                ds.scan();
+                String[] included = ds.getIncludedFiles();
+                for (int j = 0; j < included.length; j++) {
+                    javaFiles.addElement(new File(f, included[j]));
+                }
+            } else if (entries[i].endsWith(".java")) {
+                javaFiles.addElement(f);
+            }
+        }
+        // do the mapping paths/classname
+        final int count = javaFiles.size();
+        for (int i = 0; i < count; i++) {
+            File file = (File) javaFiles.elementAt(i);
+            String pathname = Path.translateFile(file.getAbsolutePath());
+            String classname = pathname.substring(0, pathname.length() - ".java".length());
+            classname = classname.replace(File.separatorChar, '.');
+            mapping.put(pathname, classname);
+        }
+        return mapping;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MAudit.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MAudit.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MAudit.java	(revision 16139)
@@ -0,0 +1,324 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.metamata;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Invokes the Metamata Audit/ Webgain Quality Analyzer on a set of Java files.
+ * <p>
+ * <i>maudit</i> performs static analysis of the Java source code and byte
+ * code files to find and report errors of style and potential problems related
+ * to performance, maintenance and robustness. As a convenience, a stylesheet
+ * is given in <tt>etc</tt> directory, so that an HTML report can be generated
+ * from the XML file.
+ *
+ */
+public class MAudit extends AbstractMetamataTask {
+
+    /* As of Metamata 2.0, the command line of MAudit is as follows:
+    Usage
+        maudit <option>... <path>... [-unused <search-path>...]
+
+    Parameters
+        path               File or directory to audit.
+        search-path        File or directory to search for declaration uses.
+
+    Options
+        -arguments  -A     <file>     Includes command line arguments from file.
+        -classpath  -cp    <path>     Sets class path (also source path unless one
+                                      explicitly set). Overrides METAPATH/CLASSPATH.
+        -exit       -x                Exits after the first error.
+        -fix        -f                Automatically fixes certain errors.
+        -fullpath                     Prints full path for locations.
+        -help       -h                Prints help and exits.
+        -list       -l                Creates listing file for each audited file.
+        -offsets    -off              Offset and length for locations.
+        -output     -o     <file>     Prints output to file.
+        -quiet      -q                Suppresses copyright and summary messages.
+        -sourcepath        <path>     Sets source path. Overrides SOURCEPATH.
+        -tab        -t                Prints a tab character after first argument.
+        -unused     -u                Finds declarations unused in search paths.
+        -verbose    -v                Prints all messages.
+        -version    -V                Prints version and exits.
+    */
+
+    //---------------------- PUBLIC METHODS ------------------------------------
+
+    /** pattern used by maudit to report the error for a file */
+    /** RE does not seems to support regexp pattern with comments so i'm stripping it*/
+    // (?:file:)?((?#filepath).+):((?#line)\\d+)\\s*:\\s+((?#message).*)
+    static final String AUDIT_PATTERN = "(?:file:)?(.+):(\\d+)\\s*:\\s+(.*)";
+
+    private File outFile = null;
+
+    private Path searchPath = null;
+
+    private Path rulesPath = null;
+
+    private boolean fix = false;
+
+    private boolean list = false;
+
+    private boolean unused = false;
+
+//  add a bunch of undocumented options for the task
+    private boolean quiet = false;
+    private boolean exit = false;
+    private boolean offsets = false;
+    private boolean verbose = false;
+    private boolean fullsemanticize = false;
+
+    /** default constructor */
+    public MAudit() {
+        super("com.metamata.gui.rc.MAudit");
+    }
+
+    /**
+     * The XML file to which the Audit result should be written to; required
+     */
+
+    public void setTofile(File outFile) {
+        this.outFile = outFile;
+    }
+
+    /**
+     * Automatically fix certain errors
+     * (those marked as fixable in the manual);
+     * optional, default=false
+     */
+    public void setFix(boolean flag) {
+        this.fix = flag;
+    }
+
+    /**
+     * Creates listing file for each audited file; optional, default false.
+     * When set, a .maudit file will be generated in the
+     * same location as the source file.
+     */
+    public void setList(boolean flag) {
+        this.list = flag;
+    }
+
+    /**
+     * Finds declarations unused in search paths; optional, default false.
+     * It will look for unused global declarations
+     * in the source code within a use domain specified by the
+     * <tt>searchpath</tt> element.
+     */
+    public void setUnused(boolean flag) {
+        this.unused = flag;
+    }
+
+    /**
+     * flag to suppress copyright and summary messages; default false.
+     * internal/testing only
+     * @ant.attribute ignore="true"
+     */
+    public void setQuiet(boolean flag) {
+        this.quiet = flag;
+    }
+
+    /**
+     * flag to tell the task to exit after the first error.
+     * internal/testing only
+     * @ant.attribute ignore="true"
+     */
+    public void setExit(boolean flag) {
+        this.exit = flag;
+    }
+
+    /**
+     * internal/testing only
+     * @ant.attribute ignore="true"
+     */
+    public void setOffsets(boolean flag) {
+        this.offsets = flag;
+    }
+
+    /**
+     * flag to print all messages; optional, default false.
+     * internal/testing only
+     * @ant.attribute ignore="true"
+     */
+    public void setVerbose(boolean flag) {
+        this.verbose = flag;
+    }
+
+    /**
+     * internal/testing only
+     * @ant.attribute ignore="true"
+     */
+    public void setFullsemanticize(boolean flag) {
+        this.fullsemanticize = flag;
+    }
+
+    /**
+     * classpath for additional audit rules
+     * these must be placed before metamata.jar !!
+     */
+    public Path createRulespath() {
+        if (rulesPath == null) {
+            rulesPath = new Path(getProject());
+        }
+        return rulesPath;
+    }
+
+    /**
+     * search path to use for unused global declarations;
+     * required when <tt>unused</tt> is set.
+     */
+    public Path createSearchpath() {
+        if (searchPath == null) {
+            searchPath = new Path(getProject());
+        }
+        return searchPath;
+    }
+
+    /**
+     * create the option vector for the command
+     */
+    protected Vector getOptions() {
+        Vector options = new Vector(512);
+        // add the source path automatically from the fileset.
+        // to avoid redundancy...
+        for (int i = 0; i < fileSets.size(); i++) {
+            FileSet fs = (FileSet) fileSets.elementAt(i);
+            Path path = createSourcepath();
+            File dir = fs.getDir(getProject());
+            path.setLocation(dir);
+        }
+
+        // there is a bug in Metamata 2.0 build 37. The sourcepath argument does
+        // not work. So we will use the sourcepath prepended to classpath. (order
+        // is important since Metamata looks at .class and .java)
+        if (sourcePath != null) {
+            sourcePath.append(classPath); // srcpath is prepended
+            classPath = sourcePath;
+            sourcePath = null; // prevent from using -sourcepath
+        }
+
+        // don't forget to modify the pattern if you change the options reporting
+        if (classPath != null) {
+            options.addElement("-classpath");
+            options.addElement(classPath.toString());
+        }
+        // suppress copyright msg when running, we will let it so that this
+        // will be the only output to the console if in xml mode
+        if (quiet) {
+            options.addElement("-quiet");
+        }
+        if (fullsemanticize) {
+            options.addElement("-full-semanticize");
+        }
+        if (verbose) {
+            options.addElement("-verbose");
+        }
+        if (offsets) {
+            options.addElement("-offsets");
+        }
+        if (exit) {
+            options.addElement("-exit");
+        }
+        if (fix) {
+            options.addElement("-fix");
+        }
+        options.addElement("-fullpath");
+
+        // generate .maudit files much more detailed than the report
+        // I don't like it very much, I think it could be interesting
+        // to get all .maudit files and include them in the XML.
+        if (list) {
+            options.addElement("-list");
+        }
+        if (sourcePath != null) {
+            options.addElement("-sourcepath");
+            options.addElement(sourcePath.toString());
+        }
+        addAllVector(options, includedFiles.keys());
+        if (unused) {
+            options.addElement("-unused");
+            options.addElement(searchPath.toString());
+        }
+        return options;
+    }
+
+    /**
+     * validate the settings
+     */
+    protected void checkOptions() throws BuildException {
+        super.checkOptions();
+        if (unused && searchPath == null) {
+            throw new BuildException("'searchpath' element must be set when "
+                + "looking for 'unused' declarations.");
+        }
+        if (!unused && searchPath != null) {
+            log("'searchpath' element ignored. 'unused' attribute is disabled.",
+                Project.MSG_WARN);
+        }
+        if (rulesPath != null) {
+            cmdl.createClasspath(getProject()).addExisting(rulesPath);
+        }
+    }
+
+    protected ExecuteStreamHandler createStreamHandler() throws BuildException {
+        // if we didn't specify a file, then use a screen report
+        if (outFile == null) {
+            return new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_ERR);
+        }
+        ExecuteStreamHandler handler = null;
+        OutputStream out = null;
+        try {
+            out = new FileOutputStream(outFile);
+            handler = new MAuditStreamHandler(this, out);
+        } catch (IOException e) {
+            throw new BuildException(e);
+        } finally {
+            if (out == null) {
+                try {
+                    out.close();
+                } catch (IOException e) {
+                }
+            }
+        }
+        return handler;
+    }
+
+    protected void cleanUp() throws BuildException {
+        super.cleanUp();
+        // at this point if -list is used, we should move
+        // the .maudit file since we cannot choose their location :(
+        // the .maudit files match the .java files
+        // we'll use includedFiles to get the .maudit files.
+
+        /*if (out != null) {
+            // close it if not closed by the handler...
+        }*/
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MAuditParser.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MAuditParser.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MAuditParser.java	(revision 16139)
@@ -0,0 +1,86 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.metamata;
+
+import java.io.File;
+import java.util.Vector;
+import org.apache.tools.ant.util.StringUtils;
+import org.apache.tools.ant.util.regexp.RegexpMatcher;
+import org.apache.tools.ant.util.regexp.RegexpMatcherFactory;
+
+/**
+ * Parser that will parse an output line of MAudit and return an
+ * interpreted violation if any.
+ *
+ * <p>
+ * MAudit is supposed to be configured with -fullpath so that it can
+ * correctly locate the file and attribute violation to the appropriate
+ * file (there might be several classes with the same name in
+ * different packages)
+ * </p>
+ *
+ */
+final class MAuditParser {
+
+    /** pattern used by maudit to report the error for a file */
+    /** RE does not seems to support regexp pattern with comments so i'm stripping it*/
+    // (?:file:)?((?#filepath).+):((?#line)\\d+)\\s*:\\s+((?#message).*)
+    private static final String AUDIT_PATTERN = "(?:file:)?(.+):(\\d+)\\s*:\\s+(.*)";
+
+    /** matcher that will be used to extract the info from the line */
+    private final RegexpMatcher matcher;
+
+    MAuditParser() {
+        /** the matcher should be the Oro one. I don't know about the other one */
+        matcher = (new RegexpMatcherFactory()).newRegexpMatcher();
+        matcher.setPattern(AUDIT_PATTERN);
+    }
+
+    /**
+     * Parse a line obtained from MAudit.
+     * @param line a line obtained from the MAudit output.
+     * @return the violation corresponding to the displayed line
+     * or <tt>null</tt> if it could not parse it. (might be a
+     * message info or copyright or summary).
+     */
+    Violation parseLine(String line) {
+        Vector matches = matcher.getGroups(line);
+        if (matches == null) {
+            return null;
+        }
+        final String file = (String) matches.elementAt(1);
+        Violation violation = new Violation();
+        violation.file = file;
+        violation.line = (String) matches.elementAt(2);
+        violation.error = (String) matches.elementAt(3);
+        // remove the pathname from any messages and let the classname only.
+        final int pos = file.lastIndexOf(File.separatorChar);
+        if ((pos != -1) && (pos != file.length() - 1)) {
+            String filename = file.substring(pos + 1);
+            violation.error = StringUtils.replace(violation.error,
+                    "file:" + file, filename);
+        }
+        return violation;
+    }
+
+    /** the inner class used to report violation information */
+    static final class Violation {
+        String file;
+        String line;
+        String error;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MAuditStreamHandler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MAuditStreamHandler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MAuditStreamHandler.java	(revision 16139)
@@ -0,0 +1,226 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.metamata;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.taskdefs.StreamPumper;
+import org.apache.tools.ant.util.DOMElementWriter;
+import org.apache.tools.ant.util.DateUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+
+/**
+ * This is a very bad stream handler for the MAudit task.
+ * All report to stdout that does not match a specific report pattern is dumped
+ * to the Ant output as warn level. The report that match the pattern is stored
+ * in a map with the key being the filepath that caused the error report.
+ * <p>
+ * The limitation with the choosen implementation is clear:
+ * <ul>
+ * <li>it does not handle multiline report( message that has \n ). the part until
+ * the \n will be stored and the other part (which will not match the pattern)
+ * will go to Ant output in Warn level.
+ * <li>it does not report error that goes to stderr.
+ * </ul>
+ *
+ */
+class MAuditStreamHandler implements ExecuteStreamHandler {
+
+    /** parent task */
+    private MAudit task;
+
+    /** reader for stdout */
+    private BufferedReader br;
+
+    /**
+     * this is where the XML output will go, should mostly be a file
+     * the caller is responsible for flushing and closing this stream
+     */
+    private OutputStream xmlOut = null;
+
+    /** error stream, might be useful to spit out error messages */
+    private OutputStream errStream;
+
+    /** thread pumping out error stream */
+    private Thread errThread;
+
+    /**
+     * the multimap. The key in the map is the filepath that caused the audit
+     * error and the value is a vector of MAudit.Violation entries.
+     */
+    private Hashtable auditedFiles = new Hashtable();
+
+    /** program start timestamp for reporting purpose */
+    private Date program_start;
+
+    MAuditStreamHandler(MAudit task, OutputStream xmlOut) {
+        this.task = task;
+        this.xmlOut = xmlOut;
+    }
+
+    /** Ignore. */
+    public void setProcessInputStream(OutputStream os) {
+    }
+
+    /** Ignore. */
+    public void setProcessErrorStream(InputStream is) {
+        errStream = new LogOutputStream(task, Project.MSG_ERR);
+        errThread = createPump(is, errStream);
+    }
+
+    /** Set the inputstream */
+    public void setProcessOutputStream(InputStream is) throws IOException {
+        br = new BufferedReader(new InputStreamReader(is));
+    }
+
+    /** Invokes parseOutput. This will block until the end :-(*/
+    public void start() throws IOException {
+        program_start = new Date();
+        errThread.start();
+        parseOutput(br);
+    }
+
+    /**
+     * Pretty dangerous business here. It serializes what was extracted from
+     * the MAudit output and write it to the output.
+     */
+    public void stop() {
+        // make sure to flush err stream
+        try {
+            errThread.join();
+        } catch (InterruptedException e) {
+        }
+        try {
+            errStream.flush();
+        } catch (IOException e) {
+        }
+        // serialize the content as XML, move this to another method
+        // this is the only code that could be needed to be overriden
+        Document doc = getDocumentBuilder().newDocument();
+        Element rootElement = doc.createElement("classes");
+        Enumeration keys = auditedFiles.keys();
+        Hashtable filemapping = task.getFileMapping();
+        final Date now = new Date();
+        rootElement.setAttribute("snapshot_created",
+            DateUtils.format(now, DateUtils.ISO8601_DATETIME_PATTERN));
+        rootElement.setAttribute("elapsed_time",
+            String.valueOf(now.getTime() - program_start.getTime()));
+        rootElement.setAttribute("program_start",
+            DateUtils.format(now, DateUtils.ISO8601_DATETIME_PATTERN));
+        rootElement.setAttribute("audited",
+            String.valueOf(filemapping.size()));
+        rootElement.setAttribute("reported",
+            String.valueOf(auditedFiles.size()));
+        int errors = 0;
+        while (keys.hasMoreElements()) {
+            String filepath = (String) keys.nextElement();
+            Vector v = (Vector) auditedFiles.get(filepath);
+            String fullclassname = (String) filemapping.get(filepath);
+            if (fullclassname == null) {
+                task.getProject().log("Could not find class mapping for "
+                    + filepath, Project.MSG_WARN);
+                continue;
+            }
+            int pos = fullclassname.lastIndexOf('.');
+            String pkg = (pos == -1) ? "" : fullclassname.substring(0, pos);
+            String clazzname = (pos == -1) ? fullclassname : fullclassname.substring(pos + 1);
+            Element clazz = doc.createElement("class");
+            clazz.setAttribute("package", pkg);
+            clazz.setAttribute("name", clazzname);
+            final int violationCount = v.size();
+            clazz.setAttribute("violations", String.valueOf(violationCount));
+            errors += violationCount;
+            for (int i = 0; i < violationCount; i++) {
+                MAuditParser.Violation violation = (MAuditParser.Violation) v.elementAt(i);
+                Element error = doc.createElement("violation");
+                error.setAttribute("line", violation.line);
+                error.setAttribute("message", violation.error);
+                clazz.appendChild(error);
+            }
+            rootElement.appendChild(clazz);
+        }
+        rootElement.setAttribute("violations", String.valueOf(errors));
+
+        // now write it to the outputstream, not very nice code
+        DOMElementWriter domWriter = new DOMElementWriter();
+        try {
+            domWriter.write(rootElement, xmlOut);
+        } catch (IOException e) {
+            throw new BuildException(e);
+        }
+    }
+
+    protected static DocumentBuilder getDocumentBuilder() {
+        try {
+            return DocumentBuilderFactory.newInstance().newDocumentBuilder();
+        } catch (Exception exc) {
+            throw new ExceptionInInitializerError(exc);
+        }
+    }
+
+    /**
+     * Creates a stream pumper to copy the given input stream to the given output stream.
+     */
+    protected Thread createPump(InputStream is, OutputStream os) {
+        final Thread result = new Thread(new StreamPumper(is, os));
+        result.setDaemon(true);
+        return result;
+    }
+
+
+    /** read each line and process it */
+    protected void parseOutput(BufferedReader br) throws IOException {
+        String line = null;
+        final MAuditParser parser = new MAuditParser();
+        while ((line = br.readLine()) != null) {
+            final MAuditParser.Violation violation = parser.parseLine(line);
+            if (violation != null) {
+                addViolation(violation.file, violation);
+            } else {
+                // this doesn't match..report it as info, it could be
+                // either the copyright, summary or a multiline message (damn !)
+                task.log(line, Project.MSG_INFO);
+            }
+        }
+    }
+
+    /** add a violation entry for the file */
+    private void addViolation(String file, MAuditParser.Violation entry) {
+        Vector violations = (Vector) auditedFiles.get(file);
+        // if there is no decl for this file yet, create it.
+        if (violations == null) {
+            violations = new Vector();
+            auditedFiles.put(file, violations);
+        }
+        violations.addElement(entry);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MMetrics.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MMetrics.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MMetrics.java	(revision 16139)
@@ -0,0 +1,265 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.metamata;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Computes the metrics of a set of Java files and write the results to an XML
+ * file. As a convenience, a stylesheet is given in <tt>etc</tt> directory,
+ * so that an HTML report can be generated from the XML file.
+ * <p>
+ * You will not be able to use this task with the evaluation version since
+ * as of Metamata 2.0, Metrics does not support command line :-(
+ *
+ *
+ */
+public class MMetrics extends AbstractMetamataTask {
+/*
+    The command line options as of Metamata 2.0 are as follows:
+
+Usage
+    mmetrics <option>... <path>...
+
+Parameters
+    path              File or directory to measure.
+
+Options
+    -arguments   -A   <file>      Includes command line arguments from file.
+    -classpath   -cp  <path>      Sets class path (also source path unless one
+                                  explicitly set). Overrides METAPATH/CLASSPATH.
+    -compilation-units            Measure compilation units.
+    -files                        Measure compilation units.
+    -format      -f   <format>    Sets output format, default output file type.
+    -help        -h               Prints help and exits.
+    -indent      -i   <string>    Sets string used to indent labels one level.
+    -methods                      Measure methods, types, and compilation units.
+    -output      -o   <file>      Sets output file name.
+    -quiet       -q               Suppresses copyright message.
+    -sourcepath       <path>      Sets source path. Overrides SOURCEPATH.
+    -types                        Measure types and compilation units.
+    -verbose     -v               Prints all messages.
+    -version     -V               Prints version and exits.
+
+Format Options
+    comma csv                     Format output as comma-separated text.
+    html htm                      Format output as an HTML table.
+    tab tab-separated tsv         Format output as tab-separated text.
+    text txt                      Format output as space-aligned text.
+*/
+
+    /** the granularity mode. Should be one of 'files', 'methods' and 'types'. */
+    private String granularity = null;
+
+    /** the XML output file */
+    private File outFile = null;
+
+    /** the location of the temporary txt report */
+    private File tmpFile;
+
+    private Path path = null;
+
+    //--------------------------- PUBLIC METHODS -------------------------------
+
+    /** default constructor */
+    public MMetrics() {
+        super("com.metamata.sc.MMetrics");
+    }
+
+    /**
+     * Attributes for granularity.
+     */
+    public static class GranularityAttribute extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[]{"compilation-units", "files", "methods", "types", "packages"};
+        }
+    }
+
+    /**
+     * set the granularity of the audit. Should be one of 'files', 'methods'
+     * or 'types'.
+     * @param granularity   the audit reporting mode.
+     */
+    public void setGranularity(GranularityAttribute granularity) {
+        this.granularity = granularity.getValue();
+    }
+
+    /**
+     * Set the output XML file
+     * @param file the xml file to write the XML report to.
+     */
+    public void setTofile(File file) {
+        this.outFile = file;
+    }
+
+    /**
+     * Set a new path (directory) to measure metrics from.
+     * @return the path instance to use.
+     */
+    public Path createPath() {
+        if (path == null) {
+            path = new Path(getProject());
+        }
+        return path;
+
+    }
+
+    //------------------- PROTECTED / PRIVATE METHODS --------------------------
+
+
+    // check for existing options and outfile, all other are optional
+    protected void checkOptions() throws BuildException {
+        super.checkOptions();
+
+        if (outFile == null) {
+            throw new BuildException("Output XML file must be set via 'tofile' attribute.");
+        }
+        if (path == null && fileSets.size() == 0) {
+            throw new BuildException("Must set either paths (path element) "
+                + "or files (fileset element)");
+        }
+        // I don't accept dirs and files at the same time,
+        // I cannot recognize the semantic in the result
+        if (path != null && fileSets.size() > 0) {
+            throw new BuildException("Cannot set paths (path element) and "
+                + "files (fileset element) at the same time");
+        }
+        tmpFile = createTmpFile();
+    }
+
+    protected void execute0(ExecuteStreamHandler handler) throws BuildException {
+        super.execute0(handler);
+        transformFile();
+    }
+
+    /**
+     * transform the generated file via the handler
+     * This function can either be called if the result is written to the output
+     * file via -output or we could use the handler directly on stdout if not.
+     * @see #createStreamHandler()
+     */
+    protected void transformFile() throws BuildException {
+        FileInputStream tmpStream = null;
+        try {
+            tmpStream = new FileInputStream(tmpFile);
+        } catch (IOException e) {
+            throw new BuildException("Error reading temporary file: " + tmpFile, e);
+        }
+        FileOutputStream xmlStream = null;
+        try {
+            xmlStream = new FileOutputStream(outFile);
+            ExecuteStreamHandler xmlHandler = new MMetricsStreamHandler(this, xmlStream);
+            xmlHandler.setProcessOutputStream(tmpStream);
+            xmlHandler.start();
+            xmlHandler.stop();
+        } catch (IOException e) {
+            throw new BuildException("Error creating output file: " + outFile, e);
+        } finally {
+            if (xmlStream != null) {
+                try {
+                    xmlStream.close();
+                } catch (IOException ignored) {
+                }
+            }
+            if (tmpStream != null) {
+                try {
+                    tmpStream.close();
+                } catch (IOException ignored) {
+                }
+            }
+        }
+    }
+
+
+    /** cleanup the temporary txt report */
+    protected void cleanUp() throws BuildException {
+        try {
+            super.cleanUp();
+        } finally {
+            if (tmpFile != null) {
+                tmpFile.delete();
+                tmpFile = null;
+            }
+        }
+    }
+
+    /**
+     * if the report is transform via a temporary txt file we should use a
+     * a normal logger here, otherwise we could use the metrics handler
+     * directly to capture and transform the output on stdout to XML.
+     */
+    protected ExecuteStreamHandler createStreamHandler() {
+        // write the report directtly to an XML stream
+        // return new MMetricsStreamHandler(this, xmlStream);
+        return new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_INFO);
+    }
+
+
+    protected Vector getOptions() {
+        Vector options = new Vector(512);
+        // there is a bug in Metamata 2.0 build 37. The sourcepath argument does
+        // not work. So we will use the sourcepath prepended to classpath. (order
+        // is important since Metamata looks at .class and .java)
+        if (sourcePath != null) {
+            sourcePath.append(classPath); // srcpath is prepended
+            classPath = sourcePath;
+            sourcePath = null; // prevent from using -sourcepath
+        }
+
+        // don't forget to modify the pattern if you change the options reporting
+        if (classPath != null) {
+            options.addElement("-classpath");
+            options.addElement(classPath.toString());
+        }
+        options.addElement("-output");
+        options.addElement(tmpFile.toString());
+
+        options.addElement("-" + granularity);
+
+        // display the metamata copyright
+        // options.addElement( "-quiet");
+        options.addElement("-format");
+
+        // need this because that's what the handler is using, it's
+        // way easier to process than any other separator
+        options.addElement("tab");
+
+        // specify a / as the indent character, used by the handler.
+        options.addElement("-i");
+        options.addElement("/");
+
+        // directories
+        String[] dirs = path.list();
+        for (int i = 0; i < dirs.length; i++) {
+            options.addElement(dirs[i]);
+        }
+        // files next.
+        addAllVector(options, includedFiles.keys());
+        return options;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MMetricsStreamHandler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MMetricsStreamHandler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MMetricsStreamHandler.java	(revision 16139)
@@ -0,0 +1,409 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.metamata;
+
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.text.DecimalFormat;
+import java.text.NumberFormat;
+import java.text.ParseException;
+import java.util.Date;
+import java.util.EmptyStackException;
+import java.util.Enumeration;
+import java.util.Stack;
+import java.util.Vector;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXTransformerFactory;
+import javax.xml.transform.sax.TransformerHandler;
+import javax.xml.transform.stream.StreamResult;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.util.DateUtils;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.AttributesImpl;
+
+/**
+ * A handy metrics handler. Most of this code was done only with the
+ * screenshots on the documentation since the evaluation version as
+ * of this writing does not allow to save metrics or to run it via
+ * command line.
+ * <p>
+ * This class can be used to transform a text file or to process the
+ * output stream directly.
+ *
+ */
+public class MMetricsStreamHandler implements ExecuteStreamHandler {
+
+    /** CLASS construct, it should be named something like 'MyClass' */
+    private static final String CLASS = "class";
+
+    /** package construct, it should be look like 'com.mycompany.something' */
+    private static final String PACKAGE = "package";
+
+    /** FILE construct, it should look like something 'MyClass.java' or 'MyClass.class' */
+    private static final String FILE = "file";
+
+    /** METHOD construct, it should looke like something 'doSomething(...)' or 'doSomething()' */
+    private static final String METHOD = "method";
+
+    private static final String[] ATTRIBUTES = {
+        "name", "vg", "loc", "dit", "noa", "nrm", "nlm", "wmc",
+        "rfc", "dac", "fanout", "cbo", "lcom", "nocl"};
+
+    /** reader for stdout */
+    private InputStream metricsOutput;
+
+    /**
+     * this is where the XML output will go, should mostly be a file
+     * the caller is responsible for flushing and closing this stream
+     */
+    private OutputStream xmlOutputStream;
+
+    /** metrics handler */
+    private TransformerHandler metricsHandler;
+
+    /** the task */
+    private Task task;
+
+    /**
+     * the stack where are stored the metrics element so that they we can
+     * know if we have to close an element or not.
+     */
+    private Stack stack = new Stack();
+
+    /** initialize this handler */
+    MMetricsStreamHandler(Task task, OutputStream xmlOut) {
+        this.task = task;
+        this.xmlOutputStream = xmlOut;
+    }
+
+    /** Ignore. */
+    public void setProcessInputStream(OutputStream p1) throws IOException {
+    }
+
+    /** Ignore. */
+    public void setProcessErrorStream(InputStream p1) throws IOException {
+    }
+
+    /** Set the inputstream */
+    public void setProcessOutputStream(InputStream is) throws IOException {
+        metricsOutput = is;
+    }
+
+    public void start() throws IOException {
+        // create the transformer handler that will be used to serialize
+        // the output.
+        TransformerFactory factory = TransformerFactory.newInstance();
+        if (!factory.getFeature(SAXTransformerFactory.FEATURE)) {
+            throw new IllegalStateException("Invalid Transformer factory feature");
+        }
+        try {
+            metricsHandler = ((SAXTransformerFactory) factory).newTransformerHandler();
+            metricsHandler.setResult(new StreamResult(new OutputStreamWriter(xmlOutputStream, "UTF-8")));
+            Transformer transformer = metricsHandler.getTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+
+            // start the document with a 'metrics' root
+            final Date now = new Date();
+            metricsHandler.startDocument();
+            AttributesImpl attr = new AttributesImpl();
+            attr.addAttribute("", "company", "company", "CDATA", "metamata");
+            attr.addAttribute("", "snapshot_created", "snapshot_created", "CDATA",
+                    DateUtils.format(now, DateUtils.ISO8601_DATETIME_PATTERN));
+            // attr.addAttribute("", "elapsed_time", "elapsed_time", "CDATA",
+            //    String.valueOf(now.getTime() - program_start.getTime()));
+            attr.addAttribute("", "program_start", "program_start", "CDATA",
+                    DateUtils.format(new Date(), DateUtils.ISO8601_DATETIME_PATTERN));
+            metricsHandler.startElement("", "metrics", "metrics", attr);
+
+            // now parse the whole thing
+            parseOutput();
+
+        } catch (Exception e) {
+            throw new BuildException(e);
+        }
+    }
+
+    /**
+     * Pretty dangerous business here.
+     */
+    public void stop() {
+        try {
+            // we need to pop everything and close elements that have not been
+            // closed yet.
+            while (stack.size() > 0) {
+                ElementEntry elem = (ElementEntry) stack.pop();
+                metricsHandler.endElement("", elem.getType(), elem.getType());
+            }
+            // close the root
+            metricsHandler.endElement("", "metrics", "metrics");
+            // document is finished for good
+            metricsHandler.endDocument();
+        } catch (SAXException e) {
+            e.printStackTrace();
+            throw new IllegalStateException(e.getMessage());
+        }
+    }
+
+    /** read each line and process it */
+    protected void parseOutput() throws IOException, SAXException {
+        BufferedReader br = new BufferedReader(new InputStreamReader(metricsOutput));
+        String line = null;
+        while ((line = br.readLine()) != null) {
+            processLine(line);
+        }
+    }
+
+    /**
+     * Process a metrics line. If the metrics is invalid and that this is not
+     * the header line, it is display as info.
+     * @param line the line to process, it is normally a line full of metrics.
+     */
+    protected void processLine(String line) throws SAXException {
+        if (line.startsWith("Construct\tV(G)\tLOC\tDIT\tNOA\tNRM\tNLM\tWMC\tRFC\tDAC\tFANOUT\tCBO\tLCOM\tNOCL")) {
+            return;
+        }
+        try {
+            MetricsElement elem = MetricsElement.parse(line);
+            startElement(elem);
+        } catch (ParseException e) {
+            //e.printStackTrace();
+            // invalid lines are sent to the output as information, it might be anything,
+            task.log(line, Project.MSG_INFO);
+        }
+    }
+
+    /**
+     * Start a new construct. Elements are popped until we are on the same
+     * parent node, then the element type is guessed and pushed on the
+     * stack.
+     * @param elem the element to process.
+     * @throws SAXException thrown if there is a problem when sending SAX events.
+     */
+    protected void startElement(MetricsElement elem) throws SAXException {
+        // if there are elements in the stack we possibly need to close one or
+        // more elements previous to this one until we got its parent
+        int indent = elem.getIndent();
+        if (stack.size() > 0) {
+            ElementEntry previous = (ElementEntry) stack.peek();
+            // close nodes until you got the parent.
+            try {
+                while (indent <= previous.getIndent() && stack.size() > 0) {
+                    stack.pop();
+                    metricsHandler.endElement("", previous.getType(), previous.getType());
+                    previous = (ElementEntry) stack.peek();
+                }
+            } catch (EmptyStackException ignored) {
+            }
+        }
+
+        // ok, now start the new construct
+        String type = getConstructType(elem);
+        Attributes attrs = createAttributes(elem);
+        metricsHandler.startElement("", type, type, attrs);
+
+        // make sure we keep track of what we did, that's history
+        stack.push(new ElementEntry(type, indent));
+    }
+
+    /**
+     * return the construct type of the element. We can hardly recognize the
+     * type of a metrics element, so we are kind of forced to do some black
+     * magic based on the name and indentation to recognize the type.
+     * @param elem  the metrics element to guess for its type.
+     * @return the type of the metrics element, either PACKAGE, FILE, CLASS or
+     * METHOD.
+     */
+    protected String getConstructType(MetricsElement elem) {
+        // ok no doubt, it's a file
+        if (elem.isCompilationUnit()) {
+            return FILE;
+        }
+
+        // same, we're sure it's a method
+        if (elem.isMethod()) {
+            return METHOD;
+        }
+
+        // if it's empty, and none of the above it should be a package
+        if (stack.size() == 0) {
+            return PACKAGE;
+        }
+
+        // ok, this is now black magic time, we will guess the type based on
+        // the previous type and its indent...
+        final ElementEntry previous = (ElementEntry) stack.peek();
+        final String prevType = previous.getType();
+        final int prevIndent = previous.getIndent();
+        final int indent = elem.getIndent();
+        // we're just under a file with a bigger indent so it's a class
+        if (prevType.equals(FILE) && indent > prevIndent) {
+            return CLASS;
+        }
+
+        // we're just under a class with a greater or equals indent, it's a class
+        // (there might be several classes in a compilation unit and inner classes as well)
+        if (prevType.equals(CLASS) && indent >= prevIndent) {
+            return CLASS;
+        }
+
+        // we assume the other are package
+        return PACKAGE;
+    }
+
+
+    /**
+     * Create all attributes of a MetricsElement skipping those who have an
+     * empty string
+     */
+    protected Attributes createAttributes(MetricsElement elem) {
+        AttributesImpl impl = new AttributesImpl();
+        int i = 0;
+        String name = ATTRIBUTES[i++];
+        impl.addAttribute("", name, name, "CDATA", elem.getName());
+        Enumeration metrics = elem.getMetrics();
+        for (; metrics.hasMoreElements(); i++) {
+            String value = (String) metrics.nextElement();
+            if (value.length() > 0) {
+                name = ATTRIBUTES[i];
+                impl.addAttribute("", name, name, "CDATA", value);
+            }
+        }
+        return impl;
+    }
+
+    /**
+     * helper class to keep track of elements via its type and indent
+     * that's all we need to guess a type.
+     */
+    private static final class ElementEntry {
+        private String type;
+        private int indent;
+
+        ElementEntry(String type, int indent) {
+            this.type = type;
+            this.indent = indent;
+        }
+
+        public String getType() {
+            return type;
+        }
+
+        public int getIndent() {
+            return indent;
+        }
+    }
+}
+
+class MetricsElement {
+
+    private static final NumberFormat METAMATA_NF;
+
+    private static final NumberFormat NEUTRAL_NF;
+
+    static {
+        METAMATA_NF = NumberFormat.getInstance();
+        METAMATA_NF.setMaximumFractionDigits(1);
+        NEUTRAL_NF = NumberFormat.getInstance();
+        if (NEUTRAL_NF instanceof DecimalFormat) {
+            ((DecimalFormat) NEUTRAL_NF).applyPattern("###0.###;-###0.###");
+        }
+        NEUTRAL_NF.setMaximumFractionDigits(1);
+    }
+
+    private int indent;
+
+    private String construct;
+
+    private Vector metrics;
+
+    MetricsElement(int indent, String construct, Vector metrics) {
+        this.indent = indent;
+        this.construct = construct;
+        this.metrics = metrics;
+    }
+
+    public int getIndent() {
+        return indent;
+    }
+
+    public String getName() {
+        return construct;
+    }
+
+    public Enumeration getMetrics() {
+        return metrics.elements();
+    }
+
+    public boolean isCompilationUnit() {
+        return (construct.endsWith(".java") || construct.endsWith(".class"));
+    }
+
+    public boolean isMethod() {
+        return (construct.endsWith("(...)") || construct.endsWith("()"));
+    }
+
+    public static MetricsElement parse(String line) throws ParseException {
+        final Vector metrics = new Vector();
+        int pos;
+
+        // i'm using indexOf since I need to know if there are empty strings
+        // between tabs and I find it easier than with StringTokenizer
+        while ((pos = line.indexOf('\t')) != -1) {
+            String token = line.substring(0, pos);
+            // only parse what coudl be a valid number. ie not constructs nor no value
+            /*if (metrics.size() != 0 || token.length() != 0) {
+                Number num = METAMATA_NF.parse(token); // parse with Metamata NF
+                token = NEUTRAL_NF.format(num.doubleValue()); // and format with a neutral NF
+            }*/
+            metrics.addElement(token);
+            line = line.substring(pos + 1);
+        }
+        metrics.addElement(line);
+
+        // there should be exactly 14 tokens (1 name + 13 metrics), if not, there is a problem !
+        if (metrics.size() != 14) {
+            throw new ParseException("Could not parse the following line as "
+                + "a metrics: -->" + line + "<--", -1);
+        }
+
+        // remove the first token it's made of the indentation string and the
+        // construct name, we'll need all this to figure out what type of
+        // construct it is since we lost all semantics :(
+        // (#indent[/]*)(#construct.*)
+        String name = (String) metrics.elementAt(0);
+        metrics.removeElementAt(0);
+        int indent = 0;
+        pos = name.lastIndexOf('/');
+        if (pos != -1) {
+            name = name.substring(pos + 1);
+            indent = pos + 1; // indentation is last position of token + 1
+        }
+        return new MetricsElement(indent, name, metrics);
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MParse.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MParse.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/metamata/MParse.java	(revision 16139)
@@ -0,0 +1,275 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.metamata;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Simple Metamata MParse task.
+ * Based on the original written by
+ * <a href="mailto:thomas.haas@softwired-inc.com">Thomas Haas</a>.
+ *
+ * This version was written for Metamata 2.0 available at
+ * <a href="http://www.metamata.com">http://www.metamata.com</a>
+ *
+ * @todo make a subclass of AbstractMetaMataTask
+ */
+public class MParse extends AbstractMetamataTask {
+
+    private File target = null;
+    private boolean verbose = false;
+    private boolean debugparser = false;
+    private boolean debugscanner = false;
+    private boolean cleanup = false;
+
+    /** The .jj file to process; required. */
+    public void setTarget(File target) {
+        this.target = target;
+    }
+
+    /** set verbose mode */
+    public void setVerbose(boolean flag) {
+        verbose = flag;
+    }
+
+    /** set scanner debug mode; optional, default false */
+    public void setDebugscanner(boolean flag) {
+        debugscanner = flag;
+    }
+
+    /** set parser debug mode; optional, default false */
+    public void setDebugparser(boolean flag) {
+        debugparser = flag;
+    }
+
+    /** Remove the intermediate Sun JavaCC file
+     * ; optional, default false.
+     */
+    public void setCleanup(boolean value) {
+        cleanup = value;
+    }
+
+    public MParse() {
+        cmdl.setVm(JavaEnvUtils.getJreExecutable("java"));
+        cmdl.setClassname("com.metamata.jj.MParse");
+    }
+
+
+    /** execute the command line */
+    public void execute() throws BuildException {
+        try {
+            setUp();
+            ExecuteStreamHandler handler = createStreamHandler();
+            _execute(handler);
+        } finally {
+            cleanUp();
+        }
+    }
+
+    /** return the default stream handler for this task */
+    protected ExecuteStreamHandler createStreamHandler() {
+        return new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_INFO);
+    }
+
+    /**
+     * check the options and build the command line
+     */
+    protected void setUp() throws BuildException {
+        checkOptions();
+
+        // set the classpath as the jar files
+        File[] jars = getMetamataLibs();
+        final Path classPath = cmdl.createClasspath(getProject());
+        for (int i = 0; i < jars.length; i++) {
+            classPath.createPathElement().setLocation(jars[i]);
+        }
+
+        // set the metamata.home property
+        final Commandline.Argument vmArgs = cmdl.createVmArgument();
+        vmArgs.setValue("-Dmetamata.home=" + metamataHome.getAbsolutePath());
+
+
+        // write all the options to a temp file and use it ro run the process
+        Vector opts = getOptions();
+        String[] options = new String[ opts.size() ];
+        opts.copyInto(options);
+
+        optionsFile = createTmpFile();
+        generateOptionsFile(optionsFile, options);
+        Commandline.Argument args = cmdl.createArgument();
+        args.setLine("-arguments " + optionsFile.getAbsolutePath());
+    }
+
+
+    /** execute the process with a specific handler */
+    protected void _execute(ExecuteStreamHandler handler) throws BuildException {
+        // target has been checked as a .jj, see if there is a matching
+        // java file and if it is needed to run to process the grammar
+        String pathname = target.getAbsolutePath();
+        int pos = pathname.length() - ".jj".length();
+        pathname = pathname.substring(0, pos) + ".java";
+        File javaFile = new File(pathname);
+        if (javaFile.exists() && target.lastModified() < javaFile.lastModified()) {
+            getProject().log("Target is already build - skipping (" + target + ")");
+            return;
+        }
+
+        final Execute process = new Execute(handler);
+        log(cmdl.describeCommand(), Project.MSG_VERBOSE);
+        process.setCommandline(cmdl.getCommandline());
+        try {
+            if (process.execute() != 0) {
+                throw new BuildException("Metamata task failed.");
+            }
+        } catch (IOException e) {
+            throw new BuildException("Failed to launch Metamata task: ", e);
+        }
+    }
+
+    /** clean up all the mess that we did with temporary objects */
+    protected void cleanUp() {
+        if (optionsFile != null) {
+            optionsFile.delete();
+            optionsFile = null;
+        }
+        if (cleanup) {
+            String name = target.getName();
+            int pos = name.length() - ".jj".length();
+            name = "__jj" + name.substring(0, pos) + ".sunjj";
+            final File sunjj = new File(target.getParent(), name);
+            if (sunjj.exists()) {
+                getProject().log("Removing stale file: " + sunjj.getName());
+                sunjj.delete();
+            }
+        }
+    }
+
+    /**
+     * return an array of files containing the path to the needed
+     * libraries to run metamata. The file are not checked for
+     * existence. You should do this yourself if needed or simply let the
+     * forked process do it for you.
+     * @return array of jars/zips needed to run metamata.
+     */
+    protected File[] getMetamataLibs() {
+        Vector files = new Vector();
+        files.addElement(new File(metamataHome, "lib/metamata.jar"));
+        files.addElement(new File(metamataHome, "bin/lib/JavaCC.zip"));
+
+        File[] array = new File[ files.size() ];
+        files.copyInto(array);
+        return array;
+    }
+
+
+    /**
+     * validate options set and resolve files and paths
+     * @throws BuildException thrown if an option has an incorrect state.
+     */
+    protected void checkOptions() throws BuildException {
+        // check that the home is ok.
+        if (metamataHome == null || !metamataHome.exists()) {
+            throw new BuildException("'metamatahome' must point to Metamata home directory.");
+        }
+        metamataHome = getProject().resolveFile(metamataHome.getPath());
+
+        // check that the needed jar exists.
+        File[] jars = getMetamataLibs();
+        for (int i = 0; i < jars.length; i++) {
+            if (!jars[i].exists()) {
+                throw new BuildException(jars[i]
+                    + " does not exist. Check your metamata installation.");
+            }
+        }
+
+        // check that the target is ok and resolve it.
+        if (target == null || !target.isFile()
+            || !target.getName().endsWith(".jj")) {
+            throw new BuildException("Invalid target: " + target);
+        }
+        target = getProject().resolveFile(target.getPath());
+    }
+
+    /**
+     * return all options of the command line as string elements
+     * @return an array of options corresponding to the setted options.
+     */
+    protected Vector getOptions() {
+        Vector options = new Vector();
+        if (verbose) {
+            options.addElement("-verbose");
+        }
+        if (debugscanner) {
+            options.addElement("-ds");
+        }
+        if (debugparser) {
+            options.addElement("-dp");
+        }
+        if (classPath != null) {
+            options.addElement("-classpath");
+            options.addElement(classPath.toString());
+        }
+        if (sourcePath != null) {
+            options.addElement("-sourcepath");
+            options.addElement(sourcePath.toString());
+        }
+        options.addElement(target.getAbsolutePath());
+        return options;
+    }
+
+    /**
+     * write all options to a file with one option / line
+     * @param tofile the file to write the options to.
+     * @param options the array of options element to write to the file.
+     * @throws BuildException thrown if there is a problem while writing
+     * to the file.
+     */
+    protected void generateOptionsFile(File tofile, String[] options) throws BuildException {
+        FileWriter fw = null;
+        try {
+            fw = new FileWriter(tofile);
+            PrintWriter pw = new PrintWriter(fw);
+            for (int i = 0; i < options.length; i++) {
+                pw.println(options[i]);
+            }
+            pw.flush();
+        } catch (IOException e) {
+            throw new BuildException("Error while writing options file " + tofile, e);
+        } finally {
+            if (fw != null) {
+                try {
+                    fw.close();
+                } catch (IOException ignored) {
+                    // ignore
+                }
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/DefaultNative2Ascii.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/DefaultNative2Ascii.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/DefaultNative2Ascii.java	(revision 16139)
@@ -0,0 +1,95 @@
+/*
+ * Copyright  2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.native2ascii;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.taskdefs.optional.Native2Ascii;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * encapsulates the handling common to diffent Native2Asciiadapter
+ * implementations.
+ *
+ * @since Ant 1.6.3
+ */
+public abstract class DefaultNative2Ascii implements Native2AsciiAdapter {
+
+    public DefaultNative2Ascii() {
+    }
+
+    /**
+     * Splits the task into setting up the command line switches
+     * (delegated to {@link #setup setup}), adding the file names
+     * (delegated to {@link #addFiles addFiles}) and running the tool
+     * (delegated to {@link #run run}).
+     */
+    public final boolean convert(Native2Ascii args, File srcFile,
+                                 File destFile) throws BuildException {
+        Commandline cmd = new Commandline();
+        setup(cmd, args);
+        addFiles(cmd, args, srcFile, destFile);
+        return run(cmd, args);
+    }
+    
+    /**
+     * Sets up the initial command line.
+     *
+     * <p>only the -encoding argument and nested arg elements get
+     * handled here.</p>
+     *
+     * @param cmd Command line to add to
+     * @param args provides the user-setting and access to Ant's
+     * logging system.
+     */
+    protected void setup(Commandline cmd, Native2Ascii args)
+        throws BuildException {
+        if (args.getEncoding() != null) {
+            cmd.createArgument().setValue("-encoding");
+            cmd.createArgument().setValue(args.getEncoding());
+        }
+        cmd.addArguments(args.getCurrentArgs());
+    }
+
+    /**
+     * Adds source and dest files to the command line.
+     *
+     * <p>This implementation adds them without any leading
+     * qualifiers, source first.</p>
+     *
+     * @param cmd Command line to add to
+     * @param log provides access to Ant's logging system.
+     * @param src the source file
+     * @param dest the destination file
+     */
+    protected void addFiles(Commandline cmd, ProjectComponent log, File src,
+                            File dest) throws BuildException {
+        cmd.createArgument().setFile(src);
+        cmd.createArgument().setFile(dest);
+    }
+
+    /**
+     * Executes the command.
+     *
+     * @param cmd Command line to execute
+     * @param log provides access to Ant's logging system.
+     * @return whether execution was successful
+     */
+    protected abstract boolean run(Commandline cmd, ProjectComponent log)
+        throws BuildException;
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/KaffeNative2Ascii.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/KaffeNative2Ascii.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/KaffeNative2Ascii.java	(revision 16139)
@@ -0,0 +1,84 @@
+/*
+ * Copyright  2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.native2ascii;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.taskdefs.ExecuteJava;
+import org.apache.tools.ant.taskdefs.optional.Native2Ascii;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Adapter to kaffe.tools.native2ascii.Native2Ascii.
+ *
+ * @since Ant 1.6.3
+ */
+public final class KaffeNative2Ascii extends DefaultNative2Ascii {
+
+    // sorted by newest Kaffe version first
+    private static final String[] N2A_CLASSNAMES = new String[] {
+        "gnu.classpath.tools.native2ascii.Native2Ascii",
+        // pre Kaffe 1.1.5
+        "kaffe.tools.native2ascii.Native2Ascii",
+    };
+
+    /**
+     * Identifies this adapter.
+     */
+    public static final String IMPLEMENTATION_NAME = "kaffe";
+
+    protected void setup(Commandline cmd, Native2Ascii args)
+        throws BuildException {
+        if (args.getReverse()) {
+            throw new BuildException("-reverse is not supported by Kaffe");
+        }
+        super.setup(cmd, args);
+    }
+
+    protected boolean run(Commandline cmd, ProjectComponent log)
+        throws BuildException {
+        ExecuteJava ej = new ExecuteJava();
+        Class c = getN2aClass();
+        if (c == null) {
+            throw new BuildException("Couldn't load Kaffe's Native2Ascii"
+                                     + " class");
+        }
+
+        cmd.setExecutable(c.getName());
+        ej.setJavaCommand(cmd);
+        ej.execute(log.getProject());
+        // otherwise ExecuteJava has thrown an exception
+        return true;
+    }
+
+    /**
+     * tries to load Kaffe Native2Ascii and falls back to the older
+     * class name if necessary.
+     *
+     * @return null if neither class can get loaded.
+     */
+    private static Class getN2aClass() {
+        for (int i = 0; i < N2A_CLASSNAMES.length; i++) {
+            try {
+                return Class.forName(N2A_CLASSNAMES[i]);
+            } catch (ClassNotFoundException cnfe) {
+            }
+        }
+        return null;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapter.java	(revision 16139)
@@ -0,0 +1,40 @@
+/*
+ * Copyright  2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.native2ascii;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.optional.Native2Ascii;
+
+/**
+ * Interface for an adapter to a native2ascii implementation.
+ *
+ * @since Ant 1.6.3
+ */
+public interface Native2AsciiAdapter {
+    /**
+     * Convert the encoding of srcFile writing to destFile.
+     *
+     * @param args Task that holds command line arguments and allows
+     * the implementation to send messages to Ant's logging system
+     * @param srcFile the source to convert
+     * @param destFile where to send output to
+     * @return whether the conversion has been successful.
+     */
+    boolean convert(Native2Ascii args, File srcFile, File destFile)
+        throws BuildException;
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapterFactory.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapterFactory.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/Native2AsciiAdapterFactory.java	(revision 16139)
@@ -0,0 +1,96 @@
+/*
+ * Copyright  2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.native2ascii;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Creates the Native2AsciiAdapter based on the user choice and
+ * potentially the VM vendor.
+ *
+ * @since Ant 1.6.3
+ */
+public class Native2AsciiAdapterFactory {
+
+    /**
+     * Determines the default choice of adapter based on the VM
+     * vendor.
+     *
+     * @return the default choice of adapter based on the VM
+     * vendor
+     */
+    public static String getDefault() {
+        if (JavaEnvUtils.isKaffe()) {
+            return KaffeNative2Ascii.IMPLEMENTATION_NAME;
+        }
+        return SunNative2Ascii.IMPLEMENTATION_NAME;
+    }
+
+    /**
+     * Creates the Native2AsciiAdapter based on the user choice and *
+     * potentially the VM vendor.
+     *
+     * @param choice the user choice (if any).
+     * @param log a ProjectComponent instance used to access Ant's
+     * logging system.
+     * @return The adapter to use.
+     */
+    public static Native2AsciiAdapter getAdapter(String choice,
+                                                 ProjectComponent log)
+        throws BuildException {
+        if ((JavaEnvUtils.isKaffe() && choice == null)
+            || KaffeNative2Ascii.IMPLEMENTATION_NAME.equals(choice)) {
+            return new KaffeNative2Ascii();
+        } else if (SunNative2Ascii.IMPLEMENTATION_NAME.equals(choice)) {
+            return new SunNative2Ascii();
+        } else if (choice != null) {
+            return resolveClassName(choice);
+        }
+
+        // This default has been good enough until Ant 1.6.3, so stick
+        // with it
+        return new SunNative2Ascii();
+    }
+
+    /**
+     * Tries to resolve the given classname into a native2ascii adapter.
+     * Throws a fit if it can't.
+     *
+     * @param className The fully qualified classname to be created.
+     * @throws BuildException This is the fit that is thrown if className
+     * isn't an instance of Native2AsciiAdapter.
+     */
+    private static Native2AsciiAdapter resolveClassName(String className)
+        throws BuildException {
+        try {
+            Class c = Class.forName(className);
+            Object o = c.newInstance();
+            return (Native2AsciiAdapter) o;
+        } catch (ClassNotFoundException cnfe) {
+            throw new BuildException("Can't load " + className, cnfe);
+        } catch (ClassCastException cce) {
+            throw new BuildException(className 
+                                     + " is not a Native2Ascii adapter", cce);
+        } catch (Throwable t) {
+            // for all other possibilities
+            throw new BuildException(className + " caused an interesting "
+                                     + "exception.", t);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/SunNative2Ascii.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/SunNative2Ascii.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/native2ascii/SunNative2Ascii.java	(revision 16139)
@@ -0,0 +1,67 @@
+/*
+ * Copyright  2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.native2ascii;
+
+import java.lang.reflect.Method;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.taskdefs.optional.Native2Ascii;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Adapter to sun.tools.native2ascii.Main.
+ *
+ * @since Ant 1.6.3
+ */
+public final class SunNative2Ascii extends DefaultNative2Ascii {
+
+    /**
+     * Identifies this adapter.
+     */
+    public static final String IMPLEMENTATION_NAME = "sun";
+
+    protected void setup(Commandline cmd, Native2Ascii args)
+        throws BuildException {
+        if (args.getReverse()) {
+            cmd.createArgument().setValue("-reverse");
+        }
+        super.setup(cmd, args);
+    }
+
+    protected boolean run(Commandline cmd, ProjectComponent log)
+        throws BuildException {
+        try {
+            Class n2aMain = Class.forName("sun.tools.native2ascii.Main");
+            Class[] param = new Class[] {String[].class};
+            Method convert = n2aMain.getMethod("convert", param);
+            if (convert == null) {
+                throw new BuildException("Could not find convert() method in "
+                                         + "sun.tools.native2ascii.Main");
+            }
+            Object o = n2aMain.newInstance();
+            return ((Boolean) convert.invoke(o, 
+                                             new Object[] {cmd.getArguments()})
+                    ).booleanValue();
+        } catch (BuildException ex) {
+            //rethrow
+            throw ex;
+        } catch (Exception ex) {
+            //wrap
+           throw new BuildException("Error starting Sun's native2ascii: ", ex);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/net/FTP.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/net/FTP.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/net/FTP.java	(revision 16139)
@@ -0,0 +1,2079 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.net;
+
+import org.apache.commons.net.ftp.FTPClient;
+import org.apache.commons.net.ftp.FTPFile;
+import org.apache.commons.net.ftp.FTPReply;
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Delete;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Basic FTP client. Performs the following actions:
+ * <ul>
+ *   <li> <strong>send</strong> - send files to a remote server. This is the
+ *   default action.</li>
+ *   <li> <strong>get</strong> - retrieve files from a remote server.</li>
+ *   <li> <strong>del</strong> - delete files from a remote server.</li>
+ *   <li> <strong>list</strong> - create a file listing.</li>
+ *   <li> <strong>chmod</strong> - change unix file permissions.</li>
+ *   <li> <strong>rmdir</strong> - remove directories, if empty, from a
+ *   remote server.</li>
+ * </ul>
+ * <strong>Note:</strong> Some FTP servers - notably the Solaris server - seem
+ * to hold data ports open after a "retr" operation, allowing them to timeout
+ * instead of shutting them down cleanly. This happens in active or passive
+ * mode, and the ports will remain open even after ending the FTP session. FTP
+ * "send" operations seem to close ports immediately. This behavior may cause
+ * problems on some systems when downloading large sets of files.
+ *
+ * @since Ant 1.3
+ */
+public class FTP
+     extends Task {
+    protected static final int SEND_FILES = 0;
+    protected static final int GET_FILES = 1;
+    protected static final int DEL_FILES = 2;
+    protected static final int LIST_FILES = 3;
+    protected static final int MK_DIR = 4;
+    protected static final int CHMOD = 5;
+    protected static final int RM_DIR = 6;
+    /** return code of ftp - not implemented in commons-net version 1.0 */
+    private static final int CODE_521 = 521;
+    /** Default port for FTP */
+    public static final int DEFAULT_FTP_PORT = 21;
+
+    private String remotedir;
+    private String server;
+    private String userid;
+    private String password;
+    private File listing;
+    private boolean binary = true;
+    private boolean passive = false;
+    private boolean verbose = false;
+    private boolean newerOnly = false;
+    private long timeDiffMillis = 0;
+    private boolean timeDiffAuto = false;
+    private int action = SEND_FILES;
+    private Vector filesets = new Vector();
+    private Vector dirCache = new Vector();
+    private int transferred = 0;
+    private String remoteFileSep = "/";
+    private int port = DEFAULT_FTP_PORT;
+    private boolean skipFailedTransfers = false;
+    private int skipped = 0;
+    private boolean ignoreNoncriticalErrors = false;
+    private boolean preserveLastModified = false;
+    private String chmod = null;
+    private String umask = null;
+    private FileUtils fileUtils = FileUtils.newFileUtils();
+
+    protected static final String[] ACTION_STRS = {
+        "sending",
+        "getting",
+        "deleting",
+        "listing",
+        "making directory",
+        "chmod",
+        "removing"
+        };
+
+    protected static final String[] COMPLETED_ACTION_STRS = {
+        "sent",
+        "retrieved",
+        "deleted",
+        "listed",
+        "created directory",
+        "mode changed",
+        "removed"
+        };
+
+    protected static final String[] ACTION_TARGET_STRS = {
+        "files",
+        "files",
+        "files",
+        "files",
+        "directory",
+        "files",
+        "directories"
+        };
+
+
+    /**
+     * internal class allowing to read the contents of a remote file system
+     * using the FTP protocol
+     * used in particular for ftp get operations
+     * differences with DirectoryScanner
+     * "" (the root of the fileset) is never included in the included directories
+     * followSymlinks defaults to false
+     */
+    protected class FTPDirectoryScanner extends DirectoryScanner {
+        protected FTPClient ftp = null;
+        private String rootPath = null;
+        /**
+         * since ant 1.6
+         * this flag should be set to true on UNIX and can save scanning time
+         */
+        private boolean remoteSystemCaseSensitive = false;
+        private boolean remoteSensitivityChecked = false;
+
+        /**
+         * constructor
+         * @param ftp  ftpclient object
+         */
+        public FTPDirectoryScanner(FTPClient ftp) {
+            super();
+            this.ftp = ftp;
+            this.setFollowSymlinks(false);
+        }
+
+
+        /**
+         * scans the remote directory,
+         * storing internally the included files, directories, ...
+         */
+        public void scan() {
+            if (includes == null) {
+                // No includes supplied, so set it to 'matches all'
+                includes = new String[1];
+                includes[0] = "**";
+            }
+            if (excludes == null) {
+                excludes = new String[0];
+            }
+
+            filesIncluded = new Vector();
+            filesNotIncluded = new Vector();
+            filesExcluded = new Vector();
+            dirsIncluded = new Vector();
+            dirsNotIncluded = new Vector();
+            dirsExcluded = new Vector();
+
+            try {
+                String cwd = ftp.printWorkingDirectory();
+                // always start from the current ftp working dir
+
+                checkIncludePatterns();
+                clearCaches();
+                ftp.changeWorkingDirectory(cwd);
+            } catch (IOException e) {
+                throw new BuildException("Unable to scan FTP server: ", e);
+            }
+        }
+
+
+        /**
+         * this routine is actually checking all the include patterns in
+         * order to avoid scanning everything under base dir
+         * @since ant1.6
+         */
+        private void checkIncludePatterns() {
+            Hashtable newroots = new Hashtable();
+            // put in the newroots vector the include patterns without
+            // wildcard tokens
+            for (int icounter = 0; icounter < includes.length; icounter++) {
+                String newpattern =
+                    SelectorUtils.rtrimWildcardTokens(includes[icounter]);
+                newroots.put(newpattern, includes[icounter]);
+            }
+            if (remotedir == null) {
+                try {
+                    remotedir = ftp.printWorkingDirectory();
+                } catch (IOException e) {
+                    throw new BuildException("could not read current ftp directory",
+                        getLocation());
+                }
+            }
+            AntFTPFile baseFTPFile = new AntFTPRootFile(ftp, remotedir);
+            rootPath = baseFTPFile.getAbsolutePath();
+            // construct it
+            if (newroots.containsKey("")) {
+                // we are going to scan everything anyway
+                scandir(rootPath, "", true);
+            } else {
+                // only scan directories that can include matched files or
+                // directories
+                Enumeration enum2 = newroots.keys();
+
+                while (enum2.hasMoreElements()) {
+                    String currentelement = (String) enum2.nextElement();
+                    String originalpattern = (String) newroots.get(currentelement);
+                    AntFTPFile myfile = new AntFTPFile(baseFTPFile, currentelement);
+                    boolean isOK = true;
+                    boolean traversesSymlinks = false;
+                    String path = null;
+
+                    if (myfile.exists()) {
+                        if (remoteSensitivityChecked
+                            && remoteSystemCaseSensitive && isFollowSymlinks()) {
+                            // cool case,
+                            //we do not need to scan all the subdirs in the relative path
+                            path = myfile.getFastRelativePath();
+                        } else {
+                            // may be on a case insensitive file system.  We want
+                            // the results to show what's really on the disk, so
+                            // we need to double check.
+                            try {
+                                path = myfile.getRelativePath();
+                                traversesSymlinks = myfile.isTraverseSymlinks();
+                            }  catch (IOException be) {
+                                throw new BuildException(be, getLocation());
+                            } catch (BuildException be) {
+                                isOK = false;
+
+                            }
+                        }
+                    } else {
+                        isOK = false;
+                    }
+                    if (isOK) {
+                        currentelement = path.replace(remoteFileSep.charAt(0), File.separatorChar);
+                        if (!isFollowSymlinks()
+                            && traversesSymlinks) {
+                            continue;
+                        }
+
+                        if (myfile.isDirectory()) {
+                            if (isIncluded(currentelement)
+                                && currentelement.length() > 0) {
+                                accountForIncludedDir(currentelement, myfile, true);
+                            }  else {
+                                if (currentelement.length() > 0) {
+                                    if (currentelement.charAt(currentelement
+                                                              .length() - 1)
+                                        != File.separatorChar) {
+                                        currentelement =
+                                            currentelement + File.separatorChar;
+                                    }
+                                }
+                                scandir(myfile.getAbsolutePath(), currentelement, true);
+                            }
+                        } else {
+                            if (isCaseSensitive
+                                && originalpattern.equals(currentelement)) {
+                                accountForIncludedFile(currentelement);
+                            } else if (!isCaseSensitive
+                                       && originalpattern
+                                       .equalsIgnoreCase(currentelement)) {
+                                accountForIncludedFile(currentelement);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        /**
+         * scans a particular directory
+         * @param dir directory to scan
+         * @param vpath  relative path to the base directory of the remote fileset
+         * always ended with a File.separator
+         * @param fast seems to be always true in practice
+         */
+        protected void scandir(String dir, String vpath, boolean fast) {
+            // avoid double scanning of directories, can only happen in fast mode
+            if (fast && hasBeenScanned(vpath)) {
+                return;
+            }
+            try {
+                if (!ftp.changeWorkingDirectory(dir)) {
+                    return;
+                }
+                String completePath = null;
+                if (!vpath.equals("")) {
+                    completePath = rootPath + remoteFileSep
+                        + vpath.replace(File.separatorChar, remoteFileSep.charAt(0));
+                } else {
+                    completePath = rootPath;
+                }
+                FTPFile[] newfiles = listFiles(completePath, false);
+
+                if (newfiles == null) {
+                    ftp.changeToParentDirectory();
+                    return;
+                }
+                for (int i = 0; i < newfiles.length; i++) {
+                    FTPFile file = newfiles[i];
+                    if (!file.getName().equals(".")
+                         && !file.getName().equals("..")) {
+                        if (isFunctioningAsDirectory(ftp, dir, file)) {
+                            String name = vpath + file.getName();
+                            boolean slowScanAllowed = true;
+                            if (!isFollowSymlinks() && file.isSymbolicLink()) {
+                                dirsExcluded.addElement(name);
+                                slowScanAllowed = false;
+                            } else if (isIncluded(name)) {
+                                accountForIncludedDir(name,
+                                    new AntFTPFile(ftp, file, completePath) , fast);
+                            } else {
+                                dirsNotIncluded.addElement(name);
+                                if (fast && couldHoldIncluded(name)) {
+                                    scandir(file.getName(),
+                                            name + File.separator, fast);
+                                }
+                            }
+                            if (!fast && slowScanAllowed) {
+                                scandir(file.getName(),
+                                        name + File.separator, fast);
+                            }
+                        } else {
+                            String name = vpath + file.getName();
+                            if (!isFollowSymlinks() && file.isSymbolicLink()) {
+                                filesExcluded.addElement(name);
+                            } else if (isFunctioningAsFile(ftp, dir, file)) {
+                                accountForIncludedFile(name);
+                            }
+                        }
+                    }
+                }
+                ftp.changeToParentDirectory();
+            } catch (IOException e) {
+                throw new BuildException("Error while communicating with FTP "
+                     + "server: ", e);
+            }
+        }
+        /**
+         * process included file
+         * @param name  path of the file relative to the directory of the fileset
+         */
+        private void accountForIncludedFile(String name) {
+            if (!filesIncluded.contains(name)
+                && !filesExcluded.contains(name)) {
+
+                if (isIncluded(name)) {
+                    if (!isExcluded(name)) {
+                        filesIncluded.addElement(name);
+                    } else {
+                        filesExcluded.addElement(name);
+                    }
+                } else {
+                    filesNotIncluded.addElement(name);
+                }
+            }
+        }
+
+        /**
+         *
+         * @param name path of the directory relative to the directory of
+         * the fileset
+         * @param file directory as file
+         * @param fast
+         */
+        private void accountForIncludedDir(String name, AntFTPFile file, boolean fast) {
+            if (!dirsIncluded.contains(name)
+                && !dirsExcluded.contains(name)) {
+
+                if (!isExcluded(name)) {
+                    if (fast) {
+                        if (file.isSymbolicLink()) {
+                            try {
+                                file.getClient().changeWorkingDirectory(file.curpwd);
+                            } catch (IOException ioe) {
+                                throw new BuildException("could not change directory to curpwd");
+                            }
+                            scandir(file.getLink(),
+                                name + File.separator, fast);
+                        } else {
+                            try {
+                                file.getClient().changeWorkingDirectory(file.curpwd);
+                            } catch (IOException ioe) {
+                                throw new BuildException("could not change directory to curpwd");
+                            }
+                            scandir(file.getName(),
+                                name + File.separator, fast);
+                        }
+                    }
+                    dirsIncluded.addElement(name);
+                } else {
+                    dirsExcluded.addElement(name);
+                    if (fast && couldHoldIncluded(name)) {
+                        try {
+                            file.getClient().changeWorkingDirectory(file.curpwd);
+                        } catch (IOException ioe) {
+                            throw new BuildException("could not change directory to curpwd");
+                        }
+                        scandir(file.getName(),
+                                name + File.separator, fast);
+                    }
+                }
+            }
+        }
+        /**
+         * temporary table to speed up the various scanning methods below
+         *
+         * @since Ant 1.6
+         */
+        private Map fileListMap = new HashMap();
+        /**
+         * List of all scanned directories.
+         *
+         * @since Ant 1.6
+         */
+        private Set scannedDirs = new HashSet();
+
+        /**
+         * Has the directory with the given path relative to the base
+         * directory already been scanned?
+         *
+         * <p>Registers the given directory as scanned as a side effect.</p>
+         *
+         * @since Ant 1.6
+         */
+        private boolean hasBeenScanned(String vpath) {
+            return !scannedDirs.add(vpath);
+        }
+
+        /**
+         * Clear internal caches.
+         *
+         * @since Ant 1.6
+         */
+        private void clearCaches() {
+            fileListMap.clear();
+            scannedDirs.clear();
+        }
+        /**
+         * list the files present in one directory.
+         * @param directory full path on the remote side
+         * @param changedir if true change to directory directory before listing
+         * @return array of FTPFile
+         */
+        public FTPFile[] listFiles(String directory, boolean changedir) {
+            //getProject().log("listing files in directory " + directory, Project.MSG_DEBUG);
+            String currentPath = directory;
+            if (changedir) {
+                try {
+                    boolean result = ftp.changeWorkingDirectory(directory);
+                    if (!result) {
+                        return null;
+                    }
+                    currentPath = ftp.printWorkingDirectory();
+                } catch (IOException ioe) {
+                    throw new BuildException(ioe, getLocation());
+                }
+            }
+            if (fileListMap.containsKey(currentPath)) {
+                getProject().log("filelist map used in listing files", Project.MSG_DEBUG);
+                return ((FTPFile[]) fileListMap.get(currentPath));
+            }
+            FTPFile[] result = null;
+            try {
+                result = ftp.listFiles();
+            } catch (IOException ioe) {
+                throw new BuildException(ioe, getLocation());
+            }
+            fileListMap.put(currentPath, result);
+            if (!remoteSensitivityChecked) {
+                checkRemoteSensitivity(result, directory);
+            }
+            return result;
+        }
+        /**
+         * cd into one directory and
+         * list the files present in one directory.
+         * @param directory full path on the remote side
+         * @return array of FTPFile
+         */
+        public FTPFile[] listFiles(String directory) {
+            return listFiles(directory, true);
+        }
+        private void checkRemoteSensitivity(FTPFile[] array, String directory) {
+            if (array == null) {
+                return;
+            }
+            boolean candidateFound = false;
+            String target = null;
+            for (int icounter = 0; icounter < array.length; icounter++) {
+                if (array[icounter].isDirectory()) {
+                    if (!array[icounter].getName().equals(".")
+                        && !array[icounter].getName().equals("..")) {
+                        candidateFound = true;
+                        target = fiddleName(array[icounter].getName());
+                        getProject().log("will try to cd to "
+                            + target + " where a directory called " + array[icounter].getName()
+                            + " exists", Project.MSG_DEBUG);
+                        for (int pcounter = 0; pcounter < array.length; pcounter++) {
+                            if (array[pcounter].getName().equals(target) && pcounter != icounter) {
+                                candidateFound = false;
+                            }
+                        }
+                        if (candidateFound) {
+                            break;
+                        }
+                    }
+                }
+            }
+            if (candidateFound) {
+                try {
+                    getProject().log("testing case sensitivity, attempting to cd to "
+                        + target, Project.MSG_DEBUG);
+                    remoteSystemCaseSensitive  = !ftp.changeWorkingDirectory(target);
+                } catch (IOException ioe) {
+                    remoteSystemCaseSensitive = true;
+                } finally {
+                    try {
+                        ftp.changeWorkingDirectory(directory);
+                    } catch (IOException ioe) {
+                        throw new BuildException(ioe, getLocation());
+                    }
+                }
+                getProject().log("remote system is case sensitive : " + remoteSystemCaseSensitive,
+                    Project.MSG_VERBOSE);
+                remoteSensitivityChecked = true;
+            }
+        }
+        private String fiddleName(String origin) {
+            StringBuffer result = new StringBuffer();
+            for (int icounter = 0; icounter < origin.length(); icounter++) {
+                if (Character.isLowerCase(origin.charAt(icounter))) {
+                    result.append(Character.toUpperCase(origin.charAt(icounter)));
+                } else if (Character.isUpperCase(origin.charAt(icounter))) {
+                    result.append(Character.toLowerCase(origin.charAt(icounter)));
+                } else {
+                    result.append(origin.charAt(icounter));
+                }
+            }
+            return result.toString();
+        }
+        /**
+         * an AntFTPFile is a representation of a remote file
+         * @since Ant 1.6
+         */
+        protected class AntFTPFile {
+            /**
+             * ftp client
+             */
+            private FTPClient client;
+            /**
+             * parent directory of the file
+             */
+            private String curpwd;
+            /**
+             * the file itself
+             */
+            private FTPFile ftpFile;
+            /**
+             *
+             */
+            private AntFTPFile parent = null;
+            private boolean relativePathCalculated = false;
+            private boolean traversesSymlinks = false;
+            private String relativePath = "";
+            /**
+             * constructor
+             * @param client ftp client variable
+             * @param ftpFile the file
+             * @param curpwd absolute remote path where the file is found
+             */
+            public AntFTPFile(FTPClient client, FTPFile ftpFile, String curpwd) {
+                this.client = client;
+                this.ftpFile = ftpFile;
+                this.curpwd = curpwd;
+            }
+            /**
+             * other constructor
+             * @param parent the parent file
+             * @param path  a relative path to the parent file
+             */
+            public AntFTPFile(AntFTPFile parent, String path) {
+                this.parent = parent;
+                this.client = parent.client;
+                Vector pathElements = SelectorUtils.tokenizePath(path);
+                try {
+                    boolean result = this.client.changeWorkingDirectory(parent.getAbsolutePath());
+                    //this should not happen, except if parent has been deleted by another process
+                    if (!result) {
+                        return;
+                    }
+                    this.curpwd = parent.getAbsolutePath();
+                } catch (IOException ioe) {
+                    throw new BuildException("could not change working dir to "
+                    + parent.curpwd);
+                }
+                for (int fcount = 0; fcount < pathElements.size() - 1; fcount++) {
+                    String currentPathElement = (String) pathElements.elementAt(fcount);
+                    try {
+                        boolean result = this.client.changeWorkingDirectory(currentPathElement);
+                        if (!result && !isCaseSensitive()
+                            && (remoteSystemCaseSensitive || !remoteSensitivityChecked)) {
+                           currentPathElement = findPathElementCaseUnsensitive(this.curpwd,
+                               currentPathElement);
+                            if (currentPathElement == null) {
+                                return;
+                            }
+                        } else if (!result) {
+                            return;
+                        }
+                        this.curpwd = this.curpwd + remoteFileSep
+                            + currentPathElement;
+                    } catch (IOException ioe) {
+                        throw new BuildException("could not change working dir to "
+                        + (String) pathElements.elementAt(fcount)
+                            + " from " + this.curpwd);
+                    }
+
+                }
+                String lastpathelement = (String) pathElements.elementAt(pathElements.size() - 1);
+                FTPFile [] theFiles = listFiles(this.curpwd);
+                this.ftpFile = getFile(theFiles, lastpathelement);
+            }
+            /**
+             * find a file in a directory in case unsensitive way
+             * @param parentPath        where we are
+             * @param soughtPathElement what is being sought
+             * @return                  the first file found or null if not found
+             */
+            private String findPathElementCaseUnsensitive(String parentPath,
+                               String soughtPathElement) {
+                // we are already in the right path, so the second parameter
+                // is false
+                FTPFile[] theFiles = listFiles(parentPath, false);
+                if (theFiles == null) {
+                    return null;
+                }
+                for (int icounter = 0; icounter < theFiles.length; icounter++) {
+                    if (theFiles[icounter].getName().equalsIgnoreCase(soughtPathElement)) {
+                        return theFiles[icounter].getName();
+                    }
+                }
+                return null;
+            }
+            /**
+             * find out if the file exists
+             * @return  true if the file exists
+             */
+            public boolean exists() {
+                return (ftpFile != null);
+            }
+            /**
+             * if the file is a symbolic link, find out to what it is pointing
+             * @return the target of the symbolic link
+             */
+            public String getLink() {
+                return ftpFile.getLink();
+            }
+            /**
+             * get the name of the file
+             * @return the name of the file
+             */
+            public String getName() {
+                return ftpFile.getName();
+            }
+            /**
+             * find out the absolute path of the file
+             * @return absolute path as string
+             */
+            public String getAbsolutePath() {
+                return curpwd + remoteFileSep + ftpFile.getName();
+            }
+            /**
+             * find out the relative path assuming that the path used to construct
+             * this AntFTPFile was spelled properly with regards to case.
+             * This is OK on a case sensitive system such as UNIX
+             * @return relative path
+             */
+            public String getFastRelativePath() {
+                String absPath = getAbsolutePath();
+                if (absPath.indexOf(rootPath + remoteFileSep) == 0) {
+                    return absPath.substring(rootPath.length() + remoteFileSep.length());
+                }
+                return null;
+            }
+            /**
+             * find out the relative path to the rootPath of the enclosing scanner.
+             * this relative path is spelled exactly like on disk,
+             * for instance if the AntFTPFile has been instantiated as ALPHA,
+             * but the file is really called alpha, this method will return alpha.
+             * If a symbolic link is encountered, it is followed, but the name of the link
+             * rather than the name of the target is returned.
+             * (ie does not behave like File.getCanonicalPath())
+             * @return                relative path, separated by remoteFileSep
+             * @throws IOException    if a change directory fails, ...
+             * @throws BuildException if one of the components of the relative path cannot
+             * be found.
+             */
+            public String getRelativePath() throws IOException, BuildException {
+                if (!relativePathCalculated) {
+                    if (parent != null) {
+                        traversesSymlinks = parent.isTraverseSymlinks();
+                        relativePath = getRelativePath(parent.getAbsolutePath(),
+                            parent.getRelativePath());
+                    } else {
+                        relativePath = getRelativePath(rootPath, "");
+                        relativePathCalculated = true;
+                    }
+                }
+                return relativePath;
+            }
+            /**
+             * get thge relative path of this file
+             * @param currentPath          base path
+             * @param currentRelativePath  relative path of the base path with regards to remote dir
+             * @return relative path
+             */
+            private String getRelativePath(String currentPath, String currentRelativePath) {
+                Vector pathElements = SelectorUtils.tokenizePath(getAbsolutePath(), remoteFileSep);
+                Vector pathElements2 = SelectorUtils.tokenizePath(currentPath, remoteFileSep);
+                String relPath = currentRelativePath;
+                for (int pcount = pathElements2.size(); pcount < pathElements.size(); pcount++) {
+                    String currentElement = (String) pathElements.elementAt(pcount);
+                    FTPFile[] theFiles = listFiles(currentPath);
+                    FTPFile theFile = null;
+                    if (theFiles != null) {
+                        theFile = getFile(theFiles, currentElement);
+                    }
+                    if (theFile == null) {
+                        throw new BuildException("could not find " + currentElement
+                            + " from " + currentPath);
+                    } else {
+                        traversesSymlinks = traversesSymlinks || theFile.isSymbolicLink();
+                        if (!relPath.equals("")) {
+                            relPath = relPath + remoteFileSep;
+                        }
+                        relPath = relPath + theFile.getName();
+                        currentPath = currentPath + remoteFileSep + theFile.getName();
+                    }
+                }
+                return relPath;
+            }
+            /**
+             * find a file matching a string in an array of FTPFile.
+             * This method will find "alpha" when requested for "ALPHA"
+             * if and only if the caseSensitive attribute is set to false.
+             * When caseSensitive is set to true, only the exact match is returned.
+             * @param theFiles  array of files
+             * @param lastpathelement  the file name being sought
+             * @return null if the file cannot be found, otherwise return the matching file.
+             */
+            public FTPFile getFile(FTPFile[] theFiles, String lastpathelement) {
+                if (theFiles == null) {
+                    return null;
+                }
+                for (int fcount = 0; fcount < theFiles.length; fcount++) {
+                     if (theFiles[fcount].getName().equals(lastpathelement)) {
+                         return theFiles[fcount];
+                     } else if (!isCaseSensitive()
+                         && theFiles[fcount].getName().equalsIgnoreCase(lastpathelement)) {
+                         return theFiles[fcount];
+                     }
+                }
+                return null;
+            }
+            /**
+             * tell if a file is a directory.
+             * note that it will return false for symbolic links pointing to directories.
+             * @return <code>true</code> for directories
+             */
+            public boolean isDirectory() {
+                return ftpFile.isDirectory();
+            }
+            /**
+             * tell if a file is a symbolic link
+             * @return <code>true</code> for symbolic links
+             */
+            public boolean isSymbolicLink() {
+                return ftpFile.isSymbolicLink();
+            }
+            /**
+             * return the attached FTP client object.
+             * Warning : this instance is really shared with the enclosing class.
+             * @return  FTP client
+             */
+            protected FTPClient getClient() {
+                return client;
+            }
+
+            /**
+             * sets the current path of an AntFTPFile
+             * @param curpwd the current path one wants to set
+             */
+            protected void setCurpwd(String curpwd) {
+                this.curpwd = curpwd;
+            }
+            /**
+             * returns the path of the directory containing the AntFTPFile.
+             * of the full path of the file itself in case of AntFTPRootFile
+             * @return parent directory of the AntFTPFile
+             */
+            public String getCurpwd() {
+                return curpwd;
+            }
+            /**
+             * find out if a symbolic link is encountered in the relative path of this file
+             * from rootPath.
+             * @return <code>true</code> if a symbolic link is encountered in the relative path.
+             * @throws IOException if one of the change directory or directory listing operations
+             * fails
+             * @throws BuildException if a path component in the relative path cannot be found.
+             */
+            public boolean isTraverseSymlinks() throws IOException, BuildException {
+                if (!relativePathCalculated) {
+                    // getRelativePath also finds about symlinks
+                    String relpath = getRelativePath();
+                }
+                return traversesSymlinks;
+            }
+        }
+        /**
+         * special class to represent the remote directory itself
+         * @since Ant 1.6
+         */
+        protected class AntFTPRootFile extends AntFTPFile {
+             private String remotedir;
+            /**
+             * constructor
+             * @param aclient FTP client
+             * @param remotedir remote directory
+             */
+             public AntFTPRootFile(FTPClient aclient, String remotedir) {
+                 super(aclient, null, remotedir);
+                 this.remotedir = remotedir;
+                 try {
+                     this.getClient().changeWorkingDirectory(this.remotedir);
+                     this.setCurpwd(this.getClient().printWorkingDirectory());
+                 } catch (IOException ioe) {
+                     throw new BuildException(ioe, getLocation());
+                 }
+             }
+            /**
+             * find the absolute path
+             * @return absolute path
+             */
+            public String getAbsolutePath() {
+                return this.getCurpwd();
+            }
+            /**
+             * find out the relative path to root
+             * @return empty string
+             * @throws BuildException actually never
+             * @throws IOException  actually never
+             */
+            public String getRelativePath() throws BuildException, IOException {
+                 return "";
+            }
+        }
+    }
+    /**
+     * check FTPFiles to check whether they function as directories too
+     * the FTPFile API seem to make directory and symbolic links incompatible
+     * we want to find out if we can cd to a symbolic link
+     * @param dir  the parent directory of the file to test
+     * @param file the file to test
+     * @return true if it is possible to cd to this directory
+     * @since ant 1.6
+     */
+    private boolean isFunctioningAsDirectory(FTPClient ftp, String dir, FTPFile file) {
+        boolean result = false;
+        String currentWorkingDir = null;
+        if (file.isDirectory()) {
+            return true;
+        } else if (file.isFile()) {
+            return false;
+        }
+        try {
+            currentWorkingDir = ftp.printWorkingDirectory();
+        } catch (IOException ioe) {
+            getProject().log("could not find current working directory " + dir
+                + " while checking a symlink",
+                Project.MSG_DEBUG);
+        }
+        if (currentWorkingDir != null) {
+            try {
+                result = ftp.changeWorkingDirectory(file.getLink());
+            } catch (IOException ioe) {
+                getProject().log("could not cd to " + file.getLink() + " while checking a symlink",
+                    Project.MSG_DEBUG);
+            }
+            if (result) {
+                boolean comeback = false;
+                try {
+                    comeback = ftp.changeWorkingDirectory(currentWorkingDir);
+                } catch (IOException ioe) {
+                    getProject().log("could not cd back to " + dir + " while checking a symlink",
+                        Project.MSG_ERR);
+                } finally {
+                    if (!comeback) {
+                        throw new BuildException("could not cd back to " + dir
+                            + " while checking a symlink");
+                    }
+                }
+            }
+        }
+        return result;
+    }
+    /**
+     * check FTPFiles to check whether they function as directories too
+     * the FTPFile API seem to make directory and symbolic links incompatible
+     * we want to find out if we can cd to a symbolic link
+     * @param dir  the parent directory of the file to test
+     * @param file the file to test
+     * @return true if it is possible to cd to this directory
+     * @since ant 1.6
+     */
+    private boolean isFunctioningAsFile(FTPClient ftp, String dir, FTPFile file) {
+        if (file.isDirectory()) {
+            return false;
+        } else if (file.isFile()) {
+            return true;
+        }
+        return !isFunctioningAsDirectory(ftp, dir, file);
+    }
+    /**
+     * Sets the remote directory where files will be placed. This may be a
+     * relative or absolute path, and must be in the path syntax expected by
+     * the remote server. No correction of path syntax will be performed.
+     *
+     * @param dir the remote directory name.
+     */
+    public void setRemotedir(String dir) {
+        this.remotedir = dir;
+    }
+
+
+    /**
+     * Sets the FTP server to send files to.
+     *
+     * @param server the remote server name.
+     */
+    public void setServer(String server) {
+        this.server = server;
+    }
+
+
+    /**
+     * Sets the FTP port used by the remote server.
+     *
+     * @param port the port on which the remote server is listening.
+     */
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+
+    /**
+     * Sets the login user id to use on the specified server.
+     *
+     * @param userid remote system userid.
+     */
+    public void setUserid(String userid) {
+        this.userid = userid;
+    }
+
+
+    /**
+     * Sets the login password for the given user id.
+     *
+     * @param password the password on the remote system.
+     */
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+
+    /**
+     * If true, uses binary mode, otherwise text mode (default is binary).
+     *
+     * @param binary if true use binary mode in transfers.
+     */
+    public void setBinary(boolean binary) {
+        this.binary = binary;
+    }
+
+
+    /**
+     * Specifies whether to use passive mode. Set to true if you are behind a
+     * firewall and cannot connect without it. Passive mode is disabled by
+     * default.
+     *
+     * @param passive true is passive mode should be used.
+     */
+    public void setPassive(boolean passive) {
+        this.passive = passive;
+    }
+
+
+    /**
+     * Set to true to receive notification about each file as it is
+     * transferred.
+     *
+     * @param verbose true if verbose notifications are required.
+     */
+    public void setVerbose(boolean verbose) {
+        this.verbose = verbose;
+    }
+
+
+    /**
+     * A synonym for <tt>depends</tt>. Set to true to transmit only new
+     * or changed files.
+     *
+     * See the related attributes timediffmillis and timediffauto.
+     *
+     * @param newer if true only transfer newer files.
+     */
+    public void setNewer(boolean newer) {
+        this.newerOnly = newer;
+    }
+
+    /**
+     * number of milliseconds to add to the time on the remote machine
+     * to get the time on the local machine.
+     *
+     * use in conjunction with <code>newer</code>
+     *
+     * @param timeDiffMillis number of milliseconds
+     *
+     * @since ant 1.6
+     */
+    public void setTimeDiffMillis(long timeDiffMillis) {
+        this.timeDiffMillis = timeDiffMillis;
+    }
+
+    /**
+     * &quot;true&quot; to find out automatically the time difference
+     * between local and remote machine.
+     *
+     * This requires right to create
+     * and delete a temporary file in the remote directory.
+     *
+     * @param timeDiffAuto true = find automatically the time diff
+     *
+     * @since ant 1.6
+     */
+    public void setTimeDiffAuto(boolean timeDiffAuto) {
+        this.timeDiffAuto = timeDiffAuto;
+    }
+
+    /**
+     * Set to true to preserve modification times for "gotten" files.
+     *
+     * @param preserveLastModified if true preserver modification times.
+     */
+    public void setPreserveLastModified(boolean preserveLastModified) {
+        this.preserveLastModified = preserveLastModified;
+    }
+
+
+    /**
+     * Set to true to transmit only files that are new or changed from their
+     * remote counterparts. The default is to transmit all files.
+     *
+     * @param depends if true only transfer newer files.
+     */
+    public void setDepends(boolean depends) {
+        this.newerOnly = depends;
+    }
+
+
+    /**
+     * Sets the remote file separator character. This normally defaults to the
+     * Unix standard forward slash, but can be manually overridden using this
+     * call if the remote server requires some other separator. Only the first
+     * character of the string is used.
+     *
+     * @param separator the file separator on the remote system.
+     */
+    public void setSeparator(String separator) {
+        remoteFileSep = separator;
+    }
+
+
+    /**
+     * Sets the file permission mode (Unix only) for files sent to the
+     * server.
+     *
+     * @param theMode unix style file mode for the files sent to the remote
+     *        system.
+     */
+    public void setChmod(String theMode) {
+        this.chmod = theMode;
+    }
+
+
+    /**
+     * Sets the default mask for file creation on a unix server.
+     *
+     * @param theUmask unix style umask for files created on the remote server.
+     */
+    public void setUmask(String theUmask) {
+        this.umask = theUmask;
+    }
+
+
+    /**
+     *  A set of files to upload or download
+     *
+     * @param set the set of files to be added to the list of files to be
+     *        transferred.
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+
+    /**
+     * Sets the FTP action to be taken. Currently accepts "put", "get", "del",
+     * "mkdir" and "list".
+     *
+     * @deprecated setAction(String) is deprecated and is replaced with
+     *      setAction(FTP.Action) to make Ant's Introspection mechanism do the
+     *      work and also to encapsulate operations on the type in its own
+     *      class.
+     * @ant.attribute ignore="true"
+     *
+     * @param action the FTP action to be performed.
+     *
+     * @throws BuildException if the action is not a valid action.
+     */
+    public void setAction(String action) throws BuildException {
+        log("DEPRECATED - The setAction(String) method has been deprecated."
+             + " Use setAction(FTP.Action) instead.");
+
+        Action a = new Action();
+
+        a.setValue(action);
+        this.action = a.getAction();
+    }
+
+
+    /**
+     * Sets the FTP action to be taken. Currently accepts "put", "get", "del",
+     * "mkdir", "chmod" and "list".
+     *
+     * @param action the FTP action to be performed.
+     *
+     * @throws BuildException if the action is not a valid action.
+     */
+    public void setAction(Action action) throws BuildException {
+        this.action = action.getAction();
+    }
+
+
+    /**
+     * The output file for the "list" action. This attribute is ignored for
+     * any other actions.
+     *
+     * @param listing file in which to store the listing.
+     */
+    public void setListing(File listing) {
+        this.listing = listing;
+    }
+
+
+    /**
+     * If true, enables unsuccessful file put, delete and get
+     * operations to be skipped with a warning and the remainder
+     * of the files still transferred.
+     *
+     * @param skipFailedTransfers true if failures in transfers are ignored.
+     */
+    public void setSkipFailedTransfers(boolean skipFailedTransfers) {
+        this.skipFailedTransfers = skipFailedTransfers;
+    }
+
+
+    /**
+     * set the flag to skip errors on directory creation.
+     * (and maybe later other server specific errors)
+     *
+     * @param ignoreNoncriticalErrors true if non-critical errors should not
+     *        cause a failure.
+     */
+    public void setIgnoreNoncriticalErrors(boolean ignoreNoncriticalErrors) {
+        this.ignoreNoncriticalErrors = ignoreNoncriticalErrors;
+    }
+
+
+    /**
+     * Checks to see that all required parameters are set.
+     *
+     * @throws BuildException if the configuration is not valid.
+     */
+    protected void checkConfiguration() throws BuildException {
+        if (server == null) {
+            throw new BuildException("server attribute must be set!");
+        }
+        if (userid == null) {
+            throw new BuildException("userid attribute must be set!");
+        }
+        if (password == null) {
+            throw new BuildException("password attribute must be set!");
+        }
+
+        if ((action == LIST_FILES) && (listing == null)) {
+            throw new BuildException("listing attribute must be set for list "
+                 + "action!");
+        }
+
+        if (action == MK_DIR && remotedir == null) {
+            throw new BuildException("remotedir attribute must be set for "
+                 + "mkdir action!");
+        }
+
+        if (action == CHMOD && chmod == null) {
+            throw new BuildException("chmod attribute must be set for chmod "
+                 + "action!");
+        }
+    }
+
+
+    /**
+     * For each file in the fileset, do the appropriate action: send, get,
+     * delete, or list.
+     *
+     * @param ftp the FTPClient instance used to perform FTP actions
+     * @param fs the fileset on which the actions are performed.
+     *
+     * @return the number of files to be transferred.
+     *
+     * @throws IOException if there is a problem reading a file
+     * @throws BuildException if there is a problem in the configuration.
+     */
+    protected int transferFiles(FTPClient ftp, FileSet fs)
+         throws IOException, BuildException {
+        DirectoryScanner ds;
+
+        if (action == SEND_FILES) {
+            ds = fs.getDirectoryScanner(getProject());
+        } else {
+            // warn that selectors are not supported
+            if (fs.getSelectors(getProject()).length != 0) {
+                getProject().log("selectors are not supported in remote filesets",
+                    Project.MSG_WARN);
+            }
+            ds = new FTPDirectoryScanner(ftp);
+            fs.setupDirectoryScanner(ds, getProject());
+            ds.setFollowSymlinks(fs.isFollowSymlinks());
+            ds.scan();
+        }
+
+        String[] dsfiles = null;
+        if (action == RM_DIR) {
+            dsfiles = ds.getIncludedDirectories();
+        } else {
+            dsfiles = ds.getIncludedFiles();
+        }
+        String dir = null;
+
+        if ((ds.getBasedir() == null)
+             && ((action == SEND_FILES) || (action == GET_FILES))) {
+            throw new BuildException("the dir attribute must be set for send "
+                 + "and get actions");
+        } else {
+            if ((action == SEND_FILES) || (action == GET_FILES)) {
+                dir = ds.getBasedir().getAbsolutePath();
+            }
+        }
+
+        // If we are doing a listing, we need the output stream created now.
+        BufferedWriter bw = null;
+
+        try {
+            if (action == LIST_FILES) {
+                File pd = fileUtils.getParentFile(listing);
+
+                if (!pd.exists()) {
+                    pd.mkdirs();
+                }
+                bw = new BufferedWriter(new FileWriter(listing));
+            }
+            if (action == RM_DIR) {
+                // to remove directories, start by the end of the list
+                // the trunk does not let itself be removed before the leaves
+                for (int i = dsfiles.length - 1; i >= 0; i--) {
+                    rmDir(ftp, dsfiles[i]);
+                }
+            }   else {
+                for (int i = 0; i < dsfiles.length; i++) {
+                    switch (action) {
+                        case SEND_FILES:
+                            sendFile(ftp, dir, dsfiles[i]);
+                            break;
+                        case GET_FILES:
+                            getFile(ftp, dir, dsfiles[i]);
+                            break;
+                        case DEL_FILES:
+                            delFile(ftp, dsfiles[i]);
+                            break;
+                        case LIST_FILES:
+                            listFile(ftp, bw, dsfiles[i]);
+                            break;
+                        case CHMOD:
+                            doSiteCommand(ftp, "chmod " + chmod + " " + resolveFile(dsfiles[i]));
+                            transferred++;
+                            break;
+                        default:
+                            throw new BuildException("unknown ftp action " + action);
+                    }
+                }
+            }
+        } finally {
+            if (bw != null) {
+                bw.close();
+            }
+        }
+
+        return dsfiles.length;
+    }
+
+
+    /**
+     * Sends all files specified by the configured filesets to the remote
+     * server.
+     *
+     * @param ftp the FTPClient instance used to perform FTP actions
+     *
+     * @throws IOException if there is a problem reading a file
+     * @throws BuildException if there is a problem in the configuration.
+     */
+    protected void transferFiles(FTPClient ftp)
+         throws IOException, BuildException {
+        transferred = 0;
+        skipped = 0;
+
+        if (filesets.size() == 0) {
+            throw new BuildException("at least one fileset must be specified.");
+        } else {
+            // get files from filesets
+            for (int i = 0; i < filesets.size(); i++) {
+                FileSet fs = (FileSet) filesets.elementAt(i);
+
+                if (fs != null) {
+                    transferFiles(ftp, fs);
+                }
+            }
+        }
+
+        log(transferred + " " + ACTION_TARGET_STRS[action] + " "
+            + COMPLETED_ACTION_STRS[action]);
+        if (skipped != 0) {
+            log(skipped + " " + ACTION_TARGET_STRS[action]
+                + " were not successfully " + COMPLETED_ACTION_STRS[action]);
+        }
+    }
+
+
+    /**
+     * Correct a file path to correspond to the remote host requirements. This
+     * implementation currently assumes that the remote end can handle
+     * Unix-style paths with forward-slash separators. This can be overridden
+     * with the <code>separator</code> task parameter. No attempt is made to
+     * determine what syntax is appropriate for the remote host.
+     *
+     * @param file the remote file name to be resolved
+     *
+     * @return the filename as it will appear on the server.
+     */
+    protected String resolveFile(String file) {
+        return file.replace(System.getProperty("file.separator").charAt(0),
+            remoteFileSep.charAt(0));
+    }
+
+
+    /**
+     * Creates all parent directories specified in a complete relative
+     * pathname. Attempts to create existing directories will not cause
+     * errors.
+     *
+     * @param ftp the FTP client instance to use to execute FTP actions on
+     *        the remote server.
+     * @param filename the name of the file whose parents should be created.
+     * @throws IOException under non documented circumstances
+     * @throws BuildException if it is impossible to cd to a remote directory
+     *
+     */
+    protected void createParents(FTPClient ftp, String filename)
+         throws IOException, BuildException {
+
+        File dir = new File(filename);
+        if (dirCache.contains(dir)) {
+            return;
+        }
+
+
+        Vector parents = new Vector();
+        String dirname;
+
+        while ((dirname = dir.getParent()) != null) {
+            File checkDir = new File(dirname);
+            if (dirCache.contains(checkDir)) {
+                break;
+            }
+            dir = checkDir;
+            parents.addElement(dir);
+        }
+
+        // find first non cached dir
+        int i = parents.size() - 1;
+
+        if (i >= 0) {
+            String cwd = ftp.printWorkingDirectory();
+            String parent = dir.getParent();
+            if (parent != null) {
+                if (!ftp.changeWorkingDirectory(resolveFile(parent))) {
+                    throw new BuildException("could not change to "
+                        + "directory: " + ftp.getReplyString());
+                }
+            }
+
+            while (i >= 0) {
+                dir = (File) parents.elementAt(i--);
+                // check if dir exists by trying to change into it.
+                if (!ftp.changeWorkingDirectory(dir.getName())) {
+                    // could not change to it - try to create it
+                    log("creating remote directory "
+                        + resolveFile(dir.getPath()), Project.MSG_VERBOSE);
+                    if (!ftp.makeDirectory(dir.getName())) {
+                        handleMkDirFailure(ftp);
+                    }
+                    if (!ftp.changeWorkingDirectory(dir.getName())) {
+                        throw new BuildException("could not change to "
+                            + "directory: " + ftp.getReplyString());
+                    }
+                }
+                dirCache.addElement(dir);
+            }
+            ftp.changeWorkingDirectory(cwd);
+        }
+    }
+    /**
+     * auto find the time difference between local and remote
+     * @param ftp handle to ftp client
+     * @return number of millis to add to remote time to make it comparable to local time
+     * @since ant 1.6
+     */
+    private long getTimeDiff(FTPClient ftp) {
+        long returnValue = 0;
+        File tempFile = findFileName(ftp);
+        try {
+            // create a local temporary file
+            fileUtils.createNewFile(tempFile);
+            long localTimeStamp = tempFile.lastModified();
+            BufferedInputStream instream = new BufferedInputStream(new FileInputStream(tempFile));
+            ftp.storeFile(tempFile.getName(), instream);
+            instream.close();
+            boolean success = FTPReply.isPositiveCompletion(ftp.getReplyCode());
+            if (success) {
+                FTPFile [] ftpFiles = ftp.listFiles(tempFile.getName());
+                if (ftpFiles.length == 1) {
+                    long remoteTimeStamp = ftpFiles[0].getTimestamp().getTime().getTime();
+                    returnValue = localTimeStamp - remoteTimeStamp;
+                }
+                ftp.deleteFile(ftpFiles[0].getName());
+            }
+            // delegate the deletion of the local temp file to the delete task
+            // because of race conditions occuring on Windows
+            Delete mydelete = (Delete) getProject().createTask("delete");
+            mydelete.setFile(tempFile.getCanonicalFile());
+            mydelete.execute();
+        } catch (Exception e) {
+            throw new BuildException(e, getLocation());
+        }
+        return returnValue;
+    }
+    /**
+     *  find a suitable name for local and remote temporary file
+     */
+    private File findFileName(FTPClient ftp) {
+        FTPFile [] theFiles = null;
+        final int maxIterations = 1000;
+        for (int counter = 1; counter < maxIterations; counter++) {
+            File localFile = fileUtils.createTempFile("ant" + Integer.toString(counter), ".tmp",
+                null);
+            String fileName = localFile.getName();
+            boolean found = false;
+            try {
+                if (counter == 1) {
+                    theFiles = ftp.listFiles();
+                }
+                for (int counter2 = 0; counter2 < theFiles.length; counter2++) {
+                    if (theFiles[counter2].getName().equals(fileName)) {
+                        found = true;
+                        break;
+                    }
+                }
+            } catch (IOException ioe) {
+                throw new BuildException(ioe, getLocation());
+            }
+            if (!found) {
+                localFile.deleteOnExit();
+                return localFile;
+            }
+        }
+        return null;
+    }
+    /**
+     * Checks to see if the remote file is current as compared with the local
+     * file. Returns true if the target file is up to date.
+     * @param ftp ftpclient
+     * @param localFile local file
+     * @param remoteFile remote file
+     * @return true if the target file is up to date
+     * @throws IOException  in unknown circumstances
+     * @throws BuildException if the date of the remote files cannot be found and the action is
+     * GET_FILES
+     */
+    protected boolean isUpToDate(FTPClient ftp, File localFile,
+                                 String remoteFile)
+         throws IOException, BuildException {
+        log("checking date for " + remoteFile, Project.MSG_VERBOSE);
+
+        FTPFile[] files = ftp.listFiles(remoteFile);
+
+        // For Microsoft's Ftp-Service an Array with length 0 is
+        // returned if configured to return listings in "MS-DOS"-Format
+        if (files == null || files.length == 0) {
+            // If we are sending files, then assume out of date.
+            // If we are getting files, then throw an error
+
+            if (action == SEND_FILES) {
+                log("Could not date test remote file: " + remoteFile
+                     + "assuming out of date.", Project.MSG_VERBOSE);
+                return false;
+            } else {
+                throw new BuildException("could not date test remote file: "
+                    + ftp.getReplyString());
+            }
+        }
+
+        long remoteTimestamp = files[0].getTimestamp().getTime().getTime();
+        long localTimestamp = localFile.lastModified();
+
+        if (this.action == SEND_FILES) {
+            return remoteTimestamp + timeDiffMillis >= localTimestamp;
+        } else {
+            return localTimestamp >= remoteTimestamp + timeDiffMillis;
+        }
+    }
+
+
+    /**
+    * Sends a site command to the ftp server
+    * @param ftp ftp client
+    * @param theCMD command to execute
+    * @throws IOException  in unknown circumstances
+    * @throws BuildException in unknown circumstances
+    */
+    protected void doSiteCommand(FTPClient ftp, String theCMD)
+         throws IOException, BuildException {
+        boolean rc;
+        String[] myReply = null;
+
+        log("Doing Site Command: " + theCMD, Project.MSG_VERBOSE);
+
+        rc = ftp.sendSiteCommand(theCMD);
+
+        if (!rc) {
+            log("Failed to issue Site Command: " + theCMD, Project.MSG_WARN);
+        } else {
+
+            myReply = ftp.getReplyStrings();
+
+            for (int x = 0; x < myReply.length; x++) {
+                if (myReply[x].indexOf("200") == -1) {
+                    log(myReply[x], Project.MSG_WARN);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Sends a single file to the remote host. <code>filename</code> may
+     * contain a relative path specification. When this is the case, <code>sendFile</code>
+     * will attempt to create any necessary parent directories before sending
+     * the file. The file will then be sent using the entire relative path
+     * spec - no attempt is made to change directories. It is anticipated that
+     * this may eventually cause problems with some FTP servers, but it
+     * simplifies the coding.
+     * @param ftp ftp client
+     * @param dir base directory of the file to be sent (local)
+     * @param filename relative path of the file to be send
+     *        locally relative to dir
+     *        remotely relative to the remotedir attribute
+     * @throws IOException  in unknown circumstances
+     * @throws BuildException in unknown circumstances
+     */
+    protected void sendFile(FTPClient ftp, String dir, String filename)
+         throws IOException, BuildException {
+        InputStream instream = null;
+
+        try {
+            // XXX - why not simply new File(dir, filename)?
+            File file = getProject().resolveFile(new File(dir, filename).getPath());
+
+            if (newerOnly && isUpToDate(ftp, file, resolveFile(filename))) {
+                return;
+            }
+
+            if (verbose) {
+                log("transferring " + file.getAbsolutePath());
+            }
+
+            instream = new BufferedInputStream(new FileInputStream(file));
+
+            createParents(ftp, filename);
+
+            ftp.storeFile(resolveFile(filename), instream);
+
+            boolean success = FTPReply.isPositiveCompletion(ftp.getReplyCode());
+
+            if (!success) {
+                String s = "could not put file: " + ftp.getReplyString();
+
+                if (skipFailedTransfers) {
+                    log(s, Project.MSG_WARN);
+                    skipped++;
+                } else {
+                    throw new BuildException(s);
+                }
+
+            } else {
+                // see if we should issue a chmod command
+                if (chmod != null) {
+                    doSiteCommand(ftp, "chmod " + chmod + " " + resolveFile(filename));
+                }
+                log("File " + file.getAbsolutePath() + " copied to " + server,
+                    Project.MSG_VERBOSE);
+                transferred++;
+            }
+        } finally {
+            if (instream != null) {
+                try {
+                    instream.close();
+                } catch (IOException ex) {
+                    // ignore it
+                }
+            }
+        }
+    }
+
+
+    /**
+     * Delete a file from the remote host.
+     * @param ftp ftp client
+     * @param filename file to delete
+     * @throws IOException  in unknown circumstances
+     * @throws BuildException if skipFailedTransfers is set to false
+     * and the deletion could not be done
+     */
+    protected void delFile(FTPClient ftp, String filename)
+         throws IOException, BuildException {
+        if (verbose) {
+            log("deleting " + filename);
+        }
+
+        if (!ftp.deleteFile(resolveFile(filename))) {
+            String s = "could not delete file: " + ftp.getReplyString();
+
+            if (skipFailedTransfers) {
+                log(s, Project.MSG_WARN);
+                skipped++;
+            } else {
+                throw new BuildException(s);
+            }
+        } else {
+            log("File " + filename + " deleted from " + server,
+                Project.MSG_VERBOSE);
+            transferred++;
+        }
+    }
+
+    /**
+     * Delete a directory, if empty, from the remote host.
+     * @param ftp ftp client
+     * @param dirname directory to delete
+     * @throws IOException  in unknown circumstances
+     * @throws BuildException if skipFailedTransfers is set to false
+     * and the deletion could not be done
+     */
+    protected void rmDir(FTPClient ftp, String dirname)
+         throws IOException, BuildException {
+        if (verbose) {
+            log("removing " + dirname);
+        }
+
+        if (!ftp.removeDirectory(resolveFile(dirname))) {
+            String s = "could not remove directory: " + ftp.getReplyString();
+
+            if (skipFailedTransfers) {
+                log(s, Project.MSG_WARN);
+                skipped++;
+            } else {
+                throw new BuildException(s);
+            }
+        } else {
+            log("Directory " + dirname + " removed from " + server,
+                Project.MSG_VERBOSE);
+            transferred++;
+        }
+    }
+
+
+    /**
+     * Retrieve a single file from the remote host. <code>filename</code> may
+     * contain a relative path specification. <p>
+     *
+     * The file will then be retreived using the entire relative path spec -
+     * no attempt is made to change directories. It is anticipated that this
+     * may eventually cause problems with some FTP servers, but it simplifies
+     * the coding.</p>
+     * @param ftp the ftp client
+     * @param dir local base directory to which the file should go back
+     * @param filename relative path of the file based upon the ftp remote directory
+     *        and/or the local base directory (dir)
+     * @throws IOException  in unknown circumstances
+     * @throws BuildException if skipFailedTransfers is false
+     * and the file cannot be retrieved.
+     */
+    protected void getFile(FTPClient ftp, String dir, String filename)
+         throws IOException, BuildException {
+        OutputStream outstream = null;
+
+        try {
+            File file = getProject().resolveFile(new File(dir, filename).getPath());
+
+            if (newerOnly && isUpToDate(ftp, file, resolveFile(filename))) {
+                return;
+            }
+
+            if (verbose) {
+                log("transferring " + filename + " to "
+                     + file.getAbsolutePath());
+            }
+
+            File pdir = fileUtils.getParentFile(file);
+
+            if (!pdir.exists()) {
+                pdir.mkdirs();
+            }
+            outstream = new BufferedOutputStream(new FileOutputStream(file));
+            ftp.retrieveFile(resolveFile(filename), outstream);
+
+            if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+                String s = "could not get file: " + ftp.getReplyString();
+
+                if (skipFailedTransfers) {
+                    log(s, Project.MSG_WARN);
+                    skipped++;
+                } else {
+                    throw new BuildException(s);
+                }
+
+            } else {
+                log("File " + file.getAbsolutePath() + " copied from "
+                     + server, Project.MSG_VERBOSE);
+                transferred++;
+                if (preserveLastModified) {
+                    outstream.close();
+                    outstream = null;
+                    FTPFile[] remote = ftp.listFiles(resolveFile(filename));
+                    if (remote.length > 0) {
+                        fileUtils.setFileLastModified(file,
+                                                      remote[0].getTimestamp()
+                                                      .getTime().getTime());
+                    }
+                }
+            }
+        } finally {
+            if (outstream != null) {
+                try {
+                    outstream.close();
+                } catch (IOException ex) {
+                    // ignore it
+                }
+            }
+        }
+    }
+
+
+    /**
+     * List information about a single file from the remote host. <code>filename</code>
+     * may contain a relative path specification. <p>
+     *
+     * The file listing will then be retrieved using the entire relative path
+     * spec - no attempt is made to change directories. It is anticipated that
+     * this may eventually cause problems with some FTP servers, but it
+     * simplifies the coding.</p>
+     * @param ftp ftp client
+     * @param bw buffered writer
+     * @param filename the directory one wants to list
+     * @throws IOException  in unknown circumstances
+     * @throws BuildException in unknown circumstances
+     */
+    protected void listFile(FTPClient ftp, BufferedWriter bw, String filename)
+            throws IOException, BuildException {
+        if (verbose) {
+            log("listing " + filename);
+        }
+        FTPFile[] ftpfiles = ftp.listFiles(resolveFile(filename));
+
+        if (ftpfiles != null && ftpfiles.length > 0) {
+            bw.write(ftpfiles[0].toString());
+            bw.newLine();
+            transferred++;
+        }
+    }
+
+
+    /**
+     * Create the specified directory on the remote host.
+     *
+     * @param ftp The FTP client connection
+     * @param dir The directory to create (format must be correct for host
+     *      type)
+     * @throws IOException  in unknown circumstances
+     * @throws BuildException if ignoreNoncriticalErrors has not been set to true
+     *         and a directory could not be created, for instance because it was
+     *         already existing. Precisely, the codes 521, 550 and 553 will trigger
+     *         a BuildException
+     */
+    protected void makeRemoteDir(FTPClient ftp, String dir)
+         throws IOException, BuildException {
+        String workingDirectory = ftp.printWorkingDirectory();
+        if (verbose) {
+            log("Creating directory: " + dir);
+        }
+        if (dir.indexOf("/") == 0) {
+            ftp.changeWorkingDirectory("/");
+        }
+        String subdir = new String();
+        StringTokenizer st = new StringTokenizer(dir, "/");
+        while (st.hasMoreTokens()) {
+            subdir = st.nextToken();
+            log("Checking " + subdir, Project.MSG_DEBUG);
+            if (!ftp.changeWorkingDirectory(subdir)) {
+                if (!ftp.makeDirectory(subdir)) {
+                    // codes 521, 550 and 553 can be produced by FTP Servers
+                    //  to indicate that an attempt to create a directory has
+                    //  failed because the directory already exists.
+                    int rc = ftp.getReplyCode();
+                    if (!(ignoreNoncriticalErrors
+                        && (rc == FTPReply.CODE_550 || rc == FTPReply.CODE_553
+                        || rc == CODE_521))) {
+                        throw new BuildException("could not create directory: "
+                            + ftp.getReplyString());
+                    }
+                    if (verbose) {
+                        log("Directory already exists");
+                    }
+                } else {
+                    if (verbose) {
+                        log("Directory created OK");
+                    }
+                    ftp.changeWorkingDirectory(subdir);
+                }
+            }
+        }
+        if (workingDirectory != null) {
+            ftp.changeWorkingDirectory(workingDirectory);
+        }
+    }
+
+    /**
+     * look at the response for a failed mkdir action, decide whether
+     * it matters or not. If it does, we throw an exception
+     * @param ftp current ftp connection
+     * @throws BuildException if this is an error to signal
+     */
+    private void handleMkDirFailure(FTPClient ftp)
+            throws BuildException {
+        int rc = ftp.getReplyCode();
+        if (!(ignoreNoncriticalErrors
+             && (rc == FTPReply.CODE_550 || rc == FTPReply.CODE_553 || rc == CODE_521))) {
+            throw new BuildException("could not create directory: "
+                + ftp.getReplyString());
+        }
+    }
+
+    /**
+     * Runs the task.
+     *
+     * @throws BuildException if the task fails or is not configured
+     *         correctly.
+     */
+    public void execute() throws BuildException {
+        checkConfiguration();
+
+        FTPClient ftp = null;
+
+        try {
+            log("Opening FTP connection to " + server, Project.MSG_VERBOSE);
+
+            ftp = new FTPClient();
+
+            ftp.connect(server, port);
+            if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+                throw new BuildException("FTP connection failed: "
+                     + ftp.getReplyString());
+            }
+
+            log("connected", Project.MSG_VERBOSE);
+            log("logging in to FTP server", Project.MSG_VERBOSE);
+
+            if (!ftp.login(userid, password)) {
+                throw new BuildException("Could not login to FTP server");
+            }
+
+            log("login succeeded", Project.MSG_VERBOSE);
+
+            if (binary) {
+                ftp.setFileType(org.apache.commons.net.ftp.FTP.IMAGE_FILE_TYPE);
+                if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+                    throw new BuildException("could not set transfer type: "
+                        + ftp.getReplyString());
+                }
+            } else {
+                ftp.setFileType(org.apache.commons.net.ftp.FTP.ASCII_FILE_TYPE);
+                if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+                    throw new BuildException("could not set transfer type: "
+                        + ftp.getReplyString());
+                }
+            }
+
+            if (passive) {
+                log("entering passive mode", Project.MSG_VERBOSE);
+                ftp.enterLocalPassiveMode();
+                if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+                    throw new BuildException("could not enter into passive "
+                         + "mode: " + ftp.getReplyString());
+                }
+            }
+
+            // For a unix ftp server you can set the default mask for all files
+            // created.
+
+            if (umask != null) {
+                doSiteCommand(ftp, "umask " + umask);
+            }
+
+            // If the action is MK_DIR, then the specified remote
+            // directory is the directory to create.
+
+            if (action == MK_DIR) {
+                makeRemoteDir(ftp, remotedir);
+            } else {
+                if (remotedir != null) {
+                    log("changing the remote directory", Project.MSG_VERBOSE);
+                    ftp.changeWorkingDirectory(remotedir);
+                    if (!FTPReply.isPositiveCompletion(ftp.getReplyCode())) {
+                        throw new BuildException("could not change remote "
+                             + "directory: " + ftp.getReplyString());
+                    }
+                }
+                if (newerOnly && timeDiffAuto) {
+                // in this case we want to find how much time span there is between local
+                // and remote
+                    timeDiffMillis = getTimeDiff(ftp);
+                }
+                log(ACTION_STRS[action] + " " + ACTION_TARGET_STRS[action]);
+                transferFiles(ftp);
+            }
+
+        } catch (IOException ex) {
+            throw new BuildException("error during FTP transfer: " + ex);
+        } finally {
+            if (ftp != null && ftp.isConnected()) {
+                try {
+                    log("disconnecting", Project.MSG_VERBOSE);
+                    ftp.logout();
+                    ftp.disconnect();
+                } catch (IOException ex) {
+                    // ignore it
+                }
+            }
+        }
+    }
+
+
+    /**
+     * an action to perform, one of
+     * "send", "put", "recv", "get", "del", "delete", "list", "mkdir", "chmod",
+     * "rmdir"
+     */
+    public static class Action extends EnumeratedAttribute {
+
+        private static final String[] VALID_ACTIONS = {
+            "send", "put", "recv", "get", "del", "delete", "list", "mkdir",
+            "chmod", "rmdir"
+            };
+
+
+        /**
+         * Get the valid values
+         *
+         * @return an array of the valid FTP actions.
+         */
+        public String[] getValues() {
+            return VALID_ACTIONS;
+        }
+
+
+        /**
+         * Get the symbolic equivalent of the action value.
+         *
+         * @return the SYMBOL representing the given action.
+         */
+        public int getAction() {
+            String actionL = getValue().toLowerCase(Locale.US);
+
+            if (actionL.equals("send") || actionL.equals("put")) {
+                return SEND_FILES;
+            } else if (actionL.equals("recv") || actionL.equals("get")) {
+                return GET_FILES;
+            } else if (actionL.equals("del") || actionL.equals("delete")) {
+                return DEL_FILES;
+            } else if (actionL.equals("list")) {
+                return LIST_FILES;
+            } else if (actionL.equals("chmod")) {
+                return CHMOD;
+            } else if (actionL.equals("mkdir")) {
+                return MK_DIR;
+            } else if (actionL.equals("rmdir")) {
+                return RM_DIR;
+            }
+            return SEND_FILES;
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/net/MimeMail.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/net/MimeMail.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/net/MimeMail.java	(revision 16139)
@@ -0,0 +1,41 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.net;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.email.EmailTask;
+
+/**
+ * A task to send SMTP email; Use <tt>mail</tt> instead
+ *
+ * @deprecated Use {@link EmailTask} instead.
+ *
+ * @since Ant1.4
+ */
+public class MimeMail extends EmailTask {
+    /**
+     * Executes this build task.
+     *
+     * @exception BuildException On error.
+     */
+    public void execute()
+        throws BuildException {
+        log("DEPRECATED - The " + getTaskName() + " task is deprecated. "
+            + "Use the mail task instead.");
+        super.execute();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/net/RExecTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/net/RExecTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/net/RExecTask.java	(revision 16139)
@@ -0,0 +1,403 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.net;
+
+import org.apache.commons.net.bsd.RExecClient;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Calendar;
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Automates the rexec protocol.
+ *
+ * @since Ant 1.6
+ */
+
+public class RExecTask extends Task {
+    /**
+     *  The userid to login with, if automated login is used
+     */
+    private String userid  = null;
+
+    /**
+     *  The password to login with, if automated login is used
+     */
+    private String password = null;
+
+    /**
+     *  The command to execute
+     */
+    private String command = null;
+
+    /**
+     *  The server to connect to.
+     */
+    private String server  = null;
+
+    /**
+     *  The tcp port to connect to.
+     */
+    private int port = RExecClient.DEFAULT_PORT;
+
+    /**
+     *  The list of read/write commands for this session
+     */
+    private Vector rexecTasks = new Vector();
+
+    /**
+     *  If true, adds a CR to beginning of login script
+     */
+    private boolean addCarriageReturn = false;
+
+    /**
+     *  Default time allowed for waiting for a valid response
+     *  for all child reads.  A value of 0 means no limit.
+     */
+    private Integer defaultTimeout = null;
+
+    /**
+     *  This class is the parent of the Read and Write tasks.
+     *  It handles the common attributes for both.
+     */
+    public class RExecSubTask {
+        protected String taskString = "";
+        public void execute(AntRExecClient rexec)
+                throws BuildException {
+            throw new BuildException("Shouldn't be able instantiate a SubTask directly");
+        }
+
+        /**
+         *  the message as nested text
+         */
+        public void addText(String s) {
+            setString(getProject().replaceProperties(s));
+        }
+
+        /**
+         * the message as an attribute
+         */
+        public void setString(String s) {
+           taskString += s;
+        }
+    }
+
+    /**
+     *  Sends text to the connected server
+     */
+    public class RExecWrite extends RExecSubTask {
+        private boolean echoString = true;
+        public void execute(AntRExecClient rexec)
+               throws BuildException {
+           rexec.sendString(taskString, echoString);
+        }
+
+        /**
+         * Whether or not the message should be echoed to the log.
+         * Defaults to <code>true</code>.
+         */
+        public void setEcho(boolean b) {
+           echoString = b;
+        }
+    }
+
+    /**
+     *  Reads the output from the connected server
+     *  until the required string is found or we time out.
+     */
+    public class RExecRead extends RExecSubTask {
+        private Integer timeout = null;
+        public void execute(AntRExecClient rexec)
+               throws BuildException {
+            rexec.waitForString(taskString, timeout);
+        }
+        /**
+         *  a timeout value that overrides any task wide timeout.
+         */
+        public void setTimeout(Integer i) {
+           this.timeout = i;
+        }
+
+        /**
+         * Sets the default timeout if none has been set already
+         * @ant.attribute ignore="true"
+         */
+        public void setDefaultTimeout(Integer defaultTimeout) {
+           if (timeout == null) {
+              timeout = defaultTimeout;
+           }
+        }
+    }
+
+    /**
+     *  This class handles the abstraction of the rexec protocol.
+     *  Currently it is a wrapper around <a
+     *  href="http://jakarta.apache.org/commons/net/index.html">Jakarta
+     *  Commons Net</a>.
+     */
+    public class AntRExecClient extends RExecClient {
+        /**
+         * Read from the rexec session until the string we are
+         * waiting for is found
+         * @param s The string to wait on
+         */
+        public void waitForString(String s) {
+            waitForString(s, null);
+        }
+
+        /**
+         * Read from the rexec session until the string we are
+         * waiting for is found or the timeout has been reached
+         * @param s The string to wait on
+         * @param timeout The maximum number of seconds to wait
+         */
+        public void waitForString(String s, Integer timeout) {
+            InputStream is = this.getInputStream();
+            try {
+                StringBuffer sb = new StringBuffer();
+                if (timeout == null || timeout.intValue() == 0) {
+                    while (sb.toString().indexOf(s) == -1) {
+                        sb.append((char) is.read());
+                    }
+                } else {
+                    Calendar endTime = Calendar.getInstance();
+                    endTime.add(Calendar.SECOND, timeout.intValue());
+                    while (sb.toString().indexOf(s) == -1) {
+                        while (Calendar.getInstance().before(endTime)
+                            && is.available() == 0) {
+                            Thread.sleep(250);
+                        }
+                        if (is.available() == 0) {
+                            throw new BuildException(
+                                "Response timed-out waiting for \"" + s + '\"',
+                                getLocation());
+                        }
+                        sb.append((char) is.read());
+                    }
+                }
+                log(sb.toString(), Project.MSG_INFO);
+            } catch (BuildException be) {
+                throw be;
+            } catch (Exception e) {
+                throw new BuildException(e, getLocation());
+            }
+        }
+
+        /**
+         * Write this string to the rexec session.
+         * @param echoString  Logs string sent
+         */
+        public void sendString(String s, boolean echoString) {
+            OutputStream os = this.getOutputStream();
+            try {
+                os.write((s + "\n").getBytes());
+                if (echoString) {
+                    log(s, Project.MSG_INFO);
+                }
+                os.flush();
+            } catch (Exception e) {
+                throw new BuildException(e, getLocation());
+            }
+        }
+        /**
+         * Read from the rexec session until the EOF is found or
+         * the timeout has been reached
+         * @param timeout The maximum number of seconds to wait
+         */
+        public void waitForEOF(Integer timeout) {
+            InputStream is = this.getInputStream();
+            try {
+                StringBuffer sb = new StringBuffer();
+                if (timeout == null || timeout.intValue() == 0) {
+                int read;
+                    while ((read = is.read()) != -1) {
+                        char c = (char) read;
+                        sb.append(c);
+                        if (c == '\n') {
+                        log(sb.toString(), Project.MSG_INFO);
+                        sb.delete(0, sb.length());
+                        }
+                    }
+                } else {
+                    Calendar endTime = Calendar.getInstance();
+                    endTime.add(Calendar.SECOND, timeout.intValue());
+                int read = 0;
+                    while (read != -1) {
+                        while (Calendar.getInstance().before(endTime) && is.available() == 0) {
+                            Thread.sleep(250);
+                        }
+                        if (is.available() == 0) {
+                        log(sb.toString(), Project.MSG_INFO);
+                            throw new BuildException(
+                                                     "Response timed-out waiting for EOF",
+                                                     getLocation());
+                        }
+                        read =  is.read();
+                        if (read != -1) {
+                        char c = (char) read;
+                        sb.append(c);
+                        if (c == '\n') {
+                                log(sb.toString(), Project.MSG_INFO);
+                                sb.delete(0, sb.length());
+                        }
+                        }
+                    }
+                }
+                if (sb.length() > 0) {
+                log(sb.toString(), Project.MSG_INFO);
+                }
+            } catch (BuildException be) {
+                throw be;
+            } catch (Exception e) {
+                throw new BuildException(e, getLocation());
+            }
+        }
+
+    }
+    /**
+     *  A string to wait for from the server.
+     *  A subTask &lt;read&gt; tag was found.  Create the object,
+     *  Save it in our list, and return it.
+     */
+
+    public RExecSubTask createRead() {
+        RExecSubTask task = (RExecSubTask) new RExecRead();
+        rexecTasks.addElement(task);
+        return task;
+    }
+    /**
+     *  Add text to send to the server
+     *  A subTask &lt;write&gt; tag was found.  Create the object,
+     *  Save it in our list, and return it.
+     */
+    public RExecSubTask createWrite() {
+        RExecSubTask task = (RExecSubTask) new RExecWrite();
+        rexecTasks.addElement(task);
+        return task;
+    }
+    /**
+     *  Verify that all parameters are included.
+     *  Connect and possibly login
+     *  Iterate through the list of Reads and writes
+     */
+    public void execute() throws BuildException {
+        /**  A server name is required to continue */
+        if (server == null) {
+            throw new BuildException("No Server Specified");
+        }
+        /**  A userid and password must appear together
+         *   if they appear.  They are not required.
+         */
+        if (userid == null && password != null) {
+            throw new BuildException("No Userid Specified");
+        }
+        if (password == null && userid != null) {
+            throw new BuildException("No Password Specified");
+        }
+
+        /**  Create the telnet client object */
+        AntRExecClient rexec = null;
+        try {
+            rexec = new AntRExecClient();
+            try {
+                rexec.connect(server, port);
+            } catch (IOException e) {
+                throw new BuildException("Can't connect to " + server);
+            }
+            /**  Login if userid and password were specified */
+            if (userid != null && password != null) {
+                login(rexec);
+            }
+            /**  Process each sub command */
+            Enumeration tasksToRun = rexecTasks.elements();
+            while (tasksToRun != null && tasksToRun.hasMoreElements()) {
+                RExecSubTask task = (RExecSubTask) tasksToRun.nextElement();
+                if (task instanceof RExecRead && defaultTimeout != null) {
+                    ((RExecRead) task).setDefaultTimeout(defaultTimeout);
+                }
+                task.execute(rexec);
+            }
+
+            /** Keep reading input stream until end of it or time-out */
+            rexec.waitForEOF(defaultTimeout);
+       } finally {
+            if (rexec != null && rexec.isConnected()) {
+                try {
+                    rexec.disconnect();
+                } catch (IOException e) {
+                    throw new BuildException("Error disconnecting from "
+                                             + server);
+                }
+            }
+        }
+    }
+    /**
+     *  Process a 'typical' login.  If it differs, use the read
+     *  and write tasks explicitely
+     */
+    private void login(AntRExecClient rexec) {
+        if (addCarriageReturn) {
+            rexec.sendString("\n", true);
+        }
+        rexec.waitForString("ogin:");
+        rexec.sendString(userid, true);
+        rexec.waitForString("assword:");
+        rexec.sendString(password, false);
+    }
+    /**
+     * Set the the comand to execute on the server;
+     */
+    public void setCommand(String c) { this.command = c; }
+    /**
+     *  send a carriage return after connecting; optional, defaults to false.
+     */
+    public void setInitialCR(boolean b) {
+        this.addCarriageReturn = b;
+    }
+    /**
+     *  Set the the login password to use
+     * required if <tt>userid</tt> is set.
+     */
+    public void setPassword(String p) { this.password = p; }
+    /**
+     *  Set the tcp port to connect to; default is 23.
+     */
+    public void setPort(int p) { this.port = p; }
+    /**
+     *  Set the hostname or address of the remote server.
+     */
+    public void setServer(String m) { this.server = m; }
+    /**
+     * set a default timeout in seconds to wait for a response,
+     * zero means forever (the default)
+     */
+    public void setTimeout(Integer i) {
+        this.defaultTimeout = i;
+    }
+    /**
+     * Set the the login id to use on the server;
+     * required if <tt>password</tt> is set.
+     */
+    public void setUserid(String u) { this.userid = u; }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/net/SetProxy.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/net/SetProxy.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/net/SetProxy.java	(revision 16139)
@@ -0,0 +1,313 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.net;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.Authenticator;
+import java.net.PasswordAuthentication;
+import java.util.Properties;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Sets Java's web proxy properties, so that tasks and code run in
+ * the same JVM can have through-the-firewall access to remote web sites,
+ * and remote ftp sites.
+ * You can nominate an http and ftp proxy, or a socks server, reset the server
+ * settings, or do nothing at all.
+ * <p>
+ * Examples
+ * <pre>&lt;setproxy/&gt;</pre>
+ * do nothing
+ * <pre>&lt;setproxy proxyhost="firewall"/&gt;</pre>
+ * set the proxy to firewall:80
+ * <pre>&lt;setproxy proxyhost="firewall" proxyport="81"/&gt;</pre>
+ * set the proxy to firewall:81
+ * <pre>&lt;setproxy proxyhost=""/&gt;</pre>
+ * stop using the http proxy; don't change the socks settings
+ * <pre>&lt;setproxy socksproxyhost="socksy"/&gt;</pre>
+ * use socks via socksy:1080
+ * <pre>&lt;setproxy socksproxyhost=""/&gt;</pre>
+ * stop using the socks server.
+ * <p>
+ * You can set a username and password for http with the <tt>proxyHost</tt>
+ * and <tt>proxyPassword</tt> attributes. On Java1.4 and above these can also be
+ * used against SOCKS5 servers.
+ * </p>
+ * @see <a href="http://java.sun.com/j2se/1.4/docs/guide/net/properties.html">
+ *  java 1.4 network property list</a>
+  *@since       Ant 1.5
+ * @ant.task category="network"
+ */
+public class SetProxy extends Task {
+
+    /**
+     * proxy details
+     */
+    protected String proxyHost = null;
+
+    /**
+     * name of proxy port
+     */
+    protected int proxyPort = 80;
+
+    /**
+     * socks host.
+     */
+    private String socksProxyHost = null;
+
+    /**
+     * Socks proxy port. Default is 1080.
+     */
+    private int socksProxyPort = 1080;
+
+
+    /**
+     * list of non proxy hosts
+     */
+    private String nonProxyHosts = null;
+
+    /**
+     * user for http only
+     */
+    private String proxyUser = null;
+
+    /**
+     * password for http only
+     */
+    private String proxyPassword = null;
+
+    /**
+     * the HTTP/ftp proxy host. Set this to "" for the http proxy
+     * option to be disabled
+     *
+     * @param hostname the new proxy hostname
+     */
+    public void setProxyHost(String hostname) {
+        proxyHost = hostname;
+    }
+
+
+    /**
+     * the HTTP/ftp proxy port number; default is 80
+     *
+     * @param port port number of the proxy
+     */
+    public void setProxyPort(int port) {
+        proxyPort = port;
+    }
+
+    /**
+     * The name of a Socks server. Set to "" to turn socks
+     * proxying off.
+     *
+     * @param host The new SocksProxyHost value
+     */
+    public void setSocksProxyHost(String host) {
+        this.socksProxyHost = host;
+    }
+
+
+    /**
+     * Set the ProxyPort for socks connections. The default value is 1080
+     *
+     * @param port The new SocksProxyPort value
+     */
+    public void setSocksProxyPort(int port) {
+        this.socksProxyPort = port;
+    }
+
+
+    /**
+     * A list of hosts to bypass the proxy on. These should be separated
+     * with the vertical bar character '|'. Only in Java 1.4 does ftp use
+     * this list.
+     * e.g. fozbot.corp.sun.com|*.eng.sun.com
+     * @param nonProxyHosts lists of hosts to talk direct to
+     */
+    public void setNonProxyHosts(String nonProxyHosts) {
+        this.nonProxyHosts = nonProxyHosts;
+    }
+
+    /**
+     * set the proxy user. Probably requires a password to accompany this
+     * setting. Default=""
+     * @param proxyUser username
+     * @since Ant1.6
+     */
+    public void setProxyUser(String proxyUser) {
+        this.proxyUser = proxyUser;
+    }
+
+    /**
+     * Set the password for the proxy. Used only if the proxyUser is set.
+     * @param proxyPassword password to go with the username
+     * @since Ant1.6
+     */
+    public void setProxyPassword(String proxyPassword) {
+        this.proxyPassword = proxyPassword;
+    }
+
+    /**
+     * if the proxy port and host settings are not null, then the settings
+     * get applied these settings last beyond the life of the object and
+     * apply to all network connections
+     * Relevant docs: buglist #4183340
+     */
+
+    public void applyWebProxySettings() {
+        boolean settingsChanged = false;
+        boolean enablingProxy = false;
+        Properties sysprops = System.getProperties();
+        if (proxyHost != null) {
+            settingsChanged = true;
+            if (proxyHost.length() != 0) {
+                traceSettingInfo();
+                enablingProxy = true;
+                sysprops.put("http.proxyHost", proxyHost);
+                String portString = Integer.toString(proxyPort);
+                sysprops.put("http.proxyPort", portString);
+                sysprops.put("https.proxyHost", proxyHost);
+                sysprops.put("https.proxyPort", portString);
+                sysprops.put("ftp.proxyHost", proxyHost);
+                sysprops.put("ftp.proxyPort", portString);
+                if (nonProxyHosts != null) {
+                    sysprops.put("http.nonProxyHosts", nonProxyHosts);
+                    sysprops.put("https.nonProxyHosts", nonProxyHosts);
+                    sysprops.put("ftp.nonProxyHosts", nonProxyHosts);
+                }
+                if (proxyUser != null) {
+                    sysprops.put("http.proxyUser", proxyUser);
+                    sysprops.put("http.proxyPassword", proxyPassword);
+                }
+            } else {
+                log("resetting http proxy", Project.MSG_VERBOSE);
+                sysprops.remove("http.proxyHost");
+                sysprops.remove("http.proxyPort");
+                sysprops.remove("http.proxyUser");
+                sysprops.remove("http.proxyPassword");
+                sysprops.remove("https.proxyHost");
+                sysprops.remove("https.proxyPort");
+                sysprops.remove("ftp.proxyHost");
+                sysprops.remove("ftp.proxyPort");
+            }
+        }
+
+        //socks
+        if (socksProxyHost != null) {
+            settingsChanged = true;
+            if (socksProxyHost.length() != 0) {
+                enablingProxy = true;
+                sysprops.put("socksProxyHost", socksProxyHost);
+                sysprops.put("socksProxyPort", Integer.toString(socksProxyPort));
+                if (proxyUser != null) {
+                    //this may be a java1.4 thingy only
+                    sysprops.put("java.net.socks.username", proxyUser);
+                    sysprops.put("java.net.socks.password", proxyPassword);
+                }
+
+            } else {
+                log("resetting socks proxy", Project.MSG_VERBOSE);
+                sysprops.remove("socksProxyHost");
+                sysprops.remove("socksProxyPort");
+                sysprops.remove("java.net.socks.username");
+                sysprops.remove("java.net.socks.password");
+            }
+        }
+
+
+        //for Java1.1 we need to tell the system that the settings are new
+        if (settingsChanged
+            && JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
+            legacyResetProxySettingsCall(enablingProxy);
+        }
+
+        if (proxyUser != null) {
+            if (enablingProxy) {
+                Authenticator.setDefault(new ProxyAuth(proxyUser,
+                                                       proxyPassword));
+            } else if (settingsChanged) {
+                Authenticator.setDefault(new ProxyAuth("", ""));
+            }
+        }
+    }
+
+    /**
+     * list out what is going on
+     */
+    private void traceSettingInfo() {
+        log("Setting proxy to "
+                + (proxyHost != null ? proxyHost : "''")
+                + ":" + proxyPort,
+                Project.MSG_VERBOSE);
+    }
+
+
+    /**
+     * make a call to sun.net.www.http.HttpClient.resetProperties();
+     * this is only needed for java 1.1; reflection is used to stop the compiler
+     * whining, and in case cleanroom JVMs dont have the class.
+     * @return true if we did something
+     */
+
+    protected boolean legacyResetProxySettingsCall(boolean setProxy) {
+        System.getProperties().put("http.proxySet", new Boolean(setProxy).toString());
+        try {
+            Class c = Class.forName("sun.net.www.http.HttpClient");
+            Method reset = c.getMethod("resetProperties", null);
+            reset.invoke(null, null);
+            return true;
+        } catch (ClassNotFoundException cnfe) {
+            return false;
+        } catch (NoSuchMethodException e) {
+            return false;
+        } catch (IllegalAccessException e) {
+            return false;
+        } catch (InvocationTargetException e) {
+            return false;
+        }
+    }
+
+
+    /**
+     * Does the work.
+     *
+     * @exception BuildException thrown in unrecoverable error.
+     */
+    public void execute() throws BuildException {
+        applyWebProxySettings();
+    }
+
+    /**
+     * @since 1.6.3
+     */
+    private static final class ProxyAuth extends Authenticator {
+        private PasswordAuthentication auth;
+
+        private ProxyAuth(String user, String pass) {
+            auth = new PasswordAuthentication(user, pass.toCharArray());
+        }
+
+        protected PasswordAuthentication getPasswordAuthentication() {
+            return auth;
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/net/TelnetTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/net/TelnetTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/net/TelnetTask.java	(revision 16139)
@@ -0,0 +1,347 @@
+/*
+ * Copyright  2000,2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.net;
+
+import org.apache.commons.net.telnet.TelnetClient;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Calendar;
+import java.util.Enumeration;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Automates the telnet protocol.
+ *
+ */
+
+public class TelnetTask extends Task {
+    /**
+     *  The userid to login with, if automated login is used
+     */
+    private String userid  = null;
+
+    /**
+     *  The password to login with, if automated login is used
+     */
+    private String password = null;
+
+    /**
+     *  The server to connect to.
+     */
+    private String server  = null;
+
+    /**
+     *  The tcp port to connect to.
+     */
+    private int port = 23;
+
+    /**
+     *  The list of read/write commands for this session
+     */
+    private Vector telnetTasks = new Vector();
+
+    /**
+     *  If true, adds a CR to beginning of login script
+     */
+    private boolean addCarriageReturn = false;
+
+    /**
+     *  Default time allowed for waiting for a valid response
+     *  for all child reads.  A value of 0 means no limit.
+     */
+    private Integer defaultTimeout = null;
+
+    /**
+     *  Verify that all parameters are included.
+     *  Connect and possibly login
+     *  Iterate through the list of Reads and writes
+     */
+    public void execute() throws BuildException {
+       /**  A server name is required to continue */
+       if (server == null) {
+           throw new BuildException("No Server Specified");
+       }
+       /**  A userid and password must appear together
+        *   if they appear.  They are not required.
+        */
+       if (userid == null && password != null) {
+           throw new BuildException("No Userid Specified");
+       }
+       if (password == null && userid != null) {
+           throw new BuildException("No Password Specified");
+       }
+
+       /**  Create the telnet client object */
+       AntTelnetClient telnet = null;
+       try {
+           telnet = new AntTelnetClient();
+           try {
+               telnet.connect(server, port);
+           } catch (IOException e) {
+               throw new BuildException("Can't connect to " + server);
+           }
+           /**  Login if userid and password were specified */
+           if (userid != null && password != null) {
+               login(telnet);
+           }
+           /**  Process each sub command */
+           Enumeration tasksToRun = telnetTasks.elements();
+           while (tasksToRun != null && tasksToRun.hasMoreElements()) {
+               TelnetSubTask task = (TelnetSubTask) tasksToRun.nextElement();
+               if (task instanceof TelnetRead && defaultTimeout != null) {
+                   ((TelnetRead) task).setDefaultTimeout(defaultTimeout);
+               }
+               task.execute(telnet);
+           }
+       } finally {
+           if (telnet != null && telnet.isConnected()) {
+               try {
+                   telnet.disconnect();
+               } catch (IOException e) {
+                   throw new BuildException("Error disconnecting from " 
+                                            + server);
+               }
+           }
+       }
+    }
+
+    /**
+     *  Process a 'typical' login.  If it differs, use the read
+     *  and write tasks explicitely
+     */
+    private void login(AntTelnetClient telnet) {
+       if (addCarriageReturn) {
+          telnet.sendString("\n", true);
+       }
+       telnet.waitForString("ogin:");
+       telnet.sendString(userid, true);
+       telnet.waitForString("assword:");
+       telnet.sendString(password, false);
+    }
+
+    /**
+     * Set the the login id to use on the server;
+     * required if <tt>password</tt> is set.
+     */
+    public void setUserid(String u) { this.userid = u; }
+
+    /**
+     *  Set the the login password to use
+     * required if <tt>userid</tt> is set.
+     */
+    public void setPassword(String p) { this.password = p; }
+
+    /**
+     *  Set the hostname or address of the remote server.
+     */
+    public void setServer(String m) { this.server = m; }
+
+    /**
+     *  Set the tcp port to connect to; default is 23.
+     */
+    public void setPort(int p) { this.port = p; }
+
+    /**
+     *  send a carriage return after connecting; optional, defaults to false.
+     */
+    public void setInitialCR(boolean b) {
+       this.addCarriageReturn = b;
+    }
+
+    /**
+     * set a default timeout in seconds to wait for a response,
+     * zero means forever (the default)
+     */
+    public void setTimeout(Integer i) {
+       this.defaultTimeout = i;
+    }
+
+    /**
+     *  A string to wait for from the server.
+     *  A subTask &lt;read&gt; tag was found.  Create the object,
+     *  Save it in our list, and return it.
+     */
+
+    public TelnetSubTask createRead() {
+        TelnetSubTask task = (TelnetSubTask) new TelnetRead();
+        telnetTasks.addElement(task);
+        return task;
+    }
+
+    /**
+     *  Add text to send to the server
+     *  A subTask &lt;write&gt; tag was found.  Create the object,
+     *  Save it in our list, and return it.
+     */
+    public TelnetSubTask createWrite() {
+        TelnetSubTask task = (TelnetSubTask) new TelnetWrite();
+        telnetTasks.addElement(task);
+        return task;
+    }
+
+    /**
+     *  This class is the parent of the Read and Write tasks.
+     *  It handles the common attributes for both.
+     */
+    public class TelnetSubTask {
+        protected String taskString = "";
+        public void execute(AntTelnetClient telnet)
+                throws BuildException {
+            throw new BuildException("Shouldn't be able instantiate a SubTask directly");
+        }
+
+        /**
+         *  the message as nested text
+         */
+        public void addText(String s) {
+            setString(getProject().replaceProperties(s));
+        }
+
+        /**
+         * the message as an attribute
+         */
+        public void setString(String s) {
+           taskString += s;
+        }
+    }
+
+    /**
+     *  Sends text to the connected server
+     */
+    public class TelnetWrite extends TelnetSubTask {
+        private boolean echoString = true;
+        public void execute(AntTelnetClient telnet)
+               throws BuildException {
+           telnet.sendString(taskString, echoString);
+        }
+
+        /**
+         * Whether or not the message should be echoed to the log.
+         * Defaults to <code>true</code>.
+         */
+        public void setEcho(boolean b) {
+           echoString = b;
+        }
+    }
+
+    /**
+     *  Reads the output from the connected server
+     *  until the required string is found or we time out.
+     */
+    public class TelnetRead extends TelnetSubTask {
+        private Integer timeout = null;
+        public void execute(AntTelnetClient telnet)
+               throws BuildException {
+            telnet.waitForString(taskString, timeout);
+        }
+        /**
+         *  a timeout value that overrides any task wide timeout.
+         */
+        public void setTimeout(Integer i) {
+           this.timeout = i;
+        }
+
+        /**
+         * Sets the default timeout if none has been set already
+         * @ant.attribute ignore="true"
+         */
+        public void setDefaultTimeout(Integer defaultTimeout) {
+           if (timeout == null) {
+              timeout = defaultTimeout;
+           }
+        }
+    }
+
+    /**
+     *  This class handles the abstraction of the telnet protocol.
+     *  Currently it is a wrapper around <a
+     *  href="http://jakarta.apache.org/commons/net/index.html">Jakarta
+     *  Commons Net</a>.
+     */
+    public class AntTelnetClient extends TelnetClient {
+        /**
+         * Read from the telnet session until the string we are
+         * waiting for is found
+         * @param s The string to wait on
+         */
+        public void waitForString(String s) {
+            waitForString(s, null);
+        }
+
+        /**
+         * Read from the telnet session until the string we are
+         * waiting for is found or the timeout has been reached
+         * @param s The string to wait on
+         * @param timeout The maximum number of seconds to wait
+         */
+        public void waitForString(String s, Integer timeout) {
+            InputStream is = this.getInputStream();
+            try {
+                StringBuffer sb = new StringBuffer();
+                if (timeout == null || timeout.intValue() == 0) {
+                    while (sb.toString().indexOf(s) == -1) {
+                        sb.append((char) is.read());
+                    }
+                } else {
+                    Calendar endTime = Calendar.getInstance();
+                    endTime.add(Calendar.SECOND, timeout.intValue());
+                    while (sb.toString().indexOf(s) == -1) {
+                        while (Calendar.getInstance().before(endTime) &&
+                               is.available() == 0) {
+                            Thread.sleep(250);
+                        }
+                        if (is.available() == 0) {
+                            log("Read before running into timeout: "
+                                + sb.toString(), Project.MSG_DEBUG);
+                            throw new BuildException(
+                                "Response timed-out waiting for \"" + s + '\"',
+                                getLocation());
+                        }
+                        sb.append((char) is.read());
+                    }
+                }
+                log(sb.toString(), Project.MSG_INFO);
+            } catch (BuildException be) {
+                throw be;
+            } catch (Exception e) {
+                throw new BuildException(e, getLocation());
+            }
+        }
+
+        /**
+        * Write this string to the telnet session.
+        * @param echoString  Logs string sent
+        */
+        public void sendString(String s, boolean echoString) {
+            OutputStream os = this.getOutputStream();
+            try {
+                os.write((s + "\n").getBytes());
+                if (echoString) {
+                    log(s, Project.MSG_INFO);
+                }
+                os.flush();
+            } catch (Exception e) {
+                throw new BuildException(e, getLocation());
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/FStatP4OutputHandler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/FStatP4OutputHandler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/FStatP4OutputHandler.java	(revision 16139)
@@ -0,0 +1,59 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.perforce;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.oro.text.perl.Perl5Util;
+
+import java.util.ArrayList;
+
+/**
+ * FStatP4OutputHandler  - spezialied Perforce output handler
+ * able to sort files recognized as managed by Perforce and files not
+ * managed by Perforce in the output
+ *
+ */
+class FStatP4OutputHandler extends P4HandlerAdapter {
+    private P4Fstat parent;
+    private ArrayList existing = new ArrayList();
+    private ArrayList nonExisting = new ArrayList();
+    private static Perl5Util util = new Perl5Util();
+
+    public FStatP4OutputHandler(P4Fstat parent) {
+        this.parent = parent;
+    }
+
+    public void process(String line) throws BuildException {
+        if (util.match("/^... clientFile (.+)$/", line)) {
+            String f = util.group(1);
+            existing.add(f);
+        } else if (util.match("/^(.+) - no such file/", line)) {
+            String f = util.group(1);
+            nonExisting.add(f);
+        }
+        parent.log(parent.util.substitute("s/^.*: //", line),
+                   Project.MSG_VERBOSE);
+    }
+
+    public ArrayList getExisting() {
+        return existing;
+    }
+
+    public ArrayList getNonExisting() {
+        return nonExisting;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Add.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Add.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Add.java	(revision 16139)
@@ -0,0 +1,141 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import java.io.File;
+import java.util.Vector;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * Adds specified files to Perforce.
+ *
+ * <b>Example Usage:</b>
+ * <table border="1">
+ * <th>Function</th><th>Command</th>
+ * <tr><td>Add files using P4USER, P4PORT and P4CLIENT settings specified</td>
+ * <td>&lt;P4add <br>P4view="//projects/foo/main/source/..." <br>P4User="fbloggs"
+ * <br>P4Port="km01:1666"
+ * <br>P4Client="fbloggsclient"&gt;<br>&lt;fileset basedir="dir" includes="**&#47;*.java"&gt;<br>
+ * &lt;/p4add&gt;</td></tr>
+ * <tr><td>Add files using P4USER, P4PORT and P4CLIENT settings defined in environment</td><td>
+ * &lt;P4add P4view="//projects/foo/main/source/..." /&gt;<br>&lt;fileset basedir="dir"
+ * includes="**&#47;*.java"&gt;<br>&lt;/p4add&gt;</td></tr>
+ * <tr><td>Specify the length of command line arguments to pass to each invocation of p4</td>
+ * <td>&lt;p4add Commandlength="450"&gt;</td></tr>
+ * </table>
+ *
+ * @ant.task category="scm"
+ */
+public class P4Add extends P4Base {
+    private static final int DEFAULT_CMD_LENGTH = 450;
+    private int changelist;
+    private String addCmd = "";
+    private Vector filesets = new Vector();
+    private int cmdLength = DEFAULT_CMD_LENGTH;
+
+    /**
+     *   Set the maximum length
+     *   of the commandline when calling Perforce to add the files.
+     *   Defaults to 450, higher values mean faster execution,
+     *   but also possible failures.
+     *   @param len maximum length of command line default is 450.
+     *   @throws BuildException if trying to set the command line length to 0 or less.
+     */
+
+    public void setCommandlength(int len) throws BuildException {
+        if (len <= 0) {
+            throw new BuildException("P4Add: Commandlength should be a positive number");
+        }
+        this.cmdLength = len;
+    }
+
+    /**
+     * If specified the open files are associated with the
+     * specified pending changelist number; otherwise the open files are
+     * associated with the default changelist.
+     *
+     * @param changelist the change list number.
+     *
+     * @throws BuildException if trying to set a change list number &lt;=0.
+     */
+    public void setChangelist(int changelist) throws BuildException {
+        if (changelist <= 0) {
+            throw new BuildException("P4Add: Changelist# should be a positive number");
+        }
+        this.changelist = changelist;
+    }
+
+    /**
+     * Add a fileset whose files will be added to Perforce.
+     *
+     * @param set the FileSet that one wants to add to Perforce Source Control.
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * Run the task.
+     *
+     * @throws BuildException if the execution of the Perforce command fails.
+     */
+    public void execute() throws BuildException {
+        if (P4View != null) {
+            addCmd = P4View;
+        }
+        P4CmdOpts = (changelist > 0) ? ("-c " + changelist) : "";
+
+        StringBuffer filelist = new StringBuffer();
+
+        for (int i = 0; i < filesets.size(); i++) {
+            FileSet fs = (FileSet) filesets.elementAt(i);
+            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+
+            String[] srcFiles = ds.getIncludedFiles();
+            if (srcFiles != null) {
+                for (int j = 0; j < srcFiles.length; j++) {
+                    File f = new File(ds.getBasedir(), srcFiles[j]);
+                    filelist.append(" ").append('"').append(f.getAbsolutePath()).append('"');
+                    if (filelist.length() > cmdLength) {
+                        execP4Add(filelist);
+                        filelist = new StringBuffer();
+                    }
+                }
+                if (filelist.length() > 0) {
+                    execP4Add(filelist);
+                }
+            } else {
+                log("No files specified to add!", Project.MSG_WARN);
+            }
+        }
+    }
+
+    private void execP4Add(StringBuffer list) {
+        log("Execing add " + P4CmdOpts + " " + addCmd + list, Project.MSG_INFO);
+        execP4Command("-s add " + P4CmdOpts + " " + addCmd + list, new SimpleP4OutputHandler(this));
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Base.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Base.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Base.java	(revision 16139)
@@ -0,0 +1,298 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import java.io.IOException;
+import org.apache.oro.text.perl.Perl5Util;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.types.Commandline;
+
+
+/** Base class for Perforce (P4) ANT tasks. See individual task for example usage.
+ *
+ * @see P4Sync
+ * @see P4Have
+ * @see P4Change
+ * @see P4Edit
+ * @see P4Submit
+ * @see P4Label
+ * @see org.apache.tools.ant.taskdefs.Execute
+ */
+public abstract class P4Base extends org.apache.tools.ant.Task {
+
+    /**Perl5 regexp in Java - cool eh? */
+    protected Perl5Util util = null;
+    /** The OS shell to use (cmd.exe or /bin/sh) */
+    protected String shell;
+
+    //P4 runtime directives
+    /** Perforce Server Port (eg KM01:1666) */
+    protected String P4Port = "";
+    /** Perforce Client (eg myclientspec) */
+    protected String P4Client = "";
+    /** Perforce User (eg fbloggs) */
+    protected String P4User = "";
+    /** Perforce view for commands. (eg //projects/foobar/main/source/... )*/
+    protected String P4View = "";
+
+    // Perforce task directives
+    /** Keep going or fail on error - defaults to fail. */
+    protected boolean failOnError = true;
+
+    //P4 g-opts and cmd opts (rtfm)
+    /** Perforce 'global' opts.
+     * Forms half of low level API */
+    protected String P4Opts = "";
+    /** Perforce command opts.
+     * Forms half of low level API */
+    protected String P4CmdOpts = "";
+
+    /** Set by the task or a handler to indicate that the task has failed.  BuildExceptions
+     * can also be thrown to indicate failure. */
+    private boolean inError = false;
+
+    /** If inError is set, then errorMessage needs to contain the reason why. */
+    private String errorMessage = "";
+    /**
+     * gets whether or not the task has encountered an error
+     * @return error flag
+     * @since ant 1.6
+     */
+    public boolean getInError() {
+        return inError;
+    }
+
+    /**
+     * sets the error flag on the task
+     * @param inError if true an error has been encountered by the handler
+     * @since ant 1.6
+     */
+    public void setInError(boolean inError) {
+        this.inError = inError;
+    }
+
+    /**
+     * gets the error message recorded by the Perforce handler
+     * @return error message
+     */
+    public String getErrorMessage() {
+        return errorMessage;
+    }
+
+    /**
+     * sets the error message
+     * @param errorMessage line of error output
+     */
+    public void setErrorMessage(String errorMessage) {
+        this.errorMessage = errorMessage;
+    }
+    //Setters called by Ant
+
+    /**
+     * The p4d server and port to connect to;
+     * optional, default "perforce:1666"
+     *
+     * @param P4Port the port one wants to set such as localhost:1666
+     */
+    public void setPort(String P4Port) {
+        this.P4Port = "-p" + P4Port;
+    }
+
+    /**
+     * The p4 client spec to use;
+     * optional, defaults to the current user
+     *
+     * @param P4Client the name of the Perforce client spec
+     */
+    public void setClient(String P4Client) {
+        this.P4Client = "-c" + P4Client;
+    }
+
+    /**
+     * The p4 username;
+     * optional, defaults to the current user
+     *
+     * @param P4User the user name
+     */
+    public void setUser(String P4User) {
+        this.P4User = "-u" + P4User;
+    }
+    /**
+     * Set global P4 options; Used on all
+     * of the Perforce tasks.
+     *
+     * @param P4Opts global options, to use a specific P4Config file for instance
+     */
+    public void setGlobalopts(String P4Opts) {
+        this.P4Opts = P4Opts;
+    }
+    /**
+     * The client, branch or label view to operate upon;
+     * optional default "//...".
+     *
+     * the view is required for the following tasks :
+     * <ul>
+     * <li>p4delete</li>
+     * <li>p4edit</li>
+     * <li>p4reopen</li>
+     * <li>p4resolve</li>
+     * </ul>
+     *
+     * @param P4View the view one wants to use
+     */
+    public void setView(String P4View) {
+        this.P4View = P4View;
+    }
+
+    /**
+     * Set extra command options; only used on some
+     * of the Perforce tasks.
+     *
+     * @param P4CmdOpts  command line options going after the particular
+     * Perforce command
+     */
+    public void setCmdopts(String P4CmdOpts) {
+        this.P4CmdOpts = P4CmdOpts;
+    }
+
+    /**
+     * whether to stop the build (true, default)
+     * or keep going if an error is returned from the p4 command
+     * @param fail indicates whether one wants to fail the build if an error comes from the
+     * Perforce command
+     */
+    public void setFailonerror(boolean fail) {
+        failOnError = fail;
+    }
+    /**
+     *  sets attributes Port, Client, User from properties
+     *  if these properties are defined.
+     *  Called automatically by UnknownElement
+     *  @see org.apache.tools.ant.UnknownElement
+     *  <table>
+     *  <tr><th>Property</th><th>Attribute</th></tr>
+     *  <tr><td>p4.port</td><td>Port</td></tr>
+     *  <tr><td>p4.client</td><td>Client</td></tr>
+     *  <tr><td>p4.user</td><td>User</td></tr>
+     *  </table>
+     */
+    public void init() {
+
+        util = new Perl5Util();
+
+        //Get default P4 settings from environment - Mark would have done something cool with
+        //introspection here.....:-)
+        String tmpprop;
+        if ((tmpprop = getProject().getProperty("p4.port")) != null) {
+            setPort(tmpprop);
+        }
+        if ((tmpprop = getProject().getProperty("p4.client")) != null) {
+            setClient(tmpprop);
+        }
+        if ((tmpprop = getProject().getProperty("p4.user")) != null) {
+            setUser(tmpprop);
+        }
+    }
+    /**
+    *  no usages found for this method
+    *  runs a Perforce command without a handler
+    * @param command the command that one wants to execute
+    * @throws BuildException if failonerror is set and the command fails
+    */
+    protected void execP4Command(String command) throws BuildException {
+        execP4Command(command, null);
+    }
+
+    /**
+     * Execute P4 command assembled by subclasses.
+     *
+     * @param command The command to run
+     * @param handler A P4Handler to process any input and output
+     *
+     * @throws BuildException if failonerror has been set to true
+     */
+    protected void execP4Command(String command, P4Handler handler) throws BuildException {
+        try {
+            // reset error flags before executing the command
+            inError = false;
+            errorMessage = "";
+            Commandline commandline = new Commandline();
+            commandline.setExecutable("p4");
+
+            //Check API for these - it's how CVS does it...
+            if (P4Port != null && P4Port.length() != 0) {
+                commandline.createArgument().setValue(P4Port);
+            }
+            if (P4User != null && P4User.length() != 0) {
+                commandline.createArgument().setValue(P4User);
+            }
+            if (P4Client != null && P4Client.length() != 0) {
+                commandline.createArgument().setValue(P4Client);
+            }
+            if (P4Opts != null && P4Opts.length() != 0) {
+                commandline.createArgument().setLine(P4Opts);
+            }
+            commandline.createArgument().setLine(command);
+
+            log(commandline.describeCommand(), Project.MSG_VERBOSE);
+
+            if (handler == null) {
+                handler = new SimpleP4OutputHandler(this);
+            }
+
+            Execute exe = new Execute(handler, null);
+
+            exe.setAntRun(getProject());
+
+            exe.setCommandline(commandline.getCommandline());
+
+            try {
+                exe.execute();
+
+                if (inError && failOnError) {
+                    throw new BuildException(errorMessage);
+                }
+            } catch (IOException e) {
+                throw new BuildException(e);
+            } finally {
+                try {
+                    handler.stop();
+                } catch (Exception e) {
+                    log(e.toString(), Project.MSG_ERR);
+                }
+            }
+
+
+        } catch (Exception e) {
+            String failMsg = "Problem exec'ing P4 command: " + e.getMessage();
+            if (failOnError) {
+                throw new BuildException(failMsg);
+            } else {
+                log(failMsg, Project.MSG_ERR);
+            }
+
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Change.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Change.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Change.java	(revision 16139)
@@ -0,0 +1,144 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Requests a new changelist from the Perforce server.
+ * P4Change creates a new changelist in perforce. P4Change sets the property
+ * ${p4.change} with the new changelist number. This should then be passed into
+ * p4edit and p4submit.
+ *
+ *
+ * @see P4Edit
+ * @see P4Submit
+ *
+ * @ant.task category="scm"
+ */
+public class P4Change extends P4Base {
+
+    protected String emptyChangeList = null;
+    protected String description = "AutoSubmit By Ant";
+
+    /**
+     * creates a new Perforce change list
+     * sets the p4.change property to the number of the new change list
+     * @throws BuildException if the word error appears in the output coming from Perforce
+     */
+    public void execute() throws BuildException {
+
+        if (emptyChangeList == null) {
+            emptyChangeList = getEmptyChangeList();
+        }
+        final Project myProj = getProject();
+
+        P4Handler handler = new P4HandlerAdapter() {
+            public void process(String line) {
+                if (util.match("/Change/", line)) {
+
+                    //Remove any non-numerical chars - should leave the change number
+                    line = util.substitute("s/[^0-9]//g", line);
+
+                    int changenumber = Integer.parseInt(line);
+                    log("Change Number is " + changenumber, Project.MSG_INFO);
+                    myProj.setProperty("p4.change", "" + changenumber);
+
+                } else if (util.match("/error/", line)) {
+                    throw new BuildException("Perforce Error, check client settings and/or server");
+                }
+
+            }
+        };
+
+        handler.setOutput(emptyChangeList);
+
+        execP4Command("change -i", handler);
+    }
+
+    /**
+     * returns the text of an empty change list
+     * @return  the text of an empty change list
+     * @throws BuildException  if the text error is displayed
+     * in the Perforce output outside of a comment line
+     */
+    public String getEmptyChangeList() throws BuildException {
+        final StringBuffer stringbuf = new StringBuffer();
+
+        execP4Command("change -o", new P4HandlerAdapter() {
+            public void process(String line) {
+                if (!util.match("/^#/", line)) {
+                    if (util.match("/error/", line)) {
+                        log("Client Error", Project.MSG_VERBOSE);
+                        throw new BuildException("Perforce Error, "
+                        + "check client settings and/or server");
+                    } else if (util.match("/<enter description here>/", line)) {
+                        // we need to escape the description in case there are /
+                        description = backslash(description);
+                        line = util.substitute("s/<enter description here>/"
+                            + description + "/", line);
+                    } else if (util.match("/\\/\\//", line)) {
+                        //Match "//" for begining of depot filespec
+                        return;
+                    }
+                    stringbuf.append(line);
+                    stringbuf.append("\n");
+                }
+            }
+        });
+        return stringbuf.toString();
+    }
+
+    /**
+     * Ensure that a string is backslashing slashes so that  it does not
+     * confuse them with Perl substitution delimiter in Oro. Backslashes are
+     * always backslashes in a string unless they escape the delimiter.
+     * @param value the string to backslash for slashes
+     * @return the backslashed string
+     * @see <a href="http://jakarta.apache.org/oro/api/org/apache/oro/text/perl/Perl5Util.html
+     * #substitute(java.lang.String,%20java.lang.String)">Oro</a>
+     */
+    public static final String backslash(String value) {
+        final StringBuffer buf = new StringBuffer(value.length());
+        final int len = value.length();
+        for (int i = 0; i < len; i++) {
+            char c = value.charAt(i);
+            if (c == '/') {
+                buf.append('\\');
+            }
+            buf.append(c);
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Description for ChangeList;optional.
+     * If none is specified, it will default to "AutoSubmit By Ant"
+     * @param desc description for the change list
+     */
+    public void setDescription(String desc) {
+        this.description = desc;
+    }
+
+} //EoF
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Counter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Counter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Counter.java	(revision 16139)
@@ -0,0 +1,147 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Obtains or sets the value of a counter.
+ *
+ * <p> When used in its base form
+ * (where only the counter name is provided), the counter value will be
+ * printed to the output stream. When the value is provided, the counter
+ * will be set to the value provided. When a property name is provided,
+ * the property will be filled with the value of the counter. You may
+ * not specify to both get and set the value of the counter in the same
+ * Task.
+ * </p>
+ * <P>
+ * The user performing this task must have Perforce &quot;review&quot; permissions
+ * as defined by Perforce protections in order for this task to succeed.
+</P>
+
+ * Example Usage:<br>
+ * &lt;p4counter name="${p4.counter}" property=${p4.change}"/&gt;
+ *
+ * @ant.task category="scm"
+ */
+
+public class P4Counter extends P4Base {
+    /**
+     * name of the counter
+     */
+    public String counter = null;
+    /**
+     * name of an optional property
+     */
+    public String property = null;
+    /**
+     * flag telling whether the value of the counter should be set
+     */
+    public boolean shouldSetValue = false;
+    /**
+     * flag telling whether a property should be set
+     */
+    public boolean shouldSetProperty = false;
+    /**
+     * new value for the counter
+     */
+    public int value = 0;
+
+    /**
+     * The name of the counter; required
+     * @param counter name of the counter
+     */
+    public void setName(String counter) {
+        this.counter = counter;
+    }
+
+    /**
+     * The new value for the counter; optional.
+     * @param value new value for the counter
+     */
+    public void setValue(int value) {
+        this.value = value;
+        shouldSetValue = true;
+    }
+
+    /**
+     * A property to be set with the value of the counter
+     * @param property the name of a property to set with the value
+     * of the counter
+     */
+    public void setProperty(String property) {
+        this.property = property;
+        shouldSetProperty = true;
+    }
+
+    /**
+     * again, properties are mutable in this tsk
+     * @throws BuildException if the required parameters are not supplied.
+     */
+    public void execute() throws BuildException {
+
+        if ((counter == null) || counter.length() == 0) {
+            throw new BuildException("No counter specified to retrieve");
+        }
+
+        if (shouldSetValue && shouldSetProperty) {
+            throw new BuildException("Cannot both set the value of the property and retrieve the "
+                + "value of the property.");
+        }
+
+        String command = "counter " + P4CmdOpts + " " + counter;
+        if (!shouldSetProperty) {
+            // NOTE kirk@radik.com 04-April-2001 -- If you put in the -s, you
+            // have to start running through regular expressions here. Much easier
+            // to just not include the scripting information than to try to add it
+            // and strip it later.
+            command = "-s " + command;
+        }
+        if (shouldSetValue) {
+            command += " " + value;
+        }
+
+        if (shouldSetProperty) {
+            final Project myProj = getProject();
+
+            P4Handler handler = new P4HandlerAdapter() {
+                public void process(String line) {
+                    log("P4Counter retrieved line \"" + line + "\"", Project.MSG_VERBOSE);
+                    try {
+                        value = Integer.parseInt(line);
+                        myProj.setProperty(property, "" + value);
+                    } catch (NumberFormatException nfe) {
+                        throw new BuildException("Perforce error. "
+                        + "Could not retrieve counter value.");
+                    }
+                }
+            };
+
+            execP4Command(command, handler);
+        } else {
+            execP4Command(command, new SimpleP4OutputHandler(this));
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Delete.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Delete.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Delete.java	(revision 16139)
@@ -0,0 +1,63 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import org.apache.tools.ant.BuildException;
+
+/** Checkout files for deletion.
+ *
+ * Example Usage:<br>
+ * &lt;p4delete change="${p4.change}" view="//depot/project/foo.txt" /&gt;<br>
+ *
+ * Simple re-write of P4Edit changing 'edit' to 'delete'.<br>
+ *
+ * @todo What to do if file is already open in one of our changelists perhaps
+ * (See also {@link P4Edit P4Edit})?<br>
+ *
+ *
+ * @ant.task category="scm"
+ */
+public class P4Delete extends P4Base {
+
+    /**
+     * number of the change list to work on
+     */
+    public String change = null;
+
+    /**
+     * An existing changelist number for the deletion; optional
+     * but strongly recommended.
+     * @param change the number of a change list
+     */
+    public void setChange(String change) {
+        this.change = change;
+    }
+
+    /**
+     * executes the p4 delete task
+     * @throws BuildException if there is no view specified
+     */
+    public void execute() throws BuildException {
+        if (change != null) {
+            P4CmdOpts = "-c " + change;
+        }
+        if (P4View == null) {
+            throw new BuildException("No view specified to delete");
+        }
+        execP4Command("-s delete " + P4CmdOpts + " " + P4View, new SimpleP4OutputHandler(this));
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Edit.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Edit.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Edit.java	(revision 16139)
@@ -0,0 +1,70 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Open file(s) for edit.
+ * P4Change should be used to obtain a new changelist for P4Edit as,
+ * although P4Edit can open files to the default change,
+ * P4Submit cannot yet submit to it.
+ * Example Usage:<br>
+ * &lt;p4edit change="${p4.change}" view="//depot/project/foo.txt" /&gt;
+ *
+ *
+ * @todo Should call reopen if file is already open in one of our changelists perhaps?
+ *
+ * @ant.task category="scm"
+ */
+
+public class P4Edit extends P4Base {
+
+    /**
+     * number of the change list to work on
+     */
+    public String change = null;
+
+    /**
+     * An existing changelist number to assign files to; optional
+     * but strongly recommended.
+     * @param change the change list number
+     */
+    public void setChange(String change) {
+        this.change = change;
+    }
+
+    /**
+     * Run the p4 edit command
+     * @throws BuildException if there is no view specified
+     */
+    public void execute() throws BuildException {
+        if (change != null) {
+            P4CmdOpts = "-c " + change;
+        }
+        if (P4View == null) {
+            throw new BuildException("No view specified to edit");
+        }
+        execP4Command("-s edit " + P4CmdOpts + " " + P4View, new SimpleP4OutputHandler(this));
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Fstat.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Fstat.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Fstat.java	(revision 16139)
@@ -0,0 +1,197 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+
+import java.io.File;
+import java.util.Vector;
+import java.util.ArrayList;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * P4Fstat--find out which files are under Perforce control and which are not.
+ *
+ * <br><b>Example Usage:</b><br>
+ * <pre>
+ * &lt;project name=&quot;p4fstat&quot; default=&quot;p4fstat&quot;
+ * basedir=&quot;C:\dev\gnu&quot;&gt;
+ *     &lt;target name=&quot;p4fstat&quot; &gt;
+ *         &lt;p4fstat showfilter=&quot;all&quot;&gt;
+ *             &lt;fileset dir=&quot;depot&quot; includes=&quot;**\/*&quot;/&gt;
+ *         &lt;/p4fstat&gt;
+ *     &lt;/target&gt;
+ * &lt;/project&gt;
+ * </pre>
+ *
+ * @ant.task category="scm"
+ */
+public class P4Fstat extends P4Base {
+
+    private int changelist;
+    private String addCmd = "";
+    private Vector filesets = new Vector();
+    private static final int DEFAULT_CMD_LENGTH = 300;
+    private int cmdLength = DEFAULT_CMD_LENGTH;
+    private static final int SHOW_ALL = 0;
+    private static final int SHOW_EXISTING = 1;
+    private static final int SHOW_NON_EXISTING = 2;
+    private int show = SHOW_NON_EXISTING;
+    private FStatP4OutputHandler handler;
+    private StringBuffer filelist;
+    private int fileNum = 0;
+    private int doneFileNum = 0;
+    private boolean debug = false;
+
+    private static final String EXISTING_HEADER
+        = "Following files exist in perforce";
+    private static final String NONEXISTING_HEADER
+        = "Following files do not exist in perforce";
+
+    /**
+     * Sets the filter that one wants applied.
+     * <table>
+     * <tr><th>Option</th><th>Meaning</th></tr>
+     * <tr><td>all</td><td>all files under Perforce control or not</td></tr>
+     * <tr><td>existing</td><td>only files under Perforce control</td></tr>
+     * <tr><td>non-existing</td><td>only files not under Perforce control or not</td></tr>
+     * </table>
+     * @param filter should be one of all|existing|non-existing.
+     */
+    public void setShowFilter(String filter) {
+        if (filter.equalsIgnoreCase("all")) {
+            show = SHOW_ALL;
+        } else if (filter.equalsIgnoreCase("existing")) {
+            show = SHOW_EXISTING;
+        } else if (filter.equalsIgnoreCase("non-existing")) {
+            show = SHOW_NON_EXISTING;
+        } else {
+            throw new BuildException("P4Fstat: ShowFilter should be one of: "
+                + "all, existing, non-existing");
+        }
+    }
+
+    /**
+     * Sets optionally a change list number.
+     * @param changelist change list that one wants information about.
+     * @throws BuildException if the change list number is negative.
+     */
+    public void setChangelist(int changelist) throws BuildException {
+        if (changelist <= 0) {
+            throw new BuildException("P4FStat: Changelist# should be a "
+                + "positive number");
+        }
+        this.changelist = changelist;
+    }
+
+    /**
+     * Adds a fileset to be examined by p4fstat.
+     * @param set the fileset to add.
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * Executes the p4fstat task.
+     * @throws BuildException if no files are specified.
+     */
+    public void execute() throws BuildException {
+        handler = new FStatP4OutputHandler(this);
+        if (P4View != null) {
+            addCmd = P4View;
+        }
+        P4CmdOpts = (changelist > 0) ? ("-c " + changelist) : "";
+
+        filelist = new StringBuffer();
+
+        for (int i = 0; i < filesets.size(); i++) {
+            FileSet fs = (FileSet) filesets.elementAt(i);
+            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+
+            String[] srcFiles = ds.getIncludedFiles();
+            fileNum = srcFiles.length;
+
+            if (srcFiles != null) {
+                for (int j = 0; j < srcFiles.length; j++) {
+                    File f = new File(ds.getBasedir(), srcFiles[j]);
+                    filelist.append(" ").append('"').append(f.getAbsolutePath()).append('"');
+                    doneFileNum++;
+                    if (filelist.length() > cmdLength) {
+
+                        execP4Fstat(filelist);
+                        filelist = new StringBuffer();
+                    }
+                }
+                if (filelist.length() > 0) {
+                    execP4Fstat(filelist);
+                }
+            } else {
+                log("No files specified to query status on!", Project.MSG_WARN);
+            }
+        }
+        if (show == SHOW_ALL || show == SHOW_EXISTING) {
+            printRes(handler.getExisting(), EXISTING_HEADER);
+        }
+        if (show == SHOW_ALL || show == SHOW_NON_EXISTING) {
+            printRes(handler.getNonExisting(), NONEXISTING_HEADER);
+        }
+    }
+
+    /**
+     * Return the number of files seen.
+     * @return the number of files seen.
+     */
+    public int getLengthOfTask() {
+        return fileNum;
+    }
+
+    /**
+     * Return the number of passes to make.
+     * IS THIS BEING USED?
+     * @return number of passes (how many filesets).
+     */
+    int getPasses() {
+        return filesets.size();
+    }
+
+    private void printRes(ArrayList ar, String header) {
+        log(header, Project.MSG_INFO);
+        for (int i = 0; i < ar.size(); i++) {
+            log((String) ar.get(i), Project.MSG_INFO);
+        }
+    }
+
+    private void execP4Fstat(StringBuffer list) {
+        String l = list.substring(0);
+        if (debug) {
+            log("Executing fstat " + P4CmdOpts + " " + addCmd + l + "\n",
+                Project.MSG_INFO);
+        }
+        execP4Command("fstat " + P4CmdOpts + " " + addCmd + l, handler);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Handler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Handler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Handler.java	(revision 16139)
@@ -0,0 +1,42 @@
+/*
+ * Copyright  2001,2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+
+/** Interface for p4 job output stream handler. Classes implementing this interface
+ * can be called back by P4Base.execP4Command();
+ *
+ */
+public interface P4Handler extends ExecuteStreamHandler {
+
+    /**
+     * processing of one line of stdout or of stderr
+     * @param line a line of stdout or stderr that the implementation will process
+     * @throws BuildException at the discretion of the implementation.
+     */
+    void process(String line) throws BuildException;
+
+    /**
+     * set any data to be written to P4's stdin
+     * @param line the text to write to P4's stdin
+     * @throws BuildException if the line cannot be processed.
+     */
+    void setOutput(String line) throws BuildException;
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4HandlerAdapter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4HandlerAdapter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4HandlerAdapter.java	(revision 16139)
@@ -0,0 +1,109 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.PumpStreamHandler;
+/**
+ * base class to manage streams around the execution of the Perforce
+ * command line client
+ */
+public abstract class P4HandlerAdapter  implements P4Handler {
+
+    String p4input = "";
+    private PumpStreamHandler myHandler = null;
+    /**
+     *  set any data to be written to P4's stdin
+     *  @param p4Input the text to write to P4's stdin
+     */
+    public void setOutput(String p4Input) {
+        this.p4input = p4Input;
+    }
+    /**
+     * subclasses of P4HandlerAdapter must implement this routine
+     * processing of one line of stdout or of stderr
+     * @param line line of stdout or stderr to process
+     */
+    public abstract void process(String line);
+
+    /**
+     * this routine gets called by the execute routine of the Execute class
+     * it connects the PumpStreamHandler to the input/output/error streams of the process.
+     * @throws BuildException
+     * @see org.apache.tools.ant.taskdefs.Execute#execute
+     */
+    public void start() throws BuildException {
+        if (p4input != null && p4input.length() > 0) {
+            myHandler = new PumpStreamHandler(new P4OutputStream(this), new P4OutputStream(this),
+                new ByteArrayInputStream(p4input.getBytes()));
+        } else {
+            myHandler = new PumpStreamHandler(new P4OutputStream(this), new P4OutputStream(this));
+        }
+        myHandler.setProcessInputStream(os);
+        myHandler.setProcessErrorStream(es);
+        myHandler.setProcessOutputStream(is);
+        myHandler.start();
+    }
+
+    /**
+     * stops the processing of streams
+     * called from P4Base#execP4Command(String command, P4Handler handler)
+     * @see P4Base#execP4Command(String, P4Handler)
+     */
+    public void stop() {
+        myHandler.stop();
+    }
+
+    OutputStream os;    //Input
+    InputStream is;     //Output
+    InputStream es;     //Error
+
+    /**
+     * connects the handler to the input stream into Perforce
+     * used indirectly by tasks requiring to send specific standard input
+     * such as p4label, p4change
+     * @param os the stream bringing input to the p4 executable
+     * @throws IOException under unknown circumstances
+     */
+    public void setProcessInputStream(OutputStream os) throws IOException {
+        this.os = os;
+    }
+
+    /**
+     * connects the handler to the stderr of the Perforce process
+     * @param is stderr coming from Perforce
+     * @throws IOException under unknown circumstances
+     */
+    public void setProcessErrorStream(InputStream is) throws IOException {
+        this.es = is;
+    }
+
+    /**
+     * connects the handler to the stdout of the Perforce process
+     * @param is stdout coming from Perforce
+     * @throws IOException under unknown circumstances
+     */
+    public void setProcessOutputStream(InputStream is) throws IOException {
+        this.is = is;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Have.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Have.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Have.java	(revision 16139)
@@ -0,0 +1,45 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import org.apache.tools.ant.BuildException;
+
+
+/** Lists Perforce files currently on client.
+ *
+ * P4Have simply dumps the current file version info into
+ * the Ant log (or stdout).
+ *
+ * @ant.task category="scm"
+ */
+public class P4Have extends P4Base {
+
+    /**
+     * Execute the Perforce <code>have</code> command.
+     *
+     * @throws BuildException if the command cannot be executed.
+     */
+    public void execute() throws BuildException {
+        execP4Command("have " + P4CmdOpts + " " + P4View, new SimpleP4OutputHandler(this));
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Integrate.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Integrate.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Integrate.java	(revision 16139)
@@ -0,0 +1,317 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Integrate file(s).
+ * P4Change should be used to obtain a new changelist for P4Integrate,
+ * although P4Integrate can open files to the default change,
+ * P4Submit cannot yet submit to it.
+ * Example Usage:<br>
+ * &lt;p4integrate change="${p4.change}"
+ * fromfile="//depot/project/dev/foo.txt" tofile="//depot/project/main/foo.txt" /&gt;
+ *
+ *
+ * @ant.task category="scm"
+ */
+
+public class P4Integrate extends P4Base {
+
+    private String change = null;
+    private String fromfile = null;
+    private String tofile = null;
+    private String branch = null;
+    private boolean restoredeletedrevisions = false;
+    private boolean forceintegrate = false;
+    private boolean leavetargetrevision = false;
+    private boolean enablebaselessmerges = false;
+    private boolean simulationmode = false;
+    private boolean reversebranchmappings = false;
+    private boolean propagatesourcefiletype = false;
+    private boolean nocopynewtargetfiles = false;
+
+    /**
+     * get the changelist number
+     *
+     * @return the changelist number set for this task
+     */
+    public String getChange() {
+        return change;
+    }
+
+    /**
+     * set the changelist number for the operation
+     *
+     * @param change An existing changelist number to assign files to; optional
+     * but strongly recommended.
+     */
+    public void setChange(String change) {
+        this.change = change;
+    }
+
+    /**
+     * get the from file specification
+     *
+     * @return the from file specification
+     */
+    public String getFromfile() {
+        return fromfile;
+    }
+
+    /**
+     * sets the from file specification
+     *
+     * @param fromf the from file specification
+     */
+    public void setFromfile(String fromf) {
+        this.fromfile = fromf;
+    }
+
+    /**
+     * get the to file specification
+     *
+     * @return the to file specification
+     */
+    public String getTofile() {
+        return tofile;
+    }
+
+    /**
+     * sets the to file specification
+     *
+     * @param tof the to file specification
+     */
+    public void setTofile(String tof) {
+        this.tofile = tof;
+    }
+
+    /**
+     * get the branch
+     *
+     * @return the name of the branch
+     */
+    public String getBranch() {
+        return branch;
+    }
+
+    /**
+     * sets the branch
+     *
+     * @param br the name of the branch to use
+     */
+    public void setBranch(String br) {
+        this.branch = br;
+    }
+
+    /**
+     * gets the restoredeletedrevisions flag
+     *
+     * @return restore deleted revisions
+     */
+    public boolean isRestoreDeletedRevisions() {
+        return restoredeletedrevisions;
+    }
+
+    /**
+     * sets the restoredeletedrevisions flag
+     *
+     * @param setrest value chosen for restoredeletedrevisions
+     */
+    public void setRestoreDeletedRevisions(boolean setrest) {
+        this.restoredeletedrevisions = setrest;
+    }
+
+    /**
+     * gets the forceintegrate flag
+     *
+     * @return restore deleted revisions
+     */
+    public boolean isForceIntegrate() {
+        return forceintegrate;
+    }
+
+    /**
+     * sets the forceintegrate flag
+     *
+     * @param setrest value chosen for forceintegrate
+     */
+    public void setForceIntegrate(boolean setrest) {
+        this.forceintegrate = setrest;
+    }
+
+    /**
+     * gets the leavetargetrevision flag
+     *
+     * @return flag indicating if the target revision should be preserved
+     */
+    public boolean isLeaveTargetRevision() {
+        return leavetargetrevision;
+    }
+
+    /**
+     * sets the leavetargetrevision flag
+     *
+     * @param setrest value chosen for leavetargetrevision
+     */
+    public void setLeaveTargetRevision(boolean setrest) {
+        this.leavetargetrevision = setrest;
+    }
+
+    /**
+     * gets the enablebaselessmerges flag
+     *
+     * @return boolean indicating if baseless merges are desired
+     */
+    public boolean isEnableBaselessMerges() {
+        return enablebaselessmerges;
+    }
+
+    /**
+     * sets the enablebaselessmerges flag
+     *
+     * @param setrest value chosen for enablebaselessmerges
+     */
+    public void setEnableBaselessMerges(boolean setrest) {
+        this.enablebaselessmerges = setrest;
+    }
+
+    /**
+     * gets the simulationmode flag
+     *
+     * @return simulation mode flag
+     */
+    public boolean isSimulationMode() {
+        return simulationmode;
+    }
+
+    /**
+     * sets the simulationmode flag
+     *
+     * @param setrest value chosen for simulationmode
+     */
+    public void setSimulationMode(boolean setrest) {
+        this.simulationmode = setrest;
+    }
+    /**
+     * returns the flag indicating if reverse branch mappings are sought
+     *
+     * @return reversebranchmappings flag
+     */
+    public boolean isReversebranchmappings() {
+        return reversebranchmappings;
+    }
+
+    /**
+     *  sets the reversebranchmappings flag
+     *
+     *  @param reversebranchmappings flag indicating if reverse branch mappings are sought
+     */
+    public void setReversebranchmappings(boolean reversebranchmappings) {
+        this.reversebranchmappings = reversebranchmappings;
+    }
+    /**
+     *  returns flag indicating if propagation of source file type is sought
+     *
+     *  @return flag set to true if you want to propagate source file type for existing target files
+     */
+    public boolean isPropagatesourcefiletype() {
+        return propagatesourcefiletype;
+    }
+    /**
+     *   sets flag indicating if one wants to propagate the source file type
+     *
+     *   @param propagatesourcefiletype
+     *   set it to true if you want to change the type of existing target files
+     *   according to type of source file.
+     */
+    public void setPropagatesourcefiletype(boolean propagatesourcefiletype) {
+        this.propagatesourcefiletype = propagatesourcefiletype;
+    }
+    /**
+     *   indicates intention to suppress the copying on the local hard disk of new target files.
+     *
+     *   @return indicates intention to suppress the copying
+     *   on the local hard disk of new target files.
+     */
+    public boolean isNocopynewtargetfiles() {
+        return nocopynewtargetfiles;
+    }
+
+    /**
+     *   sets nocopynewtargetfiles flag
+     *
+     *   @param nocopynewtargetfiles set it to true to gain speed in integration by not copying on
+     *   the local Perforce client new target files
+     */
+    public void setNocopynewtargetfiles(boolean nocopynewtargetfiles) {
+        this.nocopynewtargetfiles = nocopynewtargetfiles;
+    }
+
+    /**
+     *  execute the p4 integrate
+     *  @throws BuildException if there are missing parameters
+     */
+    public void execute() throws BuildException {
+        if (change != null) {
+            P4CmdOpts = "-c " + change;
+        }
+        if (this.forceintegrate) {
+            P4CmdOpts = P4CmdOpts + " -f";
+        }
+        if (this.restoredeletedrevisions) {
+                P4CmdOpts = P4CmdOpts + " -d";
+            }
+        if (this.leavetargetrevision) {
+            P4CmdOpts = P4CmdOpts + " -h";
+        }
+        if (this.enablebaselessmerges) {
+            P4CmdOpts = P4CmdOpts + " -i";
+        }
+        if (this.simulationmode) {
+            P4CmdOpts = P4CmdOpts + " -n";
+        }
+        if (this.reversebranchmappings) {
+            P4CmdOpts = P4CmdOpts + " -r";
+        }
+        if (this.propagatesourcefiletype) {
+            P4CmdOpts = P4CmdOpts + " -t";
+        }
+        if (this.nocopynewtargetfiles) {
+            P4CmdOpts = P4CmdOpts + "-v";
+        }
+        String command;
+        if (branch == null && fromfile != null && tofile != null) {
+           command = P4CmdOpts + " " + fromfile + " " + tofile;
+        } else if (branch != null && fromfile == null && tofile != null) {
+            command = P4CmdOpts + " -b " + branch + " " + tofile;
+        } else if (branch != null && fromfile != null) {
+            command = P4CmdOpts + " -b " + branch + " -s " + fromfile + " " + tofile;
+        } else {
+            throw new BuildException("you need to specify fromfile and tofile, "
+            + "or branch and tofile, or branch and fromfile, or branch and fromfile and tofile ");
+        }
+        execP4Command("-s integrate " + command, new SimpleP4OutputHandler(this));
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Label.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Label.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Label.java	(revision 16139)
@@ -0,0 +1,174 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ *  Creates a new Perforce label and set contents to reflect current
+ *  client file revisions.
+ *
+ *  Label name defaults to AntLabel if none set.
+ *
+ * Example Usage:
+ * <pre>
+ *   &lt;P4Label name="MyLabel-${TSTAMP}-${DSTAMP}" desc="Auto Build Label" /&gt;
+ * </pre>
+ *
+ *
+ * @ant.task category="scm"
+ */
+public class P4Label extends P4Base {
+
+    protected String name;
+    protected String desc;
+    protected String lock;
+
+    /**
+     * The name of the label; optional, default "AntLabel"
+     * @param name the name of the label
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     *Label Description; optional
+     * @param desc description of the label
+     */
+    public void setDesc(String desc) {
+        this.desc = desc;
+    }
+
+    /**
+     * when set to "locked", Perforce will lock the label once created; optional.
+     * @param lock only admissible value "locked"
+     */
+    public void setLock(String lock) {
+        this.lock = lock;
+    }
+
+    /**
+     * do the work
+     * @throws BuildException if failonerror has been set to true and Perforce fails
+     */
+    public void execute() throws BuildException {
+        log("P4Label exec:", Project.MSG_INFO);
+
+        if (P4View == null || P4View.length() < 1) {
+            log("View not set, assuming //depot/...", Project.MSG_WARN);
+            P4View = "//depot/...";
+        } else {
+            P4View = StringUtils.replace(P4View, ":", "\n\t");
+            P4View = StringUtils.replace(P4View, ";", "\n\t");
+        }
+
+        if (desc == null || desc.length() < 1) {
+            log("Label Description not set, assuming 'AntLabel'",
+                Project.MSG_WARN);
+            desc = "AntLabel";
+        }
+
+        if (lock != null && !lock.equalsIgnoreCase("locked")) {
+            log("lock attribute invalid - ignoring", Project.MSG_WARN);
+        }
+
+        if (name == null || name.length() < 1) {
+            SimpleDateFormat formatter
+                = new SimpleDateFormat("yyyy.MM.dd-hh:mm");
+            Date now = new Date();
+            name = "AntLabel-" + formatter.format(now);
+            log("name not set, assuming '" + name + "'", Project.MSG_WARN);
+        }
+
+
+        //We have to create a unlocked label first
+        String newLabel =
+                "Label: " + name
+                + "\nDescription: " + desc
+                + "\nOptions: unlocked"
+                + "\nView: \n\t" + P4View;
+
+        P4Handler handler = new P4HandlerAdapter() {
+            public void process(String line) {
+                log(line, Project.MSG_VERBOSE);
+            }
+        };
+
+        handler.setOutput(newLabel);
+
+        execP4Command("label -i", handler);
+
+        execP4Command("labelsync -l " + name, new P4HandlerAdapter() {
+            public void process(String line) {
+                log(line, Project.MSG_VERBOSE);
+            }
+        });
+
+
+        log("Created Label " + name + " (" + desc + ") with view:\n" + P4View,
+            Project.MSG_INFO);
+
+        //Now lock if required
+        if (lock != null && lock.equalsIgnoreCase("locked")) {
+
+            log("Modifying lock status to 'locked'", Project.MSG_INFO);
+
+            final StringBuffer labelSpec = new StringBuffer();
+
+            //Read back the label spec from perforce,
+            //Replace Options
+            //Submit back to Perforce
+
+            handler = new P4HandlerAdapter() {
+                public void process(String line) {
+                    log(line, Project.MSG_VERBOSE);
+
+                    if (util.match("/^Options:/", line)) {
+                        line = "Options: " + lock;
+                    }
+
+                    labelSpec.append(line + "\n");
+                }
+            };
+
+
+            execP4Command("label -o " + name, handler);
+            log(labelSpec.toString(), Project.MSG_DEBUG);
+
+            log("Now locking label...", Project.MSG_VERBOSE);
+            handler = new P4HandlerAdapter() {
+                public void process(String line) {
+                    log(line, Project.MSG_VERBOSE);
+                }
+            };
+
+            handler.setOutput(labelSpec.toString());
+            execP4Command("label -i", handler);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Labelsync.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Labelsync.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Labelsync.java	(revision 16139)
@@ -0,0 +1,148 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ *  This method syncs an existing Perforce label against the Perforce client
+ *  or against a set of files/revisions.
+ *
+ *
+ * Example Usage:
+ * <pre>
+ *   &lt;p4labelsync name="MyLabel-${TSTAMP}-${DSTAMP}"
+ *   view="//depot/...#head;//depot2/file1#25" /&gt;
+ * </pre>
+ *
+ *
+ * @ant.task category="scm"
+ */
+public class P4Labelsync extends P4Base {
+
+    protected String name;
+    private boolean add; /* -a */
+    private boolean delete; /* -n */
+    private boolean simulationmode;  /* -n */
+    /**
+     * -a flag of p4 labelsync - preserve files which exist in the label,
+     * but not in the current view
+     * @return  add attribute
+     * if set to true the task will not remove any files from the label
+     * only add files which were not there previously or update these where the revision has changed
+     * the add attribute is the -a flag of p4 labelsync
+     */
+    public boolean isAdd() {
+        return add;
+    }
+    /**
+     * -a flag of p4 labelsync - preserve files which exist in the label,
+     * but not in the current view
+     * @param add  if set to true the task will not remove any files from the label
+     * only add files which were not there previously or update these where the revision has changed
+     * the add attribute is the -a flag of p4 labelsync
+     */
+    public void setAdd(boolean add) {
+        this.add = add;
+    }
+    /**
+     * -d flag of p4 labelsync; indicates an intention of deleting from the label
+     * the files specified in the view
+     * @return  delete attribute
+     */
+    public boolean isDelete() {
+        return delete;
+    }
+
+    /**
+     * -d flag of p4 labelsync; indicates an intention of deleting from the label
+     *  the files specified in the view
+     * @param delete indicates intention of deleting from the label
+     * the files specified in the view
+     */
+    public void setDelete(boolean delete) {
+        this.delete = delete;
+    }
+
+
+    /**
+     * The name of the label; optional, default "AntLabel"
+     * @param name of the label
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+    /**
+     * -n flag of p4 labelsync - display changes without actually doing them
+     * @return -n flag of p4 labelsync
+     */
+    public boolean isSimulationmode() {
+        return simulationmode;
+    }
+    /**
+     * -n flag of p4 labelsync - display changes without actually doing them
+     * @param simulationmode display changes without actually doing them
+     */
+    public void setSimulationmode(boolean simulationmode) {
+        this.simulationmode = simulationmode;
+    }
+
+
+    /**
+     *  do the work
+     * @throws BuildException if the label name is not supplied
+     */
+    public void execute() throws BuildException {
+        log("P4Labelsync exec:", Project.MSG_INFO);
+
+        if (P4View != null && P4View.length() >= 1) {
+            P4View = StringUtils.replace(P4View, ":", "\n\t");
+            P4View = StringUtils.replace(P4View, ";", "\n\t");
+        }
+        if (P4View == null) {
+            P4View = "";
+        }
+
+        if (name == null || name.length() < 1) {
+            throw new BuildException("name attribute is compulsory for labelsync");
+        }
+
+        if (this.isSimulationmode()) {
+            P4CmdOpts = P4CmdOpts + " -n";
+        }
+        if (this.isDelete()) {
+            P4CmdOpts = P4CmdOpts + " -d";
+        }
+        if (this.isAdd()) {
+            P4CmdOpts = P4CmdOpts + " -a";
+        }
+
+        execP4Command("-s labelsync -l " + name + " " + P4CmdOpts + " " + P4View,
+            new SimpleP4OutputHandler(this));
+
+
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4OutputHandler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4OutputHandler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4OutputHandler.java	(revision 16139)
@@ -0,0 +1,33 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import org.apache.tools.ant.BuildException;
+
+/** Interface for p4 job output stream handler. Classes implementing this interface
+ * can be called back by P4Base.execP4Command();
+ *
+ */
+public interface P4OutputHandler {
+    /**
+     * implementations will be able to process lines of output from Perforce
+     * @param line a line of stdout or stderr coming from Perforce
+     * @throws BuildException implementations are allowed to throw BuildException
+     */
+    void process(String line) throws BuildException;
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4OutputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4OutputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4OutputStream.java	(revision 16139)
@@ -0,0 +1,84 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import java.io.OutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * heavily inspired from LogOutputStream
+ * this stream class calls back the P4Handler on each line of stdout or stderr read
+ */
+public class P4OutputStream extends OutputStream {
+    private P4Handler handler;
+    private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+    private boolean skip = false;
+
+    /**
+     * creates a new P4OutputStream for a P4Handler
+     * @param handler   the handler which will process the streams
+     */
+    public P4OutputStream(P4Handler handler) {
+        this.handler = handler;
+    }
+
+    /**
+     * Write the data to the buffer and flush the buffer, if a line
+     * separator is detected.
+     *
+     * @param cc data to log (byte).
+     * @throws IOException IOException  if an I/O error occurs. In particular,
+     *             an <code>IOException</code> may be thrown if the
+     *             output stream has been closed.
+     */
+    public void write(int cc) throws IOException {
+        final byte c = (byte) cc;
+        if ((c == '\n') || (c == '\r')) {
+            if (!skip) {
+                processBuffer();
+            }
+        } else {
+            buffer.write(cc);
+        }
+        skip = (c == '\r');
+    }
+
+
+    /**
+     * Converts the buffer to a string and sends it to <code>processLine</code>
+     */
+    protected void processBuffer() {
+        handler.process(buffer.toString());
+        buffer.reset();
+    }
+
+    /**
+     * Writes all remaining
+     * @throws IOException if an I/O error occurs.
+     */
+    public void close() throws IOException {
+        if (buffer.size() > 0) {
+            processBuffer();
+        }
+        super.close();
+    }
+
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Reopen.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Reopen.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Reopen.java	(revision 16139)
@@ -0,0 +1,60 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Reopen Perforce checkout files between changelists.
+ *
+ *
+ * @ant.task category="scm"
+ */
+public class P4Reopen extends P4Base {
+
+    private String toChange = "";
+
+    /**
+     * The changelist to move files to; required.
+     * @param toChange new change list number
+     * @throws BuildException if the change parameter is null or empty
+     */
+    public void setToChange(String toChange) throws BuildException {
+        if (toChange == null && !toChange.equals("")) {
+            throw new BuildException("P4Reopen: tochange cannot be null or empty");
+        }
+
+        this.toChange = toChange;
+    }
+
+    /**
+     * do the work
+     * @throws BuildException if P4View is null
+     */
+    public void execute() throws BuildException {
+        if (P4View == null) {
+            throw new BuildException("No view specified to reopen");
+        }
+        execP4Command("-s reopen -c " + toChange + " " + P4View, new SimpleP4OutputHandler(this));
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Resolve.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Resolve.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Resolve.java	(revision 16139)
@@ -0,0 +1,186 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+* Portions of this software are based upon public domain software
+* originally written at the National Center for Supercomputing Applications,
+* University of Illinois, Urbana-Champaign.
+*/
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * @ant.task category="scm"
+ */
+public class P4Resolve extends P4Base {
+    private String resolvemode = null;
+
+
+    private boolean redoall; /* -f */
+    private boolean simulationmode;  /* -n */
+    private boolean forcetextmode;  /* -t */
+    private boolean markersforall; /* -v */
+    private static final String AUTOMATIC = "automatic";
+    private static final String FORCE = "force";
+    private static final String SAFE = "safe";
+    private static final String THEIRS = "theirs";
+    private static final String YOURS = "yours";
+    private static final String[] RESOLVE_MODES = {
+        AUTOMATIC,
+        FORCE,
+        SAFE,
+        THEIRS,
+        YOURS
+    };
+   /**
+    * returns the resolve mode
+    * @return  returns the resolve mode
+    */
+    public String getResolvemode() {
+        return resolvemode;
+    }
+    /**
+     * values for resolvemode
+     * <ul>
+     * <li> automatic -am</li>
+     * <li> force -af </li>
+     * <li> safe -as </li>
+     * <li> theirs -at </li>
+     * <li> yours -ay </li>
+     * </ul>
+     * @param resolvemode one of automatic, force, safe, theirs, yours
+     */
+    public void setResolvemode(String resolvemode) {
+        boolean found = false;
+        for (int counter = 0; counter < RESOLVE_MODES.length; counter++) {
+            if (resolvemode.equals(RESOLVE_MODES[counter])) {
+                found = true;
+                break;
+            }
+        }
+        if (!found) {
+            throw new BuildException("Unacceptable value for resolve mode");
+        }
+        this.resolvemode = resolvemode;
+    }
+
+    /**
+     * allows previously resolved files to be resolved again
+     * @return flag indicating whether one wants to
+     * allow previously resolved files to be resolved again
+     */
+    public boolean isRedoall() {
+        return redoall;
+    }
+
+    /**
+     * set the redoall flag
+     * @param redoall flag indicating whether one want to
+     * allow previously resolved files to be resolved again
+     */
+    public void setRedoall(boolean redoall) {
+        this.redoall = redoall;
+    }
+
+    /**
+     * read the simulation mode flag
+     * @return flag indicating whether one wants just to simulate
+     * the p4 resolve operation whithout actually doing it
+     */
+    public boolean isSimulationmode() {
+        return simulationmode;
+    }
+
+    /**
+     * sets a flag
+     * @param simulationmode set to true, lists the integrations which would be performed,
+     * without actually doing them.
+     */
+    public void setSimulationmode(boolean simulationmode) {
+        this.simulationmode = simulationmode;
+    }
+
+    /**
+     * If set to true, attempts a textual merge, even for binary files
+     * @return flag value
+     */
+    public boolean isForcetextmode() {
+        return forcetextmode;
+    }
+
+    /**
+     * If set to true, attempts a textual merge, even for binary files
+     * @param forcetextmode set the flag value
+     */
+    public void setForcetextmode(boolean forcetextmode) {
+        this.forcetextmode = forcetextmode;
+    }
+
+    /**
+     * If set to true, puts in markers for all changes, conflicting or not
+     * @return  flag markersforall value
+     */
+    public boolean isMarkersforall() {
+        return markersforall;
+    }
+
+    /**
+      * If set to true, puts in markers for all changes, conflicting or not
+     * @param markersforall flag true or false
+     */
+    public void setMarkersforall(boolean markersforall) {
+        this.markersforall = markersforall;
+    }
+
+    /**
+     *  execute the p4 resolve
+     * @throws BuildException if there is a wrong resolve mode specified
+     *  or no view specified
+     */
+    public void execute() throws BuildException {
+        if (this.resolvemode.equals(AUTOMATIC)) {
+            P4CmdOpts = P4CmdOpts + " -am";
+        } else if (this.resolvemode.equals(FORCE)) {
+            P4CmdOpts = P4CmdOpts + " -af";
+        } else if (this.resolvemode.equals(SAFE)) {
+            P4CmdOpts = P4CmdOpts + " -as";
+        } else if (this.resolvemode.equals(THEIRS)) {
+            P4CmdOpts = P4CmdOpts + " -at";
+        } else if (this.resolvemode.equals(YOURS)) {
+            P4CmdOpts = P4CmdOpts + " -ay";
+        } else {
+            throw new BuildException("unsupported or absent resolve mode");
+        }
+        if (P4View == null) {
+            throw new BuildException("please specify a view");
+        }
+        if (this.isRedoall()) {
+            P4CmdOpts = P4CmdOpts + " -f";
+        }
+        if (this.isSimulationmode()) {
+            P4CmdOpts = P4CmdOpts + " -n";
+        }
+        if (this.isForcetextmode()) {
+            P4CmdOpts = P4CmdOpts + " -t";
+        }
+        if (this.isMarkersforall()) {
+            P4CmdOpts = P4CmdOpts + " -v";
+        }
+        execP4Command("-s resolve " + P4CmdOpts + " " + P4View, new SimpleP4OutputHandler(this));
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Revert.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Revert.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Revert.java	(revision 16139)
@@ -0,0 +1,85 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Revert Perforce open files or files in a changelist
+ *
+ *
+ * @ant.task category="scm"
+ */
+public class P4Revert extends P4Base {
+
+    private String revertChange = null;
+    private boolean onlyUnchanged = false;
+
+    /**
+     * The changelist to revert; optional.
+     * @param revertChange : the change list to revert
+     * @throws BuildException if the change list is null or empty string
+     */
+    public void setChange(String revertChange) throws BuildException {
+        if (revertChange == null && !revertChange.equals("")) {
+            throw new BuildException("P4Revert: change cannot be null or empty");
+        }
+
+        this.revertChange = revertChange;
+
+    }
+
+    /**
+     * flag to revert only unchanged files (p4 revert -a); optional, default false.
+     * @param onlyUnchanged if set to true revert only unchanged files
+     */
+    public void setRevertOnlyUnchanged(boolean onlyUnchanged) {
+        this.onlyUnchanged = onlyUnchanged;
+    }
+
+    /**
+     * do the work
+     * @throws BuildException if an error occurs during the execution of the Perforce command
+     * and failonError is set to true
+     */
+    public void execute() throws BuildException {
+
+        /* Here we can either revert any unchanged files in a changelist
+         * or
+         * any files regardless of whether they have been changed or not
+         *
+         *
+         * The whole process also accepts a p4 filespec
+         */
+        String p4cmd = "-s revert";
+        if (onlyUnchanged) {
+            p4cmd += " -a";
+        }
+
+        if (revertChange != null) {
+            p4cmd += " -c " + revertChange;
+        }
+
+        execP4Command(p4cmd + " " + P4View, new SimpleP4OutputHandler(this));
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Submit.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Submit.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Submit.java	(revision 16139)
@@ -0,0 +1,148 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import java.util.Vector;
+
+/** Submits a numbered changelist to Perforce.
+ *
+ * <B>Note:</B> P4Submit cannot (yet) submit the default changelist.
+ * This shouldn't be a problem with the ANT task as the usual flow is
+ * P4Change to create a new numbered change followed by P4Edit then P4Submit.
+ *
+ * Example Usage:-<br>
+ * &lt;p4submit change="${p4.change}" /&gt;
+ *
+ *
+ * @ant.task category="scm"
+ */
+public class P4Submit extends P4Base {
+
+    //ToDo: If dealing with default cl need to parse out <enter description here>
+    /**
+     * change list number
+     */
+    public String change;
+    /**
+     * change property
+     */
+    private String changeProperty;
+    /**
+     * needsresolveproperty
+     */
+    private String needsResolveProperty;
+    /**
+     * set the change list number to submit
+     * @param change The changelist number to submit; required.
+     */
+    public void setChange(String change) {
+        this.change = change;
+    }
+    /**
+     * property defining the change number if the change number gets renumbered
+     * @param changeProperty name of a new property to which the change number
+     * will be assigned if it changes
+     * @since ant 1.6.1
+     */
+    public void setChangeProperty(String changeProperty) {
+        this.changeProperty = changeProperty;
+    }
+    /**
+     * property defining the need to resolve the change list
+     * @param needsResolveProperty a property which will be set if the change needs resolve
+     * @since ant 1.6.1
+     */
+    public void setNeedsResolveProperty(String needsResolveProperty) {
+        this.needsResolveProperty = needsResolveProperty;
+    }
+
+    /**
+     * do the work
+     * @throws BuildException if no change list specified
+     */
+    public void execute() throws BuildException {
+        if (change != null) {
+            execP4Command("submit -c " + change, (P4HandlerAdapter) new P4SubmitAdapter(this));
+        } else {
+            //here we'd parse the output from change -o into submit -i
+            //in order to support default change.
+            throw new BuildException("No change specified (no support for default change yet....");
+        }
+    }
+
+    /**
+     * internal class used to process the output of p4 submit
+     */
+    public class P4SubmitAdapter extends SimpleP4OutputHandler {
+        public P4SubmitAdapter(P4Base parent) {
+            super(parent);
+        }
+        /**
+         * process a line of stdout/stderr coming from Perforce
+         * @param line line of stdout or stderr coming from Perforce
+         */
+        public void process(String line) {
+            super.process(line);
+            getProject().setProperty("p4.needsresolve", "0");
+            // this type of output might happen
+            // Change 18 renamed change 20 and submitted.
+            if (util.match("/renamed/", line)) {
+                try {
+                    Vector myarray = new Vector();
+                    util.split(myarray, line);
+                    boolean found = false;
+                    for (int counter = 0; counter < myarray.size(); counter++) {
+                        if (found == true) {
+                            String chnum = (String) myarray.elementAt(counter + 1);
+                            int changenumber = Integer.parseInt(chnum);
+                            log("Perforce change renamed " + changenumber, Project.MSG_INFO);
+                            getProject().setProperty("p4.change", "" + changenumber);
+                            if (changeProperty != null) {
+                                getProject().setNewProperty(changeProperty, chnum);
+                            }
+                            found = false;
+                        }
+                        if (((myarray.elementAt(counter))).equals("renamed")) {
+                            found = true;
+                        }
+                    }
+                // NumberFormatException or ArrayOutOfBondsException could happen here
+                } catch (Exception e) {
+                    String msg = "Failed to parse " + line  + "\n"
+                            + " due to " + e.getMessage();
+                    throw new BuildException(msg, e, getLocation());
+                }
+            }
+            if (util.match("/p4 submit -c/", line)) {
+                getProject().setProperty("p4.needsresolve", "1");
+                if (needsResolveProperty != null) {
+                    getProject().setNewProperty(needsResolveProperty, "true");
+                }
+            }
+
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Sync.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Sync.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/P4Sync.java	(revision 16139)
@@ -0,0 +1,105 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/** Synchronize client space to a Perforce depot view.
+ *
+ *  The API allows additional functionality of the "p4 sync" command
+ * (such as "p4 sync -f //...#have" or other exotic invocations).</P>
+ *
+ * <b>Example Usage:</b>
+ * <table border="1">
+ * <th>Function</th><th>Command</th>
+ * <tr><td>Sync to head using P4USER, P4PORT and P4CLIENT settings specified</td>
+ * <td>&lt;P4Sync <br>P4view="//projects/foo/main/source/..." <br>
+ * P4User="fbloggs" <br>P4Port="km01:1666" <br>P4Client="fbloggsclient" /&gt;</td></tr>
+ * <tr><td>Sync to head using P4USER, P4PORT and P4CLIENT settings defined in environment</td>
+ * <td>&lt;P4Sync P4view="//projects/foo/main/source/..." /&gt;</td></tr>
+ * <tr><td>Force a re-sync to head, refreshing all files</td>
+ * <td>&lt;P4Sync force="yes" P4view="//projects/foo/main/source/..." /&gt;</td></tr>
+ * <tr><td>Sync to a label</td><td>&lt;P4Sync label="myPerforceLabel" /&gt;</td></tr>
+ * </table>
+ *
+ * @todo Add decent label error handling for non-exsitant labels
+ *
+ *
+ * @ant.task category="scm"
+ */
+public class P4Sync extends P4Base {
+
+    String label;
+    private String syncCmd = "";
+
+    /**
+     * Label to sync client to; optional.
+     * @param label name of a label against which one want to sync
+     * @throws BuildException if label is null or empty string
+     */
+    public void setLabel(String label) throws BuildException {
+        if (label == null && !label.equals("")) {
+            throw new BuildException("P4Sync: Labels cannot be Null or Empty");
+        }
+
+        this.label = label;
+
+    }
+
+
+    /**
+     * force a refresh of files, if this attribute is set; false by default.
+     * @param force sync all files, whether they are supposed to be already uptodate or not.
+     * @throws BuildException if a label is set and force is null
+     */
+    public void setForce(String force) throws BuildException {
+        if (force == null && !label.equals("")) {
+            throw new BuildException("P4Sync: If you want to force, set force to non-null string!");
+        }
+        P4CmdOpts = "-f";
+    }
+
+    /**
+     * do the work
+     * @throws BuildException if an error occurs during the execution of the Perforce command
+     * and failOnError is set to true
+     */
+    public void execute() throws BuildException {
+
+
+        if (P4View != null) {
+            syncCmd = P4View;
+        }
+
+
+        if (label != null && !label.equals("")) {
+            syncCmd = syncCmd + "@" + label;
+        }
+
+
+        log("Execing sync " + P4CmdOpts + " " + syncCmd, Project.MSG_VERBOSE);
+
+        execP4Command("-s sync " + P4CmdOpts + " " + syncCmd, new SimpleP4OutputHandler(this));
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/SimpleP4OutputHandler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/SimpleP4OutputHandler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/SimpleP4OutputHandler.java	(revision 16139)
@@ -0,0 +1,93 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+/*
+ * Portions of this software are based upon public domain software
+ * originally written at the National Center for Supercomputing Applications,
+ * University of Illinois, Urbana-Champaign.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * simple implementation of P4HandlerAdapter used by tasks which are not
+ * actually processing the output from Perforce
+ */
+public class SimpleP4OutputHandler extends P4HandlerAdapter {
+
+    P4Base parent;
+
+    /**
+     * simple constructor
+     * @param parent  a P4Base instance
+     */
+    public SimpleP4OutputHandler(P4Base parent) {
+        this.parent = parent;
+    }
+
+    /**
+     * process one line of stderr/stdout
+     * if error conditions are detected, then setters are called on the
+     * parent
+     * @param line line of output
+     * @throws BuildException does not throw exceptions any more
+     */
+    public void process(String line) throws BuildException {
+        if (parent.util.match("/^exit/", line)) {
+            return;
+        }
+
+        //Throw exception on errors (except up-to-date)
+        //
+        //When a server is down, the code expects :
+        //Perforce client error:
+        //Connect to server failed; check $P4PORT.
+        //TCP connect to localhost:1666 failed.
+        //connect: localhost:1666: Connection refused
+        //Some forms producing commands (p4 -s change -o) do tag the output
+        //others don't.....
+        //Others mark errors as info, for example edit a file
+        //which is already open for edit.....
+        //Just look for error: - catches most things....
+
+        if (parent.util.match("/^error:/", line)
+            || parent.util.match("/^Perforce client error:/", line)) {
+            //when running labelsync, if view elements are in sync,
+            //Perforce produces a line of output
+            //looking like this one :
+            //error: //depot/file2 - label in sync.
+            if (!parent.util.match("/label in sync/", line)
+                && !parent.util.match("/up-to-date/", line)) {
+                parent.setInError(true);
+            } else {
+                //sync says "error:" when a file is up-to-date
+                line = parent.util.substitute("s/^[^:]*: //", line);
+            }
+        } else if (parent.util.match("/^info.*?:/", line)) {
+            //sometimes there's "info1:
+            line = parent.util.substitute("s/^[^:]*: //", line);
+        }
+        parent.log(line, parent.getInError() ? Project.MSG_ERR : Project.MSG_INFO);
+
+        if (parent.getInError()) {
+            parent.setErrorMessage(parent.getErrorMessage() + line + StringUtils.LINE_SEP);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/package.html
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/package.html	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/perforce/package.html	(revision 16139)
@@ -0,0 +1,29 @@
+<body>
+ANT Tasks for Perforce integration.
+
+These tasks provide basic P4 capabilities to automated ANT-based build systems. <b>Note:</b> 
+the tasks in this package are linked against the Jakarta ORO 2.0 library which
+brings the power of Perl 5 regular expressions to Java.
+
+These tasks also require you to have the p4 (or p4.exe) client in your path.
+
+@see <A HREF="http://ant.apache.org/">Ant Project</A>
+@see <A HREF="http://www.perforce.com/">Perforce</A>
+
+@see org.apache.tools.ant.taskdefs.optional.perforce.P4Sync
+@see org.apache.tools.ant.taskdefs.optional.perforce.P4Label
+@see org.apache.tools.ant.taskdefs.optional.perforce.P4Have
+@see org.apache.tools.ant.taskdefs.optional.perforce.P4Change
+@see org.apache.tools.ant.taskdefs.optional.perforce.P4Edit
+@see org.apache.tools.ant.taskdefs.optional.perforce.P4Submit
+@see org.apache.tools.ant.taskdefs.optional.perforce.P4Counter
+
+
+
+@author <A HREF="mailto:leslie.hughes@rubus.com">Les Hughes</A>
+
+
+<hr>
+<p align="center">Copyright &copy; 2000-2001,2003 Apache Software Foundation. All rights
+Reserved.</p>
+</body>
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/Pvcs.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/Pvcs.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/Pvcs.java	(revision 16139)
@@ -0,0 +1,652 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.pvcs;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.text.ParseException;
+import java.util.Enumeration;
+import java.util.Random;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.taskdefs.PumpStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ *
+ * Extracts the latest edition of the source code from a PVCS repository.
+ * PVCS is a version control system
+ * developed by <a href="http://www.merant.com/products/pvcs">Merant</a>.
+ * <br>
+ * Before using this tag, the user running ant must have access to the commands
+ * of PVCS (get and pcli) and must have access to the repository. Note that the way to specify
+ * the repository is platform dependent so use property to specify location of repository.
+ * <br>
+ * This version has been tested agains PVCS version 6.5 and 6.6 under Windows and Solaris.
+
+ *
+ * <b>19-04-2001</b> <p>The task now has a more robust
+ * parser. It allows for platform independant file paths
+ * and supports file names with <i>()</i>. Thanks to Erik Husby for
+ * bringing the bug to my attention.
+ *
+ * <b>27-04-2001</b> <p>UNC paths are now handled properly.
+ * Fix provided by Don Jeffery. He also added an <i>UpdateOnly</i> flag
+ * that, when true, conditions the PVCS get using the -U option to only
+ * update those files that have a modification time (in PVCS) that is newer
+ * than the existing workfile.
+ *
+ * <b>25-10-2002</b> <p>Added a revision attribute that currently is a
+ * synonym for label, but in a future release the behavior of the label
+ * attribute will change to use the -v option of GET.  See bug #13847 for
+ * discussion.
+ *
+ */
+public class Pvcs extends org.apache.tools.ant.Task {
+    private String pvcsbin;
+    private String repository;
+    private String pvcsProject;
+    private Vector pvcsProjects;
+    private String workspace;
+    private String force;
+    private String promotiongroup;
+    private String label;
+    private String revision;
+    private boolean ignorerc;
+    private boolean updateOnly;
+    private String filenameFormat;
+    private String lineStart;
+    private String userId;
+    private String config;
+    /**
+     * Constant for the thing to execute
+     */
+    private static final String PCLI_EXE = "pcli";
+
+    /*
+     * Constant for the PCLI listversionedfiles recursive i a format "get" understands
+     */
+    // private static final String PCLI_LVF_ARGS = "lvf -z -aw";
+
+    /**
+     * Constant for the thing to execute
+     */
+    private static final String GET_EXE = "get";
+
+
+    protected int runCmd(Commandline cmd, ExecuteStreamHandler out) {
+        try {
+            Project aProj = getProject();
+            Execute exe = new Execute(out);
+            exe.setAntRun(aProj);
+            exe.setWorkingDirectory(aProj.getBaseDir());
+            exe.setCommandline(cmd.getCommandline());
+            return exe.execute();
+        } catch (java.io.IOException e) {
+            String msg = "Failed executing: " + cmd.toString()
+                + ". Exception: " + e.getMessage();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+    private String getExecutable(String exe) {
+        StringBuffer correctedExe = new StringBuffer();
+        if (getPvcsbin() != null) {
+            if (pvcsbin.endsWith(File.separator)) {
+                correctedExe.append(pvcsbin);
+            } else {
+                correctedExe.append(pvcsbin).append(File.separator);
+            }
+        }
+        return correctedExe.append(exe).toString();
+    }
+
+    /**
+     * @exception org.apache.tools.ant.BuildException Something is stopping the build...
+     */
+    public void execute() throws org.apache.tools.ant.BuildException {
+        int result = 0;
+
+        if (repository == null || repository.trim().equals("")) {
+            throw new BuildException("Required argument repository not specified");
+        }
+
+        // Check workspace exists
+        // Launch PCLI listversionedfiles -z -aw
+        // Capture output
+        // build the command line from what we got the format is
+        Commandline commandLine = new Commandline();
+        commandLine.setExecutable(getExecutable(PCLI_EXE));
+
+        commandLine.createArgument().setValue("lvf");
+        commandLine.createArgument().setValue("-z");
+        commandLine.createArgument().setValue("-aw");
+        if (getWorkspace() != null) {
+            commandLine.createArgument().setValue("-sp" + getWorkspace());
+        }
+        commandLine.createArgument().setValue("-pr" + getRepository());
+
+        String uid = getUserId();
+
+        if (uid != null) {
+            commandLine.createArgument().setValue("-id" + uid);
+        }
+
+        // default pvcs project is "/"
+        if (getPvcsproject() == null && getPvcsprojects().isEmpty()) {
+            pvcsProject = "/";
+        }
+
+        if (getPvcsproject() != null) {
+            commandLine.createArgument().setValue(getPvcsproject());
+        }
+        if (!getPvcsprojects().isEmpty()) {
+            Enumeration e = getPvcsprojects().elements();
+            while (e.hasMoreElements()) {
+                String projectName = ((PvcsProject) e.nextElement()).getName();
+                if (projectName == null || (projectName.trim()).equals("")) {
+                    throw new BuildException("name is a required attribute "
+                        + "of pvcsproject");
+                }
+                commandLine.createArgument().setValue(projectName);
+            }
+        }
+
+        File tmp = null;
+        File tmp2 = null;
+        try {
+            Random rand = new Random(System.currentTimeMillis());
+            tmp = new File("pvcs_ant_" + rand.nextLong() + ".log");
+            FileOutputStream fos = new FileOutputStream(tmp);
+            tmp2 = new File("pvcs_ant_" + rand.nextLong() + ".log");
+            log(commandLine.describeCommand(), Project.MSG_VERBOSE);
+            try {
+                result = runCmd(commandLine,
+                                new PumpStreamHandler(fos,
+                                    new LogOutputStream(this,
+                                                        Project.MSG_WARN)));
+            } finally {
+                fos.close();
+            }
+
+            if (Execute.isFailure(result) && !ignorerc) {
+                String msg = "Failed executing: " + commandLine.toString();
+                throw new BuildException(msg, getLocation());
+            }
+
+            if (!tmp.exists()) {
+                throw new BuildException("Communication between ant and pvcs "
+                    + "failed. No output generated from executing PVCS "
+                    + "commandline interface \"pcli\" and \"get\"");
+            }
+
+            // Create folders in workspace
+            log("Creating folders", Project.MSG_INFO);
+            createFolders(tmp);
+
+            // Massage PCLI lvf output transforming '\' to '/' so get command works appropriately
+            massagePCLI(tmp, tmp2);
+
+            // Launch get on output captured from PCLI lvf
+            commandLine.clearArgs();
+            commandLine.setExecutable(getExecutable(GET_EXE));
+
+            if (getConfig() != null && getConfig().length() > 0) {
+                commandLine.createArgument().setValue("-c" + getConfig());
+            }
+
+            if (getForce() != null && getForce().equals("yes")) {
+                commandLine.createArgument().setValue("-Y");
+            } else {
+                commandLine.createArgument().setValue("-N");
+            }
+
+            if (getPromotiongroup() != null) {
+                commandLine.createArgument().setValue("-G"
+                    + getPromotiongroup());
+            } else {
+                if (getLabel() != null) {
+                    commandLine.createArgument().setValue("-r" + getLabel());
+                } else {
+                    if (getRevision() != null) {
+                        commandLine.createArgument().setValue("-r"
+                            + getRevision());
+                    }
+                }
+            }
+
+            if (updateOnly) {
+                commandLine.createArgument().setValue("-U");
+            }
+
+            commandLine.createArgument().setValue("@" + tmp2.getAbsolutePath());
+            log("Getting files", Project.MSG_INFO);
+            log("Executing " + commandLine.toString(), Project.MSG_VERBOSE);
+            result = runCmd(commandLine,
+                new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_WARN));
+            if (result != 0 && !ignorerc) {
+                String msg = "Failed executing: " + commandLine.toString()
+                    + ". Return code was " + result;
+                throw new BuildException(msg, getLocation());
+            }
+
+        } catch (FileNotFoundException e) {
+            String msg = "Failed executing: " + commandLine.toString()
+                + ". Exception: " + e.getMessage();
+            throw new BuildException(msg, getLocation());
+        } catch (IOException e) {
+            String msg = "Failed executing: " + commandLine.toString()
+                + ". Exception: " + e.getMessage();
+            throw new BuildException(msg, getLocation());
+        } catch (ParseException e) {
+            String msg = "Failed executing: " + commandLine.toString()
+                + ". Exception: " + e.getMessage();
+            throw new BuildException(msg, getLocation());
+        } finally {
+            if (tmp != null) {
+                tmp.delete();
+            }
+            if (tmp2 != null) {
+                tmp2.delete();
+            }
+        }
+    }
+
+    /**
+     * Parses the file and creates the folders specified in the output section
+     */
+    private void createFolders(File file) throws IOException, ParseException {
+        BufferedReader in = null;
+        try {
+            in = new BufferedReader(new FileReader(file));
+            MessageFormat mf = new MessageFormat(getFilenameFormat());
+            String line = in.readLine();
+            while (line != null) {
+                log("Considering \"" + line + "\"", Project.MSG_VERBOSE);
+                if (line.startsWith("\"\\")
+                    || line.startsWith("\"/")
+                    || (line.length() > 3 && line.startsWith("\"")
+                        && Character.isLetter(line.charAt(1))
+                        && String.valueOf(line.charAt(2)).equals(":")
+                        && String.valueOf(line.charAt(3)).equals("\\"))) {
+                    Object[] objs = mf.parse(line);
+                    String f = (String) objs[1];
+                    // Extract the name of the directory from the filename
+                    int index = f.lastIndexOf(File.separator);
+                    if (index > -1) {
+                        File dir = new File(f.substring(0, index));
+                        if (!dir.exists()) {
+                            log("Creating " + dir.getAbsolutePath(),
+                                Project.MSG_VERBOSE);
+                            if (dir.mkdirs()) {
+                                log("Created " + dir.getAbsolutePath(),
+                                    Project.MSG_INFO);
+                            } else {
+                                log("Failed to create "
+                                    + dir.getAbsolutePath(),
+                                    Project.MSG_INFO);
+                            }
+                        } else {
+                            log(dir.getAbsolutePath() + " exists. Skipping",
+                                Project.MSG_VERBOSE);
+                        }
+                    } else {
+                        log("File separator problem with " + line,
+                            Project.MSG_WARN);
+                    }
+                } else {
+                    log("Skipped \"" + line + "\"", Project.MSG_VERBOSE);
+                }
+                line = in.readLine();
+            }
+        } finally {
+            if (in != null) {
+                in.close();
+            }
+        }
+    }
+
+
+    /**
+     * Simple hack to handle the PVCS command-line tools botch when
+     * handling UNC notation.
+     */
+    private void massagePCLI(File in, File out)
+        throws FileNotFoundException, IOException {
+        BufferedReader inReader = null;
+        BufferedWriter outWriter = null;
+        try {
+            inReader = new BufferedReader(new FileReader(in));
+            outWriter = new BufferedWriter(new FileWriter(out));
+            String s = null;
+            while ((s = inReader.readLine()) != null) {
+                String sNormal = s.replace('\\', '/');
+                outWriter.write(sNormal);
+                outWriter.newLine();
+            }
+        } finally {
+            if (inReader != null) {
+                inReader.close();
+            }
+            if (outWriter != null) {
+                outWriter.close();
+            }
+        }
+    }
+
+    /**
+     * Get network name of the PVCS repository
+     * @return String
+     */
+    public String getRepository() {
+        return repository;
+    }
+
+    /**
+     *  The filenameFormat attribute defines a MessageFormat string used
+     *  to parse the output of the pcli command.  It defaults to
+     *  <code>{0}-arc({1})</code>.  Repositories where the archive
+     *   extension is not  -arc should set this.
+     */
+    public String getFilenameFormat() {
+        return filenameFormat;
+    }
+
+    /**
+     * The format of the folder names; optional.
+     * This must be in a format suitable for
+     * <code>java.text.MessageFormat</code>.
+     *  Index 1 of the format will be used as the file name.
+     *  Defaults to <code>{0}-arc({1})</code>
+     */
+    public void setFilenameFormat(String f) {
+        filenameFormat = f;
+    }
+
+    /**
+
+     * The lineStart attribute is used to parse the output of the pcli
+     * command. It defaults to <code>&quot;P:</code>.  The parser already
+     * knows about / and \\, this property is useful in cases where the
+     * repository is accessed on a Windows platform via a drive letter
+     * mapping.
+     */
+    public String getLineStart() {
+        return lineStart;
+    }
+
+    /**
+     * What a valid return value from PVCS looks like
+     *  when it describes a file.  Defaults to <code>&quot;P:</code>.
+     * If you are not using an UNC name for your repository and the
+     * drive letter <code>P</code> is incorrect for your setup, you may
+     * need to change this value, UNC names will always be
+     * accepted.
+     */
+
+    public void setLineStart(String l) {
+        lineStart = l;
+    }
+
+    /**
+     * The network name of the PVCS repository; required.
+     * @param repo String
+     */
+    public void setRepository(String repo) {
+        repository = repo;
+    }
+
+    /**
+     * Get name of the project in the PVCS repository
+     * @return String
+     */
+    public String getPvcsproject() {
+        return pvcsProject;
+    }
+
+    /**
+     * The project within the PVCS repository to extract files from;
+     * optional, default &quot;/&quot;
+     * @param prj String
+     */
+    public void setPvcsproject(String prj) {
+        pvcsProject = prj;
+    }
+
+    /**
+     * Get name of the project in the PVCS repository
+     * @return Vector
+     */
+    public Vector getPvcsprojects() {
+        return pvcsProjects;
+    }
+
+    /**
+     * Get name of the workspace to store the retrieved files
+     * @return String
+     */
+    public String getWorkspace() {
+        return workspace;
+    }
+
+    /**
+     * Workspace to use; optional.
+     * By specifying a workspace, the files are extracted to that location.
+     * A PVCS workspace is a name for a location of the workfiles and
+     * isn't as such the location itself.
+     * You define the location for a workspace using the PVCS GUI clients.
+     * If this isn't specified the default workspace for the current user is used.
+     * @param ws String
+     */
+    public void setWorkspace(String ws) {
+        workspace = ws;
+    }
+
+    /**
+     * Get name of the PVCS bin directory
+     * @return String
+     */
+    public String getPvcsbin() {
+        return pvcsbin;
+    }
+
+    /**
+     * Specifies the location of the PVCS bin directory; optional if on the PATH.
+     * On some systems the PVCS executables <i>pcli</i>
+     * and <i>get</i> are not found in the PATH. In such cases this attribute
+     * should be set to the bin directory of the PVCS installation containing
+     * the executables mentioned before. If this attribute isn't specified the
+     * tag expects the executables to be found using the PATH environment variable.
+     * @param bin PVCS bin directory
+     * @todo use a File setter and resolve paths.
+     */
+    public void setPvcsbin(String bin) {
+        pvcsbin = bin;
+    }
+
+    /**
+     * Get value of force
+     * @return String
+     */
+    public String getForce() {
+        return force;
+    }
+
+    /**
+     * Specifies the value of the force argument; optional.
+     * If set to <i>yes</i> all files that exists and are
+     * writable are overwritten. Default <i>no</i> causes the files
+     * that are writable to be ignored. This stops the PVCS command
+     * <i>get</i> to stop asking questions!
+     * @todo make a boolean setter
+     * @param f String (yes/no)
+     */
+    public void setForce(String f) {
+        if (f != null && f.equalsIgnoreCase("yes")) {
+            force = "yes";
+        } else {
+            force = "no";
+        }
+    }
+
+    /**
+     * Get value of promotiongroup
+     * @return String
+     */
+    public String getPromotiongroup() {
+        return promotiongroup;
+    }
+
+    /**
+     * Specifies the name of the promotiongroup argument
+     * @param w String
+     */
+    public void setPromotiongroup(String w) {
+        promotiongroup = w;
+    }
+
+    /**
+     * Get value of label
+     * @return String
+     */
+    public String getLabel() {
+        return label;
+    }
+
+    /**
+     * Only files marked with this label are extracted; optional.
+     * @param l String
+     */
+    public void setLabel(String l) {
+        label = l;
+    }
+
+    /**
+     * Get value of revision
+     * @return String
+     */
+    public String getRevision() {
+        return revision;
+    }
+
+    /**
+     * Only files with this revision are extract; optional.
+     * @param r String
+     */
+    public void setRevision(String r) {
+        revision = r;
+    }
+
+    /**
+     * Get value of ignorereturncode
+     * @return String
+     */
+    public boolean getIgnoreReturnCode() {
+        return ignorerc;
+    }
+
+    /**
+     * If set to true the return value from executing the pvcs
+     * commands are ignored; optional, default false.
+     */
+    public void setIgnoreReturnCode(boolean b) {
+        ignorerc = b;
+    }
+
+    /**
+     * Specify a project within the PVCS repository to extract files from.
+     * @param p
+     */
+    public void addPvcsproject(PvcsProject p) {
+        pvcsProjects.addElement(p);
+    }
+
+    public boolean getUpdateOnly() {
+        return updateOnly;
+    }
+
+    /**
+     * If set to <i>true</i> files are fetched only if
+     * newer than existing local files; optional, default false.
+     */
+    public void setUpdateOnly(boolean l) {
+        updateOnly = l;
+    }
+
+    /**
+     * returns the path of the configuration file to be used
+     * @return the path of the config file
+     */
+    public String getConfig() {
+        return config;
+    }
+
+    /**
+     * Sets a configuration file other than the default to be used.
+     * These files have a .cfg extension and are often found in archive or pvcsprop folders.
+     * @param f config file - can be given absolute or relative to ant basedir
+     */
+    public void setConfig(File f) {
+        config = f.toString();
+    }
+
+
+    public String getUserId() {
+        return userId;
+    }
+
+    /**
+     * User ID; unused.
+     * @ant.attribute ignore="true"
+     */
+
+    public void setUserId(String u) {
+        userId = u;
+    }
+
+    /**
+     * Creates a Pvcs object
+     */
+    public Pvcs() {
+        super();
+        pvcsProject = null;
+        pvcsProjects = new Vector();
+        workspace = null;
+        repository = null;
+        pvcsbin = null;
+        force = null;
+        promotiongroup = null;
+        label = null;
+        ignorerc = false;
+        updateOnly = false;
+        lineStart = "\"P:";
+        filenameFormat = "{0}-arc({1})";
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/PvcsProject.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/PvcsProject.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/pvcs/PvcsProject.java	(revision 16139)
@@ -0,0 +1,45 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.pvcs;
+
+
+
+/**
+ * represents a project within the PVCS repository to extract files from.
+ */
+public class PvcsProject {
+    private String name;
+
+    public PvcsProject() {
+        super();
+    }
+
+    /**
+     * Set the name of the project
+     */
+    public void setName(String name) {
+        PvcsProject.this.name = name;
+    }
+
+    /**
+     * Get the name of the project
+     */
+    public String getName() {
+        return name;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/scm/AntStarTeamCheckOut.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/scm/AntStarTeamCheckOut.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/scm/AntStarTeamCheckOut.java	(revision 16139)
@@ -0,0 +1,1076 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.scm;
+
+import com.starbase.starteam.Folder;
+import com.starbase.starteam.Item;
+import com.starbase.starteam.Property;
+import com.starbase.starteam.Server;
+import com.starbase.starteam.StarTeamFinder;
+import com.starbase.starteam.Type;
+import com.starbase.starteam.View;
+import com.starbase.util.Platform;
+import java.util.StringTokenizer;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+
+/**
+ * Checks out files from a specific StarTeam server, project, view, and
+ * folder.
+ * <BR><BR>
+ * This program logs in to a StarTeam server and opens up the specified
+ * project and view.  Then, it searches through that view for the given
+ * folder (or, if you prefer, it uses the root folder).  Beginning with
+ * that folder and optionally continuing recursivesly, AntStarTeamCheckOut
+ * compares each file with your include and exclude filters and checks it
+ * out only if appropriate.
+ * <BR><BR>
+ * Checked out files go to a directory you specify under the subfolder
+ * named for the default StarTeam path to the view.  That is, if you
+ * entered /home/cpovirk/work as the target folder, your project was named
+ * "OurProject," the given view was named "TestView," and that view is
+ * stored by default at "C:\projects\Test," your files would be checked
+ * out to /home/cpovirk/work/Test."  I avoided using the project name in
+ * the path because you may want to keep several versions of the same
+ * project on your computer, and I didn't want to use the view name, as
+ * there may be many "Test" or "Version 1.0" views, for example.  This
+ * system's success, of course, depends on what you set the default path
+ * to in StarTeam.
+ * <BR><BR>
+ * You can set AntStarTeamCheckOut to verbose or quiet mode.  Also, it has
+ * a safeguard against overwriting the files on your computer:  If the
+ * target directory you specify already exists, the program will throw a
+ * BuildException.  To override the exception, set <CODE>force</CODE> to
+ * true.
+ * <BR><BR>
+ * <B>This program makes use of functions from the StarTeam API.  As a result
+ * AntStarTeamCheckOut is available only to licensed users of StarTeam and
+ * requires the StarTeam SDK to function.  You must have
+ * <CODE>starteam-sdk.jar</CODE> in your classpath to run this program.
+ * For more information about the StarTeam API and how to license it, see
+ * the link below.</B>
+ *
+ * @version 1.0
+ * @see <A HREF="http://www.starbase.com/">StarBase Web Site</A>
+ *
+ * @ant.task name="starteam" category="scm" ignore="true"
+ */
+public class AntStarTeamCheckOut extends org.apache.tools.ant.Task {
+
+    /**
+     * This constant sets the filter to include all files. This default has
+     * the same result as <CODE>setIncludes("*")</CODE>.
+     *
+     * @see #getIncludes()
+     * @see #setIncludes(String includes)
+     */
+    public static final String DEFAULT_INCLUDESETTING = "*";
+
+    /**
+     * This disables the exclude filter by default. In other words, no files
+     * are excluded. This setting is equivalent to <CODE>setExcludes(null)</CODE>
+     * .
+     *
+     * @see #getExcludes()
+     * @see #setExcludes(String excludes)
+     */
+    public static final String DEFAULT_EXCLUDESETTING = null;
+
+    /**
+     * The default folder to search; the root folder. Since
+     * AntStarTeamCheckOut searches subfolders, by default it processes an
+     * entire view.
+     *
+     * @see #getFolderName()
+     * @see #setFolderName(String folderName)
+     */
+    public static final String DEFAULT_FOLDERSETTING = null;
+
+    /**
+     * This is used when formatting the output. The directory name is
+     * displayed only when it changes.
+     */
+    private Folder prevFolder = null;
+
+    /** This field keeps count of the number of files checked out.  */
+    private int checkedOut = 0;
+
+    // Change these through their GET and SET methods.
+
+    /** The name of the server you wish to connect to.  */
+    private String serverName = null;
+
+    /** The port on the server used for StarTeam.  */
+    private int serverPort = -1;
+
+    /** The name of your project.  */
+    private String projectName = null;
+
+    /**
+     * The name of the folder you want to check out files from. All subfolders
+     * will be searched, as well.
+     */
+    private String folderName = DEFAULT_FOLDERSETTING;
+
+    /** The view that the files you want are in.  */
+    private String viewName = null;
+
+    /** Your username on the StarTeam server.  */
+    private String username = null;
+
+    /** Your StarTeam password.  */
+    private String password = null;
+
+    /**
+     * The path to the root folder you want to check out to. This is a local
+     * directory.
+     */
+    private String targetFolder = null;
+
+    /**
+     * If force set to true, AntStarTeamCheckOut will overwrite files in the
+     * target directory.
+     */
+    private boolean force = false;
+
+    /**
+     * When verbose is true, the program will display all files and
+     * directories as they are checked out.
+     */
+    private boolean verbose = false;
+
+    /**
+     * Set recursion to false to check out files in only the given folder and
+     * not in its subfolders.
+     */
+    private boolean recursion = true;
+
+    // These fields deal with includes and excludes
+
+    /** All files that fit this pattern are checked out.  */
+    private String includes = DEFAULT_INCLUDESETTING;
+
+    /** All files fitting this pattern are ignored.  */
+    private String excludes = DEFAULT_EXCLUDESETTING;
+
+    /** The file delimitor on the user's system.  */
+    private String delim = Platform.getFilePathDelim();
+
+    /**
+     * whether to use the Starteam "default folder" when calculating the
+     * target paths to which files are checked out (false) or if targetFolder
+     * represents an absolute mapping to folderName.
+     */
+    private boolean targetFolderAbsolute = false;
+
+
+    /** convenient method to check for conditions */
+    private static void assertTrue(boolean value, String msg) throws BuildException {
+        if (!value) {
+            throw new BuildException(msg);
+        }
+    }
+
+
+    protected void checkParameters() throws BuildException {
+        // Check all of the properties that are required.
+        assertTrue(getServerName() != null, "ServerName must be set.");
+        assertTrue(getServerPort() != -1, "ServerPort must be set.");
+        assertTrue(getProjectName() != null, "ProjectName must be set.");
+        assertTrue(getViewName() != null, "ViewName must be set.");
+        assertTrue(getUsername() != null, "Username must be set.");
+        assertTrue(getPassword() != null, "Password must be set.");
+        assertTrue(getTargetFolder() != null, "TargetFolder must be set.");
+
+        // Because of the way I create the full target path, there
+        // must be NO slash at the end of targetFolder and folderName
+        // However, if the slash or backslash is the only character, leave it alone
+        if ((getTargetFolder().endsWith("/")
+                || getTargetFolder().endsWith("\\"))
+             && getTargetFolder().length() > 1) {
+            setTargetFolder(getTargetFolder().substring(0, getTargetFolder().length() - 1));
+        }
+
+        // Check to see if the target directory exists.
+        java.io.File dirExist = new java.io.File(getTargetFolder());
+
+        if (dirExist.isDirectory() && !getForce()) {
+            throw new BuildException("Target directory exists. Set \"force\" "
+                 + "to \"true\" to continue anyway.");
+        }
+    }
+
+
+    /**
+     * Do the execution.
+     *
+     * @exception BuildException
+     */
+    public void execute() throws BuildException {
+        log("DEPRECATED - The starteam task is deprecated.  Use stcheckout instead.",
+            Project.MSG_WARN);
+
+        // Connect to the StarTeam server, and log on.
+        Server s = getServer();
+
+        try {
+            // Search the items on this server.
+            runServer(s);
+        } finally {
+            // Disconnect from the server.
+            s.disconnect();
+        }
+        // after you are all of the properties are ok, do your thing
+        // with StarTeam.  If there are any kind of exceptions then
+        // send the message to the project log.
+
+        // Tell how many files were checked out.
+        log(checkedOut + " files checked out.");
+    }
+
+
+    /**
+     * Creates and logs in to a StarTeam server.
+     *
+     * @return A StarTeam server.
+     */
+    protected Server getServer() {
+        // Simplest constructor, uses default encryption algorithm and compression level.
+        Server s = new Server(getServerName(), getServerPort());
+
+        // Optional; logOn() connects if necessary.
+        s.connect();
+
+        // Logon using specified user name and password.
+        s.logOn(getUsername(), getPassword());
+
+        return s;
+    }
+
+
+    /**
+     * Searches for the specified project on the server.
+     *
+     * @param s A StarTeam server.
+     */
+    protected void runServer(Server s) {
+        com.starbase.starteam.Project[] projects = s.getProjects();
+
+        for (int i = 0; i < projects.length; i++) {
+            com.starbase.starteam.Project p = projects[i];
+
+            if (p.getName().equals(getProjectName())) {
+                if (getVerbose()) {
+                    log("Found " + getProjectName() + delim);
+                }
+                runProject(s, p);
+                break;
+            }
+        }
+    }
+
+
+    /**
+     * Searches for the given view in the project.
+     *
+     * @param s A StarTeam server.
+     * @param p A valid project on the given server.
+     */
+    protected void runProject(Server s, com.starbase.starteam.Project p) {
+        View[] views = p.getViews();
+
+        for (int i = 0; i < views.length; i++) {
+            View v = views[i];
+
+            if (v.getName().equals(getViewName())) {
+                if (getVerbose()) {
+                    log("Found " + getProjectName() + delim + getViewName() + delim);
+                }
+                runType(s, p, v, s.typeForName(s.getTypeNames().FILE));
+                break;
+            }
+        }
+    }
+
+
+    /**
+     * Searches for folders in the given view.
+     *
+     * @param s A StarTeam server.
+     * @param p A valid project on the server.
+     * @param v A view name from the specified project.
+     * @param t An item type which is currently always "file".
+     */
+    protected void runType(Server s, com.starbase.starteam.Project p, View v, Type t) {
+        // This is ugly; checking for the root folder.
+        Folder f = v.getRootFolder();
+
+        if (getFolderName() != null) {
+            if (getFolderName().equals("\\") || getFolderName().equals("/")) {
+                setFolderName(null);
+            } else {
+                f = StarTeamFinder.findFolder(v.getRootFolder(), getFolderName());
+                assertTrue(null != f, "ERROR: " + getProjectName() + delim
+                    + getViewName() + delim + v.getRootFolder() + delim
+                    + getFolderName() + delim
+                    + " does not exist.");
+            }
+        }
+
+        if (getVerbose() && getFolderName() != null) {
+            log("Found " + getProjectName() + delim + getViewName()
+                + delim + getFolderName() + delim + "\n");
+        }
+
+        // For performance reasons, it is important to pre-fetch all the
+        // properties we'll need for all the items we'll be searching.
+
+        // We always display the ItemID (OBJECT_ID) and primary descriptor.
+        int nProperties = 2;
+
+        // We'll need this item type's primary descriptor.
+        Property p1 = getPrimaryDescriptor(t);
+
+        // Does this item type have a secondary descriptor?
+        // If so, we'll need it.
+        Property p2 = getSecondaryDescriptor(t);
+
+        if (p2 != null) {
+            nProperties++;
+        }
+
+        // Now, build an array of the property names.
+        String[] strNames = new String[nProperties];
+        int iProperty = 0;
+
+        strNames[iProperty++] = s.getPropertyNames().OBJECT_ID;
+        strNames[iProperty++] = p1.getName();
+        if (p2 != null) {
+            strNames[iProperty++] = p2.getName();
+        }
+
+        // Pre-fetch the item properties and cache them.
+        f.populateNow(t.getName(), strNames, -1);
+
+        // Now, search for items in the selected folder.
+        runFolder(s, p, v, t, f, calcTargetFolder(v, f));
+
+        // Free up the memory used by the cached items.
+        f.discardItems(t.getName(), -1);
+    }
+
+
+    /**
+     * Returns a file object that defines the root of the local checkout tree.
+     * Depending on the value of targetFolderAbsolute, this will be either the
+     * targetFolder exactly as set by the user or the path formed by appending
+     * the default folder onto the specified target folder.
+     *
+     * @param v view from which the file is checked out, supplies the "default
+     *      folder"
+     * @param rootSourceFolder root folder of the checkout operation in Star
+     *      Team
+     * @return an object referencing the local file
+     * @see #getTargetFolderAbsolute()
+     */
+    private java.io.File calcTargetFolder(View v, Folder rootSourceFolder) {
+        java.io.File root = new java.io.File(getTargetFolder());
+
+        if (!getTargetFolderAbsolute()) {
+            // Create a variable dir that contains the name of
+            // the StarTeam folder that is the root folder in this view.
+            // Get the default path to the current view.
+            String defaultPath = v.getDefaultPath();
+
+            // convert whatever separator char is in starteam to that of the target system.
+            defaultPath = defaultPath.replace('/', java.io.File.separatorChar);
+            defaultPath = defaultPath.replace('\\', java.io.File.separatorChar);
+
+            java.io.File dir = new java.io.File(defaultPath);
+            String dirName = dir.getName();
+
+            // If it ends with separator then strip it off
+            if (dirName.endsWith(delim)) {
+                dirName = dirName.substring(0, dirName.length() - 1);
+            }
+
+            // Replace the projectName in the file's absolute path to the viewName.
+            // This makes the root target of a checkout operation equal to:
+            // targetFolder + dirName
+            StringTokenizer pathTokenizer
+                = new StringTokenizer(rootSourceFolder.getFolderHierarchy(), delim);
+            String currentToken = null;
+            boolean foundRoot = false;
+
+            while (pathTokenizer.hasMoreTokens()) {
+                currentToken = pathTokenizer.nextToken();
+                if (currentToken.equals(getProjectName()) && !foundRoot) {
+                    currentToken = dirName;
+                    foundRoot = true; // only want to do this the first time
+                }
+                root = new java.io.File(root, currentToken);
+            }
+        }
+
+        return root;
+    }
+
+
+    /**
+     * Searches for files in the given folder. This method is recursive and
+     * thus searches all subfolders.
+     *
+     * @param s A StarTeam server.
+     * @param p A valid project on the server.
+     * @param v A view name from the specified project.
+     * @param t An item type which is currently always "file".
+     * @param f The folder to search.
+     * @param tgt Target folder on local machine
+     */
+    protected void runFolder(Server s,
+                             com.starbase.starteam.Project p,
+                             View v,
+                             Type t,
+                             Folder f,
+                             java.io.File tgt) {
+        // Process all items in this folder.
+        Item[] items = f.getItems(t.getName());
+
+        for (int i = 0; i < items.length; i++) {
+            runItem(s, p, v, t, f, items[i], tgt);
+        }
+
+        // Process all subfolders recursively if recursion is on.
+        if (getRecursion()) {
+            Folder[] subfolders = f.getSubFolders();
+
+            for (int i = 0; i < subfolders.length; i++) {
+                runFolder(s, p, v, t, subfolders[i],
+                          new java.io.File(tgt, subfolders[i].getName()));
+            }
+        }
+    }
+
+
+    /**
+     * Check out one file if it matches the include filter but not the exclude
+     * filter.
+     *
+     * @param s A StarTeam server.
+     * @param p A valid project on the server.
+     * @param v A view name from the specified project.
+     * @param t An item type which is currently always "file".
+     * @param f The folder the file is localed in.
+     * @param item The file to check out.
+     * @param tgt target folder on local machine
+     */
+    protected void runItem(Server s,
+                           com.starbase.starteam.Project p,
+                           View v,
+                           Type t,
+                           Folder f,
+                           Item item,
+                           java.io.File tgt) {
+        // Get descriptors for this item type.
+        Property p1 = getPrimaryDescriptor(t);
+        Property p2 = getSecondaryDescriptor(t);
+
+        String pName = (String) item.get(p1.getName());
+
+        if (!shouldCheckout(pName)) {
+            return;
+        }
+
+        // VERBOSE MODE ONLY
+        if (getVerbose()) {
+            // Show folder only if changed.
+            boolean bShowHeader = (f != prevFolder);
+
+            if (bShowHeader) {
+                // We want to display the folder the same way you would
+                // enter it on the command line ... so we remove the
+                // View name (which is also the name of the root folder,
+                // and therefore shows up at the start of the path).
+                String strFolder = f.getFolderHierarchy();
+                int i = strFolder.indexOf(delim);
+
+                if (i >= 0) {
+                    strFolder = strFolder.substring(i + 1);
+                }
+                log("            Folder: \"" + strFolder + "\"");
+                prevFolder = f;
+
+                // If we displayed the project, view, item type, or folder,
+                // then show the list of relevant item properties.
+                StringBuffer header = new StringBuffer("                Item");
+
+                header.append(",\t").append(p1.getDisplayName());
+                if (p2 != null) {
+                    header.append(",\t").append(p2.getDisplayName());
+                }
+                log(header.toString());
+            }
+
+            // Finally, show the Item properties ...
+            // Always show the ItemID.
+            StringBuffer itemLine = new StringBuffer("                ");
+
+            itemLine.append(item.getItemID());
+
+            // Show the primary descriptor.
+            // There should always be one.
+            itemLine.append(",\t").append(formatForDisplay(p1, item.get(p1.getName())));
+
+            // Show the secondary descriptor, if there is one.
+            // Some item types have one, some don't.
+            if (p2 != null) {
+                itemLine.append(",\t").append(formatForDisplay(p2, item.get(p2.getName())));
+            }
+
+            // Show if the file is locked.
+            int locker = item.getLocker();
+
+            if (locker > -1) {
+                itemLine.append(",\tLocked by ").append(locker);
+            } else {
+                itemLine.append(",\tNot locked");
+            }
+            log(itemLine.toString());
+        }
+        // END VERBOSE ONLY
+
+        // Check it out; also ugly.
+
+        // Change the item to be checked out to a StarTeam File.
+        com.starbase.starteam.File remote = (com.starbase.starteam.File) item;
+
+        // The local file name is simply the local target path (tgt) which has
+        // been passed recursively down from the top of the tree, with the item's name appended.
+        java.io.File local = new java.io.File(tgt, (String) item.get(p1.getName()));
+
+        try {
+            remote.checkoutTo(local, Item.LockType.UNCHANGED, false, true, true);
+            checkedOut++;
+        } catch (Exception e) {
+            throw new BuildException("Failed to checkout '" + local + "'", e);
+        }
+    }
+
+
+    /**
+     * Look if the file should be checked out. Don't check it out if It fits
+     * no include filters and It fits an exclude filter.
+     *
+     * @param pName the item name to look for being included.
+     * @return whether the file should be checked out or not.
+     */
+    protected boolean shouldCheckout(String pName) {
+        boolean includeIt = matchPatterns(getIncludes(), pName);
+        boolean excludeIt = matchPatterns(getExcludes(), pName);
+
+        return (includeIt && !excludeIt);
+    }
+
+
+    /**
+     * Convenient method to see if a string match a one pattern in given set
+     * of space-separated patterns.
+     *
+     * @param patterns the space-separated list of patterns.
+     * @param pName the name to look for matching.
+     * @return whether the name match at least one pattern.
+     */
+    protected boolean matchPatterns(String patterns, String pName) {
+        if (patterns == null) {
+            return false;
+        }
+        StringTokenizer exStr = new StringTokenizer(patterns, " ");
+
+        while (exStr.hasMoreTokens()) {
+            if (DirectoryScanner.match(exStr.nextToken(), pName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * Get the primary descriptor of the given item type. Returns null if
+     * there isn't one. In practice, all item types have a primary descriptor.
+     *
+     * @param t An item type. At this point it will always be "file".
+     * @return The specified item's primary descriptor.
+     */
+    protected Property getPrimaryDescriptor(Type t) {
+        Property[] properties = t.getProperties();
+
+        for (int i = 0; i < properties.length; i++) {
+            Property p = properties[i];
+
+            if (p.isPrimaryDescriptor()) {
+                return p;
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * Get the secondary descriptor of the given item type. Returns null if
+     * there isn't one.
+     *
+     * @param t An item type. At this point it will always be "file".
+     * @return The specified item's secondary descriptor. There may not be one
+     *      for every file.
+     */
+    protected Property getSecondaryDescriptor(Type t) {
+        Property[] properties = t.getProperties();
+
+        for (int i = 0; i < properties.length; i++) {
+            Property p = properties[i];
+
+            if (p.isDescriptor() && !p.isPrimaryDescriptor()) {
+                return p;
+            }
+        }
+        return null;
+    }
+
+
+    /**
+     * Formats a property value for display to the user.
+     *
+     * @param p An item property to format.
+     * @param value
+     * @return A string containing the property, which is truncated to 35
+     *      characters for display.
+     */
+    protected String formatForDisplay(Property p, Object value) {
+        if (p.getTypeCode() == Property.Types.TEXT) {
+            String str = value.toString();
+
+            if (str.length() > 35) {
+                str = str.substring(0, 32) + "...";
+            }
+            return "\"" + str + "\"";
+        } else {
+            if (p.getTypeCode() == Property.Types.ENUMERATED) {
+                return "\"" + p.getEnumDisplayName(((Integer) value).intValue()) + "\"";
+            } else {
+                return value.toString();
+            }
+        }
+    }
+
+    // Begin SET and GET methods
+
+    /**
+     * Sets the <CODE>serverName</CODE> attribute to the given value.
+     *
+     * @param serverName The name of the server you wish to connect to.
+     * @see #getServerName()
+     */
+    public void setServerName(String serverName) {
+        this.serverName = serverName;
+    }
+
+
+    /**
+     * Gets the <CODE>serverName</CODE> attribute.
+     *
+     * @return The StarTeam server to log in to.
+     * @see #setServerName(String serverName)
+     */
+    public String getServerName() {
+        return serverName;
+    }
+
+
+    /**
+     * Sets the <CODE>serverPort</CODE> attribute to the given value. The
+     * given value must be a valid integer, but it must be a string object.
+     *
+     * @param serverPort A string containing the port on the StarTeam server
+     *      to use.
+     * @see #getServerPort()
+     */
+    public void setServerPort(int serverPort) {
+        this.serverPort = serverPort;
+    }
+
+
+    /**
+     * Gets the <CODE>serverPort</CODE> attribute.
+     *
+     * @return A string containing the port on the StarTeam server to use.
+     * @see #setServerPort(int)
+     */
+    public int getServerPort() {
+        return serverPort;
+    }
+
+
+    /**
+     * Sets the <CODE>projectName</CODE> attribute to the given value.
+     *
+     * @param projectName The StarTeam project to search.
+     * @see #getProjectName()
+     */
+    public void setProjectName(String projectName) {
+        this.projectName = projectName;
+    }
+
+
+    /**
+     * Gets the <CODE>projectName</CODE> attribute.
+     *
+     * @return The StarTeam project to search.
+     * @see #setProjectName(String projectName)
+     */
+    public String getProjectName() {
+        return projectName;
+    }
+
+
+    /**
+     * Sets the <CODE>viewName</CODE> attribute to the given value.
+     *
+     * @param viewName The view to find the specified folder in.
+     * @see #getViewName()
+     */
+    public void setViewName(String viewName) {
+        this.viewName = viewName;
+    }
+
+
+    /**
+     * Gets the <CODE>viewName</CODE> attribute.
+     *
+     * @return The view to find the specified folder in.
+     * @see #setViewName(String viewName)
+     */
+    public String getViewName() {
+        return viewName;
+    }
+
+
+    /**
+     * Sets the <CODE>folderName</CODE> attribute to the given value. To
+     * search the root folder, use a slash or backslash, or simply don't set a
+     * folder at all.
+     *
+     * @param folderName The subfolder from which to check out files.
+     * @see #getFolderName()
+     */
+    public void setFolderName(String folderName) {
+        this.folderName = folderName;
+    }
+
+
+    /**
+     * Gets the <CODE>folderName</CODE> attribute.
+     *
+     * @return The subfolder from which to check out files. All subfolders
+     *      will be searched, as well.
+     * @see #setFolderName(String folderName)
+     */
+    public String getFolderName() {
+        return folderName;
+    }
+
+
+    /**
+     * Sets the <CODE>username</CODE> attribute to the given value.
+     *
+     * @param username Your username for the specified StarTeam server.
+     * @see #getUsername()
+     */
+    public void setUsername(String username) {
+        this.username = username;
+    }
+
+
+    /**
+     * Gets the <CODE>username</CODE> attribute.
+     *
+     * @return The username given by the user.
+     * @see #setUsername(String username)
+     */
+    public String getUsername() {
+        return username;
+    }
+
+
+    /**
+     * Sets the <CODE>password</CODE> attribute to the given value.
+     *
+     * @param password Your password for the specified StarTeam server.
+     * @see #getPassword()
+     */
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+
+    /**
+     * Gets the <CODE>password</CODE> attribute.
+     *
+     * @return The password given by the user.
+     * @see #setPassword(String password)
+     */
+    public String getPassword() {
+        return password;
+    }
+
+
+    /**
+     * Sets the <CODE>targetFolder</CODE> attribute to the given value.
+     *
+     * @param targetFolder The target path on the local machine to check out to.
+     * @see #getTargetFolder()
+     */
+    public void setTargetFolder(String targetFolder) {
+        this.targetFolder = targetFolder;
+    }
+
+
+    /**
+     * Gets the <CODE>targetFolder</CODE> attribute.
+     *
+     * @return The target path on the local machine to check out to.
+     * @see #setTargetFolder(String targetFolder)
+     */
+    public String getTargetFolder() {
+        return targetFolder;
+    }
+
+
+    /**
+     * Sets the <CODE>force</CODE> attribute to the given value.
+     *
+     * @param force if true, it overwrites files in the target directory. By
+     *      default it set to false as a safeguard. Note that if the target
+     *      directory does not exist, this setting has no effect.
+     * @see #getForce()
+     */
+    public void setForce(boolean force) {
+        this.force = force;
+    }
+
+
+    /**
+     * Gets the <CODE>force</CODE> attribute.
+     *
+     * @return whether to continue if the target directory exists.
+     * @see #setForce(boolean)
+     */
+    public boolean getForce() {
+        return force;
+    }
+
+
+    /**
+     * Turns recursion on or off.
+     *
+     * @param recursion if it is true, the default, subfolders are searched
+     *      recursively for files to check out. Otherwise, only files
+     *      specified by <CODE>folderName</CODE> are scanned.
+     * @see #getRecursion()
+     */
+    public void setRecursion(boolean recursion) {
+        this.recursion = recursion;
+    }
+
+
+    /**
+     * Gets the <CODE>recursion</CODE> attribute, which tells
+     * AntStarTeamCheckOut whether to search subfolders when checking out
+     * files.
+     *
+     * @return whether to search subfolders when checking out files.
+     * @see #setRecursion(boolean)
+     */
+    public boolean getRecursion() {
+        return recursion;
+    }
+
+
+    /**
+     * Sets the <CODE>verbose</CODE> attribute to the given value.
+     *
+     * @param verbose whether to display all files as it checks them out. By
+     *      default it is false, so the program only displays the total number
+     *      of files unless you override this default.
+     * @see #getVerbose()
+     */
+    public void setVerbose(boolean verbose) {
+        this.verbose = verbose;
+    }
+
+
+    /**
+     * Gets the <CODE>verbose</CODE> attribute.
+     *
+     * @return whether to display all files as it checks them out.
+     * @see #setVerbose(boolean verbose)
+     */
+    public boolean getVerbose() {
+        return verbose;
+    }
+
+    // Begin filter getters and setters
+
+    /**
+     * Sets the include filter. When filtering files, AntStarTeamCheckOut uses
+     * an unmodified version of <CODE>DirectoryScanner</CODE>'s <CODE>match</CODE>
+     * method, so here are the patterns straight from the Ant source code:
+     * <BR>
+     * <BR>
+     * Matches a string against a pattern. The pattern contains two special
+     * characters: <BR>
+     * '*' which means zero or more characters, <BR>
+     * '?' which means one and only one character. <BR>
+     * <BR>
+     * Separate multiple inlcude filters by <I>spaces</I> , not commas as Ant
+     * uses. For example, if you want to check out all .java and .class\
+     * files, you would put the following line in your program:
+     * <CODE>setIncludes("*.java *.class");</CODE>
+     * Finally, note that filters have no effect on the <B>directories</B>
+     * that are scanned; you could not check out files from directories with
+     * names beginning only with "build," for instance. Of course, you could
+     * limit AntStarTeamCheckOut to a particular folder and its subfolders
+     * with the <CODE>setFolderName(String folderName)</CODE> command. <BR>
+     * <BR>
+     * Treatment of overlapping inlcudes and excludes: To give a simplistic
+     * example suppose that you set your include filter to "*.htm *.html" and
+     * your exclude filter to "index.*". What happens to index.html?
+     * AntStarTeamCheckOut will not check out index.html, as it matches an
+     * exclude filter ("index.*"), even though it matches the include filter,
+     * as well. <BR>
+     * <BR>
+     * Please also read the following sections before using filters:
+     *
+     * @param includes A string of filter patterns to include. Separate the
+     *      patterns by spaces.
+     * @see #getIncludes()
+     * @see #setExcludes(String excludes)
+     * @see #getExcludes()
+     */
+    public void setIncludes(String includes) {
+        this.includes = includes;
+    }
+
+
+    /**
+     * Gets the patterns from the include filter. Rather that duplicate the
+     * details of AntStarTeanCheckOut's filtering here, refer to these links:
+     *
+     * @return A string of filter patterns separated by spaces.
+     * @see #setIncludes(String includes)
+     * @see #setExcludes(String excludes)
+     * @see #getExcludes()
+     */
+    public String getIncludes() {
+        return includes;
+    }
+
+
+    /**
+     * Sets the exclude filter. When filtering files, AntStarTeamCheckOut uses
+     * an unmodified version of <CODE>DirectoryScanner</CODE>'s <CODE>match</CODE>
+     * method, so here are the patterns straight from the Ant source code:
+     * <BR>
+     * <BR>
+     * Matches a string against a pattern. The pattern contains two special
+     * characters: <BR>
+     * '*' which means zero or more characters, <BR>
+     * '?' which means one and only one character. <BR>
+     * <BR>
+     * Separate multiple exlcude filters by <I>spaces</I> , not commas as Ant
+     * uses. For example, if you want to check out all files except .XML and
+     * .HTML files, you would put the following line in your program:
+     * <CODE>setExcludes("*.XML *.HTML");</CODE>
+     * Finally, note that filters have no effect on the <B>directories</B>
+     * that are scanned; you could not skip over all files in directories
+     * whose names begin with "project," for instance. <BR>
+     * <BR>
+     * Treatment of overlapping inlcudes and excludes: To give a simplistic
+     * example suppose that you set your include filter to "*.htm *.html" and
+     * your exclude filter to "index.*". What happens to index.html?
+     * AntStarTeamCheckOut will not check out index.html, as it matches an
+     * exclude filter ("index.*"), even though it matches the include filter,
+     * as well. <BR>
+     * <BR>
+     * Please also read the following sections before using filters:
+     *
+     * @param excludes A string of filter patterns to exclude. Separate the
+     *      patterns by spaces.
+     * @see #setIncludes(String includes)
+     * @see #getIncludes()
+     * @see #getExcludes()
+     */
+    public void setExcludes(String excludes) {
+        this.excludes = excludes;
+    }
+
+
+    /**
+     * Gets the patterns from the exclude filter. Rather that duplicate the
+     * details of AntStarTeanCheckOut's filtering here, refer to these links:
+     *
+     * @return A string of filter patterns separated by spaces.
+     * @see #setExcludes(String excludes)
+     * @see #setIncludes(String includes)
+     * @see #getIncludes()
+     */
+    public String getExcludes() {
+        return excludes;
+    }
+
+
+    /**
+     * returns whether the StarTeam default path is factored into calculated
+     * target path locations (false) or whether targetFolder is an absolute
+     * mapping to the root folder named by folderName
+     *
+     * @return returns true if absolute mapping is used, false if it is not
+     *      used.
+     * @see #setTargetFolderAbsolute(boolean)
+     */
+    public boolean getTargetFolderAbsolute() {
+        return this.targetFolderAbsolute;
+    }
+
+
+    /**
+     * sets the property that indicates whether or not the Star Team "default
+     * folder" is to be used when calculation paths for items on the target
+     * (false) or if targetFolder is an absolute mapping to the root folder
+     * named by foldername.
+     *
+     * @param targetFolderAbsolute <tt>true</tt> if the absolute mapping is to
+     *      be used. <tt>false</tt> (the default) if the "default folder" is
+     *      to be factored in.
+     * @see #getTargetFolderAbsolute()
+     */
+    public void setTargetFolderAbsolute(boolean targetFolderAbsolute) {
+        this.targetFolderAbsolute = targetFolderAbsolute;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDef.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDef.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDef.java	(revision 16139)
@@ -0,0 +1,348 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.script;
+
+import org.apache.tools.ant.AntTypeDefinition;
+import org.apache.tools.ant.ComponentHelper;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.taskdefs.DefBase;
+
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Locale;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+import java.util.HashSet;
+import java.io.File;
+
+import org.apache.tools.ant.util.ScriptRunner;
+
+/**
+ * Define a task using a script
+ *
+ * @since Ant 1.6
+ */
+public class ScriptDef extends DefBase {
+    /** Used to run the script */
+    private ScriptRunner runner = new ScriptRunner();
+
+    /** the name by which this script will be activated */
+    private String name;
+
+    /** Attributes definitions of this script */
+    private List attributes = new ArrayList();
+
+    /** Nested Element definitions of this script */
+    private List nestedElements = new ArrayList();
+
+    /** The attribute names as a set */
+    private Set attributeSet;
+
+    /** The nested element definitions indexed by their names */
+    private Map nestedElementMap;
+
+    /**
+     * set the name under which this script will be activated in a build
+     * file
+     *
+     * @param name the name of the script
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Indicates whether the task supports a given attribute name
+     *
+     * @param attributeName the name of the attribute.
+     *
+     * @return true if the attribute is supported by the script.
+     */
+    public boolean isAttributeSupported(String attributeName) {
+        return attributeSet.contains(attributeName);
+    }
+
+    /**
+     * Class representing an attribute definition
+     */
+    public static class Attribute {
+        /** The attribute name */
+        private String name;
+
+        /**
+         * Set the attribute name
+         *
+         * @param name the attribute name
+         */
+        public void setName(String name) {
+            this.name = name.toLowerCase(Locale.US);
+        }
+    }
+
+    /**
+     * Add an attribute definition to this script.
+     *
+     * @param attribute the attribute definition.
+     */
+    public void addAttribute(Attribute attribute) {
+        attributes.add(attribute);
+    }
+
+    /**
+     * Class to represent a nested element definition
+     */
+    public static class NestedElement {
+        /** The name of the neseted element */
+        private String name;
+
+        /** The Ant type to which this nested element corresponds. */
+        private String type;
+
+        /** The class to be created for this nested element */
+        private String className;
+
+        /**
+         * set the tag name for this nested element
+         *
+         * @param name the name of this nested element
+         */
+        public void setName(String name) {
+            this.name = name.toLowerCase(Locale.US);
+        }
+
+        /**
+         * Set the type of this element. This is the name of an
+         * Ant task or type which is to be used when this element is to be
+         * created. This is an alternative to specifying the class name directly
+         *
+         * @param type the name of an Ant type, or task, to use for this nested
+         * element.
+         */
+        public void setType(String type) {
+            this.type = type;
+        }
+
+        /**
+         * Set the classname of the class to be used for the nested element.
+         * This specifies the class directly and is an alternative to specifying
+         * the Ant type name.
+         *
+         * @param className the name of the class to use for this nested
+         * element.
+         */
+        public void setClassName(String className) {
+            this.className = className;
+        }
+    }
+
+    /**
+     * Add a nested element definition.
+     *
+     * @param nestedElement the nested element definition.
+     */
+    public void addElement(NestedElement nestedElement) {
+        nestedElements.add(nestedElement);
+    }
+
+    /**
+     * Define the script.
+     */
+    public void execute() {
+        if (name == null) {
+            throw new BuildException("scriptdef requires a name attribute to "
+                + "name the script");
+        }
+
+        if (runner.getLanguage() == null) {
+            throw new BuildException("<scriptdef> requires a language attribute "
+                + "to specify the script language");
+        }
+
+        attributeSet = new HashSet();
+        for (Iterator i = attributes.iterator(); i.hasNext();) {
+            Attribute attribute = (Attribute) i.next();
+            if (attribute.name == null) {
+                throw new BuildException("scriptdef <attribute> elements "
+                    + "must specify an attribute name");
+            }
+
+            if (attributeSet.contains(attribute.name)) {
+                throw new BuildException("scriptdef <" + name + "> declares "
+                    + "the " + attribute.name + " attribute more than once");
+            }
+            attributeSet.add(attribute.name);
+        }
+
+        nestedElementMap = new HashMap();
+        for (Iterator i = nestedElements.iterator(); i.hasNext();) {
+            NestedElement nestedElement = (NestedElement) i.next();
+            if (nestedElement.name == null) {
+                throw new BuildException("scriptdef <element> elements "
+                    + "must specify an element name");
+            }
+            if (nestedElementMap.containsKey(nestedElement.name)) {
+                throw new BuildException("scriptdef <" + name + "> declares "
+                    + "the " + nestedElement.name + " nested element more "
+                    + "than once");
+            }
+
+            if (nestedElement.className == null
+                && nestedElement.type == null) {
+                throw new BuildException("scriptdef <element> elements "
+                    + "must specify either a classname or type attribute");
+            }
+            if (nestedElement.className != null
+                && nestedElement.type != null) {
+                throw new BuildException("scriptdef <element> elements "
+                    + "must specify only one of the classname and type "
+                    + "attributes");
+            }
+
+
+            nestedElementMap.put(nestedElement.name, nestedElement);
+        }
+
+        // find the script repository - it is stored in the project
+        Map scriptRepository = null;
+        Project project = getProject();
+        synchronized (project) {
+            scriptRepository =
+                (Map) project.getReference(MagicNames.SCRIPT_REPOSITORY);
+            if (scriptRepository == null) {
+                scriptRepository = new HashMap();
+                project.addReference(MagicNames.SCRIPT_REPOSITORY,
+                    scriptRepository);
+            }
+        }
+
+        name = ProjectHelper.genComponentName(getURI(), name);
+        scriptRepository.put(name, this);
+        AntTypeDefinition def = new AntTypeDefinition();
+        def.setName(name);
+        def.setClass(ScriptDefBase.class);
+        ComponentHelper.getComponentHelper(
+            getProject()).addDataTypeDefinition(def);
+    }
+
+    /**
+     * Create a nested element to be configured.
+     *
+     * @param elementName the name of the nested element.
+     * @return object representing the element name.
+     */
+    public Object createNestedElement(String elementName) {
+        NestedElement definition
+            = (NestedElement) nestedElementMap.get(elementName);
+        if (definition == null) {
+            throw new BuildException("<" + name + "> does not support "
+                + "the <" + elementName + "> nested element");
+        }
+
+        Object instance = null;
+        String classname = definition.className;
+        if (classname == null) {
+            instance = getProject().createTask(definition.type);
+            if (instance == null) {
+                instance = getProject().createDataType(definition.type);
+            }
+        } else {
+            /*
+            // try the context classloader
+            ClassLoader loader
+                = Thread.currentThread().getContextClassLoader();
+            */
+            ClassLoader loader = createLoader();
+
+            Class instanceClass = null;
+            try {
+                instanceClass = Class.forName(classname, true, loader);
+            } catch (Throwable e) {
+                // try normal method
+                try {
+                    instanceClass = Class.forName(classname);
+                } catch (Throwable e2) {
+                    throw new BuildException("scriptdef: Unable to load "
+                        + "class " + classname + " for nested element <"
+                        + elementName + ">", e2);
+                }
+            }
+
+            try {
+                instance = instanceClass.newInstance();
+            } catch (Throwable e) {
+                throw new BuildException("scriptdef: Unable to create "
+                    + "element of class " + classname + " for nested "
+                    + "element <" + elementName + ">", e);
+            }
+            getProject().setProjectReference(instance);
+        }
+
+        if (instance == null) {
+            throw new BuildException("<" + name + "> is unable to create "
+                + "the <" + elementName + "> nested element");
+        }
+        return instance;
+    }
+
+    /**
+     * Execute the script.
+     *
+     * @param attributes collection of attributes
+     *
+     * @param elements a list of nested element values.
+     */
+    public void executeScript(Map attributes, Map elements) {
+        runner.addBean("attributes", attributes);
+        runner.addBean("elements", elements);
+        runner.addBean("project", getProject());
+        runner.executeScript("scriptdef_" + name);
+    }
+
+
+    /**
+     * Defines the language (required).
+     *
+     * @param language the scripting language name for the script.
+     */
+    public void setLanguage(String language) {
+        runner.setLanguage(language);
+    }
+
+    /**
+     * Load the script from an external file ; optional.
+     *
+     * @param file the file containing the script source.
+     */
+    public void setSrc(File file) {
+        runner.setSrc(file);
+    }
+
+    /**
+     * Set the script text.
+     *
+     * @param text a component of the script text to be added.
+     */
+    public void addText(String text) {
+        runner.addText(text);
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDefBase.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDefBase.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/script/ScriptDefBase.java	(revision 16139)
@@ -0,0 +1,98 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.script;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DynamicConfigurator;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.List;
+import java.util.ArrayList;
+
+/**
+ * The script execution class. This class finds the defining script task
+ * and passes control to that task's executeScript method.
+ *
+ * @since Ant 1.6
+ */
+public class ScriptDefBase extends Task implements DynamicConfigurator {
+
+    /** Nested elements */
+    private Map nestedElementMap = new HashMap();
+
+    /** Attributes */
+    private Map attributes = new HashMap();
+
+    /**
+     * Locate the script defining task and execute the script by passing
+     * control to it
+     */
+    public void execute() {
+        getScript().executeScript(attributes, nestedElementMap);
+    }
+
+    private ScriptDef getScript() {
+        String name = getTaskType();
+        Map scriptRepository
+            = (Map) getProject().getReference(MagicNames.SCRIPT_REPOSITORY);
+        if (scriptRepository == null) {
+            throw new BuildException("Script repository not found for " + name);
+        }
+
+        ScriptDef definition = (ScriptDef) scriptRepository.get(getTaskType());
+        if (definition == null) {
+            throw new BuildException("Script definition not found for " + name);
+        }
+        return definition;
+    }
+
+    /**
+     * Create a nested element
+     *
+     * @param name the nested element name
+     * @return the element to be configured
+     */
+    public Object createDynamicElement(String name)  {
+        List nestedElementList = (List) nestedElementMap.get(name);
+        if (nestedElementList == null) {
+            nestedElementList = new ArrayList();
+            nestedElementMap.put(name, nestedElementList);
+        }
+        Object element = getScript().createNestedElement(name);
+        nestedElementList.add(element);
+        return element;
+    }
+
+    /**
+     * Set a task attribute
+     *
+     * @param name the attribute name.
+     * @param value the attribute's string value
+     */
+    public void setDynamicAttribute(String name, String value) {
+        ScriptDef definition = getScript();
+        if (!definition.isAttributeSupported(name)) {
+                throw new BuildException("<" + getTaskType()
+                    + "> does not support the \"" + name + "\" attribute");
+        }
+
+        attributes.put(name, value);
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/CovBase.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/CovBase.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/CovBase.java	(revision 16139)
@@ -0,0 +1,101 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.sitraka;
+
+import java.io.File;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Base class that deals with JProbe version incompatibilities.
+ *
+ * @since Ant 1.6
+ *
+ */
+public abstract class CovBase extends Task {
+    private File home;
+    private static FileUtils fu = FileUtils.newFileUtils();
+    private boolean isJProbe4 = false;
+    private static boolean isDos = Os.isFamily("dos");
+
+    /**
+     * The directory where JProbe is installed.
+     */
+    public void setHome(File value) {
+        this.home = value;
+    }
+
+    protected File getHome() {
+        return home;
+    }
+
+    protected File findCoverageJar() {
+        File loc = null;
+        if (isJProbe4) {
+            loc = fu.resolveFile(home, "lib/coverage.jar");
+        } else {
+            loc = fu.resolveFile(home, "coverage/coverage.jar");
+            if (!loc.canRead()) {
+                File newLoc = fu.resolveFile(home, "lib/coverage.jar");
+                if (newLoc.canRead()) {
+                    isJProbe4 = true;
+                    loc = newLoc;
+                }
+            }
+        }
+
+        return loc;
+    }
+
+    protected String findExecutable(String relativePath) {
+        if (isDos) {
+            relativePath += ".exe";
+        }
+
+        File loc = null;
+        if (isJProbe4) {
+            loc = fu.resolveFile(home, "bin/" + relativePath);
+        } else {
+            loc = fu.resolveFile(home, relativePath);
+            if (!loc.canRead()) {
+                File newLoc = fu.resolveFile(home, "bin/" + relativePath);
+                if (newLoc.canRead()) {
+                    isJProbe4 = true;
+                    loc = newLoc;
+                }
+            }
+        }
+        return loc.getAbsolutePath();
+    }
+
+    protected File createTempFile(String prefix) {
+        return fu.createTempFile(prefix, ".tmp", null);
+    }
+
+    protected String getParamFileArgument() {
+        return "-" + (!isJProbe4 ? "jp_" : "") + "paramfile=";
+    }
+
+    /**
+     * Are we running on a version of JProbe 4.x or higher?
+     */
+    protected boolean isJProbe4Plus() {
+        return isJProbe4;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/CovMerge.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/CovMerge.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/CovMerge.java	(revision 16139)
@@ -0,0 +1,188 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.sitraka;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * Runs the snapshot merge utility for JProbe Coverage.
+ *
+ * @ant.task name="jpcovmerge" category="metrics"
+ */
+public class CovMerge extends CovBase {
+
+    /** the name of the output snapshot */
+    private File tofile = null;
+
+    /** the filesets that will get all snapshots to merge */
+    private Vector filesets = new Vector();
+
+    private boolean verbose;
+
+    /**
+     * Set the output snapshot file.
+     */
+    public void setTofile(File value) {
+        this.tofile = value;
+    }
+
+    /**
+     * If true, perform the merge in verbose mode giving details
+     * about the snapshot processing.
+     */
+    public void setVerbose(boolean flag) {
+        this.verbose = flag;
+    }
+
+    /**
+     * add a fileset containing the snapshots to include.
+     */
+    public void addFileset(FileSet fs) {
+        filesets.addElement(fs);
+    }
+
+    //---------------- the tedious job begins here
+
+    public CovMerge() {
+    }
+
+    /** execute the jpcovmerge by providing a parameter file */
+    public void execute() throws BuildException {
+        checkOptions();
+
+        File paramfile = createParamFile();
+        try {
+            Commandline cmdl = new Commandline();
+            cmdl.setExecutable(findExecutable("jpcovmerge"));
+            if (verbose) {
+                cmdl.createArgument().setValue("-v");
+            }
+            cmdl.createArgument().setValue(getParamFileArgument()
+                                           + paramfile.getAbsolutePath());
+
+            if (isJProbe4Plus()) {
+                // last argument is the output snapshot - JProbe 4.x
+                // doesn't like it in the parameter file.
+                cmdl.createArgument().setValue(tofile.getPath());
+            }
+
+            LogStreamHandler handler
+                = new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_WARN);
+            Execute exec = new Execute(handler);
+            log(cmdl.describeCommand(), Project.MSG_VERBOSE);
+            exec.setCommandline(cmdl.getCommandline());
+
+            // JProbe process always return 0 so  we will not be
+            // able to check for failure ! :-(
+            int exitValue = exec.execute();
+            if (Execute.isFailure(exitValue)) {
+                throw new BuildException("JProbe Coverage Merging failed (" + exitValue + ")");
+            }
+        } catch (IOException e) {
+            throw new BuildException("Failed to run JProbe Coverage Merge: " + e);
+        } finally {
+            //@todo should be removed once switched to JDK1.2
+            paramfile.delete();
+        }
+    }
+
+    /** check for mandatory options */
+    protected void checkOptions() throws BuildException {
+        if (tofile == null) {
+            throw new BuildException("'tofile' attribute must be set.");
+        }
+
+        // check coverage home
+        if (getHome() == null || !getHome().isDirectory()) {
+            throw new BuildException("Invalid home directory. Must point to JProbe home directory");
+        }
+        File jar = findCoverageJar();
+        if (!jar.exists()) {
+            throw new BuildException("Cannot find Coverage directory: " + getHome());
+        }
+    }
+
+    /** get the snapshots from the filesets */
+    protected File[] getSnapshots() {
+        Vector v = new Vector();
+        final int size = filesets.size();
+        for (int i = 0; i < size; i++) {
+            FileSet fs = (FileSet) filesets.elementAt(i);
+            DirectoryScanner ds = fs.getDirectoryScanner(getProject());
+            ds.scan();
+            String[] f = ds.getIncludedFiles();
+            for (int j = 0; j < f.length; j++) {
+                String pathname = f[j];
+                File file = new File(ds.getBasedir(), pathname);
+                file = getProject().resolveFile(file.getPath());
+                v.addElement(file);
+            }
+        }
+
+        File[] files = new File[v.size()];
+        v.copyInto(files);
+        return files;
+    }
+
+
+    /**
+     * create the parameters file that contains all file to merge
+     * and the output filename.
+     */
+    protected File createParamFile() throws BuildException {
+        File[] snapshots = getSnapshots();
+        File file = createTempFile("jpcovm");
+        file.deleteOnExit();
+        FileWriter fw = null;
+        try {
+            fw = new FileWriter(file);
+            PrintWriter pw = new PrintWriter(fw);
+            for (int i = 0; i < snapshots.length; i++) {
+                pw.println(snapshots[i].getAbsolutePath());
+            }
+            if (!isJProbe4Plus()) {
+                // last file is the output snapshot - JProbe 4.x doesn't
+                // like it in the parameter file.
+                pw.println(getProject().resolveFile(tofile.getPath()));
+            }
+            pw.flush();
+        } catch (IOException e) {
+            throw new BuildException("I/O error while writing to " + file, e);
+        } finally {
+            if (fw != null) {
+                try {
+                    fw.close();
+                } catch (IOException ignored) {
+                }
+            }
+        }
+        return file;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/CovReport.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/CovReport.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/CovReport.java	(revision 16139)
@@ -0,0 +1,368 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.sitraka;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Vector;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Path;
+import org.w3c.dom.Document;
+
+
+/**
+ * Runs the JProbe Coverage 3.0 snapshot merge utility.
+ *
+ * @ant.task name="jpcovreport" category="metrics"
+ */
+public class CovReport extends CovBase {
+    /*
+      jpcoverport [options] -output=file -snapshot=snapshot.jpc
+      jpcovreport [options] [-paramfile=file] -output=<fileName> -snapshot=<fileName>
+
+      Generate a report based on the indicated snapshot
+
+      -paramfile=file
+      A text file containing the report generation options.
+
+      -format=(html|text|xml) defaults to html
+      The format of the generated report.
+
+      -type=(executive|summary|detailed|verydetailed) defaults to detailed
+      The type of report to be generated. For -format=xml,
+      use -type=verydetailed to include source code lines.
+
+      Note: A very detailed report can be VERY large.
+
+      -percent=num            Min 1 Max 101 Default 101
+      An integer representing a percentage of coverage.
+      Only methods with test case coverage less than the
+      percentage are included in reports.
+
+      -filters=string
+      A comma-separated list of filters in the form
+      <package>.<class>:V, where V can be I for Include or
+      E for Exclude. For the default package, omit <package>.
+
+      -filters_method=string
+      Optional. A comma-separated list of methods that
+      correspond one-to-one with the entries in -filters.
+
+      -output=string  Must be specified
+      The absolute path and file name for the generated
+      report file.
+
+      -snapshot=string        Must be specified
+      The absolute path and file name of the snapshot file.
+
+      -inc_src_text=(on|off)  defaults to on
+      Include text of the source code lines.
+      Only applies for -format=xml and -type=verydetailed.
+
+      -sourcepath=string      defaults to .
+      A semicolon-separated list of source paths.
+
+      /*
+
+    /** format of generated report, optional */
+    private String format = null;
+
+    /** the name of the output snapshot, mandatory */
+    private File tofile = null;
+
+    /** type of report, optional */
+    private String type = null;
+
+    /** threshold value for printing methods, optional */
+    private Integer percent = null;
+
+    /** comma separated list of filters (???)*/
+    private String filters = null;
+
+    /** name of the snapshot file to create report from */
+    private File snapshot = null;
+
+    /** sourcepath to use */
+    private Path sourcePath = null;
+
+    /** include the text for each line of code (xml report verydetailed)*/
+    private boolean includeSource = true;
+
+    private Path coveragePath = null;
+
+    /** */
+    private Reference reference = null;
+
+
+    public static class ReportFormat extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[]{"html", "text", "xml"};
+        }
+    }
+
+    /**
+     * set the format of the report: "html", "text", or "xml"
+     */
+    public void setFormat(ReportFormat value) {
+        this.format = value.getValue();
+    }
+
+    public static class ReportType extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[]{"executive", "summary", "detailed", "verydetailed"};
+        }
+    }
+
+    /**
+     * The type of report to be generated: "executive", "summary",
+     * "detailed" or "verydetailed".
+     */
+    public void setType(ReportType value) {
+        this.type = value.getValue();
+    }
+
+    /**
+     * If true, include text of the source code lines.
+     * Only applies to format="xml" and type="verydetailed"
+     */
+    public void setIncludesource(boolean value) {
+        this.includeSource = value;
+    }
+
+    /**
+     * A numeric value for the threshold for printing methods.
+     * Must be between 0 and 100.
+     */
+    public void setPercent(Integer value) {
+        this.percent = value;
+    }
+
+    /**
+     * set the filters
+     * @ant.attribute ignore="true"
+     */
+    public void setFilters(String values) {
+        this.filters = values;
+    }
+
+    /**
+     * Adds a path to source files.
+     */
+    public Path createSourcepath() {
+        if (sourcePath == null) {
+            sourcePath = new Path(getProject());
+        }
+        return sourcePath.createPath();
+    }
+
+    /**
+     * The name of the snapshot file that is the source to the report.
+     */
+    public void setSnapshot(File value) {
+        this.snapshot = value;
+    }
+
+    /**
+     * The name of the generated output file.
+     */
+    public void setTofile(File value) {
+        this.tofile = value;
+    }
+
+    /**
+     * @todo needs to be removed
+     * @ant.element ignore="true"
+     */
+    public Path createCoveragepath() {
+        if (coveragePath == null) {
+            coveragePath = new Path(getProject());
+        }
+        return coveragePath.createPath();
+    }
+
+    /**
+     * Adds a set of classes whose coverage information will be
+     * checked against.
+     */
+    public Reference createReference() {
+        if (reference == null) {
+            reference = new Reference();
+        }
+        return reference;
+    }
+
+
+    public CovReport() {
+    }
+
+    /** check for mandatory options */
+    protected void checkOptions() throws BuildException {
+        if (tofile == null) {
+            throw new BuildException("'tofile' attribute must be set.");
+        }
+        if (snapshot == null) {
+            throw new BuildException("'snapshot' attribute must be set.");
+        }
+        if (getHome() == null) {
+            throw new BuildException("'home' attribute must be set to JProbe home directory");
+        }
+        File jar = findCoverageJar();
+        if (!jar.exists()) {
+            throw new BuildException("Cannot find Coverage directory: " + getHome());
+        }
+        if (reference != null && !"xml".equals(format)) {
+            log("Ignored reference. It cannot be used in non XML report.");
+            reference = null; // nullify it so that there is no ambiguity
+        }
+
+    }
+
+    public void execute() throws BuildException {
+        checkOptions();
+        try {
+            Commandline cmdl = new Commandline();
+            // we need to run Coverage from his directory due to dll/jar issues
+            cmdl.setExecutable(findExecutable("jpcovreport"));
+            String[] params = getParameters();
+            for (int i = 0; i < params.length; i++) {
+                cmdl.createArgument().setValue(params[i]);
+            }
+
+            // use the custom handler for stdin issues
+            LogStreamHandler handler
+                = new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_WARN);
+            Execute exec = new Execute(handler);
+            log(cmdl.describeCommand(), Project.MSG_VERBOSE);
+            exec.setCommandline(cmdl.getCommandline());
+            int exitValue = exec.execute();
+            if (Execute.isFailure(exitValue)) {
+                throw new BuildException("JProbe Coverage Report failed ("
+                    + exitValue + ")");
+            }
+            log("coveragePath: " + coveragePath, Project.MSG_VERBOSE);
+            log("format: " + format, Project.MSG_VERBOSE);
+            if (reference != null && "xml".equals(format)) {
+                reference.createEnhancedXMLReport();
+            }
+
+        } catch (IOException e) {
+            throw new BuildException("Failed to execute JProbe Coverage Report.", e);
+        }
+    }
+
+
+    protected String[] getParameters() {
+        Vector v = new Vector();
+        if (format != null) {
+            v.addElement("-format=" + format);
+        }
+        if (type != null) {
+            v.addElement("-type=" + type);
+        }
+        if (percent != null) {
+            v.addElement("-percent=" + percent);
+        }
+        if (filters != null) {
+            v.addElement("-filters=" + filters);
+        }
+        v.addElement("-output=" + getProject().resolveFile(tofile.getPath()));
+        v.addElement("-snapshot=" + getProject().resolveFile(snapshot.getPath()));
+        // as a default -sourcepath use . in JProbe, so use project .
+        if (sourcePath == null) {
+            sourcePath = new Path(getProject());
+            sourcePath.createPath().setLocation(getProject().resolveFile("."));
+        }
+        v.addElement("-sourcepath=" + sourcePath);
+
+        if ("verydetailed".equalsIgnoreCase(format) && "xml".equalsIgnoreCase(type)) {
+            v.addElement("-inc_src_text=" + (includeSource ? "on" : "off"));
+        }
+
+        String[] params = new String[v.size()];
+        v.copyInto(params);
+        return params;
+    }
+
+
+    public class Reference {
+        protected Path classPath;
+        protected ReportFilters filters;
+
+        public Path createClasspath() {
+            if (classPath == null) {
+                classPath = new Path(CovReport.this.getProject());
+            }
+            return classPath.createPath();
+        }
+
+        public ReportFilters createFilters() {
+            if (filters == null) {
+                filters = new ReportFilters();
+            }
+            return filters;
+        }
+
+        protected void createEnhancedXMLReport() throws BuildException {
+            // we need a classpath element
+            if (classPath == null) {
+                throw new BuildException("Need a 'classpath' element.");
+            }
+            // and a valid one...
+            String[] paths = classPath.list();
+            if (paths.length == 0) {
+                throw new BuildException("Coverage path is invalid. It does not contain any existing path.");
+            }
+            // and we need at least one filter include/exclude.
+            if (filters == null || filters.size() == 0) {
+                createFilters();
+                log("Adding default include filter to *.*()", Project.MSG_VERBOSE);
+                ReportFilters.Include include = new ReportFilters.Include();
+                filters.addInclude(include);
+            }
+            try {
+                log("Creating enhanced XML report", Project.MSG_VERBOSE);
+                XMLReport report = new XMLReport(CovReport.this, tofile);
+                report.setReportFilters(filters);
+                report.setJProbehome(new File(getHome().getParent()));
+                Document doc = report.createDocument(paths);
+                TransformerFactory tfactory = TransformerFactory.newInstance();
+                Transformer transformer = tfactory.newTransformer();
+                transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+                transformer.setOutputProperty(OutputKeys.METHOD, "xml");
+                Source src = new DOMSource(doc);
+                Result res = new StreamResult("file:///" + tofile.toString());
+                transformer.transform(src, res);
+            } catch (Exception e) {
+                throw new BuildException("Error while performing enhanced XML "
+                    + "report from file " + tofile, e);
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/Coverage.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/Coverage.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/Coverage.java	(revision 16139)
@@ -0,0 +1,467 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.sitraka;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.CommandlineJava;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * Runs Sitraka JProbe Coverage.
+ *
+ * Options are pretty numerous, you'd better check the manual for a full
+ * descriptions of options. (not that simple since they differ from the online
+ * help, from the usage command line and from the examples...)
+ * <p>
+ * For additional information, visit <a href="http://www.sitraka.com">www.sitraka.com</a>
+ *
+ *
+ * @ant.task name="jpcoverage" category="metrics"
+ */
+public class Coverage extends CovBase {
+
+    protected Commandline cmdl = new Commandline();
+
+    protected CommandlineJava cmdlJava = new CommandlineJava();
+
+    protected String function = "coverage";
+
+    protected String seedName;
+
+    protected File inputFile;
+
+    protected File javaExe;
+
+    protected String vm;
+
+    protected boolean applet = false;
+
+    /** this is a somewhat annoying thing, set it to never */
+    protected String exitPrompt = "never";
+
+    protected Filters filters = new Filters();
+
+    protected Triggers triggers;
+
+    protected String finalSnapshot = "coverage";
+
+    protected String recordFromStart = "coverage";
+
+    protected File snapshotDir;
+
+    protected File workingDir;
+
+    protected boolean trackNatives = false;
+
+    protected Socket socket;
+
+    protected int warnLevel = 0;
+
+    protected Vector filesets = new Vector();
+
+    //--------- setters used via reflection --
+
+    /** seed name for snapshot file. Can be null, default to snap */
+    public void setSeedname(String value) {
+        seedName = value;
+    }
+
+    /**
+     * @ant.attribute ignore="true"
+     */
+    public void setInputfile(File value) {
+        inputFile = value;
+    }
+
+    /**
+     * Path to the java executable.
+     */
+    public void setJavaexe(File value) {
+        javaExe = value;
+    }
+
+    public static class Javavm extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[]{"java2", "jdk118", "jdk117"};
+        }
+    }
+
+    /**
+     * Indicates which virtual machine to run: "jdk117", "jdk118" or "java2".
+     * Can be null, default to "java2". */
+    public void setVm(Javavm value) {
+        vm = value.getValue();
+    }
+
+    /**
+     * If true, run an applet.
+     */
+    public void setApplet(boolean value) {
+        applet = value;
+    }
+
+    /**
+     * Toggles display of the console prompt: always, error, never
+     */
+    public void setExitprompt(String value) {
+        exitPrompt = value;
+    }
+
+    /**
+     * Defines class/method filters based on pattern matching.
+     * The syntax is filters is similar to a fileset.
+     */
+    public Filters createFilters() {
+        return filters;
+    }
+
+    /**
+     * Defines events to use for interacting with the
+     * collection of data performed during coverage.
+     *
+     * For example you may run a whole application but only decide
+     * to collect data once it reaches a certain method and once it
+     * exits another one.
+     */
+    public Triggers createTriggers() {
+        if (triggers == null) {
+            triggers = new Triggers();
+        }
+        return triggers;
+    }
+
+    /**
+     * Define a host and port to connect to if you want to do
+     * remote viewing.
+     */
+    public Socket createSocket() {
+        if (socket == null) {
+            socket = new Socket();
+        }
+        return socket;
+    }
+
+    public static class Finalsnapshot extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[]{"coverage", "none", "all"};
+        }
+    }
+
+    /**
+     * Type of snapshot to send at program termination: none, coverage, all.
+     * Can be null, default to none
+     */
+    public void setFinalsnapshot(String value) {
+        finalSnapshot = value;
+    }
+
+    public static class Recordfromstart extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[]{"coverage", "none", "all"};
+        }
+    }
+
+    /**
+     * "all", "coverage",  or "none".
+     */
+    public void setRecordfromstart(Recordfromstart value) {
+        recordFromStart = value.getValue();
+    }
+
+    /**
+     * Set warning level (0-3, where 0 is the least amount of warnings).
+     */
+    public void setWarnlevel(Integer value) {
+        warnLevel = value.intValue();
+    }
+
+    /**
+     * The path to the directory where snapshot files are stored.
+     * Choose a directory that is reachable by both the remote
+     * and local computers, and enter the same path on the command-line
+     * and in the viewer.
+     */
+    public void setSnapshotdir(File value) {
+        snapshotDir = value;
+    }
+
+    /**
+     * The physical path to the working directory for the VM.
+     */
+    public void setWorkingdir(File value) {
+        workingDir = value;
+    }
+
+    /**
+     * If true, track native methods.
+     */
+    public void setTracknatives(boolean value) {
+        trackNatives = value;
+    }
+
+    //
+
+    /**
+     * Adds a JVM argument.
+     */
+    public Commandline.Argument createJvmarg() {
+        return cmdlJava.createVmArgument();
+    }
+
+    /**
+     * Adds a command argument.
+     */
+    public Commandline.Argument createArg() {
+        return cmdlJava.createArgument();
+    }
+
+    /**
+     * classpath to run the files.
+     */
+    public Path createClasspath() {
+        return cmdlJava.createClasspath(getProject()).createPath();
+    }
+
+    /**
+     * classname to run as standalone or runner for filesets.
+     */
+    public void setClassname(String value) {
+        cmdlJava.setClassname(value);
+    }
+
+    /**
+     * the classnames to execute.
+     */
+    public void addFileset(FileSet fs) {
+        filesets.addElement(fs);
+    }
+
+
+    //---------------- the tedious job begins here
+
+    public Coverage() {
+    }
+
+    /** execute the jplauncher by providing a parameter file */
+    public void execute() throws BuildException {
+        File paramfile = null;
+        // if an input file is used, all other options are ignored...
+        if (inputFile == null) {
+            checkOptions();
+            paramfile = createParamFile();
+        } else {
+            paramfile = inputFile;
+        }
+        try {
+            // we need to run Coverage from his directory due to dll/jar issues
+            cmdl.setExecutable(findExecutable("jplauncher"));
+            cmdl.createArgument().setValue("-jp_input=" + paramfile.getAbsolutePath());
+
+            // use the custom handler for stdin issues
+            LogStreamHandler handler = new CoverageStreamHandler(this);
+            Execute exec = new Execute(handler);
+            log(cmdl.describeCommand(), Project.MSG_VERBOSE);
+            exec.setCommandline(cmdl.getCommandline());
+            int exitValue = exec.execute();
+            if (Execute.isFailure(exitValue)) {
+                throw new BuildException("JProbe Coverage failed (" + exitValue + ")");
+            }
+        } catch (IOException e) {
+            throw new BuildException("Failed to execute JProbe Coverage.", e);
+        } finally {
+            //@todo should be removed once switched to JDK1.2
+            if (inputFile == null && paramfile != null) {
+                paramfile.delete();
+            }
+        }
+    }
+
+    /** wheck what is necessary to check, Coverage will do the job for us */
+    protected void checkOptions() throws BuildException {
+        // check coverage home
+        if (getHome() == null || !getHome().isDirectory()) {
+            throw new BuildException("Invalid home directory. Must point to JProbe home directory");
+        }
+        File jar = findCoverageJar();
+        if (!jar.exists()) {
+            throw new BuildException("Cannot find Coverage directory: " + getHome());
+        }
+
+        // make sure snapshot dir exists and is resolved
+        if (snapshotDir == null) {
+            snapshotDir = new File(".");
+        }
+        snapshotDir = getProject().resolveFile(snapshotDir.getPath());
+        if (!snapshotDir.isDirectory() || !snapshotDir.exists()) {
+            throw new BuildException("Snapshot directory does not exists :" + snapshotDir);
+        }
+        if (workingDir == null) {
+            workingDir = new File(".");
+        }
+        workingDir = getProject().resolveFile(workingDir.getPath());
+
+        // check for info, do your best to select the java executable.
+        // JProbe 3.0 fails if there is no javaexe option. So
+        if (javaExe == null && (vm == null || "java2".equals(vm))) {
+            if (!JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
+                if (vm == null) {
+                    vm = "java2";
+                }
+                javaExe = new File(JavaEnvUtils.getJreExecutable("java"));
+            }
+        }
+    }
+
+    /**
+     * return the command line parameters. Parameters can either be passed
+     * to the command line and stored to a file (then use the -jp_input=&lt;filename&gt;)
+     * if they are too numerous.
+     */
+    protected String[] getParameters() {
+        Vector params = new Vector();
+        params.addElement("-jp_function=" + function);
+        if (vm != null) {
+            params.addElement("-jp_vm=" + vm);
+        }
+        if (javaExe != null) {
+            params.addElement("-jp_java_exe=" + getProject().resolveFile(javaExe.getPath()));
+        }
+        params.addElement("-jp_working_dir=" + workingDir.getPath());
+        params.addElement("-jp_snapshot_dir=" + snapshotDir.getPath());
+        params.addElement("-jp_record_from_start=" + recordFromStart);
+        params.addElement("-jp_warn=" + warnLevel);
+        if (seedName != null) {
+            params.addElement("-jp_output_file=" + seedName);
+        }
+        params.addElement("-jp_filter=" + filters.toString());
+        if (triggers != null) {
+            params.addElement("-jp_trigger=" + triggers.toString());
+        }
+        if (finalSnapshot != null) {
+            params.addElement("-jp_final_snapshot=" + finalSnapshot);
+        }
+        params.addElement("-jp_exit_prompt=" + exitPrompt);
+        //params.addElement("-jp_append=" + append);
+        params.addElement("-jp_track_natives=" + trackNatives);
+        //.... now the jvm
+        // arguments
+        String[] vmargs = cmdlJava.getVmCommand().getArguments();
+        for (int i = 0; i < vmargs.length; i++) {
+            params.addElement(vmargs[i]);
+        }
+        // classpath
+        Path classpath = cmdlJava.getClasspath();
+        if (classpath != null && classpath.size() > 0) {
+            params.addElement("-classpath " + classpath.toString());
+        }
+        // classname (runner or standalone)
+        if (cmdlJava.getClassname() != null) {
+            params.addElement(cmdlJava.getClassname());
+        }
+        // arguments for classname
+        String[] args = cmdlJava.getJavaCommand().getArguments();
+        for (int i = 0; i < args.length; i++) {
+            params.addElement(args[i]);
+        }
+
+        String[] array = new String[params.size()];
+        params.copyInto(array);
+        return array;
+    }
+
+
+    /**
+     * create the parameter file from the given options. The file is
+     * created with a random name in the current directory.
+     * @return the file object where are written the configuration to run
+     * JProbe Coverage
+     * @throws BuildException thrown if something bad happens while writing
+     * the arguments to the file.
+     */
+    protected File createParamFile() throws BuildException {
+        //@todo change this when switching to JDK 1.2 and use File.createTmpFile()
+        File file = createTempFile("jpcov");
+        file.deleteOnExit();
+        log("Creating parameter file: " + file, Project.MSG_VERBOSE);
+
+        // options need to be one per line in the parameter file
+        // so write them all in a single string
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw);
+        String[] params = getParameters();
+        for (int i = 0; i < params.length; i++) {
+            pw.println(params[i]);
+        }
+        pw.flush();
+        log("JProbe Coverage parameters:\n" + sw.toString(), Project.MSG_VERBOSE);
+
+        // now write them to the file
+        FileWriter fw = null;
+        try {
+            fw = new FileWriter(file);
+            fw.write(sw.toString());
+            fw.flush();
+        } catch (IOException e) {
+            throw new BuildException("Could not write parameter file " + file, e);
+        } finally {
+            if (fw != null) {
+                try {
+                    fw.close();
+                } catch (IOException ignored) {
+                }
+            }
+        }
+        return file;
+    }
+
+    /** specific pumper to avoid those nasty stdin issues */
+    static class CoverageStreamHandler extends LogStreamHandler {
+        CoverageStreamHandler(Task task) {
+            super(task, Project.MSG_INFO, Project.MSG_WARN);
+        }
+
+        /**
+         * there are some issues concerning all JProbe executable
+         * In our case a 'Press ENTER to close this window..." will
+         * be displayed in the current window waiting for enter.
+         * So I'm closing the stream right away to avoid problems.
+         */
+        public void setProcessInputStream(OutputStream os) {
+            try {
+                os.close();
+            } catch (IOException ignored) {
+            }
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/Filters.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/Filters.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/Filters.java	(revision 16139)
@@ -0,0 +1,140 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.sitraka;
+
+import java.util.Vector;
+
+/**
+ * Filters information from coverage, somewhat similar to a <tt>FileSet</tt>.
+ *
+ */
+public class Filters {
+
+    /** default regexp to exclude everything */
+    public static final String DEFAULT_EXCLUDE = "*.*():E";
+
+    /** say whether we should use the default excludes or not */
+    protected boolean defaultExclude = true;
+
+    /** user defined filters */
+    protected Vector filters = new Vector();
+
+    public Filters() {
+    }
+
+    /**
+     * Automatically exclude all classes and methods
+     * unless included in nested elements; optional, default true.
+     */
+    public void setDefaultExclude(boolean value) {
+        defaultExclude = value;
+    }
+
+    /**
+     * include classes and methods in the analysis
+     */
+    public void addInclude(Include incl) {
+        filters.addElement(incl);
+    }
+
+    /**
+     * exclude classes and methods from the analysis
+     */
+    public void addExclude(Exclude excl) {
+        filters.addElement(excl);
+    }
+
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        final int size = filters.size();
+        if (defaultExclude) {
+            buf.append(DEFAULT_EXCLUDE);
+            if (size > 0) {
+                buf.append(',');
+            }
+        }
+        for (int i = 0; i < size; i++) {
+            buf.append(filters.elementAt(i).toString());
+            if (i < size - 1) {
+                buf.append(',');
+            }
+        }
+        return buf.toString();
+    }
+
+    /**
+     * an includes or excludes element
+     */
+    public abstract static class FilterElement {
+        protected String clazz;
+        protected String method = "*"; // default is all methods
+        protected boolean enabled = true; // default is enable
+
+        /**
+         * this one is deprecated.
+         * @ant.task ignore="true"
+         */
+
+        public void setName(String value) {
+            clazz = value;
+        }
+
+        /**
+         * The classname mask as a simple regular expression;
+         * optional, defaults to "*"
+         */
+        public void setClass(String value) {
+            clazz = value;
+        }
+
+        /**
+         * The method mask as a simple regular expression;
+         * optional, defaults to "*"
+         */
+        public void setMethod(String value) {
+            method = value;
+        }
+
+        /**
+         * enable or disable the filter; optional, default true
+         */
+
+        public void setEnabled(boolean value) {
+            enabled = value;
+        }
+
+        public String toString() {
+            return clazz + "." + method + "()";
+        }
+    }
+
+    public static class Include extends FilterElement {
+        public String toString() {
+            return super.toString() + ":I" + (enabled ? "" : "#");
+        }
+    }
+
+    public static class Exclude extends FilterElement {
+        public String toString() {
+            return super.toString() + ":E" + (enabled ? "" : "#");
+        }
+    }
+}
+
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/ReportFilters.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/ReportFilters.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/ReportFilters.java	(revision 16139)
@@ -0,0 +1,131 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.sitraka;
+
+import java.util.Vector;
+import org.apache.tools.ant.util.regexp.RegexpMatcher;
+import org.apache.tools.ant.util.regexp.RegexpMatcherFactory;
+
+/**
+ * Filters information from coverage, somewhat similar to a <tt>FileSet</tt>.
+ *
+ */
+public class ReportFilters {
+
+    /** user defined filters */
+    protected Vector filters = new Vector();
+
+    /** cached matcher for each filter */
+    protected Vector matchers = null;
+
+    public ReportFilters() {
+    }
+
+    public void addInclude(Include incl) {
+        filters.addElement(incl);
+    }
+
+    public void addExclude(Exclude excl) {
+        filters.addElement(excl);
+    }
+
+    public int size() {
+        return filters.size();
+    }
+
+    /**
+     * Check whether a given &lt;classname&gt;&lt;method&gt;() is accepted by the list
+     * of filters or not.
+     * @param methodname the full method name in the format &lt;classname&gt;&lt;method&gt;()
+     */
+    public boolean accept(String methodname) {
+        // I'm deferring matcher instantiations at runtime to avoid computing
+        // the filters at parsing time
+        if (matchers == null) {
+            createMatchers();
+        }
+        boolean result = false;
+        // assert filters.size() == matchers.size()
+        final int size = filters.size();
+        for (int i = 0; i < size; i++) {
+            FilterElement filter = (FilterElement) filters.elementAt(i);
+            RegexpMatcher matcher = (RegexpMatcher) matchers.elementAt(i);
+            if (filter instanceof Include) {
+                result = result || matcher.matches(methodname);
+            } else if (filter instanceof Exclude) {
+                result = result && !matcher.matches(methodname);
+            } else {
+                //not possible
+                throw new IllegalArgumentException("Invalid filter element: "
+                    + filter.getClass().getName());
+            }
+        }
+        return result;
+    }
+
+    /** should be called only once to cache matchers */
+    protected void createMatchers() {
+        RegexpMatcherFactory factory = new RegexpMatcherFactory();
+        final int size = filters.size();
+        matchers = new Vector();
+        for (int i = 0; i < size; i++) {
+            FilterElement filter = (FilterElement) filters.elementAt(i);
+            RegexpMatcher matcher = factory.newRegexpMatcher();
+            String pattern = filter.getAsPattern();
+            matcher.setPattern(pattern);
+            matchers.addElement(matcher);
+        }
+    }
+
+
+    /** default abstract filter element class */
+    public abstract static class FilterElement {
+        protected String clazz = "*"; // default is all classes
+        protected String method = "*"; // default is all methods
+
+        public void setClass(String value) {
+            clazz = value;
+        }
+
+        public void setMethod(String value) {
+            method = value;
+        }
+
+        public String getAsPattern() {
+            StringBuffer buf = new StringBuffer(toString());
+            StringUtil.replace(buf, ".", "\\.");
+            StringUtil.replace(buf, "*", ".*");
+            StringUtil.replace(buf, "(", "\\(");
+            StringUtil.replace(buf, ")", "\\)");
+            return buf.toString();
+        }
+
+        public String toString() {
+            return clazz + "." + method + "()";
+        }
+    }
+
+    /** concrete include class */
+    public static class Include extends FilterElement {
+    }
+
+    /** concrete exclude class */
+    public static class Exclude extends FilterElement {
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/Socket.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/Socket.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/Socket.java	(revision 16139)
@@ -0,0 +1,55 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sitraka;
+
+/**
+ * Define a host and port to connect to if you want to do remote viewing.
+ * <tt>&lt;socket/&gt;</tt> defaults to host 127.0.0.1 and port 4444
+ *
+ * Otherwise it requires the host and port attributes to be set:
+ * <tt>
+ * &lt;socket host=&quote;175.30.12.1&quote; port=&quote;4567&quote;/&gt;
+ * </tt>
+ */
+public class Socket {
+
+    /** default to localhost */
+    private String host = "127.0.0.1";
+
+    /** default to 4444 */
+    private int port = 4444;
+
+    /**
+     * the host name/ip of the machine on which the Viewer is running;
+     * defaults to localhost.
+     */
+    public void setHost(String value) {
+        host = value;
+    }
+
+    /**
+     * Optional port number for the viewer; default is 4444
+     */
+    public void setPort(Integer value) {
+        port = value.intValue();
+    }
+
+    /** if no host is set, returning ':&lt;port&gt;', will take localhost */
+    public String toString() {
+        return host + ":" + port;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/StringUtil.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/StringUtil.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/StringUtil.java	(revision 16139)
@@ -0,0 +1,46 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sitraka;
+
+/**
+ * String utilities method.
+ */
+public final class StringUtil {
+    /** private constructor, it's a utility class */
+    private StringUtil() {
+    }
+
+    /**
+     * Replaces all occurences of <tt>find</tt> with <tt>replacement</tt> in the
+     * source StringBuffer.
+     * @param src the original string buffer to modify.
+     * @param find the string to be replaced.
+     * @param replacement the replacement string for <tt>find</tt> matches.
+     */
+    public static void replace(StringBuffer src, String find, String replacement) {
+        int index = 0;
+        while (index < src.length()) {
+            index = src.toString().indexOf(find, index);
+            if (index == -1) {
+                break;
+            }
+            src.delete(index, index + find.length());
+            src.insert(index, replacement);
+            index += replacement.length() + 1;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/Triggers.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/Triggers.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/Triggers.java	(revision 16139)
@@ -0,0 +1,139 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.sitraka;
+
+import java.util.Hashtable;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Trigger information. It will return as a command line argument by calling
+ * the <tt>toString()</tt> method.
+ *
+ */
+public class Triggers {
+
+    protected Vector triggers = new Vector();
+
+    public Triggers() {
+    }
+
+
+    /**
+     * add a method trigger
+     */
+    public void addMethod(Method method) {
+        triggers.addElement(method);
+    }
+
+    // -jp_trigger=ClassName.*():E:S,ClassName.MethodName():X:X
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        final int size = triggers.size();
+        for (int i = 0; i < size; i++) {
+            buf.append(triggers.elementAt(i).toString());
+            if (i < size - 1) {
+                buf.append(',');
+            }
+        }
+        return buf.toString();
+    }
+
+
+    /**
+     * A trigger for the coverage report
+     */
+    public static class Method {
+        protected String name;
+        protected String event;
+        protected String action;
+        protected String param;
+
+        /**
+         * The name of the method(s) as a regular expression. The name
+         * is the fully qualified name on the form <tt>package.classname.method</tt>
+         *  required.
+         */
+        public void setName(String value) {
+            name = value;
+        }
+
+        /**
+         * the event on the method that will trigger the action. Must be
+         * &quot;enter&quot; or &quot;exit&quot;
+         *  required.
+         */
+        public void setEvent(String value) {
+            if (eventMap.get(value) == null) {
+                throw new BuildException("Invalid event, must be one of " + eventMap);
+            }
+            event = value;
+        }
+
+        /**
+         * The action to execute; required. Must be one of &quot;clear&quot;,
+         * &quot;pause&quot;, &quot;resume&quot;, &quot;snapshot&quot;, &quot;suspend&quot;,
+         * or &quot;exit&quot;. They respectively clear recording, pause recording,
+         * resume recording, take a snapshot, suspend the recording and exit the program.
+         */
+        public void setAction(String value) throws BuildException {
+            if (actionMap.get(value) == null) {
+                throw new BuildException("Invalid action, must be one of " + actionMap);
+            }
+            action = value;
+        }
+
+        /**
+         * A alphanumeric custom name for the snapshot; optional.
+         */
+        public void setParam(String value) {
+            param = value;
+        }
+
+        // return <name>:<event>:<action>[:param]
+        public String toString() {
+            StringBuffer buf = new StringBuffer();
+            buf.append(name).append(":"); //@todo name must not be null, check for it
+            buf.append(eventMap.get(event)).append(":");
+            buf.append(actionMap.get(action));
+            if (param != null) {
+                buf.append(":").append(param);
+            }
+            return buf.toString();
+        }
+    }
+
+    /** mapping of actions to cryptic command line mnemonics */
+    private static final Hashtable actionMap = new Hashtable(3);
+
+    /** mapping of events to cryptic command line mnemonics */
+    private static final Hashtable eventMap = new Hashtable(3);
+
+    static {
+        actionMap.put("enter", "E");
+        actionMap.put("exit", "X");
+        // clear|pause|resume|snapshot|suspend|exit
+        eventMap.put("clear", "C");
+        eventMap.put("pause", "P");
+        eventMap.put("resume", "R");
+        eventMap.put("snapshot", "S");
+        eventMap.put("suspend", "A");
+        eventMap.put("exit", "X");
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/XMLReport.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/XMLReport.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/XMLReport.java	(revision 16139)
@@ -0,0 +1,542 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sitraka;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.NoSuchElementException;
+import java.util.Vector;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.ClassFile;
+import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.ClassPathLoader;
+import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.MethodInfo;
+import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.Utils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+/**
+ * Little hack to process XML report from JProbe. It will fix
+ * some reporting errors from JProbe 3.0 and makes use of a reference
+ * classpath to add classes/methods that were not reported by JProbe
+ * as being used (ie loaded)
+ *
+ */
+public class XMLReport {
+    /** task caller, can be null, used for logging purpose */
+    private Task task;
+
+    /** the XML file to process just from CovReport */
+    private File file;
+
+    /** jprobe home path. It is used to get the DTD */
+    private File jprobeHome;
+
+    /** parsed document */
+    private Document report;
+
+    /**
+     * mapping of class names to <code>ClassFile</code>s from the reference
+     * classpath.  It is used to filter the JProbe report.
+     */
+    private Hashtable classFiles;
+
+    /** mapping package name / package node for faster access */
+    private Hashtable pkgMap;
+
+    /** mapping classname / class node for faster access */
+    private Hashtable classMap;
+
+    /** method filters */
+    private ReportFilters filters;
+
+    /** create a new XML report, logging will be on stdout */
+    public XMLReport(File file) {
+        this(null, file);
+    }
+
+    /** create a new XML report, logging done on the task */
+    public XMLReport(Task task, File file) {
+        this.file = file;
+        this.task = task;
+    }
+
+    /** set the JProbe home path. Used to get the DTD */
+    public void setJProbehome(File home) {
+        jprobeHome = home;
+    }
+
+    /** set the  */
+    public void setReportFilters(ReportFilters filters) {
+        this.filters = filters;
+    }
+
+
+    /** create node maps so that we can access node faster by their name */
+    protected void createNodeMaps() {
+        pkgMap = new Hashtable();
+        classMap = new Hashtable();
+        // create a map index of all packages by their name
+        // @todo can be done faster by direct access.
+        NodeList packages = report.getElementsByTagName("package");
+        final int pkglen = packages.getLength();
+        log("Indexing " + pkglen + " packages");
+        for (int i = pkglen - 1; i > -1; i--) {
+            Element pkg = (Element) packages.item(i);
+            String pkgname = pkg.getAttribute("name");
+
+            int nbclasses = 0;
+            // create a map index of all classes by their fully
+            // qualified name.
+            NodeList classes = pkg.getElementsByTagName("class");
+            final int classlen = classes.getLength();
+            log("Indexing " + classlen + " classes in package " + pkgname);
+            for (int j = classlen - 1; j > -1; j--) {
+                Element clazz = (Element) classes.item(j);
+                String classname = clazz.getAttribute("name");
+                if (pkgname != null && pkgname.length() != 0) {
+                    classname = pkgname + "." + classname;
+                }
+
+                int nbmethods = 0;
+                NodeList methods = clazz.getElementsByTagName("method");
+                final int methodlen = methods.getLength();
+                for (int k = methodlen - 1; k > -1; k--) {
+                    Element meth = (Element) methods.item(k);
+                    StringBuffer methodname = new StringBuffer(meth.getAttribute("name"));
+                    methodname.delete(methodname.toString().indexOf("("),
+                        methodname.toString().length());
+                    String signature = classname + "." + methodname + "()";
+                    if (filters.accept(signature)) {
+                        log("kept method:" + signature);
+                        nbmethods++;
+                    } else {
+                        clazz.removeChild(meth);
+                    }
+                }
+                // if we don't keep any method, we don't keep the class
+                if (nbmethods != 0 && classFiles.containsKey(classname)) {
+                    log("Adding class '" + classname + "'");
+                    classMap.put(classname, clazz);
+                    nbclasses++;
+                } else {
+                    pkg.removeChild(clazz);
+                }
+            }
+            if (nbclasses != 0) {
+                log("Adding package '" + pkgname + "'");
+                pkgMap.put(pkgname, pkg);
+            } else {
+                pkg.getParentNode().removeChild(pkg);
+            }
+        }
+        log("Indexed " + classMap.size() + " classes in " + pkgMap.size() + " packages");
+    }
+
+    /** create the whole new document */
+    public Document createDocument(String[] classPath) throws Exception {
+
+        // Iterate over the classpath to identify reference classes
+        classFiles = new Hashtable();
+        ClassPathLoader cpl = new ClassPathLoader(classPath);
+        Enumeration e = cpl.loaders();
+        while (e.hasMoreElements()) {
+            ClassPathLoader.FileLoader fl = (ClassPathLoader.FileLoader) e.nextElement();
+            ClassFile[] classes = fl.getClasses();
+            log("Processing " + classes.length + " classes in " + fl.getFile());
+            // process all classes
+            for (int i = 0; i < classes.length; i++) {
+                classFiles.put(classes[i].getFullName(), classes[i]);
+            }
+        }
+
+        // Load the JProbe coverage XML report
+        DocumentBuilder dbuilder = newBuilder();
+        InputSource is = new InputSource(new FileInputStream(file));
+        if (jprobeHome != null) {
+            File dtdDir = new File(jprobeHome, "dtd");
+            is.setSystemId("file:///" + dtdDir.getAbsolutePath() + "/");
+        }
+        report = dbuilder.parse(is);
+        report.normalize();
+
+        // create maps for faster node access (also filters out unwanted nodes)
+        createNodeMaps();
+
+        // Make sure each class from the reference path ends up in the report
+        Enumeration classes = classFiles.elements();
+        while (classes.hasMoreElements()) {
+            ClassFile cf = (ClassFile) classes.nextElement();
+            serializeClass(cf);
+        }
+        // update the document with the stats
+        update();
+        return report;
+    }
+
+    /**
+     * JProbe does not put the java.lang prefix for classes
+     * in this package, so used this nice method so that
+     * I have the same signature for methods
+     */
+    protected String getMethodSignature(MethodInfo method) {
+        StringBuffer buf = new StringBuffer(method.getName());
+        buf.append("(");
+        String[] params = method.getParametersType();
+        for (int i = 0; i < params.length; i++) {
+            String type = params[i];
+            int pos = type.lastIndexOf('.');
+            if (pos != -1) {
+                String pkg = type.substring(0, pos);
+                if ("java.lang".equals(pkg)) {
+                    params[i] = type.substring(pos + 1);
+                }
+            }
+            buf.append(params[i]);
+            if (i != params.length - 1) {
+                buf.append(", ");
+            }
+        }
+        buf.append(")");
+        return buf.toString();
+    }
+
+    /**
+     * Convert to a CovReport-like signature - &lt;classname&gt;&#046;&lt;method&gt;().
+     */
+    protected String getMethodSignature(ClassFile clazz, MethodInfo method) {
+        StringBuffer buf = new StringBuffer(clazz.getFullName());
+        buf.append(".");
+        buf.append(method.getName());
+        buf.append("()");
+        return buf.toString();
+    }
+
+    /**
+     * Do additional work on an element to remove abstract methods that
+     * are reported by JProbe 3.0
+     */
+    protected void removeAbstractMethods(ClassFile classFile, Element classNode) {
+        MethodInfo[] methods = classFile.getMethods();
+        Hashtable methodNodeList = getMethods(classNode);
+        // assert xmlMethods.size() == methods.length()
+        final int size = methods.length;
+        for (int i = 0; i < size; i++) {
+            MethodInfo method = methods[i];
+            String methodSig = getMethodSignature(method);
+            Element methodNode = (Element) methodNodeList.get(methodSig);
+            if (methodNode != null
+                && Utils.isAbstract(method.getAccessFlags())) {
+                log("\tRemoving abstract method " + methodSig);
+                classNode.removeChild(methodNode);
+            }
+        }
+    }
+
+    /** create an empty method element with its cov.data values */
+    protected Element createMethodElement(MethodInfo method) {
+        String methodsig = getMethodSignature(method);
+        Element methodElem = report.createElement("method");
+        methodElem.setAttribute("name", methodsig);
+        // create the method cov.data element
+        Element methodData = report.createElement("cov.data");
+        methodElem.appendChild(methodData);
+        methodData.setAttribute("calls", "0");
+        methodData.setAttribute("hit_lines", "0");
+        methodData.setAttribute("total_lines", String.valueOf(method.getNumberOfLines()));
+        return methodElem;
+    }
+
+    /** create an empty package element with its default cov.data (0) */
+    protected Element createPackageElement(String pkgname) {
+        Element pkgElem = report.createElement("package");
+        pkgElem.setAttribute("name", pkgname);
+        // create the package cov.data element / default
+        // must be updated at the end of the whole process
+        Element pkgData = report.createElement("cov.data");
+        pkgElem.appendChild(pkgData);
+        pkgData.setAttribute("calls", "0");
+        pkgData.setAttribute("hit_methods", "0");
+        pkgData.setAttribute("total_methods", "0");
+        pkgData.setAttribute("hit_lines", "0");
+        pkgData.setAttribute("total_lines", "0");
+        return pkgElem;
+    }
+
+    /** create an empty class element with its default cov.data (0) */
+    protected Element createClassElement(ClassFile classFile) {
+        // create the class element
+        Element classElem = report.createElement("class");
+        classElem.setAttribute("name", classFile.getName());
+        // source file possibly does not exist in the bytecode
+        if (null != classFile.getSourceFile()) {
+            classElem.setAttribute("source", classFile.getSourceFile());
+        }
+        // create the cov.data elem
+        Element classData = report.createElement("cov.data");
+        classElem.appendChild(classData);
+        // create the class cov.data element
+        classData.setAttribute("calls", "0");
+        classData.setAttribute("hit_methods", "0");
+        classData.setAttribute("total_methods", "0");
+        classData.setAttribute("hit_lines", "0");
+        classData.setAttribute("total_lines", "0");
+        return classElem;
+    }
+
+    /** serialize a classfile into XML */
+    protected void serializeClass(ClassFile classFile) {
+        // the class already is reported so ignore it
+        String fullclassname = classFile.getFullName();
+        log("Looking for '" + fullclassname + "'");
+        Element clazz = (Element) classMap.get(fullclassname);
+
+        // ignore classes that are already reported, all the information is
+        // already there.
+        if (clazz != null) {
+            log("Ignoring " + fullclassname);
+            removeAbstractMethods(classFile, clazz);
+            return;
+        }
+
+        // ignore interfaces files, there is no code in there to cover.
+        if (Utils.isInterface(classFile.getAccess())) {
+            return;
+        }
+
+        Vector methods = getFilteredMethods(classFile);
+        // no need to process, there are no methods to add for this class.
+        if (methods.size() == 0) {
+            return;
+        }
+
+        String pkgname = classFile.getPackage();
+        // System.out.println("Looking for package " + pkgname);
+        Element pkgElem = (Element) pkgMap.get(pkgname);
+        if (pkgElem == null) {
+            pkgElem = createPackageElement(pkgname);
+            report.getDocumentElement().appendChild(pkgElem);
+            pkgMap.put(pkgname, pkgElem); // add the pkg to the map
+        }
+        // this is a brand new class, so we have to create a new node
+
+        // create the class element
+        Element classElem = createClassElement(classFile);
+        pkgElem.appendChild(classElem);
+
+        int total_lines = 0;
+        int total_methods = 0;
+        final int count = methods.size();
+        for (int i = 0; i < count; i++) {
+            // create the method element
+            MethodInfo method = (MethodInfo) methods.elementAt(i);
+            if (Utils.isAbstract(method.getAccessFlags())) {
+                continue; // no need to report abstract methods
+            }
+            Element methodElem = createMethodElement(method);
+            classElem.appendChild(methodElem);
+            total_lines += method.getNumberOfLines();
+            total_methods++;
+        }
+        // create the class cov.data element
+        Element classData = getCovDataChild(classElem);
+        classData.setAttribute("total_methods", String.valueOf(total_methods));
+        classData.setAttribute("total_lines", String.valueOf(total_lines));
+
+        // add itself to the node map
+        classMap.put(fullclassname, classElem);
+    }
+
+    protected Vector getFilteredMethods(ClassFile classFile) {
+        MethodInfo[] methodlist = classFile.getMethods();
+        Vector methods = new Vector(methodlist.length);
+        for (int i = 0; i < methodlist.length; i++) {
+            MethodInfo method = methodlist[i];
+            String signature = getMethodSignature(classFile, method);
+            if (filters.accept(signature)) {
+                methods.addElement(method);
+                log("keeping " + signature);
+            } else {
+//              log("discarding " + signature);
+            }
+        }
+        return methods;
+    }
+
+
+    /** update the count of the XML, that is accumulate the stats on
+     * methods, classes and package so that the numbers are valid
+     * according to the info that was appended to the XML.
+     */
+    protected void update() {
+        int calls = 0;
+        int hit_methods = 0;
+        int total_methods = 0;
+        int hit_lines = 0;
+        int total_lines = 0;
+
+        // use the map for access, all nodes should be there
+        Enumeration e = pkgMap.elements();
+        while (e.hasMoreElements()) {
+            Element pkgElem = (Element) e.nextElement();
+            String pkgname = pkgElem.getAttribute("name");
+            Element[] classes = getClasses(pkgElem);
+            int pkg_calls = 0;
+            int pkg_hit_methods = 0;
+            int pkg_total_methods = 0;
+            int pkg_hit_lines = 0;
+            int pkg_total_lines = 0;
+            //System.out.println("Processing package '" + pkgname + "': "
+            // + classes.length + " classes");
+            for (int j = 0; j < classes.length; j++) {
+                Element clazz = classes[j];
+                String classname = clazz.getAttribute("name");
+                if (pkgname != null && pkgname.length() != 0) {
+                    classname = pkgname + "." + classname;
+                }
+                // there's only cov.data as a child so bet on it
+                Element covdata = getCovDataChild(clazz);
+                try {
+                    pkg_calls += Integer.parseInt(covdata.getAttribute("calls"));
+                    pkg_hit_methods += Integer.parseInt(covdata.getAttribute("hit_methods"));
+                    pkg_total_methods += Integer.parseInt(covdata.getAttribute("total_methods"));
+                    pkg_hit_lines += Integer.parseInt(covdata.getAttribute("hit_lines"));
+                    pkg_total_lines += Integer.parseInt(covdata.getAttribute("total_lines"));
+                } catch (NumberFormatException ex) {
+                    log("Error parsing '" + classname + "' (" + j + "/"
+                        + classes.length + ") in package '" + pkgname + "'");
+                    throw ex;
+                }
+            }
+            Element covdata = getCovDataChild(pkgElem);
+            covdata.setAttribute("calls", String.valueOf(pkg_calls));
+            covdata.setAttribute("hit_methods", String.valueOf(pkg_hit_methods));
+            covdata.setAttribute("total_methods", String.valueOf(pkg_total_methods));
+            covdata.setAttribute("hit_lines", String.valueOf(pkg_hit_lines));
+            covdata.setAttribute("total_lines", String.valueOf(pkg_total_lines));
+            calls += pkg_calls;
+            hit_methods += pkg_hit_methods;
+            total_methods += pkg_total_methods;
+            hit_lines += pkg_hit_lines;
+            total_lines += pkg_total_lines;
+        }
+        Element covdata = getCovDataChild(report.getDocumentElement());
+        covdata.setAttribute("calls", String.valueOf(calls));
+        covdata.setAttribute("hit_methods", String.valueOf(hit_methods));
+        covdata.setAttribute("total_methods", String.valueOf(total_methods));
+        covdata.setAttribute("hit_lines", String.valueOf(hit_lines));
+        covdata.setAttribute("total_lines", String.valueOf(total_lines));
+    }
+
+    protected Element getCovDataChild(Element parent) {
+        NodeList children = parent.getChildNodes();
+        int len = children.getLength();
+        for (int i = 0; i < len; i++) {
+            Node child = children.item(i);
+            if (child.getNodeType() == Node.ELEMENT_NODE) {
+                Element elem = (Element) child;
+                if ("cov.data".equals(elem.getNodeName())) {
+                    return elem;
+                }
+            }
+        }
+        throw new NoSuchElementException("Could not find 'cov.data' "
+            + "element in parent '" + parent.getNodeName() + "'");
+    }
+
+    protected Hashtable getMethods(Element clazz) {
+        Hashtable map = new Hashtable();
+        NodeList children = clazz.getChildNodes();
+        int len = children.getLength();
+        for (int i = 0; i < len; i++) {
+            Node child = children.item(i);
+            if (child.getNodeType() == Node.ELEMENT_NODE) {
+                Element elem = (Element) child;
+                if ("method".equals(elem.getNodeName())) {
+                    String name = elem.getAttribute("name");
+                    map.put(name, elem);
+                }
+            }
+        }
+        return map;
+    }
+
+    protected Element[] getClasses(Element pkg) {
+        Vector v = new Vector();
+        NodeList children = pkg.getChildNodes();
+        int len = children.getLength();
+        for (int i = 0; i < len; i++) {
+            Node child = children.item(i);
+            if (child.getNodeType() == Node.ELEMENT_NODE) {
+                Element elem = (Element) child;
+                if ("class".equals(elem.getNodeName())) {
+                    v.addElement(elem);
+                }
+            }
+        }
+        Element[] elems = new Element[v.size()];
+        v.copyInto(elems);
+        return elems;
+
+    }
+
+    protected Element[] getPackages(Element snapshot) {
+        Vector v = new Vector();
+        NodeList children = snapshot.getChildNodes();
+        int len = children.getLength();
+        for (int i = 0; i < len; i++) {
+            Node child = children.item(i);
+            if (child.getNodeType() == Node.ELEMENT_NODE) {
+                Element elem = (Element) child;
+                if ("package".equals(elem.getNodeName())) {
+                    v.addElement(elem);
+                }
+            }
+        }
+        Element[] elems = new Element[v.size()];
+        v.copyInto(elems);
+        return elems;
+    }
+
+    private static DocumentBuilder newBuilder() {
+        try {
+            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+            factory.setIgnoringComments(true);
+            factory.setValidating(false);
+            return factory.newDocumentBuilder();
+        } catch (Exception e) {
+            throw new ExceptionInInitializerError(e);
+        }
+    }
+
+    public void log(String message) {
+        if (task == null) {
+            //System.out.println(message);
+        } else {
+            task.log(message, Project.MSG_DEBUG);
+        }
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/ClassFile.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/ClassFile.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/ClassFile.java	(revision 16139)
@@ -0,0 +1,144 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sitraka.bytecode;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ClassCPInfo;
+import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ConstantPool;
+import org.apache.tools.ant.taskdefs.optional.depend.constantpool.Utf8CPInfo;
+import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.attributes.AttributeInfo;
+
+
+/**
+ * Object representing a class.
+ *
+ * Information are kept to the strict minimum for JProbe reports so
+ * that not too many objects are created for a class, otherwise the
+ * JVM can quickly run out of memory when analyzing a great deal of
+ * classes and keeping them in memory for global analysis.
+ *
+ */
+public final class ClassFile {
+
+    private MethodInfo[] methods;
+
+    private String sourceFile;
+
+    private String fullname;
+
+    private int access_flags;
+
+    public ClassFile(InputStream is) throws IOException {
+        DataInputStream dis = new DataInputStream(is);
+        ConstantPool constantPool = new ConstantPool();
+
+        /* int magic = */ dis.readInt(); // 0xCAFEBABE
+        /* int minor = */ dis.readShort();
+        /* int major = */ dis.readShort();
+
+        constantPool.read(dis);
+        constantPool.resolve();
+
+        // class information
+        access_flags = dis.readShort();
+        int this_class = dis.readShort();
+        fullname = ((ClassCPInfo) constantPool.getEntry(this_class)).getClassName().replace('/', '.');
+        /* int super_class = */ dis.readShort();
+
+        // skip interfaces...
+        int count = dis.readShort();
+        dis.skipBytes(count * 2); // short
+
+        // skip fields...
+        int numFields = dis.readShort();
+        for (int i = 0; i < numFields; i++) {
+            // 3 short: access flags, name index, descriptor index
+            dis.skip(2 * 3);
+            // attribute list...
+            int attributes_count = dis.readUnsignedShort();
+            for (int j = 0; j < attributes_count; j++) {
+                dis.skipBytes(2); // skip attr_id (short)
+                int len = dis.readInt();
+                dis.skipBytes(len);
+            }
+        }
+
+        // read methods
+        int method_count = dis.readShort();
+        methods = new MethodInfo[method_count];
+        for (int i = 0; i < method_count; i++) {
+            methods[i] = new MethodInfo();
+            methods[i].read(constantPool, dis);
+        }
+
+        // get interesting attributes.
+        int attributes_count = dis.readUnsignedShort();
+        for (int j = 0; j < attributes_count; j++) {
+            int attr_id = dis.readShort();
+            int len = dis.readInt();
+            String attr_name = Utils.getUTF8Value(constantPool, attr_id);
+            if (AttributeInfo.SOURCE_FILE.equals(attr_name)) {
+                int name_index = dis.readShort();
+                sourceFile = ((Utf8CPInfo) constantPool.getEntry(name_index)).getValue();
+            } else {
+                dis.skipBytes(len);
+            }
+        }
+    }
+
+    public int getAccess() {
+        return access_flags;
+    }
+
+    public String getSourceFile() {
+        return sourceFile;
+    }
+
+    public MethodInfo[] getMethods() {
+        return methods;
+    }
+
+    public String getFullName() {
+        return fullname;
+    }
+
+    public String getName() {
+        String name = getFullName();
+        int pos = name.lastIndexOf('.');
+        if (pos == -1) {
+            return "";
+        }
+        return name.substring(pos + 1);
+    }
+
+    public String getPackage() {
+        String name = getFullName();
+        int pos = name.lastIndexOf('.');
+        if (pos == -1) {
+            return "";
+        }
+        return name.substring(0, pos);
+    }
+
+}
+
+
+
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/ClassPathLoader.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/ClassPathLoader.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/ClassPathLoader.java	(revision 16139)
@@ -0,0 +1,336 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sitraka.bytecode;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.NoSuchElementException;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+/**
+ * Core of the bytecode analyzer. It loads classes from a given classpath.
+ *
+ */
+public class ClassPathLoader {
+
+    public static final FileLoader NULL_LOADER = new NullLoader();
+
+    /** the list of files to look for */
+    private File[] files;
+
+    /**
+     * create a new instance with a given classpath. It must be urls
+     * separated by the platform specific path separator.
+     * @param classPath the classpath to load all the classes from.
+     */
+    public ClassPathLoader(String classPath) {
+        StringTokenizer st = new StringTokenizer(classPath, File.pathSeparator);
+        Vector entries = new Vector();
+        while (st.hasMoreTokens()) {
+            File file = new File(st.nextToken());
+            entries.addElement(file);
+        }
+        files = new File[entries.size()];
+        entries.copyInto(files);
+    }
+
+    /**
+     * create a new instance with a given set of urls.
+     * @param entries valid file urls (either .jar, .zip or directory)
+     */
+    public ClassPathLoader(String[] entries) {
+        files = new File[entries.length];
+        for (int i = 0; i < entries.length; i++) {
+            files[i] = new File(entries[i]);
+        }
+    }
+
+    /**
+     * create a new instance with a given set of urls
+     * @param entries file urls to look for classes (.jar, .zip or directory)
+     */
+    public ClassPathLoader(File[] entries) {
+        files = entries;
+    }
+
+    /** the interface to implement to look up for specific resources */
+    public interface FileLoader {
+        /** the file url that is looked for .class files */
+        File getFile();
+
+        /** return the set of classes found in the file */
+        ClassFile[] getClasses() throws IOException;
+    }
+
+    /**
+     * @return the set of <tt>FileLoader</tt> loaders matching the given classpath.
+     */
+    public Enumeration loaders() {
+        return new LoaderEnumeration();
+    }
+
+    /**
+     * return the whole set of classes in the classpath. Note that this method
+     * can be very resource demanding since it must load all bytecode from
+     * all classes in all resources in the classpath at a time.
+     * To process it in a less resource demanding way, it is maybe better to
+     * use the <tt>loaders()</tt> that will return loader one by one.
+     *
+     * @return the hashtable containing ALL classes that are found in the given
+     * classpath. Note that the first entry of a given classname will shadow
+     * classes with the same name (as a classloader does)
+     */
+    public Hashtable getClasses() throws IOException {
+        Hashtable map = new Hashtable();
+        Enumeration e = loaders();
+        while (e.hasMoreElements()) {
+            FileLoader loader = (FileLoader) e.nextElement();
+            System.out.println("Processing " + loader.getFile());
+            long t0 = System.currentTimeMillis();
+            ClassFile[] classes = loader.getClasses();
+            long dt = System.currentTimeMillis() - t0;
+            System.out.println("" + classes.length + " classes loaded in " + dt + "ms");
+            for (int j = 0; j < classes.length; j++) {
+                String name = classes[j].getFullName();
+                // do not allow duplicates entries to preserve 'classpath' behavior
+                // first class in wins
+                if (!map.containsKey(name)) {
+                    map.put(name, classes[j]);
+                }
+            }
+        }
+        return map;
+    }
+
+    /** the loader enumeration that will return loaders */
+    private class LoaderEnumeration implements Enumeration {
+        private int index = 0;
+
+        public boolean hasMoreElements() {
+            return index < files.length;
+        }
+
+        public Object nextElement() {
+            if (index >= files.length) {
+                throw new NoSuchElementException();
+            }
+            File file = files[index++];
+            if (!file.exists()) {
+                return new NullLoader(file);
+            }
+            if (file.isDirectory()) {
+                // it's a directory
+                return new DirectoryLoader(file);
+            } else if (file.getName().endsWith(".zip") || file.getName().endsWith(".jar")) {
+                // it's a jar/zip file
+                return new JarLoader(file);
+            }
+            return new NullLoader(file);
+
+        }
+    }
+
+    /**
+     * useful methods to read the whole input stream in memory so that
+     * it can be accessed faster. Processing rt.jar and tools.jar from JDK 1.3.1
+     * brings time from 50s to 7s.
+     */
+    public static InputStream getCachedStream(InputStream is) throws IOException {
+        final InputStream bis = new BufferedInputStream(is);
+        final byte[] buffer = new byte[8192];
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream(2048);
+        int n;
+        bos.reset();
+        while ((n = bis.read(buffer, 0, buffer.length)) != -1) {
+            bos.write(buffer, 0, n);
+        }
+        is.close();
+        return new ByteArrayInputStream(bos.toByteArray());
+    }
+}
+
+/** a null loader to return when the file is not valid */
+final class NullLoader implements ClassPathLoader.FileLoader {
+    private File file;
+
+    NullLoader() {
+        this(null);
+    }
+
+    NullLoader(File file) {
+        this.file = file;
+    }
+
+    public File getFile() {
+        return file;
+    }
+
+    public ClassFile[] getClasses() throws IOException {
+        return new ClassFile[0];
+    }
+}
+
+/**
+ * jar loader specified in looking for classes in jar and zip
+ * @todo read the jar manifest in case there is a Class-Path
+ * entry.
+ */
+final class JarLoader implements ClassPathLoader.FileLoader {
+    private File file;
+
+    JarLoader(File file) {
+        this.file = file;
+    }
+
+    public File getFile() {
+        return file;
+    }
+
+    public ClassFile[] getClasses() throws IOException {
+        ZipFile zipFile = new ZipFile(file);
+        Vector v = new Vector();
+        Enumeration entries = zipFile.entries();
+        while (entries.hasMoreElements()) {
+            ZipEntry entry = (ZipEntry) entries.nextElement();
+            if (entry.getName().endsWith(".class")) {
+                InputStream is = ClassPathLoader.getCachedStream(zipFile.getInputStream(entry));
+                ClassFile classFile = new ClassFile(is);
+                is.close();
+                v.addElement(classFile);
+            }
+        }
+        ClassFile[] classes = new ClassFile[v.size()];
+        v.copyInto(classes);
+        return classes;
+    }
+}
+
+/**
+ * directory loader that will look all classes recursively
+ * @todo should discard classes which package name does not
+ * match the directory ?
+ */
+final class DirectoryLoader implements ClassPathLoader.FileLoader {
+    private File directory;
+    private static final FilenameFilter DIRECTORY_FILTER = new DirectoryFilter();
+    private static final FilenameFilter CLASS_FILTER = new ClassFilter();
+
+    DirectoryLoader(File dir) {
+        directory = dir;
+    }
+
+    public File getFile() {
+        return directory;
+    }
+
+    public ClassFile[] getClasses() throws IOException {
+        Vector v = new Vector(127);
+        Vector files = listFiles(directory, CLASS_FILTER, true);
+        final int filesCount = files.size();
+        for (int i = 0; i < filesCount; i++) {
+            File file = (File) files.elementAt(i);
+            InputStream is = null;
+            try {
+                is = ClassPathLoader.getCachedStream(new FileInputStream(file));
+                ClassFile classFile = new ClassFile(is);
+                is.close();
+                is = null;
+                v.addElement(classFile);
+            } finally {
+                if (is != null) {
+                    try {
+                        is.close();
+                    } catch (IOException ignored) {
+                    }
+                }
+            }
+        }
+        ClassFile[] classes = new ClassFile[v.size()];
+        v.copyInto(classes);
+        return classes;
+    }
+
+    /**
+     * List files that obeys to a specific filter recursively from a given base
+     * directory.
+     * @param   directory   the directory where to list the files from.
+     * @param   filter      the file filter to apply
+     * @param   recurse     tells whether or not the listing is recursive.
+     * @return  the list of <tt>File</tt> objects that applies to the given
+     *          filter.
+     */
+    public static Vector listFiles(File directory, FilenameFilter filter, boolean recurse) {
+        if (!directory.isDirectory()) {
+            throw new IllegalArgumentException(directory + " is not a directory");
+        }
+        Vector list = new Vector(512);
+        listFilesTo(list, directory, filter, recurse);
+        return list;
+    }
+
+    /**
+     * List and add files to a given list. As a convenience it sends back the
+     * instance of the list given as a parameter.
+     * @param   list    the list of files where the filtered files should be added
+     * @param   directory   the directory where to list the files from.
+     * @param   filter      the file filter to apply
+     * @param   recurse     tells whether or not the listing is recursive.
+     * @return  the list instance that was passed as the <tt>list</tt> argument.
+     */
+    private static Vector listFilesTo(Vector list, File directory,
+                                      FilenameFilter filter, boolean recurse) {
+        String[] files = directory.list(filter);
+        for (int i = 0; i < files.length; i++) {
+            list.addElement(new File(directory, files[i]));
+        }
+        files = null;   // we don't need it anymore
+        if (recurse) {
+            String[] subdirs = directory.list(DIRECTORY_FILTER);
+            for (int i = 0; i < subdirs.length; i++) {
+                listFilesTo(list, new File(directory, subdirs[i]), filter, recurse);
+            }
+        }
+        return list;
+    }
+
+}
+
+/** Convenient filter that accepts only directory <tt>File</tt> */
+final class DirectoryFilter implements FilenameFilter {
+    public boolean accept(File directory, String name) {
+        File pathname = new File(directory, name);
+        return pathname.isDirectory();
+    }
+}
+
+/** convenient filter to accept only .class files */
+final class ClassFilter implements FilenameFilter {
+    public boolean accept(File dir, String name) {
+        return name.endsWith(".class");
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/MethodInfo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/MethodInfo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/MethodInfo.java	(revision 16139)
@@ -0,0 +1,144 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sitraka.bytecode;
+
+import java.io.DataInputStream;
+import java.io.IOException;
+import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ConstantPool;
+import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.attributes.AttributeInfo;
+
+/**
+ * Method info structure.
+ * @todo give a more appropriate name to methods.
+ *
+ */
+public final class MethodInfo {
+    private int access_flags;
+    private int loc = -1;
+    private String name;
+    private String descriptor;
+
+    public MethodInfo() {
+    }
+
+    public void read(ConstantPool constantPool, DataInputStream dis) throws IOException {
+        access_flags = dis.readShort();
+
+        int name_index = dis.readShort();
+        name = Utils.getUTF8Value(constantPool, name_index);
+
+        int descriptor_index = dis.readShort();
+        descriptor = Utils.getUTF8Value(constantPool, descriptor_index);
+
+        int attributes_count = dis.readUnsignedShort();
+        for (int i = 0; i < attributes_count; i++) {
+            int attr_id = dis.readShort();
+            String attr_name = Utils.getUTF8Value(constantPool, attr_id);
+            int len = dis.readInt();
+            if (AttributeInfo.CODE.equals(attr_name)) {
+                readCode(constantPool, dis);
+            } else {
+                dis.skipBytes(len);
+            }
+        }
+
+    }
+
+    protected void readCode(ConstantPool constantPool, DataInputStream dis) throws IOException {
+        // skip max_stack (short), max_local (short)
+        dis.skipBytes(2 * 2);
+
+        // skip bytecode...
+        int bytecode_len = dis.readInt();
+        dis.skip(bytecode_len);
+
+        // skip exceptions... 1 exception = 4 short.
+        int exception_count = dis.readShort();
+        dis.skipBytes(exception_count * 4 * 2);
+
+        // read attributes...
+        int attributes_count = dis.readUnsignedShort();
+        for (int i = 0; i < attributes_count; i++) {
+            int attr_id = dis.readShort();
+            String attr_name = Utils.getUTF8Value(constantPool, attr_id);
+            int len = dis.readInt();
+            if (AttributeInfo.LINE_NUMBER_TABLE.equals(attr_name)) {
+                // we're only interested in lines of code...
+                loc = dis.readShort();
+                // skip the table which is 2*loc*short
+                dis.skip(loc * 2 * 2);
+            } else {
+                dis.skipBytes(len);
+            }
+        }
+    }
+
+    public int getAccessFlags() {
+        return access_flags;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getDescriptor() {
+        return descriptor;
+    }
+
+    public String getFullSignature() {
+        return getReturnType() + " " + getShortSignature();
+    }
+
+    public String getShortSignature() {
+        StringBuffer buf = new StringBuffer(getName());
+        buf.append("(");
+        String[] params = getParametersType();
+        for (int i = 0; i < params.length; i++) {
+            buf.append(params[i]);
+            if (i != params.length - 1) {
+                buf.append(", ");
+            }
+        }
+        buf.append(")");
+        return buf.toString();
+    }
+
+    public String getReturnType() {
+        return Utils.getMethodReturnType(getDescriptor());
+    }
+
+    public String[] getParametersType() {
+        return Utils.getMethodParams(getDescriptor());
+    }
+
+    public int getNumberOfLines() {
+        return loc;
+    }
+
+    public String getAccess() {
+        return Utils.getMethodAccess(access_flags);
+    }
+
+    public String toString() {
+        StringBuffer sb = new StringBuffer();
+        sb.append("Method: ").append(getAccess()).append(" ");
+        sb.append(getFullSignature());
+        return sb.toString();
+    }
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/Utils.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/Utils.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/Utils.java	(revision 16139)
@@ -0,0 +1,380 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.sitraka.bytecode;
+
+import java.util.Vector;
+import org.apache.tools.ant.taskdefs.optional.depend.constantpool.ConstantPool;
+import org.apache.tools.ant.taskdefs.optional.depend.constantpool.Utf8CPInfo;
+
+/**
+ * Utilities mostly to manipulate methods and access flags.
+ *
+ */
+public class Utils {
+    /** public access flag */
+    public static final short ACC_PUBLIC = 1;
+    /** private access flag */
+    public static final short ACC_PRIVATE = 2;
+    /** protected access flag */
+    public static final short ACC_PROTECTED = 4;
+    /** static access flag */
+    public static final short ACC_STATIC = 8;
+    /** final access flag */
+    public static final short ACC_FINAL = 16;
+    /** super access flag */
+    public static final short ACC_SUPER = 32;
+    /** synchronized access flag */
+    public static final short ACC_SYNCHRONIZED = 32;
+    /** volatile access flag */
+    public static final short ACC_VOLATILE = 64;
+    /** transient access flag */
+    public static final short ACC_TRANSIENT = 128;
+    /** native access flag */
+    public static final short ACC_NATIVE = 256;
+    /** interface access flag */
+    public static final short ACC_INTERFACE = 512;
+    /** abstract access flag */
+    public static final short ACC_ABSTRACT = 1024;
+    /** strict access flag */
+    public static final short ACC_STRICT = 2048;
+
+    /** private constructor */
+    private Utils() {
+    }
+
+    /**
+     * return an UTF8 value from the pool located a a specific index.
+     * @param pool the constant pool to look at
+     * @param index index of the UTF8 value in the constant pool
+     * @return the value of the string if it exists
+     * @throws ClassCastException if the index is not an UTF8 constant.
+     */
+    public static String getUTF8Value(ConstantPool pool, int index) {
+        return ((Utf8CPInfo) pool.getEntry(index)).getValue();
+    }
+
+    /**
+     * parse all parameters from a descritor into fields of java name.
+     * @param descriptor of a method.
+     * @return the parameter list of a given method descriptor. Each string
+     * represent a java object with its fully qualified classname or the
+     * primitive name such as int, long, ...
+     */
+    public static String[] getMethodParams(String descriptor) {
+        int i = 0;
+        if (descriptor.charAt(i) != '(') {
+            throw new IllegalArgumentException("Method descriptor should start with a '('");
+        }
+        Vector params = new Vector();
+        StringBuffer param = new StringBuffer();
+        i++;
+        while ((i = descriptor2java(descriptor, i, param)) < descriptor.length()) {
+            params.add(param.substring(0));
+            param = new StringBuffer();
+            if (descriptor.charAt(i) == ')') {
+                i++;
+                break;
+            }
+        }
+        String[] array = new String[params.size()];
+        params.copyInto(array);
+        return array;
+    }
+
+    /**
+     * return the object type of a return type.
+     * @param descriptor
+     * @return get the return type objet of a given descriptor
+     */
+    public static String getMethodReturnType(String descriptor) {
+        int pos = descriptor.indexOf(')');
+        StringBuffer rettype = new StringBuffer();
+        descriptor2java(descriptor, pos + 1, rettype);
+        return rettype.toString();
+    }
+
+    /**
+     * Parse a single descriptor symbol and returns it java equivalent.
+     * @param descriptor the descriptor symbol.
+     * @param i the index to look at the symbol in the descriptor string
+     * @param sb the stringbuffer to return the java equivalent of the symbol
+     * @return the index after the descriptor symbol
+     */
+    public static int descriptor2java(String descriptor, int i, StringBuffer sb) {
+        // get the dimension
+        StringBuffer dim = new StringBuffer();
+        for (; descriptor.charAt(i) == '['; i++) {
+            dim.append("[]");
+        }
+        // now get the type
+        switch (descriptor.charAt(i)) {
+            case 'B':
+                sb.append("byte");
+                break;
+            case 'C':
+                sb.append("char");
+                break;
+            case 'D':
+                sb.append("double");
+                break;
+            case 'F':
+                sb.append("float");
+                break;
+            case 'I':
+                sb.append("int");
+                break;
+            case 'J':
+                sb.append("long");
+                break;
+            case 'S':
+                sb.append("short");
+                break;
+            case 'Z':
+                sb.append("boolean");
+                break;
+            case 'V':
+                sb.append("void");
+                break;
+            case 'L':
+                // it is a class
+                int pos = descriptor.indexOf(';', i + 1);
+                String classname = descriptor.substring(i + 1, pos).replace('/', '.');
+                sb.append(classname);
+                i = pos;
+                break;
+            default:
+                //@todo, yeah this happens because I got things like:
+                // ()Ljava/lang/Object; and it will return and ) will be here
+                // think about it.
+
+                //ooooops should never happen
+                //throw new IllegalArgumentException("Invalid descriptor
+                // symbol: '" + i + "' in '" + descriptor + "'");
+        }
+        sb.append(dim.toString());
+        return ++i;
+    }
+
+    /**
+     * check for abstract access
+     * @param access_flags access flags
+     */
+    public static boolean isAbstract(int access_flags) {
+        return (access_flags & ACC_ABSTRACT) != 0;
+    }
+
+    /**
+     * check for public access
+     * @param access_flags access flags
+     */
+    public static boolean isPublic(int access_flags) {
+        return (access_flags & ACC_PUBLIC) != 0;
+    }
+
+    /**
+     * check for a static access
+     * @param access_flags access flags
+     */
+    public static boolean isStatic(int access_flags) {
+        return (access_flags & ACC_STATIC) != 0;
+    }
+
+    /**
+     *  check for native access
+     * @param access_flags access flags
+     */
+    public static boolean isNative(int access_flags) {
+        return (access_flags & ACC_NATIVE) != 0;
+    }
+
+    /**
+     * check for class access
+     * @param access_flags access flags
+     */
+    public static boolean isClass(int access_flags) {
+        return !isInterface(access_flags);
+    }
+
+    /**
+     * check for strict access
+     * @param access_flags access flags
+     */
+    public static boolean isStrict(int access_flags) {
+        return (access_flags & ACC_STRICT) != 0;
+    }
+
+    /**
+     * check for interface access
+     * @param access_flags access flags
+     */
+    public static boolean isInterface(int access_flags) {
+        return (access_flags & ACC_INTERFACE) != 0;
+    }
+
+    /**
+     * check for private access
+     * @param access_flags access flags
+     */
+    public static boolean isPrivate(int access_flags) {
+        return (access_flags & ACC_PRIVATE) != 0;
+    }
+
+    /**
+     * check for transient flag
+     * @param access_flags access flags
+     */
+    public static boolean isTransient(int access_flags) {
+        return (access_flags & ACC_TRANSIENT) != 0;
+    }
+
+    /**
+     * check for volatile flag
+     * @param access_flags access flags
+     */
+    public static boolean isVolatile(int access_flags) {
+        return (access_flags & ACC_VOLATILE) != 0;
+    }
+
+    /**
+     * check for super flag
+     * @param access_flags access flag
+     */
+    public static boolean isSuper(int access_flags) {
+        return (access_flags & ACC_SUPER) != 0;
+    }
+
+    /**
+     * check for protected flag
+     * @param access_flags access flags
+     */
+    public static boolean isProtected(int access_flags) {
+        return (access_flags & ACC_PROTECTED) != 0;
+    }
+
+    /**
+     * chck for final flag
+     * @param access_flags access flags
+     */
+    public static boolean isFinal(int access_flags) {
+        return (access_flags & ACC_FINAL) != 0;
+    }
+
+    /**
+     * check for synchronized flag
+     * @param access_flags access flags
+     */
+    public static boolean isSynchronized(int access_flags) {
+        return (access_flags & ACC_SYNCHRONIZED) != 0;
+    }
+
+    /**
+     * return the method access flag as java modifiers
+     * @param access_flags access flags
+     * @return the access flags as modifier strings
+     */
+    public static String getMethodAccess(int access_flags) {
+        StringBuffer sb = new StringBuffer();
+        if (isPublic(access_flags)) {
+            sb.append("public ");
+        } else if (isPrivate(access_flags)) {
+            sb.append("private ");
+        } else if (isProtected(access_flags)) {
+            sb.append("protected ");
+        }
+        if (isFinal(access_flags)) {
+            sb.append("final ");
+        }
+        if (isStatic(access_flags)) {
+            sb.append("static ");
+        }
+        if (isSynchronized(access_flags)) {
+            sb.append("synchronized ");
+        }
+        if (isNative(access_flags)) {
+            sb.append("native ");
+        }
+        if (isAbstract(access_flags)) {
+            sb.append("abstract ");
+        }
+        return sb.toString().trim();
+    }
+
+    /**
+     * return the field access flag as java modifiers
+     * @param access_flags access flags
+     * @return the access flags as modifier strings
+     */
+    public static String getFieldAccess(int access_flags) {
+        StringBuffer sb = new StringBuffer();
+        if (isPublic(access_flags)) {
+            sb.append("public ");
+        } else if (isPrivate(access_flags)) {
+            sb.append("private ");
+        } else if (isProtected(access_flags)) {
+            sb.append("protected ");
+        }
+        if (isFinal(access_flags)) {
+            sb.append("final ");
+        }
+        if (isStatic(access_flags)) {
+            sb.append("static ");
+        }
+        if (isVolatile(access_flags)) {
+            sb.append("volatile ");
+        }
+        if (isTransient(access_flags)) {
+            sb.append("transient ");
+        }
+        return sb.toString().trim();
+    }
+
+    /**
+     * return the class access flag as java modifiers
+     * @param access_flags access flags
+     * @return the access flags as modifier strings
+     */
+    public static String getClassAccess(int access_flags) {
+        StringBuffer sb = new StringBuffer();
+        if (isPublic(access_flags)) {
+            sb.append("public ");
+        } else if (isProtected(access_flags)) {
+            sb.append("protected ");
+        } else if (isPrivate(access_flags)) {
+            sb.append("private ");
+        }
+        if (isFinal(access_flags)) {
+            sb.append("final ");
+        }
+        if (isSuper(access_flags)) {
+            sb.append("/*super*/ ");
+        }
+        if (isInterface(access_flags)) {
+            sb.append("interface ");
+        }
+        if (isAbstract(access_flags)) {
+            sb.append("abstract ");
+        }
+        if (isClass(access_flags)) {
+            sb.append("class ");
+        }
+        return sb.toString().trim();
+    }
+}
+
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/attributes/AttributeInfo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/attributes/AttributeInfo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sitraka/bytecode/attributes/AttributeInfo.java	(revision 16139)
@@ -0,0 +1,47 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.attributes;
+
+/**
+ * Attribute info structure that provides base methods
+ *
+ */
+public interface AttributeInfo {
+
+    String SOURCE_FILE = "SourceFile";
+
+    String CONSTANT_VALUE = "ConstantValue";
+
+    String CODE = "Code";
+
+    String EXCEPTIONS = "Exceptions";
+
+    String LINE_NUMBER_TABLE = "LineNumberTable";
+
+    String LOCAL_VARIABLE_TABLE = "LocalVariableTable";
+
+    String INNER_CLASSES = "InnerClasses";
+
+    String SOURCE_DIR = "SourceDir";
+
+    String SYNTHETIC = "Synthetic";
+
+    String DEPRECATED = "Deprecated";
+
+    String UNKNOWN = "Unknown";
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOS.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOS.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOS.java	(revision 16139)
@@ -0,0 +1,452 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sos;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * A base class for creating tasks for executing commands on SourceOffSite.
+ *
+ *  These tasks were inspired by the VSS tasks
+ *
+ */
+
+public abstract class SOS extends Task implements SOSCmd {
+
+    private String sosCmdDir = null;
+    private String sosUsername = null;
+    private String sosPassword = "";
+    private String projectPath = null;
+    private String vssServerPath = null;
+    private String sosServerPath = null;
+    private String sosHome = null;
+    private String localPath = null;
+    private String version = null;
+    private String label = null;
+    private String comment = null;
+    private String filename = null;
+
+    private boolean noCompress = false;
+    private boolean noCache = false;
+    private boolean recursive = false;
+    private boolean verbose = false;
+
+    /** Commandline to be executed */
+    protected Commandline commandLine;
+
+    /**
+     * Flag to disable the cache when set.
+     * Required if SOSHOME is set as an environment variable.
+     * Defaults to false.
+     *
+     * @param  nocache  True to disable caching.
+     */
+    public final void setNoCache(boolean nocache) {
+        noCache = nocache;
+    }
+
+    /**
+     * Flag to disable compression when set. Defaults to false.
+     *
+     * @param  nocompress  True to disable compression.
+     */
+    public final void setNoCompress(boolean nocompress) {
+        noCompress = nocompress;
+    }
+
+    /**
+     * The directory where soscmd(.exe) is located.
+     * soscmd must be on the path if omitted.
+     *
+     * @param  dir  The new sosCmd value
+     */
+    public final void setSosCmd(String dir) {
+        sosCmdDir = Project.translatePath(dir);
+    }
+
+    /**
+     * The SourceSafe username.
+     *
+     * @param  username  The new username value
+     *
+     * @ant.attribute group="required"
+     */
+    public final void setUsername(String username) {
+        sosUsername = username;
+    }
+
+    /**
+     * The SourceSafe password.
+     *
+     * @param  password  The new password value
+     */
+    public final void setPassword(String password) {
+        sosPassword = password;
+    }
+
+    /**
+     * The SourceSafe project path.
+     *
+     * @param  projectpath  The new projectpath value
+     *
+     * @ant.attribute group="required"
+     */
+    public final void setProjectPath(String projectpath) {
+        if (projectpath.startsWith(SOSCmd.PROJECT_PREFIX)) {
+            projectPath = projectpath;
+        } else {
+            projectPath = SOSCmd.PROJECT_PREFIX + projectpath;
+        }
+    }
+
+    /**
+     * The path to the location of the ss.ini file.
+     *
+     * @param  vssServerPath  The new vssServerPath value
+     *
+     * @ant.attribute group="required"
+     */
+    public final void setVssServerPath(String vssServerPath) {
+        this.vssServerPath = vssServerPath;
+    }
+
+    /**
+     * Path to the SourceOffSite home directory.
+     *
+     * @param  sosHome  The new sosHome value
+     */
+    public final void setSosHome(String sosHome) {
+        this.sosHome = sosHome;
+    }
+
+    /**
+     * The address and port of SourceOffSite Server,
+     * for example 192.168.0.1:8888.
+     *
+     * @param  sosServerPath  The new sosServerPath value
+     *
+     * @ant.attribute group="required"
+     */
+    public final void setSosServerPath(String sosServerPath) {
+        this.sosServerPath = sosServerPath;
+    }
+
+    /**
+     * Override the working directory and get to the specified path.
+     *
+     * @param  path  The new localPath value
+     */
+    public final void setLocalPath(Path path) {
+        localPath = path.toString();
+    }
+
+    /**
+     * Enable verbose output. Defaults to false.
+     *
+     * @param  verbose  True for verbose output.
+     */
+    public void setVerbose(boolean verbose) {
+        this.verbose = verbose;
+    }
+
+    // Special setters for the sub-classes
+
+    protected void setInternalFilename(String file) {
+        filename = file;
+    }
+
+    protected void setInternalRecursive(boolean recurse) {
+        recursive = recurse;
+    }
+
+    protected void setInternalComment(String text) {
+        comment = text;
+    }
+
+    protected void setInternalLabel(String text) {
+        label = text;
+    }
+
+    protected void setInternalVersion(String text) {
+        version = text;
+    }
+
+    /**
+     * Get the executable to run. Add the path if it was specifed in the build file
+     *
+     * @return the executable to run
+     */
+    protected String getSosCommand() {
+        if (sosCmdDir == null) {
+            return COMMAND_SOS_EXE;
+        } else {
+            return sosCmdDir + File.separator + COMMAND_SOS_EXE;
+        }
+    }
+
+    /**
+     * Get the comment
+     * @return if it was set, null if not
+     */
+    protected String getComment() {
+        return comment;
+    }
+
+    /**
+     * Get the version
+     * @return if it was set, null if not
+     */
+    protected String getVersion() {
+        return version;
+    }
+
+    /**
+     * Get the label
+     * @return if it was set, null if not
+     */
+    protected String getLabel() {
+        return label;
+    }
+
+    /**
+     * Get the username
+     * @return if it was set, null if not
+     */
+    protected String getUsername() {
+        return sosUsername;
+    }
+
+    /**
+     * Get the password
+     * @return empty string if it wans't set
+     */
+    protected String getPassword() {
+        return sosPassword;
+    }
+
+    /**
+     * Get the project path
+     * @return if it was set, null if not
+     */
+    protected String getProjectPath() {
+        return projectPath;
+    }
+
+    /**
+     * Get the VSS server path
+     * @return if it was set, null if not
+     */
+    protected String getVssServerPath() {
+        return vssServerPath;
+    }
+
+    /**
+     * Get the SOS home directory
+     * @return if it was set, null if not
+     */
+    protected String getSosHome() {
+        return sosHome;
+    }
+
+    /**
+     * Get the SOS serve path
+     * @return if it was set, null if not
+     */
+    protected String getSosServerPath() {
+        return sosServerPath;
+    }
+
+    /**
+     * Get the filename to be acted upon
+     * @return if it was set, null if not
+     */
+    protected String getFilename() {
+        return filename;
+    }
+
+    /**
+     * Get the NoCompress flag
+     *
+     * @return the 'nocompress' Flag if the attribute was 'true',
+     *         otherwise an empty string
+     */
+    protected String getNoCompress() {
+        return noCompress ? FLAG_NO_COMPRESSION : "";
+    }
+
+    /**
+     * Get the NoCache flag
+     *
+     * @return the 'nocache' Flag if the attribute was 'true', otherwise an empty string
+     */
+    protected String getNoCache() {
+        return noCache ? FLAG_NO_CACHE : "";
+    }
+
+    /**
+     * Get the 'verbose' Flag
+     *
+     * @return the 'verbose' Flag if the attribute was 'true', otherwise an empty string
+     */
+    protected String getVerbose() {
+        return verbose ? FLAG_VERBOSE : "";
+    }
+
+    /**
+     * Get the 'recursive' Flag
+     *
+     * @return the 'recursive' Flag if the attribute was 'true', otherwise an empty string
+     */
+    protected String getRecursive() {
+        return recursive ? FLAG_RECURSION : "";
+    }
+
+    /**
+     * Builds and returns the working directory.
+     * <p>
+     * The localpath is created if it didn't exist
+     *
+     * @return the absolute path of the working directory
+     */
+    protected String getLocalPath() {
+        if (localPath == null) {
+            return getProject().getBaseDir().getAbsolutePath();
+        } else {
+            // make sure localDir exists, create it if it doesn't
+            File dir = getProject().resolveFile(localPath);
+            if (!dir.exists()) {
+                boolean done = dir.mkdirs();
+                if (!done) {
+                    String msg = "Directory " + localPath + " creation was not "
+                        + "successful for an unknown reason";
+                    throw new BuildException(msg, getLocation());
+                }
+                getProject().log("Created dir: " + dir.getAbsolutePath());
+            }
+            return dir.getAbsolutePath();
+        }
+    }
+
+    /**
+     * Subclasses implement the logic required to construct the command line.
+     *
+     * @return   The command line to execute.
+     */
+    abstract Commandline buildCmdLine();
+
+
+    /**
+     * Execute the created command line.
+     *
+     * @throws BuildException
+     */
+    public void execute()
+        throws BuildException {
+        int result = 0;
+        buildCmdLine();
+        result = run(commandLine);
+        if (result == 255) {  // This is the exit status
+            String msg = "Failed executing: " + commandLine.toString();
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+    /**
+     * Execute the created command line.
+     *
+     * @param  cmd              The command line to run.
+     * @return                  int the exit code.
+     * @throws  BuildException
+     */
+    protected int run(Commandline cmd) {
+        try {
+            Execute exe = new Execute(new LogStreamHandler(this,
+                    Project.MSG_INFO,
+                    Project.MSG_WARN));
+
+            exe.setAntRun(getProject());
+            exe.setWorkingDirectory(getProject().getBaseDir());
+            exe.setCommandline(cmd.getCommandline());
+            exe.setVMLauncher(false);  // Use the OS VM launcher so we get environment variables
+            return exe.execute();
+        } catch (java.io.IOException e) {
+            throw new BuildException(e, getLocation());
+        }
+    }
+
+    /** Sets the executable and add the required attributes to the command line. */
+    protected void getRequiredAttributes() {
+        // Get the path to the soscmd(.exe)
+        commandLine.setExecutable(getSosCommand());
+        // SOS server address is required
+        if (getSosServerPath() == null) {
+            throw new BuildException("sosserverpath attribute must be set!", getLocation());
+        }
+        commandLine.createArgument().setValue(FLAG_SOS_SERVER);
+        commandLine.createArgument().setValue(getSosServerPath());
+        // Login info is required
+        if (getUsername() == null) {
+            throw new BuildException("username attribute must be set!", getLocation());
+        }
+        commandLine.createArgument().setValue(FLAG_USERNAME);
+        commandLine.createArgument().setValue(getUsername());
+        // The SOS class knows that the SOS server needs the password flag,
+        // even if there is no password ,so we send a " "
+        commandLine.createArgument().setValue(FLAG_PASSWORD);
+        commandLine.createArgument().setValue(getPassword());
+        // VSS Info is required
+        if (getVssServerPath() == null) {
+            throw new BuildException("vssserverpath attribute must be set!", getLocation());
+        }
+        commandLine.createArgument().setValue(FLAG_VSS_SERVER);
+        commandLine.createArgument().setValue(getVssServerPath());
+        // VSS project is required
+        if (getProjectPath() == null) {
+            throw new BuildException("projectpath attribute must be set!", getLocation());
+        }
+        commandLine.createArgument().setValue(FLAG_PROJECT);
+        commandLine.createArgument().setValue(getProjectPath());
+    }
+
+    /** Adds the optional attributes to the command line. */
+    protected void getOptionalAttributes() {
+        // -verbose
+        commandLine.createArgument().setValue(getVerbose());
+        // Disable Compression
+        commandLine.createArgument().setValue(getNoCompress());
+        // Path to the SourceOffSite home directory /home/user/.sos
+        if (getSosHome() == null) {
+            // If -soshome was not specified then we can look for nocache
+            commandLine.createArgument().setValue(getNoCache());
+        } else {
+            commandLine.createArgument().setValue(FLAG_SOS_HOME);
+            commandLine.createArgument().setValue(getSosHome());
+        }
+        //If a working directory was specified then add it to the command line
+        if (getLocalPath() != null) {
+            commandLine.createArgument().setValue(FLAG_WORKING_DIR);
+            commandLine.createArgument().setValue(getLocalPath());
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckin.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckin.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckin.java	(revision 16139)
@@ -0,0 +1,101 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sos;
+
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Commits and unlocks files in Visual SourceSafe via a SourceOffSite server.
+ *
+ *
+ * @ant.task name="soscheckin" category="scm"
+ */
+public class SOSCheckin extends SOS {
+
+    /**
+     * The filename to act upon.
+     * If no file is specified then the task
+     * acts upon the project.
+     *
+     * @param  filename  The new file value
+     */
+    public final void setFile(String filename) {
+        super.setInternalFilename(filename);
+    }
+
+    /**
+     * Flag to recursively apply the action. Defaults to false.
+     *
+     * @param  recursive  True for recursive operation.
+     */
+    public void setRecursive(boolean recursive) {
+        super.setInternalRecursive(recursive);
+    }
+
+    /**
+     * The comment to apply to all files being labelled.
+     *
+     * @param  comment  The new comment value
+     */
+    public void setComment(String comment) {
+        super.setInternalComment(comment);
+    }
+
+    /**
+     * Build the command line. <p>
+     *
+     * CheckInFile required parameters: -server -name -password -database -project
+     *  -file<br>
+     * CheckInFile optional parameters: -workdir -log -verbose -nocache -nocompression
+     *  -soshome<br>
+     * CheckInProject required parameters: -server -name -password -database
+     *  -project<br>
+     * CheckInProject optional parameters: workdir -recursive -log -verbose
+     *  -nocache -nocompression -soshome<br>
+     *
+     * @return    Commandline the generated command to be executed
+     */
+    protected Commandline buildCmdLine() {
+        commandLine = new Commandline();
+
+        // If we find a "file" attribute then act on a file otherwise act on a project
+        if (getFilename() != null) {
+            // add -command CheckInFile to the commandline
+            commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND);
+            commandLine.createArgument().setValue(SOSCmd.COMMAND_CHECKIN_FILE);
+            // add -file xxxxx to the commandline
+            commandLine.createArgument().setValue(SOSCmd.FLAG_FILE);
+            commandLine.createArgument().setValue(getFilename());
+        } else {
+            // add -command CheckInProject to the commandline
+            commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND);
+            commandLine.createArgument().setValue(SOSCmd.COMMAND_CHECKIN_PROJECT);
+            // look for a recursive option
+            commandLine.createArgument().setValue(getRecursive());
+        }
+
+        getRequiredAttributes();
+        getOptionalAttributes();
+
+        // Look for a comment
+        if (getComment() != null) {
+            commandLine.createArgument().setValue(SOSCmd.FLAG_COMMENT);
+            commandLine.createArgument().setValue(getComment());
+        }
+        return commandLine;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckout.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckout.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCheckout.java	(revision 16139)
@@ -0,0 +1,85 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sos;
+
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Retrieves and locks files in Visual SourceSafe via a SourceOffSite server.
+ *
+ *
+ * @ant.task name="soscheckout" category="scm"
+ */
+public class SOSCheckout extends SOS {
+
+    /**
+     * The filename to act upon.
+     * If no file is specified then the task
+     * acts upon the project.
+     *
+     * @param  filename  The new file value
+     */
+    public final void setFile(String filename) {
+        super.setInternalFilename(filename);
+    }
+
+    /**
+     * Flag to recursively apply the action. Defaults to false.
+     *
+     * @param  recursive  True for recursive operation.
+     */
+    public void setRecursive(boolean recursive) {
+        super.setInternalRecursive(recursive);
+    }
+
+    /**
+     * Build the command line <br>
+     *
+     * CheckOutFile required parameters: -server -name -password -database -project -file<br>
+     * CheckOutFile optional parameters: -workdir -verbose -nocache -nocompression -soshome<br>
+     *
+     * CheckOutProject required parameters: -server -name -password -database -project<br>
+     * CheckOutProject optional parameters:-workdir -recursive -verbose -nocache
+     * -nocompression -soshome<br>
+     *
+     * @return    Commandline the generated command to be executed
+     */
+    protected Commandline buildCmdLine() {
+        commandLine = new Commandline();
+
+        // If we find a "file" attribute then act on a file otherwise act on a project
+        if (getFilename() != null) {
+            // add -command CheckOutFile to the commandline
+            commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND);
+            commandLine.createArgument().setValue(SOSCmd.COMMAND_CHECKOUT_FILE);
+            // add -file xxxxx to the commandline
+            commandLine.createArgument().setValue(SOSCmd.FLAG_FILE);
+            commandLine.createArgument().setValue(getFilename());
+        } else {
+            // add -command CheckOutProject to the commandline
+            commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND);
+            commandLine.createArgument().setValue(SOSCmd.COMMAND_CHECKOUT_PROJECT);
+            // look for a recursive option
+            commandLine.createArgument().setValue(getRecursive());
+        }
+
+        getRequiredAttributes();
+        getOptionalAttributes();
+
+        return commandLine;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCmd.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCmd.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSCmd.java	(revision 16139)
@@ -0,0 +1,52 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sos;
+
+/**
+ * Interface to hold constants used by the SOS tasks
+ *
+ */
+public interface SOSCmd {
+    // soscmd Command options
+    String COMMAND_SOS_EXE = "soscmd";
+    String COMMAND_GET_FILE = "GetFile";
+    String COMMAND_GET_PROJECT = "GetProject";
+    String COMMAND_CHECKOUT_FILE = "CheckOutFile";
+    String COMMAND_CHECKOUT_PROJECT = "CheckOutProject";
+    String COMMAND_CHECKIN_FILE = "CheckInFile";
+    String COMMAND_CHECKIN_PROJECT = "CheckInProject";
+    String COMMAND_HISTORY = "GetFileHistory";
+    String COMMAND_LABEL = "AddLabel";
+    String PROJECT_PREFIX = "$";
+    // soscmd Option flags
+    String FLAG_COMMAND = "-command";
+    String FLAG_VSS_SERVER = "-database";
+    String FLAG_USERNAME = "-name";
+    String FLAG_PASSWORD = "-password";
+    String FLAG_COMMENT = "-log";
+    String FLAG_WORKING_DIR = "-workdir";
+    String FLAG_RECURSION = "-recursive";
+    String FLAG_VERSION = "-revision";
+    String FLAG_LABEL = "-label";
+    String FLAG_NO_COMPRESSION = "-nocompress";
+    String FLAG_NO_CACHE = "-nocache";
+    String FLAG_SOS_SERVER = "-server";
+    String FLAG_SOS_HOME = "-soshome";
+    String FLAG_PROJECT = "-project";
+    String FLAG_FILE = "-file";
+    String FLAG_VERBOSE = "-verbose";
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSGet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSGet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSGet.java	(revision 16139)
@@ -0,0 +1,116 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sos;
+
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Retrieves a read-only copy of the specified project or file
+ * from Visual SourceSafe via a SourceOffSite server.
+ *
+ *
+ * @ant.task name="sosget" category="scm"
+ */
+public class SOSGet extends SOS {
+
+    /**
+     * The Filename to act upon.
+     * If no file is specified then the tasks
+     * act upon the project.
+     *
+     * @param  filename  The new file value
+     */
+    public final void setFile(String filename) {
+        super.setInternalFilename(filename);
+    }
+
+    /**
+     * Flag to recursively apply the action. Defaults to false
+     *
+     * @param  recursive  True for recursive operation.
+     */
+    public void setRecursive(boolean recursive) {
+        super.setInternalRecursive(recursive);
+    }
+
+    /**
+     * Set the version number to get -
+     * only works with SOSGet on a file.
+     *
+     * @param  version  The new version value
+     */
+    public void setVersion(String version) {
+        super.setInternalVersion(version);
+    }
+
+    /**
+     * The labeled version to operate on in SourceSafe.
+     *
+     * @param  label  The new label value
+     */
+    public void setLabel(String label) {
+        super.setInternalLabel(label);
+    }
+
+    /**
+     * Build the command line <br>
+     *
+     * GetFile required parameters: -server -name -password -database -project -file<br>
+     * GetFile optional parameters: -workdir -revision -verbose -nocache -nocompression -soshome<br>
+     *
+     * GetProject required parameters: -server -name -password -database -project<br>
+     * GetProject optional parameters: -label -workdir -recursive -verbose -nocache
+     * -nocompression -soshome<br>
+     *
+     * @return    Commandline the generated command to be executed
+     */
+    protected Commandline buildCmdLine() {
+        commandLine = new Commandline();
+
+        // If we find a "file" attribute then act on a file otherwise act on a project
+        if (getFilename() != null) {
+            // add -command GetFile to the commandline
+            commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND);
+            commandLine.createArgument().setValue(SOSCmd.COMMAND_GET_FILE);
+            // add -file xxxxx to the commandline
+            commandLine.createArgument().setValue(SOSCmd.FLAG_FILE);
+            commandLine.createArgument().setValue(getFilename());
+            // look for a version attribute
+            if (getVersion() != null) {
+                //add -revision xxxxx to the commandline
+                commandLine.createArgument().setValue(SOSCmd.FLAG_VERSION);
+                commandLine.createArgument().setValue(getVersion());
+            }
+        } else {
+            // add -command GetProject to the commandline
+            commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND);
+            commandLine.createArgument().setValue(SOSCmd.COMMAND_GET_PROJECT);
+            // look for a recursive option
+            commandLine.createArgument().setValue(getRecursive());
+            // look for a label option
+            if (getLabel() != null) {
+                commandLine.createArgument().setValue(SOSCmd.FLAG_LABEL);
+                commandLine.createArgument().setValue(getLabel());
+            }
+        }
+
+        getRequiredAttributes();
+        getOptionalAttributes();
+
+        return commandLine;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSLabel.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSLabel.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/SOSLabel.java	(revision 16139)
@@ -0,0 +1,91 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sos;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Labels Visual SourceSafe files via a SourceOffSite server.
+ *
+ *
+ * @ant.task name="soslabel" category="scm"
+ */
+public class SOSLabel extends SOS {
+
+    /**
+     * The version number to label.
+     *
+     * @param  version  The new version value
+     */
+    public void setVersion(String version) {
+        super.setInternalVersion(version);
+    }
+
+    /**
+     * The label to apply the the files in SourceSafe.
+     *
+     * @param  label  The new label value
+     *
+     * @ant.attribute group="required"
+     */
+    public void setLabel(String label) {
+        super.setInternalLabel(label);
+    }
+
+    /**
+     * The comment to apply to all files being labelled.
+     *
+     * @param  comment  The new comment value
+     */
+    public void setComment(String comment) {
+        super.setInternalComment(comment);
+    }
+
+    /**
+     *  Build the command line <br>
+     *  AddLabel required parameters: -server -name -password -database -project -label<br>
+     *  AddLabel optional parameters: -verbose -comment<br>
+     *
+     * @return    Commandline the generated command to be executed
+     */
+    protected Commandline buildCmdLine() {
+        commandLine = new Commandline();
+
+        // add -command AddLabel to the commandline
+        commandLine.createArgument().setValue(SOSCmd.FLAG_COMMAND);
+        commandLine.createArgument().setValue(SOSCmd.COMMAND_LABEL);
+
+        getRequiredAttributes();
+
+        // a label is required
+        if (getLabel() == null) {
+            throw new BuildException("label attribute must be set!", getLocation());
+        }
+        commandLine.createArgument().setValue(SOSCmd.FLAG_LABEL);
+        commandLine.createArgument().setValue(getLabel());
+
+        // -verbose
+        commandLine.createArgument().setValue(getVerbose());
+        // Look for a comment
+        if (getComment() != null) {
+            commandLine.createArgument().setValue(SOSCmd.FLAG_COMMENT);
+            commandLine.createArgument().setValue(getComment());
+        }
+        return commandLine;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/package.html
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/package.html	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sos/package.html	(revision 16139)
@@ -0,0 +1,15 @@
+<html>
+<body>
+  <p>
+    Ant tasks for working with a SourceOffSite source control system.
+  </p>
+  <p>
+    The &lt;SOSGet&gt; Retreives file(s) from a SOS database<br>
+    The &lt;SOSCheckin&gt; Commits and unlocks file(s) in a SOS database<br>
+    The &lt;SOSCheckout&gt; Retreives and lock file(s) in a SOS database<br>
+    The &lt;SOSLabel&gt; Label a SOS database<br>
+  </p>
+<p align="center">Copyright &copy; 2002 Apache Software Foundation. All rights
+Reserved.</p>
+</body>
+</html>
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sound/AntSoundPlayer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sound/AntSoundPlayer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sound/AntSoundPlayer.java	(revision 16139)
@@ -0,0 +1,236 @@
+/*
+ * Copyright  2000-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.sound;
+
+// ant includes
+import java.io.File;
+import java.io.IOException;
+import javax.sound.sampled.AudioFormat;
+import javax.sound.sampled.AudioInputStream;
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.Clip;
+import javax.sound.sampled.DataLine;
+import javax.sound.sampled.Line;
+import javax.sound.sampled.LineEvent;
+import javax.sound.sampled.LineListener;
+import javax.sound.sampled.LineUnavailableException;
+import javax.sound.sampled.UnsupportedAudioFileException;
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.Project;
+
+
+
+/**
+ * This class is designed to be used by any AntTask that requires audio output.
+ *
+ * It implements the BuildListener interface to listen for BuildEvents and could
+ * be easily extended to provide audio output upon any specific build events occuring.
+ *
+ * I have only tested this with .WAV and .AIFF sound file formats. Both seem to work fine.
+ *
+ */
+
+public class AntSoundPlayer implements LineListener, BuildListener {
+
+    private File fileSuccess = null;
+    private int loopsSuccess = 0;
+    private Long durationSuccess = null;
+
+    private File fileFail = null;
+    private int loopsFail = 0;
+    private Long durationFail = null;
+
+    public AntSoundPlayer() {
+
+    }
+
+    /**
+     * @param file the location of the audio file to be played when the
+     *        build is successful
+     * @param loops the number of times the file should be played when
+     *        the build is successful
+     * @param duration the number of milliseconds the file should be
+     *        played when the build is successful
+     */
+    public void addBuildSuccessfulSound(File file, int loops, Long duration) {
+        this.fileSuccess = file;
+        this.loopsSuccess = loops;
+        this.durationSuccess = duration;
+    }
+
+
+    /**
+     * @param fileFail the location of the audio file to be played
+     *        when the build fails
+     * @param loopsFail the number of times the file should be played
+     *        when the build is fails
+     * @param durationFail the number of milliseconds the file should be
+     *        played when the build fails
+     */
+    public void addBuildFailedSound(File fileFail, int loopsFail, Long durationFail) {
+        this.fileFail = fileFail;
+        this.loopsFail = loopsFail;
+        this.durationFail = durationFail;
+    }
+
+    /**
+     * Plays the file for duration milliseconds or loops.
+     */
+    private void play(Project project, File file, int loops, Long duration) {
+
+        Clip audioClip = null;
+
+        AudioInputStream audioInputStream = null;
+
+
+        try {
+            audioInputStream = AudioSystem.getAudioInputStream(file);
+        } catch (UnsupportedAudioFileException uafe) {
+            project.log("Audio format is not yet supported: "
+                + uafe.getMessage());
+        } catch (IOException ioe) {
+            ioe.printStackTrace();
+        }
+
+        if (audioInputStream != null) {
+            AudioFormat format = audioInputStream.getFormat();
+            DataLine.Info   info = new DataLine.Info(Clip.class, format,
+                                             AudioSystem.NOT_SPECIFIED);
+            try {
+                audioClip = (Clip) AudioSystem.getLine(info);
+                audioClip.addLineListener(this);
+                audioClip.open(audioInputStream);
+            } catch (LineUnavailableException e) {
+                project.log("The sound device is currently unavailable");
+                return;
+            } catch (IOException e) {
+                e.printStackTrace();
+            }
+
+            if (duration != null) {
+                playClip(audioClip, duration.longValue());
+            } else {
+                playClip(audioClip, loops);
+            }
+            audioClip.drain();
+            audioClip.close();
+        } else {
+            project.log("Can't get data from file " + file.getName());
+        }
+    }
+
+    private void playClip(Clip clip, int loops) {
+
+        clip.loop(loops);
+        while (clip.isRunning()) {
+        }
+    }
+
+    private void playClip(Clip clip, long duration) {
+        clip.loop(Clip.LOOP_CONTINUOUSLY);
+        try {
+            Thread.sleep(duration);
+        } catch (InterruptedException e) {
+        }
+    }
+
+    /**
+     * This is implemented to listen for any line events and closes the
+     * clip if required.
+     */
+    public void update(LineEvent event) {
+        if (event.getType().equals(LineEvent.Type.STOP)) {
+            Line line = event.getLine();
+            line.close();
+        } else if (event.getType().equals(LineEvent.Type.CLOSE)) {
+            /*
+             *  There is a bug in JavaSound 0.90 (jdk1.3beta).
+             *  It prevents correct termination of the VM.
+             *  So we have to exit ourselves.
+             */
+            //System.exit(0);
+        }
+    }
+
+
+    /**
+     *  Fired before any targets are started.
+     */
+    public void buildStarted(BuildEvent event) {
+    }
+
+    /**
+     *  Fired after the last target has finished. This event
+     *  will still be thrown if an error occurred during the build.
+     *
+     *  @see BuildEvent#getException()
+     */
+    public void buildFinished(BuildEvent event) {
+        if (event.getException() == null && fileSuccess != null) {
+            // build successfull!
+            play(event.getProject(), fileSuccess, loopsSuccess, durationSuccess);
+        } else if (event.getException() != null && fileFail != null) {
+            play(event.getProject(), fileFail, loopsFail, durationFail);
+        }
+    }
+
+    /**
+     *  Fired when a target is started.
+     *
+     *  @see BuildEvent#getTarget()
+     */
+    public void targetStarted(BuildEvent event) {
+    }
+
+    /**
+     *  Fired when a target has finished. This event will
+     *  still be thrown if an error occurred during the build.
+     *
+     *  @see BuildEvent#getException()
+     */
+    public void targetFinished(BuildEvent event) {
+    }
+
+    /**
+     *  Fired when a task is started.
+     *
+     *  @see BuildEvent#getTask()
+     */
+    public void taskStarted(BuildEvent event) {
+    }
+
+    /**
+     *  Fired when a task has finished. This event will still
+     *  be throw if an error occurred during the build.
+     *
+     *  @see BuildEvent#getException()
+     */
+    public void taskFinished(BuildEvent event) {
+    }
+
+    /**
+     *  Fired whenever a message is logged.
+     *
+     *  @see BuildEvent#getMessage()
+     *  @see BuildEvent#getPriority()
+     */
+    public void messageLogged(BuildEvent event) {
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sound/SoundTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sound/SoundTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/sound/SoundTask.java	(revision 16139)
@@ -0,0 +1,179 @@
+/*
+ * Copyright  2000-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.sound;
+
+import java.io.File;
+import java.util.Random;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Plays a sound file at the end of the build, according to whether the build failed or succeeded.
+ *
+ * There are three attributes to be set:
+ *
+ * <code>source</code>: the location of the audio file to be played
+ * <code>duration</code>: play the sound file continuously until "duration" milliseconds has expired
+ * <code>loops</code>: the number of times the sound file should be played until stopped
+ *
+ * I have only tested this with .WAV and .AIFF sound file formats. Both seem
+ * to work fine.
+ *
+ * plans for the future:
+ * - use the midi api to define sounds (or drum beat etc) in xml and have
+ *   Ant play them back
+ *
+ */
+
+public class SoundTask extends Task {
+
+    private BuildAlert success = null;
+    private BuildAlert fail = null;
+
+    /**
+     * add a sound when the build succeeds
+     */
+    public BuildAlert createSuccess() {
+        success = new BuildAlert();
+        return success;
+    }
+
+    /**
+     * add a sound when the build fails
+     */
+    public BuildAlert createFail() {
+        fail = new BuildAlert();
+        return fail;
+     }
+
+    public SoundTask() {
+    }
+
+    public void init() {
+    }
+
+    public void execute() {
+
+        AntSoundPlayer soundPlayer = new AntSoundPlayer();
+
+        if (success == null) {
+            log("No nested success element found.", Project.MSG_WARN);
+        } else {
+            soundPlayer.addBuildSuccessfulSound(success.getSource(),
+              success.getLoops(), success.getDuration());
+        }
+
+        if (fail == null) {
+            log("No nested failure element found.", Project.MSG_WARN);
+        } else {
+            soundPlayer.addBuildFailedSound(fail.getSource(),
+              fail.getLoops(), fail.getDuration());
+        }
+
+        getProject().addBuildListener(soundPlayer);
+
+    }
+
+    /**
+     * A class to be extended by any BuildAlert's that require the output
+     * of sound.
+     */
+    public class BuildAlert {
+        private File source = null;
+        private int loops = 0;
+        private Long duration = null;
+
+        /**
+         * Sets the duration in milliseconds the file should be played; optional.
+         */
+        public void setDuration(Long duration) {
+            this.duration = duration;
+        }
+
+        /**
+         * Sets the location of the file to get the audio; required.
+         *
+         * @param source the name of a sound-file directory or of the audio file
+         */
+        public void setSource(File source) {
+            this.source = source;
+        }
+
+        /**
+         * Sets the number of times the source file should be played; optional.
+         *
+         * @param loops the number of loops to play the source file
+         */
+        public void setLoops(int loops) {
+            this.loops = loops;
+        }
+
+        /**
+         * Gets the location of the file to get the audio.
+         */
+        public File getSource() {
+            File nofile = null;
+            // Check if source is a directory
+            if (source.exists()) {
+                if (source.isDirectory()) {
+                    // get the list of files in the dir
+                    String[] entries = source.list();
+                    Vector files = new Vector();
+                    for (int i = 0; i < entries.length; i++) {
+                        File f = new File(source, entries[i]);
+                        if (f.isFile()) {
+                            files.addElement(f);
+                        }
+                    }
+                    if (files.size() < 1) {
+                        throw new BuildException("No files found in directory " + source);
+                    }
+                    int numfiles = files.size();
+                    // get a random number between 0 and the number of files
+                    Random rn = new Random();
+                    int x = rn.nextInt(numfiles);
+                    // set the source to the file at that location
+                    this.source = (File) files.elementAt(x);
+                }
+            } else {
+                log(source + ": invalid path.", Project.MSG_WARN);
+                this.source = nofile;
+            }
+            return this.source;
+        }
+
+        /**
+         * Sets the number of times the source file should be played.
+         *
+         * @return the number of loops to play the source file
+         */
+        public int getLoops() {
+            return this.loops;
+        }
+
+        /**
+         * Gets the duration in milliseconds the file should be played.
+         */
+        public Long getDuration() {
+            return this.duration;
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashScreen.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashScreen.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashScreen.java	(revision 16139)
@@ -0,0 +1,133 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.splash;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JProgressBar;
+import javax.swing.JWindow;
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildListener;
+
+class SplashScreen extends JWindow implements ActionListener, BuildListener {
+
+    private JLabel text;
+    private JProgressBar pb;
+    private int total;
+    private final int min = 0;
+    private final int max = 200;
+
+    public SplashScreen(String msg) {
+        init(null);
+        setText(msg);
+    }
+
+    public SplashScreen(ImageIcon img) {
+        init(img);
+    }
+
+    protected void init(ImageIcon img) {
+
+        JPanel pan = (JPanel) getContentPane();
+        JLabel piccy;
+        if (img == null) {
+            piccy = new JLabel();
+        } else {
+            piccy = new JLabel(img);
+        }
+
+        piccy.setBorder(BorderFactory.createLineBorder(Color.black, 1));
+        text = new JLabel("Building....", JLabel.CENTER);
+        text.setFont(new Font("Sans-Serif", Font.BOLD, 12));
+        text.setBorder(BorderFactory.createEtchedBorder());
+
+        pb = new JProgressBar(min, max);
+        pb.setBorder(BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.LOWERED));
+        JPanel pan2 = new JPanel();
+        pan2.setLayout(new BorderLayout());
+
+        pan2.add(text, BorderLayout.NORTH);
+        pan2.add(pb, BorderLayout.SOUTH);
+
+        pan.setLayout(new BorderLayout());
+        pan.add(piccy, BorderLayout.CENTER);
+        pan.add(pan2, BorderLayout.SOUTH);
+
+        pan.setBorder(BorderFactory.createBevelBorder(javax.swing.border.BevelBorder.RAISED));
+
+        pack();
+
+        Dimension size = getSize();
+        Dimension scr = Toolkit.getDefaultToolkit().getScreenSize();
+        int x = (scr.width - size.width) / 2;
+        int y = (scr.height - size.height) / 2;
+        setBounds(x, y, size.width, size.height);
+    }
+
+    public void setText(String txt) {
+        text.setText(txt);
+    }
+
+    public void actionPerformed(ActionEvent a) {
+        if (total < max) {
+            total++;
+        } else {
+            total = min;
+        }
+        pb.setValue(total);
+    }
+
+    public void buildStarted(BuildEvent event) {
+        actionPerformed(null);
+    }
+
+    public void buildFinished(BuildEvent event) {
+        pb.setValue(max);
+        setVisible(false);
+        dispose();
+    }
+    public void targetStarted(BuildEvent event) {
+        actionPerformed(null);
+    }
+
+    public void targetFinished(BuildEvent event) {
+        actionPerformed(null);
+    }
+
+    public void taskStarted(BuildEvent event) {
+        actionPerformed(null);
+    }
+
+    public void taskFinished(BuildEvent event) {
+        actionPerformed(null);
+    }
+
+    public void messageLogged(BuildEvent event) {
+        actionPerformed(null);
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/splash/SplashTask.java	(revision 16139)
@@ -0,0 +1,229 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.splash;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.net.URLConnection;
+import javax.swing.ImageIcon;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Creates a splash screen. The splash screen is displayed
+ * for the duration of the build and includes a handy progress bar as
+ * well. Use in conjunction with the sound task to provide interest
+ * whilst waiting for your builds to complete...
+ * @since Ant1.5
+ */
+public class SplashTask extends Task {
+
+    private String imgurl = null;
+    private String proxy = null;
+    private String user = null;
+    private String password = null;
+    private String port = "80";
+    private int showDuration = 5000;
+    private boolean useProxy = false;
+
+    private static SplashScreen splash = null;
+
+    /**
+     * A URL pointing to an image to display; optional, default antlogo.gif
+     * from the classpath.
+     */
+    public void setImageURL(String imgurl) {
+        this.imgurl = imgurl;
+    }
+
+    /**
+     * flag to enable proxy settings; optional, deprecated : consider
+     * using &lt;setproxy&gt; instead
+     * @deprecated use org.apache.tools.ant.taskdefs.optional.SetProxy
+     */
+    public void setUseproxy(boolean useProxy) {
+        this.useProxy = useProxy;
+    }
+
+    /**
+     * name of proxy; optional.
+     */
+    public void setProxy(String proxy) {
+        this.proxy = proxy;
+    }
+
+    /**
+     * Proxy port; optional, default 80.
+     */
+    public void setPort(String port) {
+        this.port = port;
+    }
+
+    /**
+     * Proxy user; optional, default =none.
+     */
+    public void setUser(String user) {
+        this.user = user;
+    }
+
+    /**
+     * Proxy password; required if <tt>user</tt> is set.
+     */
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    /**
+     * how long to show the splash screen in milliseconds,
+     * optional; default 5000 ms.
+     */
+    public void setShowduration(int duration) {
+        this.showDuration = duration;
+    }
+
+
+    public void execute() throws BuildException {
+        if (splash != null) {
+            splash.setVisible(false);
+            getProject().removeBuildListener(splash);
+            splash.dispose();
+            splash = null;
+        }
+
+        log("Creating new SplashScreen", Project.MSG_VERBOSE);
+        InputStream in = null;
+
+        if (imgurl != null) {
+            try {
+                URLConnection conn = null;
+
+                if (useProxy && (proxy != null && proxy.length() > 0)
+                    && (port != null && port.length() > 0)) {
+
+                    log("Using proxied Connection",  Project.MSG_DEBUG);
+                    System.getProperties().put("http.proxySet", "true");
+                    System.getProperties().put("http.proxyHost", proxy);
+                    System.getProperties().put("http.proxyPort", port);
+
+                    URL url = new URL(imgurl);
+
+                    conn = url.openConnection();
+                    if (user != null && user.length() > 0) {
+                        String encodedcreds =
+                            new sun.misc.BASE64Encoder().encode((new String(user + ":" + password)).getBytes());
+                        conn.setRequestProperty("Proxy-Authorization",
+                                                encodedcreds);
+                    }
+
+                } else {
+                    System.getProperties().put("http.proxySet", "false");
+                    System.getProperties().put("http.proxyHost", "");
+                    System.getProperties().put("http.proxyPort", "");
+                    log("Using Direction HTTP Connection", Project.MSG_DEBUG);
+                    URL url = new URL(imgurl);
+                    conn = url.openConnection();
+                }
+                conn.setDoInput(true);
+                conn.setDoOutput(false);
+
+                in = conn.getInputStream();
+
+                // Catch everything - some of the above return nulls,
+                // throw exceptions or generally misbehave
+                // in the event of a problem etc
+
+            } catch (Throwable ioe) {
+                log("Unable to download image, trying default Ant Logo",
+                    Project.MSG_DEBUG);
+                log("(Exception was \"" + ioe.getMessage() + "\"",
+                    Project.MSG_DEBUG);
+            }
+        }
+
+        if (in == null) {
+            ClassLoader cl = SplashTask.class.getClassLoader();
+            if (cl != null) {
+                in = cl.getResourceAsStream("images/ant_logo_large.gif");
+            } else {
+                in = ClassLoader
+                    .getSystemResourceAsStream("images/ant_logo_large.gif");
+            }
+        }
+
+        boolean success = false;
+        if (in != null) {
+            DataInputStream din = new DataInputStream(in);
+            try {
+                ByteArrayOutputStream bout = new ByteArrayOutputStream();
+                int data;
+                while ((data = din.read()) != -1) {
+                    bout.write((byte) data);
+                }
+
+                log("Got ByteArray, creating splash",  Project.MSG_DEBUG);
+
+                try {
+                    ImageIcon img = new ImageIcon(bout.toByteArray());
+                    splash = new SplashScreen(img);
+                    success = true;
+                } catch (Throwable e) {
+                    logHeadless(e);
+                }
+            } catch (Exception e) {
+                throw new BuildException(e);
+            } finally {
+                try {
+                    din.close();
+                } catch (IOException ioe) {
+                    // swallow if there was an error before so that
+                    // original error will be passed up
+                    if (success) {
+                        throw new BuildException(ioe);
+                    }
+                }
+            }
+        } else {
+            try {
+                splash = new SplashScreen("Image Unavailable.");
+                success = true;
+            } catch (Throwable e) {
+                logHeadless(e);
+            }
+        }
+
+        if (success) {
+            splash.setVisible(true);
+            splash.toFront();
+            getProject().addBuildListener(splash);
+            try {
+                Thread.sleep(showDuration);
+            } catch (InterruptedException e) {
+            }
+        }
+    }
+
+    private void logHeadless(Throwable e) {
+        log("failed to display SplashScreen, caught "
+            + e.getClass().getName() + " with message: " + e.getMessage(),
+            Project.MSG_WARN);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/AbstractSshMessage.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/AbstractSshMessage.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/AbstractSshMessage.java	(revision 16139)
@@ -0,0 +1,167 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ssh;
+
+import com.jcraft.jsch.Channel;
+import com.jcraft.jsch.ChannelExec;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.InputStream;
+import java.text.NumberFormat;
+
+import org.apache.tools.ant.BuildException;
+
+public abstract class AbstractSshMessage {
+
+    private Session session;
+    private boolean verbose;
+    private LogListener listener = new LogListener() {
+        public void log(String message) {
+            // do nothing;
+        }
+    };
+
+    public AbstractSshMessage(Session session) {
+        this(false, session);
+    }
+
+    /**
+     * @since Ant 1.6.2
+     */
+    public AbstractSshMessage(boolean verbose, Session session) {
+        this.verbose = verbose;
+        this.session = session;
+    }
+
+    protected Channel openExecChannel(String command) throws JSchException {
+        ChannelExec channel = (ChannelExec) session.openChannel("exec");
+        channel.setCommand(command);
+
+        return channel;
+    }
+
+    protected void sendAck(OutputStream out) throws IOException {
+        byte[] buf = new byte[1];
+        buf[0] = 0;
+        out.write(buf);
+        out.flush();
+    }
+
+    /**
+     * Reads the response, throws a BuildException if the response
+     * indicates an error.
+     */
+    protected void waitForAck(InputStream in)
+        throws IOException, BuildException {
+        int b = in.read();
+
+        // b may be 0 for success,
+        //          1 for error,
+        //          2 for fatal error,
+
+        if (b == -1) {
+            // didn't receive any response
+            throw new BuildException("No response from server");
+        } else if (b != 0) {
+            StringBuffer sb = new StringBuffer();
+
+            int c = in.read();
+            while (c > 0 && c != '\n') {
+                sb.append((char) c);
+                c = in.read();
+            }
+
+            if (b == 1) {
+                throw new BuildException("server indicated an error: "
+                                         + sb.toString());
+            } else if (b == 2) {
+                throw new BuildException("server indicated a fatal error: "
+                                         + sb.toString());
+            } else {
+                throw new BuildException("unknown response, code " + b
+                                         + " message: " + sb.toString());
+            }
+        }
+    }
+
+    public abstract void execute() throws IOException, JSchException;
+
+    public void setLogListener(LogListener aListener) {
+        listener = aListener;
+    }
+
+    protected void log(String message) {
+        listener.log(message);
+    }
+
+    protected void logStats(long timeStarted,
+                             long timeEnded,
+                             int totalLength) {
+        double duration = (timeEnded - timeStarted) / 1000.0;
+        NumberFormat format = NumberFormat.getNumberInstance();
+        format.setMaximumFractionDigits(2);
+        format.setMinimumFractionDigits(1);
+        listener.log("File transfer time: " + format.format(duration)
+            + " Average Rate: " + format.format(totalLength / duration)
+            + " B/s");
+    }
+
+    /**
+     * @since Ant 1.6.2
+     */
+    protected final boolean getVerbose() {
+        return verbose;
+    }
+
+    /*
+     * Track progress every 10% if 100kb < filesize < 1mb. For larger
+     * files track progress for every percent transmitted.
+     */
+    protected final int trackProgress(int filesize, int totalLength, 
+                                      int percentTransmitted) {
+
+        int percent = (int) Math.round(Math.floor((totalLength /
+                                                   (double)filesize) * 100));
+
+        if (percent > percentTransmitted) {
+            if (filesize < 1048576) {
+                if (percent % 10 == 0) {
+                    if (percent == 100) {
+                        System.out.println(" 100%");
+                    } else {
+                        System.out.print("*");
+                    }
+                }
+            } else {
+                if (percent == 50) {
+                    System.out.println(" 50%");
+                } else if (percent == 100) {
+                    System.out.println(" 100%");
+                } else {
+                    System.out.print(".");
+                }
+            }
+        }
+
+        return percent;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Directory.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Directory.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Directory.java	(revision 16139)
@@ -0,0 +1,123 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ssh;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.StringTokenizer;
+import java.io.File;
+
+public class Directory {
+
+    private File directory;
+    private ArrayList childDirectories;
+    private ArrayList files;
+    private Directory parent;
+
+    public Directory(File directory) {
+        this(directory,  null);
+    }
+
+    public Directory(File directory , Directory parent) {
+        this.parent = parent;
+        this.childDirectories = new ArrayList();
+        this.files = new ArrayList();
+        this.directory = directory;
+    }
+
+    public void addDirectory(Directory directory) {
+        if (!childDirectories.contains(directory)) {
+            childDirectories.add(directory);
+        }
+    }
+
+    public void addFile(File file) {
+        files.add(file);
+    }
+
+    public Iterator directoryIterator() {
+        return childDirectories.iterator();
+    }
+
+    public Iterator filesIterator() {
+        return files.iterator();
+    }
+
+    public Directory getParent() {
+        return parent;
+    }
+
+    public boolean isRoot() {
+        return parent == null;
+    }
+
+    public File getDirectory() {
+        return directory;
+    }
+
+    public Directory getChild(File dir) {
+        for (int i = 0; i < childDirectories.size(); i++) {
+            Directory current = (Directory) childDirectories.get(i);
+            if (current.getDirectory().equals(dir)) {
+                return current;
+            }
+        }
+
+        return null;
+    }
+
+    public boolean equals(Object obj) {
+        if (obj == this) {
+            return true;
+        }
+
+        if (!(obj instanceof Directory)) {
+            return false;
+        }
+
+        Directory d = (Directory) obj;
+
+        return this.directory.equals(d.directory);
+    }
+
+    public int hashCode() {
+        return directory.hashCode();
+    }
+
+    public String[] getPath() {
+        return getPath(directory.getAbsolutePath());
+    }
+
+    public static String[] getPath(String thePath) {
+        StringTokenizer tokenizer = new StringTokenizer(thePath,
+                File.separator);
+        String[] path = new String[ tokenizer.countTokens() ];
+
+        int i = 0;
+        while (tokenizer.hasMoreTokens()) {
+            path[i] = tokenizer.nextToken();
+            i++;
+        }
+
+        return path;
+    }
+
+    public int fileSize() {
+        return files.size();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/LogListener.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/LogListener.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/LogListener.java	(revision 16139)
@@ -0,0 +1,22 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ssh;
+
+public interface LogListener {
+    void log(String message);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHBase.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHBase.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHBase.java	(revision 16139)
@@ -0,0 +1,187 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ssh;
+
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.JSch;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Base class for Ant tasks using jsch.
+ *
+ * @since Ant 1.6
+ */
+public abstract class SSHBase extends Task implements LogListener {
+
+    /** Default listen port for SSH daemon */
+    private static final int SSH_PORT = 22;
+
+    private String host;
+    private String keyfile;
+    private String knownHosts;
+    private int port = SSH_PORT;
+    private boolean failOnError = true;
+    private boolean verbose;
+    private SSHUserInfo userInfo;
+
+    /**
+     * Constructor for SSHBase.
+     */
+    public SSHBase() {
+        super();
+        userInfo = new SSHUserInfo();
+    }
+
+    /**
+     * Remote host, either DNS name or IP.
+     *
+     * @param host  The new host value
+     */
+    public void setHost(String host) {
+        this.host = host;
+    }
+
+    public String getHost() {
+        return host;
+    }
+
+    public void setFailonerror(boolean failure) {
+        failOnError = failure;
+    }
+
+    public boolean getFailonerror() {
+        return failOnError;
+    }
+
+    /**
+     * @since Ant 1.6.2
+     */
+    public void setVerbose(boolean failure) {
+        verbose = failure;
+    }
+
+    /**
+     * @since Ant 1.6.2
+     */
+    public boolean getVerbose() {
+        return verbose;
+    }
+
+    /**
+     * Username known to remote host.
+     *
+     * @param username  The new username value
+     */
+    public void setUsername(String username) {
+        userInfo.setName(username);
+    }
+
+
+    /**
+     * Sets the password for the user.
+     *
+     * @param password  The new password value
+     */
+    public void setPassword(String password) {
+        userInfo.setPassword(password);
+    }
+
+    /**
+     * Sets the keyfile for the user.
+     *
+     * @param keyfile  The new keyfile value
+     */
+    public void setKeyfile(String keyfile) {
+        userInfo.setKeyfile(keyfile);
+    }
+
+    /**
+     * Sets the passphrase for the users key.
+     *
+     * @param passphrase  The new passphrase value
+     */
+    public void setPassphrase(String passphrase) {
+        userInfo.setPassphrase(passphrase);
+    }
+
+    /**
+     * Sets the path to the file that has the identities of
+     * all known hosts.  This is used by SSH protocol to validate
+     * the identity of the host.  The default is
+     * <i>${user.home}/.ssh/known_hosts</i>.
+     *
+     * @param knownHosts a path to the known hosts file.
+     */
+    public void setKnownhosts(String knownHosts) {
+        this.knownHosts = knownHosts;
+    }
+
+    /**
+     * Setting this to true trusts hosts whose identity is unknown.
+     *
+     * @param yesOrNo if true trust the identity of unknown hosts.
+     */
+    public void setTrust(boolean yesOrNo) {
+        userInfo.setTrust(yesOrNo);
+    }
+
+    /**
+     * Changes the port used to connect to the remote host.
+     *
+     * @param port port number of remote host.
+     */
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    public int getPort() {
+        return port;
+    }
+
+    public void init() throws BuildException {
+        super.init();
+        this.knownHosts = System.getProperty("user.home") + "/.ssh/known_hosts";
+        this.port = SSH_PORT;
+    }
+
+    protected Session openSession() throws JSchException {
+        JSch jsch = new JSch();
+        if (null != userInfo.getKeyfile()) {
+            jsch.addIdentity(userInfo.getKeyfile());
+        }
+
+        if (!userInfo.getTrust() && knownHosts != null) {
+            log("Using known hosts: " + knownHosts, Project.MSG_DEBUG);
+            jsch.setKnownHosts(knownHosts);
+        }
+
+        Session session = jsch.getSession(userInfo.getName(), host, port);
+        session.setUserInfo(userInfo);
+        log("Connecting to " + host + ":" + port);
+        session.connect();
+        return session;
+    }
+
+    protected SSHUserInfo getUserInfo() {
+        return userInfo;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHExec.java	(revision 16139)
@@ -0,0 +1,264 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ssh;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.TeeOutputStream;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.StringReader;
+
+import com.jcraft.jsch.ChannelExec;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+/**
+ * Executes a command on a remote machine via ssh.
+ *
+ * @created   February 2, 2003
+ * @since     Ant 1.6
+ */
+public class SSHExec extends SSHBase {
+
+    private static final int BUFFER_SIZE = 1024;
+
+    /** the command to execute via ssh */
+    private String command = null;
+
+    /** units are milliseconds, default is 0=infinite */
+    private long maxwait = 0;
+
+    /** for waiting for the command to finish */
+    private Thread thread = null;
+
+    private String outputProperty = null;   // like <exec>
+    private File outputFile = null;   // like <exec>
+    private boolean append = false;   // like <exec>
+
+    private static final String TIMEOUT_MESSAGE =
+        "Timeout period exceeded, connection dropped.";
+
+    /**
+     * Constructor for SSHExecTask.
+     */
+    public SSHExec() {
+        super();
+    }
+
+    /**
+     * Sets the command to execute on the remote host.
+     *
+     * @param command  The new command value
+     */
+    public void setCommand(String command) {
+        this.command = command;
+    }
+
+    /**
+     * The connection can be dropped after a specified number of
+     * milliseconds. This is sometimes useful when a connection may be
+     * flaky. Default is 0, which means &quot;wait forever&quot;.
+     *
+     * @param timeout  The new timeout value in seconds
+     */
+    public void setTimeout(long timeout) {
+        maxwait = timeout;
+    }
+
+    /**
+     * If used, stores the output of the command to the given file.
+     *
+     * @param output  The file to write to.
+     */
+    public void setOutput(File output) {
+        outputFile = output;
+    }
+
+    /**
+     * Determines if the output is appended to the file given in
+     * <code>setOutput</code>. Default is false, that is, overwrite
+     * the file.
+     *
+     * @param append  True to append to an existing file, false to overwrite.
+     */
+    public void setAppend(boolean append) {
+        this.append = append;
+    }
+
+    /**
+     * If set, the output of the command will be stored in the given property.
+     *
+     * @param property  The name of the property in which the command output
+     *      will be stored.
+     */
+    public void setOutputproperty(String property) {
+        outputProperty = property;
+    }
+
+    /**
+     * Execute the command on the remote host.
+     *
+     * @exception BuildException  Most likely a network error or bad parameter.
+     */
+    public void execute() throws BuildException {
+        if (getHost() == null) {
+            throw new BuildException("Host is required.");
+        }
+        if (getUserInfo().getName() == null) {
+            throw new BuildException("Username is required.");
+        }
+        if (getUserInfo().getKeyfile() == null
+            && getUserInfo().getPassword() == null) {
+            throw new BuildException("Password or Keyfile is required.");
+        }
+        if (command == null) {
+            throw new BuildException("Command is required.");
+        }
+
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        TeeOutputStream tee = new TeeOutputStream(out, System.out);
+
+        Session session = null;
+        try {
+            // execute the command
+            session = openSession();
+            session.setTimeout((int) maxwait);
+            final ChannelExec channel = (ChannelExec) session.openChannel("exec");
+            channel.setCommand(command);
+            channel.setOutputStream(tee);
+            channel.setExtOutputStream(tee);
+            channel.connect();
+
+            // wait for it to finish
+            thread =
+                new Thread() {
+                    public void run() {
+                        while (!channel.isEOF()) {
+                            if (thread == null) {
+                                return;
+                            }
+                            try {
+                                sleep(500);
+                            } catch (Exception e) {
+                                // ignored
+                            }
+                        }
+                    }
+                };
+
+            thread.start();
+            thread.join(maxwait);
+
+            if (thread.isAlive()) {
+                // ran out of time
+                thread = null;
+                if (getFailonerror()) {
+                    throw new BuildException(TIMEOUT_MESSAGE);
+                } else {
+                    log(TIMEOUT_MESSAGE, Project.MSG_ERR);
+                }
+            } else {
+                // completed successfully
+                if (outputProperty != null) {
+                    getProject().setProperty(outputProperty, out.toString());
+                }
+                if (outputFile != null) {
+                    writeToFile(out.toString(), append, outputFile);
+                }
+
+                // this is the wrong test if the remote OS is OpenVMS,
+                // but there doesn't seem to be a way to detect it.
+                int ec = channel.getExitStatus();
+                if (ec != 0) {
+                    String msg = "Remote command failed with exit status " + ec;
+                    if (getFailonerror()) {
+                        throw new BuildException(msg);
+                    } else {
+                        log(msg, Project.MSG_ERR);
+                    }
+                }
+            }
+        } catch (BuildException e) {
+            throw e;
+        } catch (JSchException e) {
+            if (e.getMessage().indexOf("session is down") >= 0) {
+                if (getFailonerror()) {
+                    throw new BuildException(TIMEOUT_MESSAGE, e);
+                } else {
+                    log(TIMEOUT_MESSAGE, Project.MSG_ERR);
+                }
+            } else {
+                if (getFailonerror()) {
+                    throw new BuildException(e);
+                } else {
+                    log("Caught exception: " + e.getMessage(),
+                        Project.MSG_ERR);
+                }
+            }
+        } catch (Exception e) {
+            if (getFailonerror()) {
+                throw new BuildException(e);
+            } else {
+                log("Caught exception: " + e.getMessage(), Project.MSG_ERR);
+            }
+        } finally {
+            if (session != null && session.isConnected()) {
+                session.disconnect();
+            }
+        }
+    }
+
+
+    /**
+     * Writes a string to a file. If destination file exists, it may be
+     * overwritten depending on the "append" value.
+     *
+     * @param from           string to write
+     * @param to             file to write to
+     * @param append         if true, append to existing file, else overwrite
+     * @exception Exception  most likely an IOException
+     */
+    private void writeToFile(String from, boolean append, File to)
+        throws IOException {
+        FileWriter out = null;
+        try {
+            out = new FileWriter(to.getAbsolutePath(), append);
+            StringReader in = new StringReader(from);
+            char[] buffer = new char[8192];
+            int bytesRead;
+            while (true) {
+                bytesRead = in.read(buffer);
+                if (bytesRead == -1) {
+                    break;
+                }
+                out.write(buffer, 0, bytesRead);
+            }
+            out.flush();
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHUserInfo.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHUserInfo.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/SSHUserInfo.java	(revision 16139)
@@ -0,0 +1,171 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ssh;
+
+import com.jcraft.jsch.UserInfo;
+
+/**
+ */
+public class SSHUserInfo implements UserInfo {
+
+    private String name;
+    private String password = null;
+    private String keyfile;
+    private String passphrase = null;
+    private boolean firstTime = true;
+    private boolean trustAllCertificates;
+
+    public SSHUserInfo() {
+        super();
+        this.trustAllCertificates = false;
+    }
+
+    public SSHUserInfo(String password, boolean trustAllCertificates) {
+        super();
+        this.password = password;
+        this.trustAllCertificates = trustAllCertificates;
+    }
+
+    /**
+     * Gets the user name.
+     * @return the user name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Gets the pass phrase of the user.
+     * @param message a message
+     * @return the passphrase
+     */
+    public String getPassphrase(String message) {
+        return passphrase;
+    }
+
+    /**
+     * Gets the user's password.
+     * @return the user's password
+     */
+    public String getPassword() {
+        return password;
+    }
+
+    /**
+     * Prompts a string.
+     * @param str the string
+     * @return whether the string was prompted
+     */
+    public boolean prompt(String str) {
+        return false;
+    }
+
+    /**
+     * Indicates whether a retry was done.
+     * @return whether a retry was done
+     */
+    public boolean retry() {
+        return false;
+    }
+
+    /**
+     * Sets the name.
+     * @param name The name to set
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Sets the passphrase.
+     * @param passphrase The passphrase to set
+     */
+    public void setPassphrase(String passphrase) {
+        this.passphrase = passphrase;
+    }
+
+    /**
+     * Sets the password.
+     * @param password The password to set
+     */
+    public void setPassword(String password) {
+        this.password = password;
+    }
+
+    /**
+     * Sets the trust.
+     * @param trust whether to trust or not.
+     */
+    public void setTrust(boolean trust) {
+        this.trustAllCertificates = trust;
+    }
+
+    /**
+     * @return whether to trust or not.
+     */
+    public boolean getTrust() {
+        return this.trustAllCertificates;
+    }
+
+    /**
+     * Returns the passphrase.
+     * @return String
+     */
+    public String getPassphrase() {
+        return passphrase;
+    }
+
+    /**
+     * Returns the keyfile.
+     * @return String
+     */
+    public String getKeyfile() {
+        return keyfile;
+    }
+
+    /**
+     * Sets the keyfile.
+     * @param keyfile The keyfile to set
+     */
+    public void setKeyfile(String keyfile) {
+        this.keyfile = keyfile;
+    }
+
+    public boolean promptPassphrase(String message) {
+        return true;
+    }
+
+    public boolean promptPassword(String passwordPrompt) {
+        //log(passwordPrompt, Project.MSG_DEBUG);
+        if (firstTime) {
+            firstTime = false;
+            return true;
+        }
+        return firstTime;
+    }
+
+    public boolean promptYesNo(String message) {
+        //log(prompt, Project.MSG_DEBUG);
+        return trustAllCertificates;
+    }
+
+    public void showMessage(String message) {
+        //log(message, Project.MSG_DEBUG);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/Scp.java	(revision 16139)
@@ -0,0 +1,329 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ssh;
+
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+
+import java.io.IOException;
+import java.io.File;
+
+import java.util.List;
+import java.util.LinkedList;
+import java.util.Iterator;
+import java.util.ArrayList;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * Ant task for sending files to remote machine over ssh/scp.
+ *
+ * @since Ant 1.6
+ */
+public class Scp extends SSHBase {
+
+    private String fromUri;
+    private String toUri;
+    private List fileSets = null;
+    private boolean isFromRemote, isToRemote;
+
+    /**
+     * Sets the file to be transferred.  This can either be a remote
+     * file or a local file.  Remote files take the form:<br>
+     * <i>user:password@host:/directory/path/file.example</i><br>
+     * Files to transfer can also include a wildcard to include all
+     * files in a remote directory.  For example:<br>
+     * <i>user:password@host:/directory/path/*</i><br>
+     * @param aFromUri a string representing the file to transfer.
+     */
+    public void setFile(String aFromUri) {
+        this.fromUri = aFromUri;
+        this.isFromRemote = isRemoteUri(this.fromUri);
+    }
+
+    /**
+     * Sets the location where files will be transferred to.
+     * This can either be a remote directory or a local directory.
+     * Remote directories take the form of:<br>
+     * <i>user:password@host:/directory/path/</i><br>
+     * This parameter is required.
+
+     * @param aToUri a string representing the target of the copy.
+     */
+    public void setTodir(String aToUri) {
+        this.toUri = aToUri;
+        this.isToRemote = isRemoteUri(this.toUri);
+    }
+
+    /**
+     * Similiar to {@link #setFile setFile} but explicitly states that
+     * the file is a local file.  This is the only way to specify a
+     * local file with a @ character.
+     * @since Ant 1.6.2
+     */
+    public void setLocalFile(String aFromUri) {
+        this.fromUri = aFromUri;
+        this.isFromRemote = false;
+    }
+
+    /**
+     * Similiar to {@link #setFile setFile} but explicitly states that
+     * the file is a remote file.
+     * @since Ant 1.6.2
+     */
+    public void setRemoteFile(String aFromUri) {
+        this.fromUri = aFromUri;
+        this.isFromRemote = true;
+     }
+
+    /**
+     * Similiar to {@link #setTodir setTodir} but explicitly states
+     * that the directory is a local.  This is the only way to specify
+     * a local directory with a @ character.
+     * @since Ant 1.6.2
+     */
+    public void setLocalTodir(String aToUri) {
+        this.toUri = aToUri;
+        this.isToRemote = false;
+    }
+
+    /**
+     * Similiar to {@link #setTodir setTodir} but explicitly states
+     * that the directory is a remote.
+     * @since Ant 1.6.2
+     */
+    public void setRemoteTodir(String aToUri) {
+        this.toUri = aToUri;
+        this.isToRemote = true;
+    }
+
+    /**
+     * Changes the file name to the given name while receiving it,
+     * only useful if receiving a single file.
+     * @since Ant 1.6.2
+     */
+    public void setLocalTofile(String aToUri) {
+        this.toUri = aToUri;
+        this.isToRemote = false;
+    }
+
+    /**
+     * Changes the file name to the given name while sending it,
+     * only useful if sending a single file.
+     * @since Ant 1.6.2
+     */
+    public void setRemoteTofile(String aToUri) {
+        this.toUri = aToUri;
+        this.isToRemote = true;
+    }
+
+    /**
+     * Adds a FileSet tranfer to remote host.  NOTE: Either
+     * addFileSet() or setFile() are required.  But, not both.
+     *
+     * @param set FileSet to send to remote host.
+     */
+    public void addFileset(FileSet set) {
+        if (fileSets == null) {
+            fileSets = new LinkedList();
+        }
+        fileSets.add(set);
+    }
+
+    public void init() throws BuildException {
+        super.init();
+        this.toUri = null;
+        this.fromUri = null;
+        this.fileSets = null;
+    }
+
+    public void execute() throws BuildException {
+        if (toUri == null) {
+            throw new BuildException("Either 'todir' or 'tofile' attribute "
+                                     + "is required.");
+        }
+
+        if (fromUri == null && fileSets == null) {
+            throw new BuildException("Either the 'file' attribute or one "
+                + "FileSet is required.");
+        }
+
+        try {
+            if (isFromRemote && !isToRemote) {
+                download(fromUri, toUri);
+            } else if (!isFromRemote && isToRemote) {
+                if (fileSets != null) {
+                    upload(fileSets, toUri);
+                } else {
+                    upload(fromUri, toUri);
+                }
+            } else if (isFromRemote && isToRemote) {
+                throw new BuildException("Copying from a remote server to a remote server is not supported.");
+            } else {
+                throw new BuildException("'todir' and 'file' attributes "
+                    + "must have syntax like the following: "
+                    + "user:password@host:/path");
+            }
+        } catch (Exception e) {
+            if (getFailonerror()) {
+                throw new BuildException(e);
+            } else {
+                log("Caught exception: " + e.getMessage(), Project.MSG_ERR);
+            }
+        }
+    }
+
+    private void download(String fromSshUri, String toPath)
+        throws JSchException, IOException {
+        String file = parseUri(fromSshUri);
+
+        Session session = null;
+        try {
+            session = openSession();
+            ScpFromMessage message =
+                new ScpFromMessage(getVerbose(), session, file,
+                                   getProject().resolveFile(toPath),
+                                   fromSshUri.endsWith("*"));
+            log("Receiving file: " + file);
+            message.setLogListener(this);
+            message.execute();
+        } finally {
+            if (session != null) {
+                session.disconnect();
+            }
+        }
+    }
+
+    private void upload(List fileSet, String toSshUri)
+        throws IOException, JSchException {
+        String file = parseUri(toSshUri);
+
+        Session session = null;
+        try {
+            List list = new ArrayList(fileSet.size());
+            for (Iterator i = fileSet.iterator(); i.hasNext();) {
+                FileSet set = (FileSet) i.next();
+                Directory d = createDirectory(set);
+                if (d != null) {
+                    list.add(d);
+                }
+            }
+            if (!list.isEmpty()) {
+                session = openSession();
+                ScpToMessage message = new ScpToMessage(getVerbose(), session,
+                                                        list, file);
+                message.setLogListener(this);
+                message.execute();
+            }
+        } finally {
+            if (session != null) {
+                session.disconnect();
+            }
+        }
+    }
+
+    private void upload(String fromPath, String toSshUri)
+        throws IOException, JSchException {
+        String file = parseUri(toSshUri);
+
+        Session session = null;
+        try {
+            session = openSession();
+            ScpToMessage message =
+                new ScpToMessage(getVerbose(), session, 
+                                 getProject().resolveFile(fromPath), file);
+            message.setLogListener(this);
+            message.execute();
+        } finally {
+            if (session != null) {
+                session.disconnect();
+            }
+        }
+    }
+
+    private String parseUri(String uri) {
+        int indexOfAt = uri.indexOf('@');
+        int indexOfColon = uri.indexOf(':');
+        if (indexOfColon > -1 && indexOfColon < indexOfAt) {
+            // user:password@host:/path notation
+            setUsername(uri.substring(0, indexOfColon));
+            setPassword(uri.substring(indexOfColon + 1, indexOfAt));
+        } else {
+            // no password, will require passphrase
+            setUsername(uri.substring(0, indexOfAt));
+        }
+
+        if (getUserInfo().getPassword() == null
+            && getUserInfo().getPassphrase() == null) {
+            throw new BuildException("neither password nor passphrase for user "
+                                     + getUserInfo().getName() + " has been "
+                                     + "given.  Can't authenticate.");
+        }
+
+        int indexOfPath = uri.indexOf(':', indexOfAt + 1);
+        if (indexOfPath == -1) {
+            throw new BuildException("no remote path in " + uri);
+        }
+
+        setHost(uri.substring(indexOfAt + 1, indexOfPath));
+        String remotePath = uri.substring(indexOfPath + 1);
+        if (remotePath.equals("")) {
+            remotePath = ".";
+        }
+        return remotePath;
+    }
+
+    private boolean isRemoteUri(String uri) {
+        boolean isRemote = true;
+        int indexOfAt = uri.indexOf('@');
+        if (indexOfAt < 0) {
+            isRemote = false;
+        }
+        return isRemote;
+    }
+
+    private Directory createDirectory(FileSet set) {
+        DirectoryScanner scanner = set.getDirectoryScanner(getProject());
+        Directory root = new Directory(scanner.getBasedir());
+        String[] files = scanner.getIncludedFiles();
+        if (files.length != 0) {
+            for (int j = 0; j < files.length; j++) {
+                String[] path = Directory.getPath(files[j]);
+                Directory current = root;
+                File currentParent = scanner.getBasedir();
+                for (int i = 0; i < path.length; i++) {
+                    File file = new File(currentParent, path[i]);
+                    if (file.isDirectory()) {
+                        current.addDirectory(new Directory(file));
+                        current = current.getChild(file);
+                        currentParent = current.getDirectory();
+                    } else if (file.isFile()) {
+                        current.addFile(file);
+                    }
+                }
+            }
+        } else {
+            // skip
+            root = null;
+        }
+        return root;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpFromMessage.java	(revision 16139)
@@ -0,0 +1,206 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ssh;
+
+import java.util.StringTokenizer;
+import java.io.File;
+import java.io.IOException;
+import java.io.EOFException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.FileOutputStream;
+import java.io.ByteArrayOutputStream;
+import com.jcraft.jsch.JSchException;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.Channel;
+
+public class ScpFromMessage extends AbstractSshMessage {
+
+    private final byte LINE_FEED = 0x0a;
+    private final int BUFFER_SIZE = 1024;
+
+    private String remoteFile;
+    private File localFile;
+    private boolean isRecursive = false;
+
+    /**
+     * @since Ant 1.6.2
+     */
+    public ScpFromMessage(boolean verbose,
+                          Session session,
+                          String aRemoteFile,
+                          File aLocalFile,
+                          boolean recursive) {
+        super(verbose, session);
+        this.remoteFile = aRemoteFile;
+        this.localFile = aLocalFile;
+        this.isRecursive = recursive;
+    }
+
+    public ScpFromMessage(Session session,
+                           String aRemoteFile,
+                           File aLocalFile,
+                           boolean recursive) {
+        this(false, session, aRemoteFile, aLocalFile, recursive);
+    }
+
+    public void execute() throws IOException, JSchException {
+        String command = "scp -f ";
+        if (isRecursive) {
+            command += "-r ";
+        }
+        command += remoteFile;
+        Channel channel = openExecChannel(command);
+        try {
+            // get I/O streams for remote scp
+            OutputStream out = channel.getOutputStream();
+            InputStream in = channel.getInputStream();
+
+            channel.connect();
+
+            sendAck(out);
+            startRemoteCpProtocol(in, out, localFile);
+        } finally {
+            if (channel != null) {
+                channel.disconnect();
+            }
+        }
+        log("done\n");
+    }
+
+    private void startRemoteCpProtocol(InputStream in,
+                                       OutputStream out,
+                                       File localFile) throws IOException {
+        File startFile = localFile;
+        while (true) {
+            // C0644 filesize filename - header for a regular file
+            // T time 0 time 0\n - present if perserve time.
+            // D directory - this is the header for a directory.
+            ByteArrayOutputStream stream = new ByteArrayOutputStream();
+            while (true) {
+                int read = in.read();
+                if (read < 0) {
+                    return;
+                }
+                if ((byte) read == LINE_FEED) {
+                    break;
+                }
+                stream.write(read);
+            }
+            String serverResponse = stream.toString("UTF-8");
+            if (serverResponse.charAt(0) == 'C') {
+                parseAndFetchFile(serverResponse, startFile, out, in);
+            } else if (serverResponse.charAt(0) == 'D') {
+                startFile = parseAndCreateDirectory(serverResponse,
+                        startFile);
+                sendAck(out);
+            } else if (serverResponse.charAt(0) == 'E') {
+                startFile = startFile.getParentFile();
+                sendAck(out);
+            } else if (serverResponse.charAt(0) == '\01'
+                    || serverResponse.charAt(0) == '\02') {
+                // this indicates an error.
+                throw new IOException(serverResponse.substring(1));
+            }
+        }
+    }
+
+    private File parseAndCreateDirectory(String serverResponse,
+                                         File localFile) {
+        int start = serverResponse.indexOf(" ");
+        // appears that the next token is not used and it's zero.
+        start = serverResponse.indexOf(" ", start + 1);
+        String directoryName = serverResponse.substring(start + 1);
+        if (localFile.isDirectory()) {
+            File dir = new File(localFile, directoryName);
+            dir.mkdir();
+            log("Creating: " + dir);
+            return dir;
+        }
+        return null;
+    }
+
+    private void parseAndFetchFile(String serverResponse,
+                                   File localFile,
+                                   OutputStream out,
+                                   InputStream in) throws IOException {
+        int start = 0;
+        int end = serverResponse.indexOf(" ", start + 1);
+        String command = serverResponse.substring(start, end);
+        start = end + 1;
+        end = serverResponse.indexOf(" ", start + 1);
+        int filesize = Integer.parseInt(serverResponse.substring(start, end));
+        String filename = serverResponse.substring(end + 1);
+        log("Receiving: " + filename + " : " + filesize);
+        File transferFile = (localFile.isDirectory())
+                ? new File(localFile, filename)
+                : localFile;
+        fetchFile(transferFile, filesize, out, in);
+        waitForAck(in);
+        sendAck(out);
+    }
+
+    private void fetchFile(File localFile,
+                            int filesize,
+                            OutputStream out,
+                            InputStream in) throws IOException {
+        byte[] buf = new byte[BUFFER_SIZE];
+        sendAck(out);
+
+        // read a content of lfile
+        FileOutputStream fos = new FileOutputStream(localFile);
+        int length;
+        int totalLength = 0;
+        long startTime = System.currentTimeMillis();
+
+        // only track progress for files larger than 100kb in verbose mode
+        boolean trackProgress = getVerbose() && filesize > 102400;
+        // since filesize keeps on decreasing we have to store the
+        // initial filesize
+        int initFilesize = filesize;
+        int percentTransmitted = 0;
+
+        try {
+            while (true) {
+                length = in.read(buf, 0,
+                        (buf.length < filesize) ? buf.length : filesize);
+                if (length < 0) {
+                    throw new EOFException("Unexpected end of stream.");
+                }
+                fos.write(buf, 0, length);
+                filesize -= length;
+                totalLength += length;
+                if (filesize == 0) {
+                    break;
+                }
+
+                if (trackProgress) {
+                    percentTransmitted = trackProgress(initFilesize, 
+                                                       totalLength, 
+                                                       percentTransmitted);
+                }
+            }
+        } finally {
+            long endTime = System.currentTimeMillis();
+            logStats(startTime, endTime, totalLength);
+            fos.flush();
+            fos.close();
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessage.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessage.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/ssh/ScpToMessage.java	(revision 16139)
@@ -0,0 +1,222 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ssh;
+
+import com.jcraft.jsch.Channel;
+import com.jcraft.jsch.Session;
+import com.jcraft.jsch.JSchException;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.FileInputStream;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.Iterator;
+
+public class ScpToMessage extends AbstractSshMessage {
+
+    private final int BUFFER_SIZE = 1024;
+
+    private File localFile;
+    private String remotePath;
+    private List directoryList;
+
+    /**
+     * @since Ant 1.6.2
+     */
+    public ScpToMessage(boolean verbose,
+                        Session session,
+                        File aLocalFile,
+                        String aRemotePath) {
+        this(verbose, session, aRemotePath);
+
+        this.localFile = aLocalFile;
+    }
+
+    /**
+     * @since Ant 1.6.2
+     */
+    public ScpToMessage(boolean verbose,
+                        Session session,
+                        List aDirectoryList,
+                        String aRemotePath) {
+        this(verbose, session, aRemotePath);
+
+        this.directoryList = aDirectoryList;
+    }
+
+    /**
+     * @since Ant 1.6.2
+     */
+    private ScpToMessage(boolean verbose,
+                         Session session,
+                         String aRemotePath) {
+        super(verbose, session);
+        this.remotePath = aRemotePath;
+    }
+
+    public ScpToMessage(Session session,
+                        File aLocalFile,
+                        String aRemotePath) {
+        this(false, session, aLocalFile, aRemotePath);
+    }
+
+    public ScpToMessage(Session session,
+                         List aDirectoryList,
+                         String aRemotePath) {
+        this(false, session, aDirectoryList, aRemotePath);
+    }
+
+    public void execute() throws IOException, JSchException {
+        if (directoryList != null) {
+            doMultipleTransfer();
+        }
+        if (localFile != null) {
+            doSingleTransfer();
+        }
+        log("done.\n");
+    }
+
+    private void doSingleTransfer() throws IOException, JSchException {
+        String cmd = "scp -t " + remotePath;
+        Channel channel = openExecChannel(cmd);
+        try {
+
+            OutputStream out = channel.getOutputStream();
+            InputStream in = channel.getInputStream();
+
+            channel.connect();
+
+            waitForAck(in);
+            sendFileToRemote(localFile, in, out);
+        } finally {
+            if (channel != null) {
+                channel.disconnect();
+            }
+        }
+    }
+
+    private void doMultipleTransfer() throws IOException, JSchException {
+        Channel channel = openExecChannel("scp -r -d -t " + remotePath);
+        try {
+            OutputStream out = channel.getOutputStream();
+            InputStream in = channel.getInputStream();
+
+            channel.connect();
+
+            waitForAck(in);
+            for (Iterator i = directoryList.iterator(); i.hasNext();) {
+                Directory current = (Directory) i.next();
+                sendDirectory(current, in, out);
+            }
+        } finally {
+            if (channel != null) {
+                channel.disconnect();
+            }
+        }
+    }
+
+    private void sendDirectory(Directory current,
+                               InputStream in,
+                               OutputStream out) throws IOException {
+        for (Iterator fileIt = current.filesIterator(); fileIt.hasNext();) {
+            sendFileToRemote((File) fileIt.next(), in, out);
+        }
+        for (Iterator dirIt = current.directoryIterator(); dirIt.hasNext();) {
+            Directory dir = (Directory) dirIt.next();
+            sendDirectoryToRemote(dir, in, out);
+        }
+    }
+
+    private void sendDirectoryToRemote(Directory directory,
+                                        InputStream in,
+                                        OutputStream out) throws IOException {
+        String command = "D0755 0 ";
+        command += directory.getDirectory().getName();
+        command += "\n";
+
+        out.write(command.getBytes());
+        out.flush();
+
+        waitForAck(in);
+        sendDirectory(directory, in, out);
+        out.write("E\n".getBytes());
+        waitForAck(in);
+    }
+
+    private void sendFileToRemote(File localFile,
+                                   InputStream in,
+                                   OutputStream out) throws IOException {
+        // send "C0644 filesize filename", where filename should not include '/'
+        int filesize = (int) localFile.length();
+        String command = "C0644 " + filesize + " ";
+        command += localFile.getName();
+        command += "\n";
+
+        out.write(command.getBytes());
+        out.flush();
+
+        waitForAck(in);
+
+        // send a content of lfile
+        FileInputStream fis = new FileInputStream(localFile);
+        byte[] buf = new byte[BUFFER_SIZE];
+        long startTime = System.currentTimeMillis();
+        int totalLength = 0;
+
+        // only track progress for files larger than 100kb in verbose mode
+        boolean trackProgress = getVerbose() && filesize > 102400;
+        // since filesize keeps on decreasing we have to store the
+        // initial filesize
+        int initFilesize = filesize;
+        int percentTransmitted = 0;
+
+        try {
+            log("Sending: " + localFile.getName() + " : " + localFile.length());
+            while (true) {
+                int len = fis.read(buf, 0, buf.length);
+                if (len <= 0) {
+                    break;
+                }
+                out.write(buf, 0, len);
+                totalLength += len;
+
+                if (trackProgress) {
+                    percentTransmitted = trackProgress(initFilesize, 
+                                                       totalLength, 
+                                                       percentTransmitted);
+                }
+            }
+            out.flush();
+            sendAck(out);
+            waitForAck(in);
+        } finally {
+            long endTime = System.currentTimeMillis();
+            logStats(startTime, endTime, totalLength);
+            fis.close();
+        }
+    }
+
+    public File getLocalFile() {
+        return localFile;
+    }
+
+    public String getRemotePath() {
+        return remotePath;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckin.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckin.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckin.java	(revision 16139)
@@ -0,0 +1,362 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.starteam;
+
+import com.starbase.starteam.Folder;
+import com.starbase.starteam.Item;
+import com.starbase.starteam.Status;
+import com.starbase.starteam.View;
+import com.starbase.starteam.ViewConfiguration;
+import java.io.IOException;
+import java.util.Enumeration;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Checks files into a StarTeam project.
+ * Optionally adds files and in the local tree that
+ * are not managed by the repository to its control.
+ * Created: Sat Dec 15 20:26:07 2001
+ *
+ * @version 1.0
+ *
+ * @ant.task name="stcheckin" category="scm" product="Starteam"
+ */
+public class StarTeamCheckin extends TreeBasedTask {
+
+    /**
+     * Constructor for StarTeamCheckin.
+     */
+    public StarTeamCheckin() {
+        // we want this to have a false default, unlike for Checkin.
+        setRecursive(false);
+    }
+
+    private boolean createFolders = true;
+
+    /**
+     * The comment which will be stored with the checkin.
+     */
+    private String comment = null;
+
+    /**
+     * holder for the add Uncontrolled attribute.  If true, all
+     * local files not in StarTeam will be added to the repository.
+     */
+    private boolean addUncontrolled = false;
+
+    /**
+     * Sets the value of createFolders
+     *
+     * @param argCreateFolders Value to assign to this.createFolders
+     */
+    public void setCreateFolders(boolean argCreateFolders) {
+        this.createFolders = argCreateFolders;
+    }
+
+
+    /**
+     * Get the comment attribute for this operation
+     * @return value of comment.
+     */
+    public String getComment() {
+        return this.comment;
+    }
+
+    /**
+     * Optional checkin comment to be saved with the file.
+     * @param comment  Value to assign to comment.
+     */
+    public void setComment(String comment) {
+        this.comment = comment;
+    }
+
+    /**
+     * Get the value of addUncontrolled.
+     * @return value of addUncontrolled.
+     */
+    public boolean isAddUncontrolled() {
+        return this.addUncontrolled;
+    }
+
+    /**
+     * if true, any files or folders NOT in StarTeam will be
+     * added to the repository.  Defaults to "false".
+     * @param addUncontrolled  Value to assign to addUncontrolled.
+     */
+    public void setAddUncontrolled(boolean addUncontrolled) {
+        this.addUncontrolled = addUncontrolled;
+    }
+
+    /**
+     * This attribute tells whether unlocked files on checkin (so that
+     * other users may access them) checkout or to leave the checkout status
+     * alone (default).
+     * @see #setUnlocked(boolean)
+     */
+    private int lockStatus = Item.LockType.UNCHANGED;
+
+    /**
+     * Set to do an unlocked checkout; optional, default is false;
+     * If true, file will be unlocked so that other users may
+     * change it.  If false, lock status will not change.
+     * @param v  true means do an unlocked checkout
+     *           false means leave status alone.
+     */
+    public void setUnlocked(boolean v) {
+        if (v) {
+            this.lockStatus = Item.LockType.UNLOCKED;
+        } else {
+            this.lockStatus = Item.LockType.UNCHANGED;
+        }
+    }
+
+    /**
+     * Override of base-class abstract function creates an
+     * appropriately configured view.  For checkins this is
+     * always the current or "tip" view.
+     *
+     * @param raw the unconfigured <code>View</code>
+     * @return the snapshot <code>View</code> appropriately configured.
+     */
+    protected View createSnapshotView(View raw) {
+        return new View(raw, ViewConfiguration.createTip());
+    }
+
+    /**
+     * Implements base-class abstract function to define tests for
+     * any preconditons required by the task.
+     *
+     * @exception BuildException thrown if both rootLocalFolder
+     * and viewRootLocalFolder are defined
+     */
+    protected void testPreconditions() throws BuildException {
+    }
+    /**
+     * Implements base-class abstract function to emit to the log an
+     * entry describing the parameters that will be used by this operation.
+     *
+     * @param starteamrootFolder
+     *               root folder in StarTeam for the operation
+     * @param targetrootFolder
+     *               root local folder for the operation
+     * (whether specified by the user or not).
+     */
+    protected void logOperationDescription(
+        Folder starteamrootFolder, java.io.File targetrootFolder) {
+        log((this.isRecursive() ? "Recursive" : "Non-recursive")
+            + " Checkin from"
+            + (null == getRootLocalFolder() ? " (default): " : ": ")
+            + targetrootFolder.getAbsolutePath());
+
+        log("Checking in to: " + starteamrootFolder.getFolderHierarchy());
+        logIncludes();
+        logExcludes();
+
+        if (this.lockStatus == Item.LockType.UNLOCKED) {
+            log("  Items will be checked in unlocked.");
+        } else {
+            log("  Items will be checked in with no change in lock status.");
+        }
+
+        if (this.isForced()) {
+            log("  Items will be checked in in accordance with repository "
+                + "status and regardless of lock status.");
+        } else {
+            log("  Items will be checked in regardless of repository status "
+                + "only if locked.");
+        }
+
+
+    }
+
+    /**
+     * Implements base-class abstract function to perform the checkout
+     * operation on the files in each folder of the tree.
+     *
+     * @param starteamFolder the StarTeam folder to which files
+     *                       will be checked in
+     * @param targetFolder local folder from which files will be checked in
+     * @exception BuildException if any error occurs
+     */
+    protected void visit(Folder starteamFolder, java.io.File targetFolder)
+            throws BuildException {
+        try {
+            if (null != getRootLocalFolder()) {
+                starteamFolder.setAlternatePathFragment(
+                    targetFolder.getAbsolutePath());
+            }
+
+            Folder[] foldersList = starteamFolder.getSubFolders();
+            Item[] stFiles = starteamFolder.getItems(getTypeNames().FILE);
+
+            // note, it's important to scan the items BEFORE we make the
+            // UnmatchedFileMap because that creates a bunch of NEW
+            // folders and files (unattached to repository) and we
+            // don't want to include those in our traversal.
+
+            UnmatchedFileMap ufm =
+                new CheckinMap().init(
+                    targetFolder.getAbsoluteFile(), starteamFolder);
+
+
+            for (int i = 0, size = foldersList.length; i < size; i++) {
+                Folder stFolder = foldersList[i];
+                java.io.File subfolder =
+                    new java.io.File(targetFolder, stFolder.getName());
+
+                ufm.removeControlledItem(subfolder);
+
+                if (isRecursive()) {
+                    visit(stFolder, subfolder);
+                }
+            }
+
+
+            for (int i = 0, size = stFiles.length; i < size; i++) {
+                com.starbase.starteam.File stFile =
+                    (com.starbase.starteam.File) stFiles[i];
+                processFile(stFile);
+
+                ufm.removeControlledItem(
+                    new java.io.File(targetFolder, stFile.getName()));
+            }
+
+            if (this.addUncontrolled) {
+                ufm.processUncontrolledItems();
+            }
+
+        } catch (IOException e) {
+            throw new BuildException(e);
+        }
+
+    }
+
+    /**
+     * provides a string showing from and to full paths for logging
+     *
+     * @param remotefile the Star Team file being processed.
+     *
+     * @return a string showing from and to full paths
+     */
+    private String describeCheckin(com.starbase.starteam.File remotefile) {
+        StringBuffer sb = new StringBuffer();
+        sb.append(remotefile.getFullName())
+          .append(" --> ")
+          .append(getFullRepositoryPath(remotefile));
+        return sb.toString();
+    }
+
+    /**
+     * Processes (checks-out) <code>stFiles</code>files from StarTeam folder.
+     *
+     * @param eachFile repository file to process
+     * @param targetFolder a java.io.File (Folder) to work
+     * @throws IOException when StarTeam API fails to work with files
+     */
+    private void processFile(com.starbase.starteam.File eachFile)
+        throws IOException {
+        String filename = eachFile.getName();
+
+        // If the file doesn't pass the include/exclude tests, skip it.
+        if (!shouldProcess(filename)) {
+            log("Excluding " + getFullRepositoryPath(eachFile));
+                return;
+        }
+
+        boolean checkin = true;
+        int fileStatus = (eachFile.getStatus());
+
+        // We try to update the status once to give StarTeam
+        // another chance.
+
+        if (fileStatus == Status.MERGE || fileStatus == Status.UNKNOWN) {
+            eachFile.updateStatus(true, true);
+            fileStatus = (eachFile.getStatus());
+        }
+
+        if (fileStatus == Status.MODIFIED) {
+            log("Checking in: " + describeCheckin(eachFile));
+        } else if (fileStatus == Status.MISSING) {
+            log("Local file missing: " + describeCheckin(eachFile));
+            checkin = false;
+        } else {
+            if (isForced()) {
+                log("Forced checkin of " + describeCheckin(eachFile)
+                    + " over status " + Status.name(fileStatus));
+            } else {
+                log("Skipping: " + getFullRepositoryPath(eachFile)
+                    + " - status: " + Status.name(fileStatus));
+                checkin = false;
+            }
+        }
+        if (checkin) {
+            eachFile.checkin(this.comment, this.lockStatus,
+                             this.isForced(), true, true);
+        }
+    }
+
+    /**
+     * handles the deletion of uncontrolled items
+     */
+    private class CheckinMap extends UnmatchedFileMap {
+        protected boolean isActive() {
+            return StarTeamCheckin.this.addUncontrolled;
+        }
+
+
+        /**
+         * This override adds all its members to the repository.  It is assumed
+         * that this method will not be called until all the items in the
+         * corresponding folder have been processed, and that the internal map
+         * will contain only uncontrolled items.
+         */
+        void processUncontrolledItems() throws BuildException {
+            if (this.isActive()) {
+                Enumeration e = this.keys();
+                while (e.hasMoreElements()) {
+                    java.io.File local = (java.io.File) e.nextElement();
+                    Item remoteItem = (Item) this.get(local);
+                    remoteItem.update();
+
+                    // once we find a folder that isn't in the repository,
+                    // we know we can add it.
+                    if (local.isDirectory()) {
+                        Folder folder = (Folder) remoteItem;
+                        log("Added uncontrolled folder "
+                            + folder.getFolderHierarchy()
+                            + " from " + local.getAbsoluteFile());
+                        if (isRecursive()) {
+                            UnmatchedFileMap submap =
+                                new CheckinMap().init(local, folder);
+                            submap.processUncontrolledItems();
+                        }
+                    } else {
+                        com.starbase.starteam.File remoteFile =
+                            (com.starbase.starteam.File) remoteItem;
+                        log("Added uncontrolled file "
+                            + TreeBasedTask.getFullRepositoryPath(remoteFile)
+                            + " from " + local.getAbsoluteFile());
+
+                    }
+                }
+            }
+        }
+    }
+
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckout.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckout.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamCheckout.java	(revision 16139)
@@ -0,0 +1,640 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.starteam;
+
+import com.starbase.starteam.Folder;
+import com.starbase.starteam.Item;
+import com.starbase.starteam.Status;
+import com.starbase.starteam.View;
+import com.starbase.starteam.ViewConfiguration;
+import java.io.IOException;
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+
+/**
+ * Checks out files from a StarTeam project.
+ * It also creates all working directories on the
+ * local directory if appropriate. Ant Usage:
+ * <pre>
+ * &lt;taskdef name="starteamcheckout"
+ * classname="org.apache.tools.ant.taskdefs.StarTeamCheckout"/&gt;
+ * &lt;starteamcheckout username="BuildMaster" password="ant" starteamFolder="Source"
+ * starteamurl="servername:portnum/project/view"
+ * createworkingdirectories="true"/&gt;
+ * </pre>
+ *
+ * @version 1.1
+ * @see <A HREF="http://www.starbase.com/">StarBase Web Site</A>
+ *
+ * @ant.task name="stcheckout" category="scm"
+ */
+public class StarTeamCheckout extends TreeBasedTask {
+
+    /**
+     * holder for the createDirs attribute
+     */
+    private boolean createDirs = true;
+
+    /**
+     * holder for the deleteUncontrolled attribute.  If true,
+     * all local files not in StarTeam will be deleted.
+     */
+    private boolean deleteUncontrolled = true;
+
+    /**
+     * holder for the deleteUncontrolled attribute.  If true,
+     * (default) local non-binary files will be checked out using the local
+     * platform's EOL convention.  If false, checkouts will preserve the
+     * server's EOL convention.
+     */
+    private boolean convertEOL = true;
+
+
+    /**
+     * flag (defaults to true) to create all directories
+     * that are in the Starteam repository even if they are empty.
+     *
+     * @param value  the value to set the attribute to.
+     */
+    public void setCreateWorkingDirs(boolean value) {
+        this.createDirs = value;
+    }
+
+    /**
+     * Whether or not all local files <i>not<i> in StarTeam should be deleted.
+     * Optional, defaults to <code>true</code>.
+     * @param value  the value to set the attribute to.
+     */
+    public void setDeleteUncontrolled(boolean value) {
+        this.deleteUncontrolled = value;
+    }
+
+    /**
+     * Set whether or not files should be checked out using the
+     * local machine's EOL convention.
+     * Optional, defaults to <code>true</code>.
+     * @param value  the value to set the attribute to.
+     */
+    public void setConvertEOL(boolean value) {
+        this.convertEOL = value;
+    }
+
+    /**
+     * Sets the label StarTeam is to use for checkout; defaults to the most recent file.
+     * The label must exist in starteam or an exception will be thrown.
+     * @param label the label to be used
+     */
+    public void setLabel(String label) {
+        _setLabel(label);
+    }
+
+    /**
+     * This attribute tells whether to do a locked checkout, an unlocked
+     * checkout or to leave the checkout status alone (default).  A locked
+     * checkout locks all other users out from making changes.  An unlocked
+     * checkout reverts all local files to their previous repository status
+     * and removes the lock.
+     * @see #setLocked(boolean)
+     * @see #setUnlocked(boolean)
+     */
+    private int lockStatus = Item.LockType.UNCHANGED;
+
+    /**
+     * Set to do a locked checkout; optional default is false.
+     * @param v  True to do a locked checkout, false to checkout without
+     *           changing status/.
+     * @exception BuildException if both locked and unlocked are set true
+     */
+    public void setLocked(boolean v) throws BuildException {
+        setLockStatus(v, Item.LockType.EXCLUSIVE);
+    }
+
+
+    /**
+     * Set to do an unlocked checkout. Default is false;
+     * @param v  True to do an unlocked checkout, false to checkout without
+     *           changing status.
+     * @exception BuildException if both locked and unlocked are set true
+     */
+    public void setUnlocked(boolean v) throws BuildException {
+        setLockStatus(v, Item.LockType.UNLOCKED);
+    }
+
+    private void setLockStatus(boolean v, int newStatus)
+            throws BuildException {
+        if (v) {
+            if (this.lockStatus == Item.LockType.UNCHANGED) {
+                this.lockStatus = newStatus;
+            } else if (this.lockStatus != newStatus) {
+                throw new BuildException(
+                        "Error: cannot set locked and unlocked both true.");
+            }
+        }
+    }
+
+    /**
+     * should checked out files get the timestamp from the repository
+     * or the time they are checked out.  True means use the repository
+     * timestamp.
+     */
+    private boolean useRepositoryTimeStamp = false;
+
+    /**
+     * sets the useRepositoryTimestmp member.
+     *
+     * @param useRepositoryTimeStamp
+     *               true means checked out files will get the repository timestamp.
+     *               false means the checked out files will be timestamped at the time
+     *               of checkout.
+     */
+    public void setUseRepositoryTimeStamp(boolean useRepositoryTimeStamp) {
+        this.useRepositoryTimeStamp = useRepositoryTimeStamp;
+    }
+
+    /**
+     * returns the value of the useRepositoryTimestamp member
+     *
+     * @return the value of the useRepositoryTimestamp member
+     */
+    public boolean getUseRepositoryTimeStamp() {
+        return this.useRepositoryTimeStamp;
+    }
+
+    /**
+     * List files, dates, and statuses as of this date; optional.
+     * If not specified, the most recent version of each file will be listed.
+     *
+     * @param asOfDateParam the date as of which the listing to be made
+     * @since Ant 1.6
+     */
+    public void setAsOfDate(String asOfDateParam) {
+        _setAsOfDate(asOfDateParam);
+    }
+
+    /**
+     * Date Format with which asOfDate parameter to be parsed; optional.
+     * Must be a SimpleDateFormat compatible string.
+     * If not specified, and asOfDateParam is specified, parse will use ISO8601
+     * datetime and date formats.
+     *
+     * @param asOfDateFormat the SimpleDateFormat-compatible format string
+     * @since Ant 1.6
+     */
+    public void setAsOfDateFormat(String asOfDateFormat) {
+        _setAsOfDateFormat(asOfDateFormat);
+    }
+
+    /**
+     * Override of base-class abstract function creates an
+     * appropriately configured view for checkouts - either
+     * the current view or a view from this.label or the raw
+     * view itself in the case of a revision label.
+     *
+     * @param raw    the unconfigured <code>View</code>
+     *
+     * @return the snapshot <code>View</code> appropriately configured.
+     * @exception BuildException on error
+     */
+    protected View createSnapshotView(View raw) throws BuildException {
+
+        int labelID = getLabelID(raw);
+
+        // if a label has been supplied and it is a view label, use it
+        // to configure the view
+        if (this.isUsingViewLabel()) {
+            return new View(raw, ViewConfiguration.createFromLabel(labelID));
+        }
+        // if a label has been supplied and it is a revision label, use the raw
+        // the view as the snapshot
+        else if (this.isUsingRevisionLabel()) {
+            return raw;
+        }
+        // if a date has been supplied use a view configured to the date.
+        View view = getViewConfiguredByDate(raw);
+        if (view != null) {
+            return view;
+        }
+        // otherwise, use this view configured as the tip.
+        else {
+            return new View(raw, ViewConfiguration.createTip());
+        }
+    }
+
+
+    /**
+     * Implements base-class abstract function to define tests for
+     * any preconditons required by the task.
+     *
+     * @exception BuildException thrown if both rootLocalFolder
+     * and viewRootLocalFolder are defined
+     */
+    protected void testPreconditions() throws BuildException {
+        if (this.isUsingRevisionLabel() && this.createDirs) {
+            log("Ignoring createworkingdirs while using a revision label."
+                + "  Folders will be created only as needed.",
+                Project.MSG_WARN);
+            this.createDirs = false;
+        }
+        if (lockStatus != Item.LockType.UNCHANGED) {
+            boolean lockStatusBad = false;
+            if (null != getLabel()) {
+                log("Neither locked nor unlocked may be true"
+                    + " when checking out a labeled version.",
+                    Project.MSG_ERR);
+                lockStatusBad = true;
+            } else if (null != getAsOfDate()) {
+                log("Neither locked nor unlocked may be true"
+                    + " when checking out by date.",
+                    Project.MSG_ERR);
+                lockStatusBad = true;
+            }
+            if (lockStatusBad) {
+                throw new BuildException(
+                    "Lock status may not be changed"
+                    + " when checking out a non-current version.");
+            }
+        }
+        if (null != getLabel() && null != getAsOfDate()) {
+            throw new BuildException(
+                "Both label and asOfDate specified.  "
+                + "Unable to process request.");
+        }
+
+    }
+
+    /**
+     * extenders should emit to the log an entry describing the parameters
+     * that will be used by this operation.
+     *
+     * @param starteamrootFolder
+     *               root folder in StarTeam for the operation
+     * @param targetrootFolder
+     *               root local folder for the operation (whether specified
+     * by the user or not.
+     */
+
+    protected void logOperationDescription(
+        Folder starteamrootFolder, java.io.File targetrootFolder) {
+        log((this.isRecursive() ? "Recursive" : "Non-recursive")
+            + " Checkout from: " + starteamrootFolder.getFolderHierarchy());
+
+        log("  Checking out to"
+            + (null == getRootLocalFolder() ? "(default): " : ": ")
+            + targetrootFolder.getAbsolutePath());
+
+
+        logLabel();
+        logAsOfDate();
+        logIncludes();
+        logExcludes();
+
+        if (this.lockStatus == Item.LockType.EXCLUSIVE) {
+            log("  Items will be checked out with Exclusive locks.");
+        } else if (this.lockStatus == Item.LockType.UNLOCKED) {
+            log("  Items will be checked out unlocked "
+                 + "(even if presently locked).");
+        } else {
+            log("  Items will be checked out with no change in lock status.");
+        }
+        log("  Items will be checked out with "
+            + (this.useRepositoryTimeStamp ? "repository timestamps."
+                                        : "the current timestamp."));
+        log("  Items will be checked out "
+            + (this.isForced() ? "regardless of" : "in accordance with")
+            + " repository status.");
+        if (this.deleteUncontrolled) {
+            log("  Local items not found in the repository will be deleted.");
+        }
+        log("  Items will be checked out "
+            + (this.convertEOL ? "using the local machine's EOL convention"
+             : "without changing the EOL convention used on the server"));
+        log("  Directories will be created"
+            + (this.createDirs ? " wherever they exist in the repository, even if empty."
+             : " only where needed to check out files."));
+
+    }
+
+
+    /**
+     * Implements base-class abstract function to perform the checkout
+     * operation on the files in each folder of the tree.
+     *
+     * @param starteamFolder the StarTeam folder from which files to be
+     *                       checked out
+     * @param targetFolder the local mapping of rootStarteamFolder
+     * @exception BuildException if any error occurs
+     */
+    protected void visit(Folder starteamFolder, java.io.File targetFolder)
+            throws BuildException {
+        try {
+
+
+            if (null != getRootLocalFolder()) {
+                starteamFolder.setAlternatePathFragment(
+                    targetFolder.getAbsolutePath());
+            }
+
+            if (!targetFolder.exists()) {
+                if (!this.isUsingRevisionLabel()) {
+                    if (this.createDirs) {
+                        if (targetFolder.mkdirs()) {
+                            log("Creating folder: " + targetFolder);
+                        } else {
+                            throw new BuildException(
+                                "Failed to create local folder " + targetFolder);
+                        }
+                    }
+                }
+            }
+
+
+            Folder[] foldersList = starteamFolder.getSubFolders();
+            Item[] filesList = starteamFolder.getItems(getTypeNames().FILE);
+
+            if (this.isUsingRevisionLabel()) {
+
+                // prune away any files not belonging to the revision label
+                // this is one ugly API from Starteam SDK
+
+                Hashtable labelItems = new Hashtable(filesList.length);
+                int s = filesList.length;
+                int[] ids = new int[s];
+                for (int i = 0; i < s; i++) {
+                    ids[i] = filesList[i].getItemID();
+                    labelItems.put(new Integer(ids[i]), new Integer(i));
+                }
+                int[] foundIds = getLabelInUse().getLabeledItemIDs(ids);
+                s = foundIds.length;
+                Item[] labeledFiles = new Item[s];
+                for (int i = 0; i < s; i++) {
+                    Integer id = new Integer(foundIds[i]);
+                    labeledFiles[i] =
+                        filesList[((Integer) labelItems.get(id)).intValue()];
+                }
+                filesList = labeledFiles;
+            }
+
+
+            // note, it's important to scan the items BEFORE we make the
+            // Unmatched file map because that creates a bunch of NEW
+            // folders and files (unattached to repository) and we
+            // don't want to include those in our traversal.
+
+            UnmatchedFileMap ufm =
+                new CheckoutMap().
+                    init(targetFolder.getAbsoluteFile(), starteamFolder);
+
+
+
+            for (int i = 0; i < foldersList.length; i++) {
+                Folder stFolder = foldersList[i];
+
+                java.io.File subfolder =
+                     new java.io.File(targetFolder, stFolder.getName());
+
+                 ufm.removeControlledItem(subfolder);
+
+                 if (isRecursive()) {
+                         visit(stFolder, subfolder);
+                     }
+                 }
+
+            for (int i = 0; i < filesList.length; i++) {
+                com.starbase.starteam.File stFile =
+                    (com.starbase.starteam.File) filesList[i];
+                processFile(stFile, targetFolder);
+
+                ufm.removeControlledItem(
+                    new java.io.File(targetFolder, stFile.getName()));
+            }
+            if (this.deleteUncontrolled) {
+                ufm.processUncontrolledItems();
+            }
+        } catch (IOException e) {
+            throw new BuildException(e);
+        }
+    }
+
+
+    /**
+     * provides a string showing from and to full paths for logging
+     *
+     * @param remotefile the Star Team file being processed.
+     *
+     * @return a string showing from and to full paths
+     */
+    private String describeCheckout(com.starbase.starteam.File remotefile,
+                                    java.io.File localFile) {
+        StringBuffer sb = new StringBuffer();
+        sb.append(getFullRepositoryPath(remotefile))
+          .append(" --> ");
+        if (null == localFile) {
+            sb.append(remotefile.getFullName());
+        } else {
+            sb.append(localFile);
+        }
+        return sb.toString();
+    }
+    private String describeCheckout(com.starbase.starteam.File remotefile) {
+        return describeCheckout(remotefile, null);
+    }
+    /**
+     * Processes (checks out) <code>stFiles</code>files from StarTeam folder.
+     *
+     * @param eachFile repository file to process
+     * @param targetFolder a java.io.File (Folder) to work
+     * @throws IOException when StarTeam API fails to work with files
+     */
+    private void processFile(com.starbase.starteam.File eachFile,
+                             File targetFolder)
+    throws IOException {
+        String filename = eachFile.getName();
+
+        java.io.File localFile = new java.io.File(targetFolder, filename);
+
+        // If the file doesn't pass the include/exclude tests, skip it.
+        if (!shouldProcess(filename)) {
+            log("Excluding " + getFullRepositoryPath(eachFile),
+                Project.MSG_INFO);
+                return;
+        }
+
+        if (this.isUsingRevisionLabel()) {
+            if (!targetFolder.exists()) {
+                if (targetFolder.mkdirs()) {
+                    log("Creating folder: " + targetFolder);
+                } else {
+                    throw new BuildException(
+                        "Failed to create local folder " + targetFolder);
+                }
+            }
+            boolean success = eachFile.checkoutByLabelID(
+                localFile,
+                getIDofLabelInUse(),
+                this.lockStatus,
+                !this.useRepositoryTimeStamp,
+                true,
+                false);
+            if (success) {
+                log("Checked out " + describeCheckout(eachFile, localFile));
+            }
+        } else {
+            boolean checkout = true;
+
+            // Just a note: StarTeam has a status for NEW which implies
+            // that there is an item  on your local machine that is not
+            // in the repository.  These are the items that show up as
+            // NOT IN VIEW in the Starteam GUI.
+            // One would think that we would want to perhaps checkin the
+            // NEW items (not in all cases! - Steve Cohen 15 Dec 2001)
+            // Unfortunately, the sdk doesn't really work, and we can't
+            // actually see  anything with a status of NEW. That is why
+            // we can just check out  everything here without worrying
+            // about losing anything.
+
+            int fileStatus = (eachFile.getStatus());
+
+            // We try to update the status once to give StarTeam
+            // another chance.
+
+            if (fileStatus == Status.MERGE
+                || fileStatus == Status.UNKNOWN) {
+                eachFile.updateStatus(true, true);
+                fileStatus = (eachFile.getStatus());
+            }
+
+            log(eachFile.toString() + " has status of "
+                + Status.name(fileStatus), Project.MSG_DEBUG);
+
+
+            switch (fileStatus) {
+            case Status.OUTOFDATE:
+            case Status.MISSING:
+                log("Checking out: " + describeCheckout(eachFile));
+                break;
+            default:
+                if (isForced()) {
+                    log("Forced checkout of "
+                        + describeCheckout(eachFile)
+                        + " over status " + Status.name(fileStatus));
+                } else {
+                    log("Skipping: " + getFullRepositoryPath(eachFile)
+                        + " - status: " + Status.name(fileStatus));
+                    checkout = false;
+                }
+            }
+
+            if (checkout) {
+                if (!targetFolder.exists()) {
+                    if (targetFolder.mkdirs()) {
+                        log("Creating folder: " + targetFolder);
+                    } else {
+                        throw new BuildException(
+                            "Failed to create local folder " + targetFolder);
+                    }
+                }
+                eachFile.checkout(this.lockStatus,
+                                 !this.useRepositoryTimeStamp, this.convertEOL, true);
+            }
+        }
+    }
+    /**
+     * handles the deletion of uncontrolled items
+     */
+    private class CheckoutMap extends UnmatchedFileMap {
+        protected boolean isActive() {
+            return StarTeamCheckout.this.deleteUncontrolled;
+        }
+
+        /**
+         * override of the base class init.  It can be much simpler, since
+         * the action to be taken is simply to delete the local files.  No
+         * further interaction with the repository is necessary.
+         *
+         * @param localFolder
+         *        the local folder from which the mappings will be made.
+         * @param remoteFolder
+         *        not used in this implementation
+         */
+        UnmatchedFileMap init(java.io.File localFolder, Folder remoteFolder) {
+            if (!localFolder.exists()) {
+                return this;
+            }
+
+            String[] localFiles = localFolder.list();
+
+            for (int i = 0; i < localFiles.length; i++) {
+                java.io.File localFile =
+                    new java.io.File(localFolder, localFiles[i]).getAbsoluteFile();
+
+                log("adding " + localFile + " to UnmatchedFileMap",
+                    Project.MSG_DEBUG);
+
+                if (localFile.isDirectory()) {
+                    this.put(localFile, "");
+                } else {
+                    this.put(localFile, "");
+                }
+            }
+            return this;
+        }
+
+
+
+        /**
+         * deletes uncontrolled items from the local tree.  It is assumed
+         * that this method will not be called until all the items in the
+         * corresponding folder have been processed, and that the internal map
+         * will contain only uncontrolled items.
+         */
+        void processUncontrolledItems() throws BuildException {
+            if (this.isActive()) {
+                Enumeration e = this.keys();
+                while (e.hasMoreElements()) {
+                    java.io.File local = (java.io.File) e.nextElement();
+                    delete(local);
+                }
+            }
+        }
+
+        /**
+         * deletes all files and if the file is a folder recursively deletes
+         * everything in it.
+         *
+         * @param local  The local file or folder to be deleted.
+         */
+        void delete(java.io.File local) {
+            // once we find a folder that isn't in the repository,
+            // anything below it can be deleted.
+            if (local.isDirectory() && isRecursive()) {
+                String[] contents = local.list();
+                for (int i = 0; i < contents.length; i++) {
+                    java.io.File file = new java.io.File(local, contents[i]);
+                    delete(file);
+                }
+            }
+            local.delete();
+            log("Deleted uncontrolled item " + local.getAbsolutePath());
+        }
+    }
+
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamLabel.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamLabel.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamLabel.java	(revision 16139)
@@ -0,0 +1,190 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.starteam;
+
+import com.starbase.starteam.Label;
+import com.starbase.starteam.View;
+import com.starbase.util.OLEDate;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Creates a view label in StarTeam at the specified view.
+ *
+ * Ant Usage:
+ * <pre>
+ * &lt;taskdef name="stlabel"
+ *          classname="org.apache.tools.ant.taskdefs.optional.starteam.StarTeamLabel"/&lt;
+ *     &lt;stlabel
+ * label="1.0" lastbuild="20011514100000" description="Successful Build"
+ * username="BuildMaster" password="ant"
+ * starteamurl="server:port/project/view"/&gt;
+ * </pre>
+ *
+ * @see <A HREF="http://www.starbase.com/">StarBase Web Site</A>
+ *
+ * @ant.task name="stlabel" category="scm"
+ */
+public class StarTeamLabel extends StarTeamTask {
+
+    /**
+     * The name of the label to be set in Starteam.
+     */
+    private String labelName;
+
+    /**
+     * The label description to be set in Starteam.
+     */
+    private String description;
+
+    /**
+     * If true, this will be a build label.  If false, it will be a non-build
+     * label.  The default is false.  Has no effect if revision label is
+     * true.
+     */
+    private boolean buildlabel = false;
+
+    /**
+     * If true, this will be a revision label.  If false, it will be a build
+     * label.  The default is false.
+     */
+    private boolean revisionlabel = false;
+
+    /**
+     * The time of the last successful. The new label will be a snapshot of the
+     * repository at this time. String should be formatted as "yyyyMMddHHmmss"
+     */
+    private OLEDate lastBuild = null;
+
+    private static final SimpleDateFormat DATE_FORMAT =
+            new SimpleDateFormat("yyyyMMddHHmmss");
+
+
+    /**
+    * The name to be given to the label; required.
+    */
+    public void setLabel(String label) {
+        this.labelName = label;
+    }
+
+    /**
+     * Description of the label to be stored in the StarTeam project.
+     */
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    /**
+     * set the type of label based on the supplied value - if true, this
+     * label will be a revision label, if false, a build label.
+     *
+     * @param buildlabel If true this will be a revision label; if false,
+     * a build label
+     */
+    public void setBuildLabel(boolean buildlabel) {
+        this.buildlabel = buildlabel;
+    }
+
+    /**
+     * set the type of label based on the supplied value - if true, this
+     * label will be a revision label, if false, a build label.
+     *
+     * @param revisionlabel If true this will be a revision label; if false,
+     * a build label
+     */
+    public void setRevisionLabel(boolean revisionlabel) {
+        this.revisionlabel = revisionlabel;
+    }
+
+
+
+    /**
+     * The timestamp of the build that will be stored with the label; required.
+     * Must be formatted <code>yyyyMMddHHmmss</code>
+     */
+    public void setLastBuild(String lastbuild) throws BuildException {
+        try {
+            Date lastBuildTime = DATE_FORMAT.parse(lastbuild);
+            this.lastBuild = new OLEDate(lastBuildTime);
+        } catch (ParseException e) {
+            throw new BuildException("Unable to parse the date '"
+                + lastbuild + "'", e);
+        }
+    }
+
+    /**
+     * This method does the work of creating the new view and checking it into
+     * Starteam.
+     *
+     */
+    public void execute() throws BuildException {
+
+        if (this.revisionlabel && this.buildlabel) {
+            throw new BuildException("'revisionlabel' and 'buildlabel' "
+                + "both specified.  A revision label cannot be a build label.");
+        }
+
+        try {
+            View snapshot = openView();
+
+            // Create the new label and update the repository
+
+            if (this.revisionlabel) {
+                new Label(snapshot, this.labelName, this.description).update();
+                log("Created Revision Label " + this.labelName);
+            } else if (null != lastBuild) {
+                new Label(snapshot, this.labelName, this.description, this.lastBuild,
+                          this.buildlabel).update();
+                log("Created View Label ("
+                    + (this.buildlabel ? "" : "non-") + "build) " + this.labelName
+                    + " as of " + this.lastBuild.toString());
+            } else {
+                new Label(snapshot, this.labelName, this.description,
+                          this.buildlabel).update();
+                log("Created View Label ("
+                    + (this.buildlabel ? "" : "non-") + "build) " + this.labelName);
+            }
+        } catch (Exception e) {
+            throw new BuildException(e);
+        } finally {
+            disconnectFromServer();
+        }
+
+    }
+
+    /**
+     * Override of base-class abstract function creates an
+     * appropriately configured view.  For labels this a view
+     * configured as of this.lastBuild.
+     *
+     * @param raw the unconfigured <code>View</code>
+     * @return the snapshot <code>View</code> appropriately configured.
+     */
+    protected View createSnapshotView(View raw) {
+        /*
+        if (this.revisionlabel) {
+            return raw;
+        }
+        return new View(raw, ViewConfiguration.createFromTime(this.lastBuild));
+        */
+        return raw;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamList.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamList.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamList.java	(revision 16139)
@@ -0,0 +1,319 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.starteam;
+
+import com.starbase.starteam.File;
+import com.starbase.starteam.Folder;
+import com.starbase.starteam.Item;
+import com.starbase.starteam.Status;
+import com.starbase.starteam.View;
+import com.starbase.starteam.ViewConfiguration;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Enumeration;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Produces a listing of the contents of the StarTeam repository
+ * at the specified view and StarTeamFolder.
+ *
+ * Created: Tue Dec 25 06:51:14 2001
+ *
+ * @version 1.0
+ *
+ * @ant.task name="stlist" category="scm"
+ */
+
+public class StarTeamList extends TreeBasedTask {
+    private boolean listUncontrolled = true;
+    /**
+     * List files, dates, and statuses as of this label; optional.
+     * The label must exist in starteam or an exception will be thrown.
+     * If not specified, the most recent version of each file will be listed.
+     *
+     * @param label the label to be listed
+     */
+    public void setLabel(String label) {
+        _setLabel(label);
+    }
+
+    /**
+     * List files, dates, and statuses as of this date; optional.
+     * If not specified, the most recent version of each file will be listed.
+     *
+     * @param asOfDateParam the date as of which the listing to be made
+     * @since Ant 1.6
+     */
+    public void setAsOfDate(String asOfDateParam) {
+        _setAsOfDate(asOfDateParam);
+    }
+
+    /**
+     * Date Format with which asOfDate parameter to be parsed; optional.
+     * Must be a SimpleDateFormat compatible string.
+     * If not specified, and asOfDateParam is specified, parse will use ISO8601
+     * datetime and date formats.
+     *
+     * @param asOfDateFormat the SimpleDateFormat-compatible format string
+     * @since Ant 1.6
+     */
+    public void setAsOfDateFormat(String asOfDateFormat) {
+        _setAsOfDateFormat(asOfDateFormat);
+    }
+
+
+    /**
+     * Override of base-class abstract function creates an
+     * appropriately configured view for checkoutlists - either
+     * the current view or a view from this.label.
+     *
+     * @param raw the unconfigured <code>View</code>
+     * @return the snapshot <code>View</code> appropriately configured.
+     */
+    protected View createSnapshotView(View raw) {
+
+        int labelID = getLabelID(raw);
+
+        // if a label has been supplied, use it to configure the view
+        // otherwise use current view
+        if (labelID >= 0) {
+            return new View(raw, ViewConfiguration.createFromLabel(labelID));
+        }
+        // if a date has been supplied use a view configured to the date.
+        View view = getViewConfiguredByDate(raw);
+        if (view != null) {
+            return view;
+        }
+        // otherwise, use this view configured as the tip.
+        else {
+            return new View(raw, ViewConfiguration.createTip());
+        }
+    }
+
+    /**
+     * Required base-class abstract function implementation checks for
+     * incompatible parameters.
+     *
+     * @exception BuildException thrown on incompatible params specified
+     */
+    protected void testPreconditions() throws BuildException {
+        if (null != getLabel() && null != getAsOfDate()) {
+            throw new BuildException(
+                "Both label and asOfDate specified.  "
+                + "Unable to process request.");
+        }
+    }
+
+    /**
+     * extenders should emit to the log an entry describing the parameters
+     * that will be used by this operation.
+     *
+     * @param starteamrootFolder
+     *               root folder in StarTeam for the operation
+     * @param targetrootFolder
+     *               root local folder for the operation (whether specified by the user or not.
+     */
+    protected void logOperationDescription(Folder starteamrootFolder,
+                                           java.io.File targetrootFolder) {
+        log((this.isRecursive() ? "Recursive" : "Non-recursive")
+            + " Listing of: " + starteamrootFolder.getFolderHierarchy());
+
+        log("Listing against local folder"
+            + (null == getRootLocalFolder() ? " (default): " : ": ")
+            + targetrootFolder.getAbsolutePath(),
+                    Project.MSG_INFO);
+        logLabel();
+        logAsOfDate();
+        logIncludes();
+        logExcludes();
+
+
+    }
+    /**
+     * Implements base-class abstract function to perform the checkout
+     * operation on the files in each folder of the tree.
+     *
+     * @param starteamFolder the StarTeam folder from which files to be
+     *                       checked out
+     * @param targetFolder the local mapping of rootStarteamFolder
+     */
+    protected void visit(Folder starteamFolder, java.io.File targetFolder)
+            throws BuildException {
+        try {
+            if (null != getRootLocalFolder()) {
+                starteamFolder.setAlternatePathFragment(
+                    targetFolder.getAbsolutePath());
+
+            }
+            Folder[] subFolders = starteamFolder.getSubFolders();
+            Item[] files = starteamFolder.getItems(getTypeNames().FILE);
+
+            UnmatchedFileMap ufm =
+                new UnmatchedListingMap().init(
+                    targetFolder.getAbsoluteFile(), starteamFolder);
+
+            log("");
+            log("Listing StarTeam folder "
+                + starteamFolder.getFolderHierarchy());
+            log(" against local folder " + targetFolder.getAbsolutePath());
+
+
+            // For all Files in this folder, we need to check
+            // if there have been modifications.
+
+            for (int i = 0; i < files.length; i++) {
+                File eachFile = (File) files[i];
+                String filename = eachFile.getName();
+                java.io.File localFile =
+                        new java.io.File(targetFolder, filename);
+
+                ufm.removeControlledItem(localFile);
+
+                // If the file doesn't pass the include/exclude tests, skip it.
+                if (!shouldProcess(filename)) {
+                    continue;
+                }
+
+                list(eachFile, localFile);
+            }
+
+
+            // Now we recursively call this method on all sub folders in this
+            // folder unless recursive attribute is off.
+            for (int i = 0; i < subFolders.length; i++) {
+                java.io.File targetSubfolder =
+                        new java.io.File(targetFolder, subFolders[i].getName());
+                ufm.removeControlledItem(targetSubfolder);
+                if (isRecursive()) {
+                    visit(subFolders[i], targetSubfolder);
+                }
+            }
+            if (this.listUncontrolled) {
+                ufm.processUncontrolledItems();
+            }
+
+        } catch (IOException e) {
+            throw new BuildException(e);
+        }
+    }
+
+    private static final SimpleDateFormat SDF =
+        new SimpleDateFormat("yyyy-MM-dd hh:mm:ss zzz");
+
+    protected void list(File reposFile, java.io.File localFile)
+            throws IOException {
+        StringBuffer b = new StringBuffer();
+        int status = reposFile.getStatus();
+        java.util.Date displayDate = null;
+        if (status == Status.NEW) {
+            displayDate = new java.util.Date(localFile.lastModified());
+        } else {
+            displayDate = reposFile.getModifiedTime().createDate();
+        }
+        b.append(pad(Status.name(status), 12)).append(' ');
+        b.append(pad(getUserName(reposFile.getLocker()), 20))
+                .append(' ')
+                .append(SDF.format(displayDate))
+                .append(rpad(String.valueOf(reposFile.getSize()), 9))
+                .append(' ')
+                .append(reposFile.getName());
+
+        log(b.toString());
+    }
+
+    private static final String blankstr = blanks(30);
+
+    private static String blanks(int len) {
+        StringBuffer b = new StringBuffer();
+        for (int i = 0; i < len; i++) {
+            b.append(' ');
+        }
+        return b.toString();
+    }
+
+    protected static String pad(String s, int padlen) {
+        return (s + blankstr).substring(0, padlen);
+    }
+
+    protected static String rpad(String s, int padlen) {
+        s = blankstr + s;
+        return s.substring(s.length() - padlen);
+    }
+
+    /**
+     * handles the list of uncontrolled items
+     */
+    private class UnmatchedListingMap extends UnmatchedFileMap {
+
+        protected boolean isActive() {
+            return StarTeamList.this.listUncontrolled;
+        }
+
+        /**
+         * lists uncontrolled items from the local tree.  It is assumed
+         * that this method will not be called until all the items in the
+         * corresponding folder have been processed, and that the internal map
+         * will contain only uncontrolled items.
+         */
+        void processUncontrolledItems() throws BuildException {
+            if (this.isActive()) {
+                Enumeration e = this.keys();
+
+                // handle the files so they appear first
+                while (e.hasMoreElements()) {
+                    java.io.File local = (java.io.File) e.nextElement();
+                    Item remoteItem = (Item) this.get(local);
+
+                    // once we find a folder that isn't in the repository,
+                    // we know we can add it.
+                    if (local.isFile()) {
+                        com.starbase.starteam.File remoteFile =
+                            (com.starbase.starteam.File) remoteItem;
+                        try {
+                            list(remoteFile, local);
+                        } catch (IOException ie) {
+                            throw new BuildException("IOError in stlist", ie);
+                        }
+                    }
+                }
+                // now do it again for the directories so they appear last.
+                e = this.keys();
+                while (e.hasMoreElements()) {
+                    java.io.File local = (java.io.File) e.nextElement();
+                    Item remoteItem = (Item) this.get(local);
+
+                    // once we find a folder that isn't in the repository,
+                    // we know we can add it.
+                    if (local.isDirectory()) {
+                        Folder folder = (Folder) remoteItem;
+                        if (isRecursive()) {
+                            log("Listing uncontrolled folder "
+                                + folder.getFolderHierarchy()
+                                + " from " + local.getAbsoluteFile());
+                            UnmatchedFileMap submap =
+                                new UnmatchedListingMap().init(local, folder);
+                            submap.processUncontrolledItems();
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/StarTeamTask.java	(revision 16139)
@@ -0,0 +1,371 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.starteam;
+
+import com.starbase.starteam.BuildNumber;
+import com.starbase.starteam.Server;
+import com.starbase.starteam.StarTeamFinder;
+import com.starbase.starteam.TypeNames;
+import com.starbase.starteam.User;
+import com.starbase.starteam.View;
+import java.util.StringTokenizer;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Common super class for all StarTeam tasks.
+ * At this level of the hierarchy we are concerned only with obtaining a
+ * connection to the StarTeam server.  The subclass <code>TreeBasedTask</code>,
+ * also abstract defines the tree-walking behavior common to many subtasks.
+ *
+ * @see TreeBasedTask
+ * @version 1.1
+ */
+
+public abstract class StarTeamTask extends Task {
+
+    // ATTRIBUTES
+
+    /**
+     * The username of the connection
+     */
+    private String userName;
+
+    /**
+     * The username of the connection
+     */
+    private String password;
+
+    /**
+     * name of Starteam server to connect to
+     */
+    private String servername;
+
+    /**
+     * port of Starteam server to connect to
+     */
+    private String serverport;
+
+    /**
+     * name of Starteam project to connect to
+     */
+    private String projectname;
+
+    /**
+     * name of Starteam view to connect to
+     */
+    private String viewname;
+
+    /**
+     *The starteam server through which all activities will be done.
+     */
+    private Server server = null;
+
+    private void logStarteamVersion() {
+        log("StarTeam version: "
+            + BuildNumber.getDisplayString(), Project.MSG_VERBOSE);
+    }
+
+
+    /////////////////////////////////////////////////////////
+    // GET/SET methods.
+    // Setters, of course are where ant user passes in values.
+    /////////////////////////////////////////////////////////
+
+    /**
+     * Set the name of StarTeamServer;
+     * required if <tt>URL</tt> is not set.
+     * @param servername a <code>String</code> value
+     * @see #setURL(String)
+     */
+    public final void setServername(String servername) {
+        this.servername = servername;
+    }
+
+    /**
+     * returns the name of the StarTeamServer
+     *
+     * @return the name of the StarTeam server
+     * @see #getURL()
+     */
+    public final String getServername() {
+        return this.servername;
+    }
+
+    /**
+     * set the port number of the StarTeam connection;
+     * required if <tt>URL</tt> is not set.
+     * @param serverport port number to be set
+     * @see #setURL(String)
+     */
+    public final void setServerport(String serverport) {
+        this.serverport = serverport;
+    }
+
+    /**
+     * returns the port number of the StarTeam connection
+     *
+     * @return the port number of the StarTeam connection
+     * @see #getURL()
+     */
+    public final String getServerport() {
+        return this.serverport;
+    }
+
+    /**
+     * set the name of the StarTeam project to be acted on;
+     * required if <tt>URL</tt> is not set.
+     *
+     * @param projectname the name of the StarTeam project to be acted on
+     * @see #setURL(String)
+     */
+    public final void setProjectname(String projectname) {
+        this.projectname = projectname;
+    }
+
+    /**
+     * returns the name of the StarTeam project to be acted on
+     *
+     * @return the name of the StarTeam project to be acted on
+     * @see #getURL()
+     */
+    public final String getProjectname() {
+        return this.projectname;
+    }
+
+    /**
+     * set the name of the StarTeam view to be acted on;
+     * required if <tt>URL</tt> is not set.
+     *
+     * @param viewname the name of the StarTeam view to be acted on
+     * @see #setURL(String)
+     */
+    public final void setViewname(String viewname) {
+        this.viewname = viewname;
+    }
+
+    /**
+     * returns the name of the StarTeam view to be acted on
+     *
+     * @return the name of the StarTeam view to be acted on
+     * @see #getURL()
+     */
+    public final String getViewname() {
+        return this.viewname;
+    }
+
+
+    /**
+     * Set the server name, server port,
+     * project name and project folder in one shot;
+     * optional, but the server connection must be specified somehow.
+     *
+     * @param url a <code>String</code> of the form
+     *             "servername:portnum/project/view"
+     * @see #setServername(String)
+     * @see #setServerport(String)
+     * @see #setProjectname(String)
+     * @see #setViewname(String)
+     */
+    public final void setURL(String url) {
+        StringTokenizer t = new StringTokenizer(url, "/");
+        if (t.hasMoreTokens()) {
+            String unpw = t.nextToken();
+            int pos = unpw.indexOf(":");
+            if (pos > 0) {
+                this.servername = unpw.substring(0, pos);
+                this.serverport = unpw.substring(pos + 1);
+                if (t.hasMoreTokens()) {
+                    this.projectname = t.nextToken();
+                    if (t.hasMoreTokens()) {
+                        this.viewname = t.nextToken();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * convenience method returns whole URL at once
+     * returns
+     * as a single string
+     */
+    /**
+     * a convenience method which returns the whole StarTeam
+     * connection information as a single URL string of
+     *
+     * @return a <code>String</code> of the form
+     *         "servername:portnum/project/view"
+     * @see #getServername()
+     * @see #getServerport()
+     * @see #getProjectname()
+     * @see #getViewname()
+     */
+    public final String getURL() {
+        return this.servername + ":"
+            + this.serverport + "/"
+            + this.projectname + "/"
+            + ((null == this.viewname) ? "" : this.viewname);
+    }
+
+    /**
+     * returns an URL string useful for interacting with many StarTeamFinder
+     * methods.
+     *
+     * @return the URL string for this task.
+     */
+    protected final String getViewURL() {
+        return getUserName() + ":" + getPassword() + "@" + getURL();
+    }
+    /**
+     * set the name of the StarTeam user, needed for the connection
+     *
+     * @param userName name of the user to be logged in
+     */
+    public final void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    /**
+     * returns the name of the StarTeam user
+     *
+     * @return the name of the StarTeam user
+     */
+    public final String getUserName() {
+        return this.userName;
+    }
+
+    /**
+     * set the password to be used for login; required.
+     *
+     * @param password the password to be used for login
+     */
+    public final void setPassword(String password) {
+        this.password = password;
+    }
+
+    /**
+     * returns the password used for login
+     *
+     * @return the password used for login
+     */
+    public final String getPassword() {
+        return this.password;
+    }
+
+    /**
+     * returns a reference to the server which may be used for informational
+     * purposes by subclasses.
+     *
+     * @return a reference to the server
+     */
+    protected final Server getServer() {
+        return this.server;
+    }
+
+    /**
+     * disconnects from the StarTeam server.  Should be called from the
+     * finally clause of every StarTeamTask-based execute method.
+     */
+    protected final void disconnectFromServer() {
+        if (null != this.server) {
+            this.server.disconnect();
+            log("successful disconnect from StarTeam Server " + servername,
+                Project.MSG_VERBOSE);
+        }
+    }
+
+    /**
+     * returns a list of TypeNames known to the server.
+     *
+     * @return a reference to the server's TypeNames
+     */
+    protected final TypeNames getTypeNames() {
+        return this.server.getTypeNames();
+    }
+    /**
+     * Derived classes must override <code>createSnapshotView</code>
+     * defining the kind of configured view appropriate to its task.
+     *
+     * @param rawview the unconfigured <code>View</code>
+     * @return the snapshot <code>View</code> appropriately configured.
+     */
+    protected abstract View createSnapshotView(View rawview)
+    throws BuildException;
+
+    /**
+     * All subclasses will call on this method to open the view needed for
+     * processing.  This method also saves a reference to the
+     * <code>Server</code> that may be accessed for information at various
+     * points in the process.
+     *
+     * @return the <code>View</code> that will be used for processing.
+     * @see #createSnapshotView(View)
+     * @see #getServer()
+     */
+    protected View openView() throws BuildException {
+
+        logStarteamVersion();
+        View view = null;
+        try {
+            view = StarTeamFinder.openView(getViewURL());
+        } catch (Exception e) {
+            throw new BuildException(
+                "Failed to connect to " + getURL(), e);
+        }
+
+        if (null == view) {
+            throw new BuildException("Cannot find view" + getURL()
+                + " in repository()");
+        }
+
+        View snapshot = createSnapshotView(view);
+        log("Connected to StarTeam view " + getURL(),
+            Project.MSG_VERBOSE);
+        this.server = snapshot.getServer();
+        return snapshot;
+    }
+
+    /**
+     * Returns the name of the user with the supplied ID or a blank string
+     * if user not found.
+     *
+     * @param userID a user's ID
+     * @return the name of the user with ID userID
+     */
+    protected final String getUserName(int userID) {
+        User u = this.server.getUser(userID);
+        if (null == u) {
+            return "";
+        }
+        return u.getName();
+    }
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/TreeBasedTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/TreeBasedTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/starteam/TreeBasedTask.java	(revision 16139)
@@ -0,0 +1,873 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.starteam;
+
+import com.starbase.starteam.Folder;
+import com.starbase.starteam.Label;
+import com.starbase.starteam.PropertyNames;
+import com.starbase.starteam.StarTeamFinder;
+import com.starbase.starteam.View;
+import com.starbase.starteam.ViewConfiguration;
+import com.starbase.util.OLEDate;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.DateUtils;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+
+/**
+ * TreeBasedTask.java
+ * This abstract class is the base for any tasks that are tree-based, that
+ * is, for tasks which iterate over a tree of folders in StarTeam which
+ * is reflected in a tree of folder the local machine.
+ *
+ * This class provides the tree-iteration functionality.  Derived classes
+ * will implement their specific task functionality by the visitor pattern,
+ * specifically by implementing the method
+ * <code>visit(Folder rootStarteamFolder, java.io.File rootLocalFolder)</code>
+ *
+ * Created: Sat Dec 15 16:55:19 2001
+ *
+ * @version 1.0
+ * @see <A HREF="http://www.starbase.com/">StarBase Web Site</A>
+ */
+
+public abstract class TreeBasedTask extends StarTeamTask {
+
+
+    ///////////////////////////////////////////////////////////////
+    // default values for attributes.
+    ///////////////////////////////////////////////////////////////
+    /**
+     * This constant sets the filter to include all files. This default has
+     * the same result as <CODE>setIncludes("*")</CODE>.
+     *
+     * @see #getIncludes()
+     * @see #setIncludes(String includes)
+     */
+    public static final String DEFAULT_INCLUDESETTING = "*";
+
+    /**
+     * This disables the exclude filter by default. In other words, no files
+     * are excluded. This setting is equivalent to
+     * <CODE>setExcludes(null)</CODE>.
+     *
+     * @see #getExcludes()
+     * @see #setExcludes(String excludes)
+     */
+    public static final String DEFAULT_EXCLUDESETTING = null;
+
+    //ATTRIBUTES settable from ant.
+
+    /**
+     * The root folder of the operation in StarTeam.
+     */
+    private String rootStarteamFolder = "/";
+
+    /**
+     * The local folder corresponding to starteamFolder.  If not specified
+     * the Star Team default folder will be used.
+     */
+    private String rootLocalFolder = null;
+
+    /**
+     * All files that fit this pattern are checked out.
+     */
+    private String includes = DEFAULT_INCLUDESETTING;
+
+    /**
+     * All files fitting this pattern are ignored.
+     */
+    private String excludes = DEFAULT_EXCLUDESETTING;
+
+    /**
+     * StarTeam label on which to perform task.
+     */
+    private String label = null;
+
+    /**
+     * Set recursion to false to check out files in only the given folder
+     * and not in its subfolders.
+     */
+    private boolean recursive = true;
+
+    /**
+     * Set preloadFileInformation to true to load all file information from the server
+     * at once.  Increases performance significantly for projects with many files and/or folders.
+     */
+    private boolean preloadFileInformation = true;
+
+    /**
+     * If forced set to true, files in the target directory will
+     * be processed regardless of status in the repository.
+     * Usually this should be  true if rootlocalfolder is set
+     * because status will be relative to the default folder, not
+     * to the one being processed.
+     */
+    private boolean forced = false;
+
+    private Label labelInUse = null;
+
+    /**
+     * holder for the asofdate attribute
+     */
+    private String asOfDate = null;
+
+    /**
+     * holder for the asofdateformat attribute
+     */
+    private String asOfDateFormat = null;
+
+
+
+    ///////////////////////////////////////////////////////////////
+    // GET/SET methods.
+    // Setters, of course are where ant user passes in values.
+    ///////////////////////////////////////////////////////////////
+
+    /**
+     * Set the root of the subtree in the StarTeam repository from which to
+     * work; optional.  Defaults to the root folder of the view ('/').
+     * @param rootStarteamFolder the root folder
+     */
+    public void setRootStarteamFolder(String rootStarteamFolder) {
+        this.rootStarteamFolder = rootStarteamFolder;
+    }
+
+    /**
+     * returns the root folder in the Starteam repository
+     * used for this operation
+     * @return the root folder in use
+     */
+    public String getRootStarteamFolder() {
+        return this.rootStarteamFolder;
+    }
+
+    /**
+     * Set the local folder that will be the root of the tree
+     * to which files are checked out; optional.
+     * If this is not supplied, then the StarTeam "default folder"
+     * associated with <tt>rootstarteamfolder</tt> is used.
+     *
+     * @param rootLocalFolder
+     *               the local folder that will mirror
+     *               this.rootStarteamFolder
+     */
+    public void setRootLocalFolder(String rootLocalFolder) {
+        this.rootLocalFolder = rootLocalFolder;
+    }
+
+
+
+    /**
+     * Returns the local folder specified by the user,
+     * corresponding to the starteam folder for this operation
+     * or null if not specified.
+     *
+     * @return the local folder that mirrors this.rootStarteamFolder
+     */
+    public String getRootLocalFolder() {
+        return this.rootLocalFolder;
+    }
+
+
+    /**
+     * Declare files to include using standard <tt>includes</tt> patterns; optional.
+     * @param includes A string of filter patterns to include. Separate the
+     *                 patterns by spaces.
+     * @see #getIncludes()
+     * @see #setExcludes(String excludes)
+     * @see #getExcludes()
+     */
+    public void setIncludes(String includes) {
+        this.includes = includes;
+    }
+
+    /**
+     * Gets the patterns from the include filter. Rather that duplicate the
+     * details of AntStarTeamCheckOut's filtering here, refer to these
+     * links:
+     *
+     * @return A string of filter patterns separated by spaces.
+     * @see #setIncludes(String includes)
+     * @see #setExcludes(String excludes)
+     * @see #getExcludes()
+     */
+    public String getIncludes() {
+        return includes;
+    }
+
+    /**
+     * if excludes have been specified, emit the list to the log
+     */
+    protected void logIncludes() {
+        if (this.DEFAULT_INCLUDESETTING != this.includes) {
+            log("  Includes specified: " + this.includes);
+        }
+    }
+
+    /**
+     * Declare files to exclude using standard <tt>excludes</tt> patterns; optional.
+     * When filtering files, AntStarTeamCheckOut
+     * uses an unmodified version of <CODE>DirectoryScanner</CODE>'s
+     * <CODE>match</CODE> method, so here are the patterns straight from the
+     * Ant source code:
+     * <BR><BR>
+     * Matches a string against a pattern. The pattern contains two special
+     * characters:
+     * <BR>'*' which means zero or more characters,
+     * <BR>'?' which means one and only one character.
+     * <BR><BR>
+     *  For example, if you want to check out all files except .XML and
+     * .HTML files, you would put the following line in your program:
+     * <CODE>setExcludes("*.XML,*.HTML");</CODE>
+     * Finally, note that filters have no effect on the <B>directories</B>
+     * that are scanned; you could not skip over all files in directories
+     * whose names begin with "project," for instance.
+     * <BR><BR>
+     * Treatment of overlapping inlcudes and excludes: To give a simplistic
+     * example suppose that you set your include filter to "*.htm *.html"
+     * and your exclude filter to "index.*". What happens to index.html?
+     * AntStarTeamCheckOut will not check out index.html, as it matches an
+     * exclude filter ("index.*"), even though it matches the include
+     * filter, as well.
+     * <BR><BR>
+     * Please also read the following sections before using filters:
+     *
+     * @param excludes A string of filter patterns to exclude. Separate the
+     *                 patterns by spaces.
+     * @see #setIncludes(String includes)
+     * @see #getIncludes()
+     * @see #getExcludes()
+     */
+    public void setExcludes(String excludes) {
+        this.excludes = excludes;
+    }
+
+    /**
+     * Gets the patterns from the exclude filter. Rather that duplicate the
+     * details of AntStarTeanCheckOut's filtering here, refer to these
+     * links:
+     *
+     * @return A string of filter patterns separated by spaces.
+     * @see #setExcludes(String excludes)
+     * @see #setIncludes(String includes)
+     * @see #getIncludes()
+     */
+    public String getExcludes() {
+        return excludes;
+    }
+
+    /**
+     * if excludes have been specified, emit the list to the log
+     */
+    protected void logExcludes() {
+        if (this.DEFAULT_EXCLUDESETTING != this.excludes) {
+            log("  Excludes specified: " + this.excludes);
+        }
+    }
+
+
+    /**
+     * protected function to allow subclasses to set the label (or not).
+     * sets the StarTeam label
+     *
+     * @param label name of the StarTeam label to be set
+     */
+    protected void _setLabel(String label) {
+        if (null != label) {
+            label = label.trim();
+            if (label.length() > 0) {
+                this.label = label;
+            }
+        }
+    }
+
+    /**
+     * non-public method callable only by derived classes that implement
+     * setAsOfDate (so that derived tasks that do not accept this
+     * parameter will fail if user attempts to use it.
+     *
+     * @param asOfDate asOfDate entered by user.
+     * @since Ant 1.6
+     */
+    protected void _setAsOfDate(String asOfDate) {
+        if (asOfDate != null && asOfDate.length() > 0) {
+            this.asOfDate = asOfDate;
+        }
+    }
+
+    /**
+     * non-public method callable only by derived classes that implement
+     * setAsOfDateFormat (so that derived tasks that do not accept this
+     * parameter will fail if user attempts to use it.
+     *
+     * @param asOfDateFormat asOfDate format entered by user.
+     * @since Ant 1.6
+     */
+    protected void _setAsOfDateFormat(String asOfDateFormat) {
+        if (asOfDateFormat != null && asOfDateFormat.length() > 0) {
+            this.asOfDateFormat = asOfDateFormat;
+        }
+    }
+
+
+    /**
+     * return the asOfDate entered by the user for internal use by derived
+     * classes.
+     *
+     * @return the asOfDate entered by the user
+     * @since Ant 1.6
+     */
+    protected String getAsOfDate() {
+        return this.asOfDate;
+    }
+
+
+
+    /**
+     * If an asofDate parameter has been supplied by the user return a
+     * StarTeam view based on the configuration of the StarTeam view
+     * specified the user as of the date specified in the parameter.
+     * If no asofDate has been specified, return null.
+     *
+     * This method is meant to be called from within implementations of the
+     * <code>createSnapshotView</code> abstract method.
+     *
+     * @param raw    the raw view to be configured as of the supplied date
+     *
+     * @return the view as configured.
+     * @exception BuildException
+     *                   thrown if the date is not parsable by the default or
+     *                   supplied format patterns.
+     * @since Ant 1.6
+     */
+    protected View getViewConfiguredByDate(View raw) throws BuildException {
+        if (this.asOfDate == null) {
+            return null;
+        }
+        Date asOfDate = null;
+        SimpleDateFormat fmt = null;
+        if (this.asOfDateFormat != null) {
+            fmt = new SimpleDateFormat(this.asOfDateFormat);
+            try {
+                asOfDate = fmt.parse(this.asOfDate);
+            }
+            catch (ParseException px)
+            {
+                throw new BuildException("AsOfDate "
+                                         + this.asOfDate
+                                         + " not parsable by supplied format "
+                                         + this.asOfDateFormat);
+            }
+        } else {
+            try {
+                asOfDate = DateUtils.parseIso8601DateTimeOrDate(
+                    this.asOfDate);
+            } catch (ParseException px) {
+                throw new BuildException("AsOfDate "
+                                         + this.asOfDate
+                                         + " not parsable by default"
+                                         + " ISO8601 formats");
+            }
+        }
+        return new View(raw, ViewConfiguration.createFromTime(
+            new OLEDate(asOfDate)));
+    }
+
+
+
+    /**
+     * return the label passed to the task by the user as a string
+     *
+     * @return the label passed to the task by the user as a string
+     */
+    protected String getLabel() {
+        return this.label;
+    }
+
+    /**
+     * Get the value of recursive.
+     * @return value of recursive.
+     */
+    public boolean isRecursive() {
+        return this.recursive;
+    }
+
+    /**
+     * Flag to set to include files in subfolders in the operation; optional,
+     * default true.
+     * @param v  Value to assign to recursive.
+     */
+    public void setRecursive(boolean v) {
+        this.recursive = v;
+    }
+
+    /**
+     * Get the value of preloadFileInformation.
+     * @return value of preloadFileInformation.
+     */
+    public boolean isPreloadFileInformation() {
+        return this.preloadFileInformation;
+    }
+
+    /**
+     * Flag to set to preload file information from the server; optional,
+     * default true.
+     * Increases performance significantly for projects with many files
+     * and/or folders.
+     * @param v  Value to assign to preloadFileInformation.
+     */
+    public void setPreloadFileInformation(boolean v) {
+        this.preloadFileInformation = v;
+    }
+
+    /**
+     * Get the value of forced.
+     * @return value of forced.
+     */
+    public boolean isForced() {
+        return this.forced;
+    }
+
+    /**
+     * Flag to force actions regardless of the status
+     * that StarTeam is maintaining for the file; optional, default false.
+     * If <tt>rootlocalfolder</tt> is set then
+     * this should be set "true" as otherwise the checkout will be based on statuses
+     * which do not relate to the target folder.
+     * @param v  Value to assign to forced.
+     */
+    public void setForced(boolean v) {
+        this.forced = v;
+    }
+
+    /**
+     *  returns true if a label has been specified and it is a view label.
+     *
+     * @return  true if a label has been specified and it is a view label
+     */
+    protected boolean isUsingViewLabel() {
+        return null != this.labelInUse && this.labelInUse.isViewLabel();
+    }
+    /**
+     *  returns true if a label has been specified and it is a revision label.
+     *
+     * @return  true if a label has been specified and it is a revision label
+     */
+    protected boolean isUsingRevisionLabel() {
+        return null != this.labelInUse && this.labelInUse.isRevisionLabel();
+    }
+
+    /**
+     * returns the label being used
+     *
+     * @return the label being used
+     */
+    protected Label getLabelInUse() {
+        return this.labelInUse;
+    }
+
+    /**
+     * show the label in the log and its type.
+     */
+    protected void logLabel() {
+        if (this.isUsingViewLabel()) {
+            log("  Using view label " + getLabel());
+        } else if (this.isUsingRevisionLabel()) {
+            log("  Using revision label " + getLabel());
+        }
+    }
+
+    /**
+     * show the asofDate in the log
+     * @since Ant 1.6
+     */
+    protected void logAsOfDate() {
+        if (null != this.asOfDate) {
+            log("  Using view as of date " + getAsOfDate());
+        }
+    }
+
+
+
+
+
+    ///////////////////////////////////////////////////////////////
+    // INCLUDE-EXCLUDE processing
+    ///////////////////////////////////////////////////////////////
+
+    /**
+     * Look if the file should be processed by the task.
+     * Don't process it if it fits no include filters or if
+     * it fits an exclude filter.
+     *
+     * @param pName  the item name to look for being included.
+     *
+     * @return whether the file should be processed or not.
+     */
+    protected boolean shouldProcess(String pName) {
+        boolean includeIt = matchPatterns(getIncludes(), pName);
+        boolean excludeIt = matchPatterns(getExcludes(), pName);
+        return (includeIt && !excludeIt);
+    }
+
+    /**
+     * Convenience method to see if a string match a one pattern
+     * in given set of space-separated patterns.
+     * @param patterns the space-separated list of patterns.
+     * @param pName the name to look for matching.
+     * @return whether the name match at least one pattern.
+     */
+    protected boolean matchPatterns(String patterns, String pName) {
+        if (patterns == null) {
+            return false;
+        }
+        StringTokenizer exStr = new StringTokenizer(patterns, ",");
+        while (exStr.hasMoreTokens()) {
+            if (DirectoryScanner.match(exStr.nextToken(), pName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Finds and opens the root starteam folder of the operation specified
+     * by this task.  This will be one of the following cases:
+     *
+     * @return Starteam's root folder for the operation.
+     * @exception BuildException
+     *                   if the root folder cannot be found in the repository
+     */
+    private final Folder configureRootStarteamFolder()
+        throws BuildException {
+        Folder starteamrootfolder = null;
+        try {
+            // no root local mapping has been specified.
+            View snapshot = openView();
+
+            // find the starteam folder specified to be the root of the
+            // operation.  Throw if it can't be found.
+
+            starteamrootfolder =
+                    StarTeamFinder.findFolder(snapshot.getRootFolder(),
+                            this.rootStarteamFolder);
+
+            if (this.isPreloadFileInformation()) {
+                PropertyNames pn = getServer().getPropertyNames();
+                String[] props = new String[] {pn.FILE_NAME, pn.FILE_PATH,
+                                               pn.FILE_STATUS, pn.MODIFIED_TIME,
+                                               pn.FILE_FILE_TIME_AT_CHECKIN,
+                                               pn.MODIFIED_USER_ID, pn.FILE_SIZE};
+
+                int depth = this.isRecursive() ? -1 : 0;
+                starteamrootfolder.populateNow(getServer().getTypeNames().FILE,
+                                                props, depth);
+            }
+
+
+        } catch (BuildException e) {
+            throw e;
+        } catch (Exception e) {
+            StringBuffer msg = new StringBuffer("Unable to find root folder ")
+                    .append(this.rootStarteamFolder)
+                    .append(" in repository at ")
+                    .append(getURL());
+            if (this.label != null) {
+                msg.append(" using specified label ").append(this.label);
+            }
+            if (this.asOfDate != null) {
+                msg.append(" as of specified date ")
+                    .append(this.asOfDate);
+            }
+            throw new BuildException(msg.toString(), e);
+
+        }
+
+        if (null == starteamrootfolder) {
+            throw new BuildException("Unable to find root folder "
+                + this.rootStarteamFolder + " in repository at " + getURL());
+        }
+
+        return starteamrootfolder;
+    }
+
+    /**
+     * Returns the local folder mapped to the given StarTeam root folder
+     * of the operation.  There are two cases here, depending on whether
+     * <code>rootLocalFolder</code> is defined.
+     * If <code>rootLocalFolder</code> is defined, it will be used to
+     * establish a root mapping.  Otherwise, the repository's default root
+     * folder will be used.
+     *
+     * @param starteamrootfolder
+     *               root Starteam folder initialized for the operation
+     *
+     * @return the local folder corresponding to the root Starteam folder.
+     * @see findRootStarteamFolder
+     */
+    private final java.io.File getLocalRootMapping(Folder starteamrootfolder) {
+        // set the local folder.
+        String localrootfolder;
+        if (null != this.rootLocalFolder) {
+            localrootfolder = rootLocalFolder;
+        } else  {
+            // either use default path or root local mapping,
+            // which is now embedded in the root folder
+            localrootfolder = starteamrootfolder.getPathFragment();
+        }
+
+        return new java.io.File(localrootfolder);
+
+    }
+
+    /**
+     * extenders should emit to the log an entry describing the parameters
+     * that will be used by this operation.
+     *
+     * @param starteamrootFolder
+     *               root folder in StarTeam for the operation
+     * @param targetrootFolder
+     *               root local folder for the operation (whether specified by the user or not.
+     */
+    protected abstract void logOperationDescription(
+        Folder starteamrootFolder, java.io.File targetrootFolder);
+
+    /**
+     * This method does the work of opening the supplied  Starteam view and
+     * calling the <code>visit()</code> method to perform the task.
+     * Derived classes can customize the called methods
+     * <code>testPreconditions()</code> and <code>visit()</code>.
+     *
+     * @exception BuildException if any error occurs in the processing
+     * @see <code>testPreconditions()</code>
+     * @see <code>visit()</code>
+     */
+
+    public final void execute() throws BuildException {
+        try {
+
+            Folder starteamrootfolder = configureRootStarteamFolder();
+
+            // set the local folder.
+            java.io.File localrootfolder =
+                getLocalRootMapping(starteamrootfolder);
+
+            testPreconditions();
+
+            // Tell user what he is doing
+            logOperationDescription(starteamrootfolder, localrootfolder);
+
+            // Inspect everything in the root folder and then recursively
+            visit(starteamrootfolder, localrootfolder);
+
+        } catch (Exception e) {
+            throw new BuildException(e);
+        } finally {
+            disconnectFromServer();
+        }
+    }
+
+    private void findLabel(View v) throws BuildException {
+        Label[] allLabels = v.getLabels();
+        for (int i = 0; i < allLabels.length; i++) {
+            Label stLabel = allLabels[i];
+            log("checking label " + stLabel.getName(), Project.MSG_DEBUG);
+            if (stLabel != null && !stLabel.isDeleted() && stLabel.getName().equals(this.label)) {
+                if (!stLabel.isRevisionLabel() && !stLabel.isViewLabel()) {
+                    throw new BuildException("Unexpected label type.");
+                }
+                log("using label " + stLabel.getName(), Project.MSG_VERBOSE);
+                this.labelInUse = stLabel;
+                return;
+            }
+        }
+        throw new BuildException("Error: label "
+                + this.label
+                + " does not exist in view "
+                + v.getFullName());
+
+    }
+
+    /**
+     * Helper method calls on the StarTeam API to retrieve an ID number
+     * for the specified view, corresponding to this.label.
+     * @param v the <code>View</code> in which to search for <code>this.label</code>
+     * @return the ID number corresponding to <code>this.label</code> or -1 if
+     *         no label was provided.
+     * @exception BuildException if <code>this.label</code> does not correspond
+     *                           to any label in the supplied view
+     */
+    protected int getLabelID(View v) throws BuildException {
+        if (null != this.label) {
+            findLabel(v);
+            return this.labelInUse.getID();
+        }
+        return -1;
+    }
+
+    protected int getIDofLabelInUse() {
+        if (null != this.labelInUse) {
+            return this.labelInUse.getID();
+        }
+        return -1;
+    }
+
+    /**
+     * Derived classes must override this class to define actual processing
+     * to be performed on each folder in the tree defined for the task
+     *
+     * @param rootStarteamFolder
+     *               the StarTeam folderto be visited
+     * @param rootLocalFolder
+     *               the local mapping of rootStarteamFolder
+     *
+     * @exception BuildException
+     */
+    protected abstract void visit(Folder rootStarteamFolder,
+                                  java.io.File rootLocalFolder)
+            throws BuildException;
+
+
+
+
+    /**
+     * Derived classes must override this method to define tests for
+     * any preconditons required by the task.  This method is called at
+     * the beginning of the execute() method.
+     *
+     * @exception BuildException throw if any fatal error exists in the
+     * parameters supplied.  If there is a non-fatal condition, just writing
+     * to the log may be appropriate.
+     * @see <code>execute()</code>
+     */
+    protected abstract void testPreconditions() throws BuildException;
+
+
+    /**
+     * Return the full repository path name of a file.  Surprisingly there's
+     * no method in com.starbase.starteam.File to provide this.
+     *
+     * @param remotefile the Star Team file whose path is to be returned
+     *
+     * @return the full repository path name of a file.
+     */
+    public static String getFullRepositoryPath(
+        com.starbase.starteam.File remotefile) {
+        StringBuffer sb = new StringBuffer();
+        sb.append(remotefile.getParentFolderHierarchy())
+          .append(remotefile.getName());
+        return sb.toString();
+    }
+
+    /**
+     * This class implements a map of existing local files to possibly
+     * existing repository files.  The map is created by a TreeBasedTask
+     * upon recursing into a directory.  Each local item is mapped to an
+     * unattached StarTeam object of the proper type, File->File and
+     * Directory->Folder.
+     *
+     * As the TreeBased does its work, it deletes from the map all items
+     * it has processed.
+     *
+     * When the TreeBased task processes all the items from the repository,
+     * whatever items left in the UnmatchedFileMap are uncontrolled items
+     * and can be processed as appropriate to the task.  In the case of
+     * Checkouts, they can be optionally deleted from the local tree.  In the
+     * case of Checkins they can optionally be added to the repository.
+     */
+    protected abstract class UnmatchedFileMap extends Hashtable {
+
+        /**
+         * initializes the UnmatchedFileMap with entries from the local folder
+         * These will be mapped to the corresponding StarTeam entry even though
+         * it will not, in fact, exist in the repository.  But through it, it
+         * can be added, listed, etc.
+         *
+         * @param localFolder
+         *        the local folder from which the mappings will be made.
+         * @param remoteFolder
+         *        the corresponding StarTeam folder which will be processed.
+         */
+        UnmatchedFileMap init(java.io.File localFolder, Folder remoteFolder) {
+            if (!localFolder.exists()) {
+                return this;
+            }
+
+            String[] localFiles = localFolder.list();
+
+            for (int i = 0; i < localFiles.length; i++) {
+                String fn = localFiles[i];
+                java.io.File localFile =
+                    new java.io.File(localFolder, localFiles[i]).getAbsoluteFile();
+
+                log("adding " + localFile + " to UnmatchedFileMap",
+                    Project.MSG_DEBUG);
+
+                if (localFile.isDirectory()) {
+                    this.put(localFile, new Folder(remoteFolder, fn, fn));
+                } else {
+                    com.starbase.starteam.File remoteFile =
+                        new com.starbase.starteam.File(remoteFolder);
+                    remoteFile.setName(fn);
+                    this.put(localFile, remoteFile);
+                }
+            }
+            return this;
+        }
+
+        /**
+         * remove an item found to be controlled from the map.
+         *
+         * @param localFile the local item found to be controlled.
+         */
+        void removeControlledItem(java.io.File localFile) {
+            if (isActive()) {
+                log("removing processed " + localFile.getAbsoluteFile()
+                    + " from UnmatchedFileMap", Project.MSG_DEBUG);
+                this.remove(localFile.getAbsoluteFile());
+            }
+        }
+        /**
+         * override will perform the action appropriate for its task to perform
+         * on items which are on the local tree but not in StarTeam.  It is
+         * assumed that this method will not be called until all the items in
+         * the corresponding folder have been processed, and that the internal
+         * map * will contain only uncontrolled items.
+         */
+        abstract void processUncontrolledItems() throws BuildException;
+
+        /**
+         * overrides must define this to declare how this method knows if it
+         * is active.  This presents extra clock cycles when the functionality
+         * is not called for.
+         *
+         * @return True if this object is to perform its functionality.
+         */
+        protected abstract boolean isActive();
+
+    }
+
+}
+
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/unix/AbstractAccessTask.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/unix/AbstractAccessTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/unix/AbstractAccessTask.java	(revision 16139)
@@ -0,0 +1,105 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * Since the initial version of this file was deveolped on the clock on
+ * an NSF grant I should say the following boilerplate:
+ *
+ * This material is based upon work supported by the National Science
+ * Foundaton under Grant No. EIA-0196404. Any opinions, findings, and
+ * conclusions or recommendations expressed in this material are those
+ * of the author and do not necessarily reflect the views of the
+ * National Science Foundation.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.unix;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * @since Ant 1.6
+ *
+ * @ant.task category="filesystem"
+ */
+
+public abstract class AbstractAccessTask
+    extends org.apache.tools.ant.taskdefs.ExecuteOn {
+
+    /**
+     * Chmod task for setting file and directory permissions.
+     */
+    public AbstractAccessTask() {
+        super.setParallel(true);
+        super.setSkipEmptyFilesets(true);
+    }
+
+    /**
+     * Set the file which should have its access attributes modified.
+     */
+    public void setFile(File src) {
+        FileSet fs = new FileSet();
+        fs.setFile(src);
+        addFileset(fs);
+    }
+
+    /**
+     * Prevent the user from specifying a different command.
+     *
+     * @ant.attribute ignore="true"
+     * @param cmdl A user supplied command line that we won't accept.
+     */
+    public void setCommand(Commandline cmdl) {
+        throw new BuildException(getTaskType()
+                                 + " doesn\'t support the command attribute",
+                                 getLocation());
+    }
+
+    /**
+     * Prevent the skipping of empty filesets
+     *
+     * @ant.attribute ignore="true"
+     * @param skip A user supplied boolean we won't accept.
+     */
+    public void setSkipEmptyFilesets(boolean skip) {
+        throw new BuildException(getTaskType() + " doesn\'t support the "
+                                 + "skipemptyfileset attribute",
+                                 getLocation());
+    }
+
+    /**
+     * Prevent the use of the addsourcefile atribute.
+     *
+     * @ant.attribute ignore="true"
+     * @param b A user supplied boolean we won't accept.
+     */
+    public void setAddsourcefile(boolean b) {
+        throw new BuildException(getTaskType()
+            + " doesn\'t support the addsourcefile attribute", getLocation());
+    }
+
+    /**
+     * Automatically approve Unix OS's.
+     */
+    protected boolean isValidOs() {
+        return Os.isFamily("unix") && super.isValidOs();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chgrp.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chgrp.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chgrp.java	(revision 16139)
@@ -0,0 +1,84 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * Since the initial version of this file was deveolped on the clock on
+ * an NSF grant I should say the following boilerplate:
+ *
+ * This material is based upon work supported by the National Science
+ * Foundaton under Grant No. EIA-0196404. Any opinions, findings, and
+ * conclusions or recommendations expressed in this material are those
+ * of the author and do not necessarily reflect the views of the
+ * National Science Foundation.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.unix;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Chgrp equivalent for unix-like environments.
+ *
+ *
+ * @since Ant 1.6
+ *
+ * @ant.task category="filesystem"
+ */
+public class Chgrp extends AbstractAccessTask {
+
+    private boolean haveGroup = false;
+
+    /**
+     * Chgrp task for setting unix group of a file.
+     */
+    public Chgrp() {
+        super.setExecutable("chgrp");
+    }
+
+    /**
+     * Set the group atribute.
+     *
+     * @param group    The new group for the file(s) or directory(ies)
+     */
+    public void setGroup(String group) {
+        createArg().setValue(group);
+        haveGroup = true;
+    }
+
+    /**
+     * Ensure that all the required arguments and other conditions have
+     * been set.
+     */
+    protected void checkConfiguration() {
+        if (!haveGroup) {
+            throw new BuildException("Required attribute group not set in "
+                                     + "chgrp", getLocation());
+        }
+        super.checkConfiguration();
+    }
+
+    /**
+     * We don't want to expose the executable atribute, so overide it.
+     *
+     * @param e User supplied executable that we won't accept.
+     */
+    public void setExecutable(String e) {
+        throw new BuildException(getTaskType()
+                                 + " doesn\'t support the executable"
+                                 + " attribute", getLocation());
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chown.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chown.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/unix/Chown.java	(revision 16139)
@@ -0,0 +1,84 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * Since the initial version of this file was deveolped on the clock on
+ * an NSF grant I should say the following boilerplate:
+ *
+ * This material is based upon work supported by the National Science
+ * Foundaton under Grant No. EIA-0196404. Any opinions, findings, and
+ * conclusions or recommendations expressed in this material are those
+ * of the author and do not necessarily reflect the views of the
+ * National Science Foundation.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.unix;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Chown equivalent for unix-like environments.
+ *
+ *
+ * @since Ant 1.6
+ *
+ * @ant.task category="filesystem"
+ */
+public class Chown extends AbstractAccessTask {
+
+    private boolean haveOwner = false;
+
+    /**
+     * Chown task for setting file and directory permissions.
+     */
+    public Chown() {
+        super.setExecutable("chown");
+    }
+
+    /**
+     * Set the owner atribute.
+     *
+     * @param owner    The new owner for the file(s) or directory(ies)
+     */
+    public void setOwner(String owner) {
+        createArg().setValue(owner);
+        haveOwner = true;
+    }
+
+    /**
+     * Ensure that all the required arguments and other conditions have
+     * been set.
+     */
+    protected void checkConfiguration() {
+        if (!haveOwner) {
+            throw new BuildException("Required attribute owner not set in"
+                                     + " chown", getLocation());
+        }
+        super.checkConfiguration();
+    }
+
+    /**
+     * We don't want to expose the executable atribute, so overide it.
+     *
+     * @param e User supplied executable that we won't accept.
+     */
+    public void setExecutable(String e) {
+        throw new BuildException(getTaskType()
+                                 + " doesn\'t support the executable"
+                                 + " attribute", getLocation());
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/unix/Symlink.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/unix/Symlink.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/unix/Symlink.java	(revision 16139)
@@ -0,0 +1,731 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * Since the initial version of this file was developed on the clock on
+ * an NSF grant I should say the following boilerplate:
+ *
+ * This material is based upon work supported by the National Science
+ * Foundaton under Grant No. EIA-0196404. Any opinions, findings, and
+ * conclusions or recommendations expressed in this material are those
+ * of the author and do not necessarily reflect the views of the
+ * National Science Foundation.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.unix;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileNotFoundException;
+
+import java.util.Vector;
+import java.util.Properties;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+
+import org.apache.tools.ant.util.FileUtils;
+
+import org.apache.tools.ant.types.FileSet;
+
+import org.apache.tools.ant.taskdefs.Execute;
+
+/**
+ * Creates, Records and Restores Symlinks.
+ *
+ * <p> This task performs several related operations. In the most trivial
+ * and default usage, it creates a link specified in the link attribute to
+ * a resource specified in the resource attribute. The second usage of this
+ * task is to traverse a directory structure specified by a fileset,
+ * and write a properties file in each included directory describing the
+ * links found in that directory. The third usage is to traverse a
+ * directory structure specified by a fileset, looking for properties files
+ * (also specified as included in the fileset) and recreate the links
+ * that have been previously recorded for each directory. Finally, it can be
+ * used to remove a symlink without deleting the file or directory it points
+ * to.
+ *
+ * <p> Examples of use:
+ *
+ * <p> Make a link named &quot;foo&quot; to a resource named
+ * &quot;bar.foo&quot; in subdir:
+ * <pre>
+ * &lt;symlink link=&quot;${dir.top}/foo&quot; resource=&quot;${dir.top}/subdir/bar.foo&quot;/&gt;
+ * </pre>
+ *
+ * <p> Record all links in subdir and its descendants in files named
+ * &quot;dir.links&quot;
+ * <pre>
+ * &lt;symlink action=&quot;record&quot; linkfilename=&quot;dir.links&quot;&gt;
+ *    &lt;fileset dir=&quot;${dir.top}&quot; includes=&quot;subdir&#47;**&quot; /&gt;
+ * &lt;/symlink&gt;
+ * </pre>
+ *
+ * <p> Recreate the links recorded in the previous example:
+ * <pre>
+ * &lt;symlink action=&quot;recreate&quot;&gt;
+ *    &lt;fileset dir=&quot;${dir.top}&quot; includes=&quot;subdir&#47;**&#47;dir.links&quot; /&gt;
+ * &lt;/symlink&gt;
+ * </pre>
+ *
+ * <p> Delete a link named &quot;foo&quot; to a resource named
+ * &quot;bar.foo&quot; in subdir:
+ * <pre>
+ * &lt;symlink action=&quot;delete&quot; link=&quot;${dir.top}/foo&quot;/&gt;
+ * </pre>
+ *
+ * <p><strong>LIMITATIONS:</strong> Because Java has no direct support for
+ * handling symlinks this task divines them by comparing canonical and
+ * absolute paths. On non-unix systems this may cause false positives.
+ * Furthermore, any operating system on which the command
+ * <code>ln -s link resource</code> is not a valid command on the command line
+ * will not be able to use action= &quot;delete&quot;, action=&quot;single&quot;
+ * or action=&quot;recreate&quot;, but action=&quot;record&quot; should still
+ * work. Finally, the lack of support for symlinks in Java means that all links
+ * are recorded as links to the <strong>canonical</strong> resource name.
+ * Therefore the link: <code>link --> subdir/dir/../foo.bar</code> will be
+ * recorded as <code>link=subdir/foo.bar</code> and restored as
+ * <code>link --> subdir/foo.bar</code>.
+ *
+ */
+public class Symlink extends Task {
+
+    // Attributes with setter methods:
+    private String resource;
+    private String link;
+    private String action;
+    private Vector fileSets = new Vector();
+    private String linkFileName;
+    private boolean overwrite;
+    private boolean failonerror;
+
+    private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+    /**
+     * Initialize the task.
+     * @throws BuildException on error.
+     */
+    public void init() throws BuildException {
+        super.init();
+        failonerror = true;   // default behavior is to fail on an error
+        overwrite = false;    // default behavior is to not overwrite
+        action = "single";      // default behavior is make a single link
+        fileSets = new Vector();
+    }
+
+    /**
+     * The standard method for executing any task.
+     * @throws BuildException on error.
+     */
+    public void execute() throws BuildException {
+        try {
+            if (action.equals("single")) {
+                doLink(resource, link);
+            } else if (action.equals("delete")) {
+                try {
+                    log("Removing symlink: " + link);
+                    Symlink.deleteSymlink(link);
+                } catch (FileNotFoundException fnfe) {
+                    handleError(fnfe.toString());
+                } catch (IOException ioe) {
+                    handleError(ioe.toString());
+                }
+            } else if (action.equals("recreate")) {
+                Properties listOfLinks;
+                Enumeration keys;
+
+                if (fileSets.size() == 0) {
+                    handleError("File set identifying link file(s) "
+                                + "required for action recreate");
+                    return;
+                }
+                listOfLinks = loadLinks(fileSets);
+
+                keys = listOfLinks.keys();
+
+                while (keys.hasMoreElements()) {
+                    link = (String) keys.nextElement();
+                    resource = listOfLinks.getProperty(link);
+                    // handle the case where the link exists
+                    // and points to a directory (bug 25181)
+                    try {
+                        File test = new File(link);
+                        File testRes = new File(resource);
+                        if (!FILE_UTILS.isSymbolicLink(test.getParentFile(),
+                                               test.getName())) {
+                            doLink(resource, link);
+                        } else {
+                            if (!test.getCanonicalPath().
+                                equals(testRes.getCanonicalPath())) {
+                                Symlink.deleteSymlink(link);
+                                doLink(resource, link);
+                            } // else the link exists, do nothing
+                        }
+                    } catch (IOException ioe) {
+                        handleError("IO exception while creating "
+                                    + "link");
+                    }
+                }
+            } else if (action.equals("record")) {
+                Vector vectOfLinks;
+                Hashtable byDir = new Hashtable();
+                Enumeration links, dirs;
+
+                if (fileSets.size() == 0) {
+                    handleError("Fileset identifying links to "
+                                + "record required");
+                    return;
+                }
+                if (linkFileName == null) {
+                    handleError("Name of file to record links in "
+                                + "required");
+                    return;
+                }
+                // fill our vector with file objects representing
+                // links (canonical)
+                vectOfLinks = findLinks(fileSets);
+
+                // create a hashtable to group them by parent directory
+                links = vectOfLinks.elements();
+                while (links.hasMoreElements()) {
+                    File thisLink = (File) links.nextElement();
+                    String parent = thisLink.getParent();
+                    if (byDir.containsKey(parent)) {
+                        ((Vector) byDir.get(parent)).addElement(thisLink);
+                    } else {
+                        byDir.put(parent, new Vector());
+                        ((Vector) byDir.get(parent)).addElement(thisLink);
+                    }
+                }
+                // write a Properties file in each directory
+                dirs = byDir.keys();
+                while (dirs.hasMoreElements()) {
+                    String dir = (String) dirs.nextElement();
+                    Vector linksInDir = (Vector) byDir.get(dir);
+                    Properties linksToStore = new Properties();
+                    Enumeration eachlink = linksInDir.elements();
+                    File writeTo;
+
+                    // fill up a Properties object with link and resource
+                    // names
+                    while (eachlink.hasMoreElements()) {
+                        File alink = (File) eachlink.nextElement();
+                        try {
+                            linksToStore.put(alink.getName(),
+                                             alink.getCanonicalPath());
+                        } catch (IOException ioe) {
+                            handleError("Couldn't get canonical "
+                                        + "name of a parent link");
+                        }
+                    }
+                    // Get a place to record what we are about to write
+                    writeTo = new File(dir + File.separator
+                                       + linkFileName);
+
+                    writePropertyFile(linksToStore, writeTo,
+                                      "Symlinks from " + writeTo.getParent());
+                }
+            } else {
+                handleError("Invalid action specified in symlink");
+            }
+        } finally {
+            // return all variables to their default state,
+            // ready for the next invocation.
+            resource = null;
+            link = null;
+            action = "single";
+            fileSets = new Vector();
+            linkFileName = null;
+            overwrite = false;
+            failonerror = true;
+        }
+    }
+
+    /* ********************************************************** *
+     *               Begin Attribute Setter Methods               *
+     * ********************************************************** */
+
+    /**
+     * The setter for the overwrite attribute. If set to false (default)
+     * the task will not overwrite existing links, and may stop the build
+     * if a link already exists depending on the setting of failonerror.
+     *
+     * @param owrite If true overwrite existing links.
+     */
+    public void setOverwrite(boolean owrite) {
+        this.overwrite = owrite;
+    }
+
+    /**
+     * The setter for the failonerror attribute. If set to true (default)
+     * the entire build fails upon error. If set to false, the error is
+     * logged and the build will continue.
+     *
+     * @param foe    If true throw BuildException on error else log it.
+     */
+    public void setFailOnError(boolean foe) {
+        this.failonerror = foe;
+    }
+
+    /**
+     * Set the action to be performed.  May be
+     * &quot;single&quot;, &quot;multi&quot; or &quot;record&quot;.
+     *
+     * @param typ    The action to perform.
+     */
+    public void setAction(String typ) {
+        this.action = typ;
+    }
+
+    /**
+     * Set the same of the link.
+     * Only used when action = &quot;single&quot;.
+     *
+     * @param lnk     The name for the link.
+     */
+    public void setLink(String lnk) {
+        this.link = lnk;
+    }
+
+    /**
+     * Set the name of the resource to which a link should be created.
+     * Only used when action = &quot;single&quot;.
+     *
+     * @param src      The resource to be linked.
+     */
+    public void setResource(String src) {
+        this.resource = src;
+    }
+
+    /**
+     * Set the name of the file to which links will be written.
+     * Only used when action = &quot;record&quot;.
+     *
+     * @param lf      The name of the file to write links to.
+     */
+    public void setLinkfilename(String lf) {
+        this.linkFileName = lf;
+    }
+
+    /**
+     * Add a fileset to this task.
+     *
+     * @param set      The fileset to add.
+     */
+    public void addFileset(FileSet set) {
+        fileSets.addElement(set);
+    }
+
+    /* ********************************************************** *
+     *                 Begin Public Utility Methods               *
+     * ********************************************************** */
+
+    /**
+     * Deletes a symlink without deleting the resource it points to.
+     *
+     * <p>This is a convenience method that simply invokes
+     * <code>deleteSymlink(java.io.File)</code>.
+     *
+     * @param path    A string containing the path of the symlink to delete.
+     *
+     * @throws FileNotFoundException   When the path results in a
+     *                                   <code>File</code> that doesn't exist.
+     * @throws IOException             If calls to <code>File.rename</code>
+     *                                   or <code>File.delete</code> fail.
+     */
+    public static void deleteSymlink(String path)
+        throws IOException, FileNotFoundException {
+        deleteSymlink(new File(path));
+    }
+
+    /**
+     * Deletes a symlink without deleting the resource it points to.
+     *
+     * <p>This is a utility method that removes a unix symlink without removing
+     * the resource that the symlink points to. If it is accidentally invoked
+     * on a real file, the real file will not be harmed, but an exception
+     * will be thrown when the deletion is attempted. This method works by
+     * getting the canonical path of the link, using the canonical path to
+     * rename the resource (breaking the link) and then deleting the link.
+     * The resource is then returned to its original name inside a finally
+     * block to ensure that the resource is unharmed even in the event of
+     * an exception.
+     *
+     * @param linkfil    A <code>File</code> object of the symlink to delete.
+     *
+     * @throws FileNotFoundException   When the path results in a
+     *                                   <code>File</code> that doesn't exist.
+     * @throws IOException             If calls to <code>File.rename</code>,
+     *                                   <code>File.delete</code> or
+     *                                   <code>File.getCanonicalPath</code>
+     *                                   fail.
+     */
+
+    public static void deleteSymlink(File linkfil)
+        throws IOException, FileNotFoundException {
+
+        if (!linkfil.exists()) {
+            throw new FileNotFoundException("No such symlink: " + linkfil);
+        }
+        // find the resource of the existing link:
+        String canstr = linkfil.getCanonicalPath();
+        File canfil = new File(canstr);
+
+        // rename the resource, thus breaking the link:
+        String parentStr = canfil.getParent();
+        File parentDir = new File(parentStr);
+
+        File temp = FILE_UTILS.createTempFile("symlink", ".tmp", parentDir);
+        temp.deleteOnExit();
+        try {
+            try {
+                FILE_UTILS.rename(canfil, temp);
+            } catch (IOException e) {
+                throw new IOException("Couldn't rename resource when "
+                                      + "attempting to delete " + linkfil);
+            }
+            // delete the (now) broken link:
+            if (!linkfil.delete()) {
+                throw new IOException("Couldn't delete symlink: " + linkfil
+                                      + " (was it a real file? is this not a "
+                                      + "UNIX system?)");
+            }
+        } finally {
+            // return the resource to its original name:
+            try {
+                FILE_UTILS.rename(temp, canfil);
+            } catch (IOException e) {
+                throw new IOException("Couldn't return resource " + temp
+                                      + " to its original name: " + canstr
+                                      + "\n THE RESOURCE'S NAME ON DISK HAS "
+                                      + "BEEN CHANGED BY THIS ERROR!\n");
+            }
+        }
+    }
+
+    /* ********************************************************** *
+     *                    Begin Private Methods                   *
+     * ********************************************************** */
+
+    /**
+     * Writes a properties file.
+     *
+     * This method use <code>Properties.store</code>
+     * and thus report exceptions that occur while writing the file.
+     *
+     * @param properties     The properties object to be written.
+     * @param propertyfile   The File to write to.
+     * @param comment        The comment to place at the head of the file.
+     */
+
+    private void writePropertyFile(Properties properties,
+                                   File propertyfile,
+                                   String comment)
+        throws BuildException {
+
+        FileOutputStream fos = null;
+        try {
+            fos = new FileOutputStream(propertyfile);
+            properties.store(fos, comment);
+        } catch (IOException ioe) {
+            throw new BuildException(ioe, getLocation());
+        } finally {
+            FILE_UTILS.close(fos);
+        }
+    }
+
+    /**
+     * Handles errors correctly based on the setting of failonerror.
+     *
+     * @param msg    The message to log, or include in the
+     *                  <code>BuildException</code>.
+     */
+    private void handleError(String msg) {
+        if (failonerror) {
+            throw new BuildException(msg);
+        }
+        log(msg);
+    }
+
+    /**
+     * Conducts the actual construction of a link.
+     *
+     * <p> The link is constructed by calling <code>Execute.runCommand</code>.
+     *
+     * @param resource   The path of the resource we are linking to.
+     * @param link       The name of the link we wish to make.
+     */
+
+    private void doLink(String resource, String link) throws BuildException {
+
+        if (resource == null) {
+            handleError("Must define the resource to symlink to!");
+            return;
+        }
+        if (link == null) {
+            handleError("Must define the link name for symlink!");
+            return;
+        }
+        File linkfil = new File(link);
+
+        String[] cmd = new String[] {"ln", "-s", resource, link};
+
+        try {
+            if (overwrite && linkfil.exists()) {
+                deleteSymlink(linkfil);
+            }
+        } catch (FileNotFoundException fnfe) {
+            handleError("Symlink disappeared before it was deleted: " + link);
+        } catch (IOException ioe) {
+            handleError("Unable to overwrite preexisting link: " + link);
+        }
+        log(cmd[0] + " " + cmd[1] + " " + cmd[2] + " " + cmd[3]);
+        Execute.runCommand(this, cmd);
+    }
+
+    /**
+     * Simultaneously get included directories and included files.
+     *
+     * @param ds   The scanner with which to get the files and directories.
+     * @return     A vector of <code>String</code> objects containing the
+     *                included file names and directory names.
+     */
+    private Vector scanDirsAndFiles(DirectoryScanner ds) {
+        String[] files, dirs;
+        Vector list = new Vector();
+
+        ds.scan();
+
+        files = ds.getIncludedFiles();
+        dirs = ds.getIncludedDirectories();
+
+        for (int i = 0; i < files.length; i++) {
+            list.addElement(files[i]);
+        }
+        for (int i = 0; i < dirs.length; i++) {
+            list.addElement(dirs[i]);
+        }
+        return list;
+    }
+
+    /**
+     * Finds all the links in all supplied filesets.
+     *
+     * <p> This method is invoked when the action attribute is
+     * &quot;record&quot;. This means that filesets are interpreted
+     * as the directories in which links may be found.
+     *
+     * <p> The basic method followed here is, for each fileset:
+     *   <ol>
+     *      <li> Compile a list of all matches </li>
+     *      <li> Convert matches to <code>File</code> objects </li>
+     *      <li> Remove all non-symlinks using
+     *             <code>FileUtils.isSymbolicLink</code> </li>
+     *      <li> Convert all parent directories to the canonical form </li>
+     *      <li> Add the remaining links from each file set to a
+     *             master list of links unless the link is already recorded
+     *             in the list</li>
+     *   </ol>
+     *
+     * @param fileSets   The filesets specified by the user.
+     * @return           A Vector of <code>File</code> objects containing the
+     *                     links (with canonical parent directories).
+     */
+
+    private Vector findLinks(Vector fileSets) {
+        Vector result = new Vector();
+
+        // loop through the supplied file sets:
+        FSLoop: for (int i = 0; i < fileSets.size(); i++) {
+            FileSet fsTemp = (FileSet) fileSets.elementAt(i);
+            String workingDir = null;
+            Vector links = new Vector();
+            Vector linksFiles = new Vector();
+            Enumeration enumLinks;
+
+            DirectoryScanner ds;
+
+            File tmpfil = null;
+            try {
+                tmpfil = fsTemp.getDir(this.getProject());
+                workingDir = tmpfil.getCanonicalPath();
+            } catch (IOException ioe) {
+                handleError("Exception caught getting "
+                            + "canonical path of working dir " + tmpfil
+                            + " in a FileSet passed to the symlink "
+                            + "task. Further processing of this "
+                            + "fileset skipped");
+                continue FSLoop;
+            }
+            // Get a vector of String with file names that match the pattern:
+            ds = fsTemp.getDirectoryScanner(this.getProject());
+            links = scanDirsAndFiles(ds);
+
+            // Now convert the strings to File Objects
+            // using the canonical version of the working dir:
+            enumLinks = links.elements();
+
+            while (enumLinks.hasMoreElements()) {
+                linksFiles.addElement(new File(workingDir
+                                               + File.separator
+                                               + (String) enumLinks
+                                               .nextElement()));
+            }
+            // Now loop through and remove the non-links:
+
+            enumLinks = linksFiles.elements();
+
+            File parentNext, next;
+            String nameParentNext;
+            Vector removals = new Vector();
+            while (enumLinks.hasMoreElements()) {
+                next = (File) enumLinks.nextElement();
+                nameParentNext = next.getParent();
+
+                parentNext = new File(nameParentNext);
+                try {
+                    if (!FILE_UTILS.isSymbolicLink(parentNext, next.getName())) {
+                        removals.addElement(next);
+                    }
+                } catch (IOException ioe) {
+                    handleError("Failed checking " + next
+                                + " for symbolic link. FileSet skipped.");
+                    continue FSLoop;
+                    // Otherwise this file will be falsely recorded as a link,
+                    // if failonerror = false, hence the warn and skip.
+                }
+            }
+            enumLinks = removals.elements();
+
+            while (enumLinks.hasMoreElements()) {
+                linksFiles.removeElement(enumLinks.nextElement());
+            }
+            // Now we have what we want so add it to results, ensuring that
+            // no link is returned twice and we have a canonical reference
+            // to the link. (no symlinks in the parent dir)
+
+            enumLinks = linksFiles.elements();
+            while (enumLinks.hasMoreElements()) {
+                File temp, parent;
+                next = (File) enumLinks.nextElement();
+                try {
+                    parent = new File(next.getParent());
+                    parent = new File(parent.getCanonicalPath());
+                    temp = new File(parent, next.getName());
+                    if (!result.contains(temp)) {
+                        result.addElement(temp);
+                    }
+                } catch (IOException ioe) {
+                    handleError("IOException: " + next + " omitted");
+                }
+            }
+            // Note that these links are now specified with a full
+            // canonical path irrespective of the working dir of the
+            // file set so it is ok to mix them in the same vector.
+        }
+        return result;
+    }
+
+    /**
+     * Load the links from a properties file.
+     *
+     * <p> This method is only invoked when the action attribute is set to
+     * &quot;multi&quot;. The filesets passed in are assumed to specify the
+     * names of the property files with the link information and the
+     * subdirectories in which to look for them.
+     *
+     * <p> The basic method follwed here is, for each file set:
+     *   <ol>
+     *      <li> Get the canonical version of the dir attribute </li>
+     *      <li> Scan for properties files </li>
+     *      <li> load the contents of each properties file found. </li>
+     *   </ol>
+     *
+     * @param fileSets    The <code>FileSet</code>s for this task
+     * @return            The links to be made.
+     */
+    private Properties loadLinks(Vector fileSets) {
+        Properties finalList = new Properties();
+        Enumeration keys;
+        String key, value;
+        String[] includedFiles;
+
+        // loop through the supplied file sets:
+        FSLoop: for (int i = 0; i < fileSets.size(); i++) {
+            String workingDir;
+            FileSet fsTemp = (FileSet) fileSets.elementAt(i);
+
+            DirectoryScanner ds;
+
+            try {
+                File linelength = fsTemp.getDir(this.getProject());
+                workingDir = linelength.getCanonicalPath();
+            } catch (IOException ioe) {
+                handleError("Exception caught getting "
+                            + "canonical path of working dir "
+                            + "of a FileSet passed to symlink "
+                            + "task. FileSet skipped.");
+                continue FSLoop;
+            }
+            ds = fsTemp.getDirectoryScanner(this.getProject());
+            ds.setFollowSymlinks(false);
+            ds.scan();
+            includedFiles = ds.getIncludedFiles();
+
+            // loop through the files identified by each file set
+            // and load their contents.
+            for (int j = 0; j < includedFiles.length; j++) {
+                File inc = new File(workingDir + File.separator
+                                    + includedFiles[j]);
+                String inDir;
+                Properties propTemp = new Properties();
+
+                try {
+                    propTemp.load(new FileInputStream(inc));
+                    inDir = inc.getParent();
+                    inDir = (new File(inDir)).getCanonicalPath();
+                } catch (FileNotFoundException fnfe) {
+                    handleError("Unable to find " + includedFiles[j]
+                                + "FileSet skipped.");
+                    continue FSLoop;
+                } catch (IOException ioe) {
+                    handleError("Unable to open " + includedFiles[j]
+                                + " or its parent dir"
+                                + "FileSet skipped.");
+                    continue FSLoop;
+                }
+                keys = propTemp.keys();
+                propTemp.list(System.out);
+                // Write the contents to our master list of links
+                // This method assumes that all links are defined in
+                // terms of absolute paths, or paths relative to the
+                // working directory
+                while (keys.hasMoreElements()) {
+                    key = (String) keys.nextElement();
+                    value = propTemp.getProperty(key);
+                    finalList.put(inDir + File.separator + key, value);
+                }
+            }
+        }
+        return finalList;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSS.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSS.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSS.java	(revision 16139)
@@ -0,0 +1,688 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.vss;
+
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import java.io.File;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Execute;
+import org.apache.tools.ant.taskdefs.LogStreamHandler;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * A base class for creating tasks for executing commands on Visual SourceSafe.
+ * <p>
+ * The class extends the 'exec' task as it operates by executing the ss.exe program
+ * supplied with SourceSafe. By default the task expects ss.exe to be in the path,
+ * you can override this be specifying the ssdir attribute.
+ * </p>
+ * <p>
+ * This class provides set and get methods for 'login' and 'vsspath' attributes. It
+ * also contains constants for the flags that can be passed to SS.
+ * </p>
+ *
+ */
+public abstract class MSVSS extends Task implements MSVSSConstants {
+
+    private String ssDir = null;
+    private String vssLogin = null;
+    private String vssPath = null;
+    private String serverPath = null;
+
+    /**  Version */
+    private String version = null;
+    /**  Date */
+    private String date = null;
+    /**  Label */
+    private String label = null;
+    /**  Auto response */
+    private String autoResponse = null;
+    /**  Local path */
+    private String localPath = null;
+    /**  Comment */
+    private String comment = null;
+    /**  From label */
+    private String fromLabel = null;
+    /**  To label */
+    private String toLabel = null;
+    /**  Output file name */
+    private String outputFileName = null;
+    /**  User */
+    private String user = null;
+    /**  From date */
+    private String fromDate = null;
+    /**  To date */
+    private String toDate = null;
+    /**  History style */
+    private String style = null;
+    /**  Quiet defaults to false */
+    private boolean quiet = false;
+    /**  Recursive defaults to false */
+    private boolean recursive = false;
+    /**  Writable defaults to false */
+    private boolean writable = false;
+    /**  Fail on error defaults to true */
+    private boolean failOnError = true;
+    /**  Get local copy for checkout defaults to true */
+    private boolean getLocalCopy = true;
+    /**  Number of days offset for History */
+    private int numDays = Integer.MIN_VALUE;
+    /**  Date format for History */
+    private DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.SHORT);
+    /**  Timestamp for retreived files */
+    private CurrentModUpdated timestamp = null;
+    /**  Behaviour for writable files */
+    private WritableFiles writableFiles = null;
+
+    /**
+     * Each sub-class must implemnt this method and return the constructed
+     * command line to be executed. It is up to the sub-task to determine the
+     * required attrubutes and their order.
+     * @return    The Constructed command line.
+     */
+    abstract Commandline buildCmdLine();
+
+    /**
+     * Directory where <code>ss.exe</code> resides.
+     * By default the task expects it to be in the PATH.
+     * @param  dir  The directory containing ss.exe.
+     */
+    public final void setSsdir(String dir) {
+        this.ssDir = Project.translatePath(dir);
+    }
+
+    /**
+     * Login to use when accessing VSS, formatted as "username,password".
+     * <p>
+     * You can omit the password if your database is not password protected.
+     * If you have a password and omit it, Ant will hang.
+     * @param  vssLogin  The login string to use.
+     */
+    public final void setLogin(final String vssLogin) {
+        this.vssLogin = vssLogin;
+    }
+
+    /**
+     * SourceSafe path which specifies the project/file(s) you wish to perform
+     * the action on.
+     * <p>
+     * A prefix of 'vss://' will be removed if specified.
+     * @param  vssPath  The VSS project path.
+     * @ant.attribute group="required"
+     */
+    public final void setVsspath(final String vssPath) {
+        String projectPath;
+        if (vssPath.startsWith("vss://")) { //$NON-NLS-1$
+            projectPath = vssPath.substring(5);
+        } else {
+            projectPath = vssPath;
+        }
+
+        if (projectPath.startsWith(PROJECT_PREFIX)) {
+            this.vssPath = projectPath;
+        } else {
+            this.vssPath = PROJECT_PREFIX + projectPath;
+        }
+    }
+
+    /**
+     * Directory where <code>srssafe.ini</code> resides.
+     * @param  serverPath  The path to the VSS server.
+     */
+    public final void setServerpath(final String serverPath) {
+        this.serverPath = serverPath;
+    }
+
+    /**
+     * Indicates if the build should fail if the Sourcesafe command does. Defaults to true.
+     * @param failOnError True if task should fail on any error.
+     */
+    public final void setFailOnError(final boolean failOnError) {
+        this.failOnError = failOnError;
+    }
+
+    /**
+     * Executes the task. <br>
+     * Builds a command line to execute ss.exe and then calls Exec's run method
+     * to execute the command line.
+     * @throws BuildException if the command cannot execute.
+     */
+    public void execute() throws BuildException {
+        int result = 0;
+        Commandline commandLine = buildCmdLine();
+        result = run(commandLine);
+        if (Execute.isFailure(result) && getFailOnError()) {
+            String msg = "Failed executing: " + formatCommandLine(commandLine)
+                     + " With a return code of " + result;
+            throw new BuildException(msg, getLocation());
+        }
+    }
+
+    // Special setters for the sub-classes
+
+    protected void setInternalComment(final String comment) {
+        this.comment = comment;
+    }
+
+    protected void setInternalAutoResponse(final String autoResponse) {
+        this.autoResponse = autoResponse;
+    }
+
+    protected void setInternalDate(final String date) {
+        this.date = date;
+    }
+
+    protected void setInternalDateFormat(final DateFormat dateFormat) {
+        this.dateFormat = dateFormat;
+    }
+
+    protected void setInternalFailOnError(final boolean failOnError) {
+        this.failOnError = failOnError;
+    }
+
+    protected void setInternalFromDate(final String fromDate) {
+        this.fromDate = fromDate;
+    }
+
+    protected void setInternalFromLabel(final String fromLabel) {
+        this.fromLabel = fromLabel;
+    }
+
+    protected void setInternalLabel(final String label) {
+        this.label = label;
+    }
+
+    protected void setInternalLocalPath(final String localPath) {
+        this.localPath = localPath;
+    }
+
+    protected void setInternalNumDays(final int numDays) {
+        this.numDays = numDays;
+    }
+
+    protected void setInternalOutputFilename(final String outputFileName) {
+        this.outputFileName = outputFileName;
+    }
+
+    protected void setInternalQuiet(final boolean quiet) {
+        this.quiet = quiet;
+    }
+
+    protected void setInternalRecursive(final boolean recursive) {
+        this.recursive = recursive;
+    }
+
+    protected void setInternalStyle(final String style) {
+        this.style = style;
+    }
+
+    protected void setInternalToDate(final String toDate) {
+        this.toDate = toDate;
+    }
+
+    protected void setInternalToLabel(final String toLabel) {
+        this.toLabel = toLabel;
+    }
+
+    protected void setInternalUser(final String user) {
+        this.user = user;
+    }
+
+    protected void setInternalVersion(final String version) {
+        this.version = version;
+    }
+
+    protected void setInternalWritable(final boolean writable) {
+        this.writable = writable;
+    }
+
+    protected void setInternalFileTimeStamp(final CurrentModUpdated timestamp) {
+        this.timestamp = timestamp;
+    }
+
+    protected void setInternalWritableFiles(final WritableFiles writableFiles) {
+        this.writableFiles = writableFiles;
+    }
+
+    protected void setInternalGetLocalCopy(final boolean getLocalCopy) {
+        this.getLocalCopy = getLocalCopy;
+    }
+
+    /**
+     * Gets the sscommand string. "ss" or "c:\path\to\ss"
+     * @return    The path to ss.exe or just ss if sscommand is not set.
+     */
+    protected String getSSCommand() {
+        if (ssDir == null) {
+            return SS_EXE;
+        }
+        return ssDir.endsWith(File.separator) ? ssDir + SS_EXE : ssDir
+                 + File.separator + SS_EXE;
+    }
+
+    /**
+     * Gets the vssserverpath string.
+     * @return    null if vssserverpath is not set.
+     */
+    protected String getVsspath() {
+        return vssPath;
+    }
+
+    /**
+     * Gets the quiet string. -O-
+     * @return An empty string if quiet is not set or is false.
+     */
+    protected String getQuiet() {
+        return quiet ? FLAG_QUIET : "";
+    }
+
+    /**
+     * Gets the recursive string. "-R"
+     * @return An empty string if recursive is not set or is false.
+     */
+    protected String getRecursive() {
+        return recursive ? FLAG_RECURSION : "";
+    }
+
+    /**
+     * Gets the writable string. "-W"
+     * @return An empty string if writable is not set or is false.
+     */
+    protected String getWritable() {
+        return writable ? FLAG_WRITABLE : "";
+    }
+
+    /**
+     * Gets the label string. "-Lbuild1"
+     * Max label length is 32 chars
+     * @return An empty string if label is not set.
+     */
+    protected String getLabel() {
+    	String shortLabel="";
+        if (label != null && label.length() > 0) {
+                shortLabel = FLAG_LABEL + getShortLabel();
+        } 
+        return shortLabel;
+    }
+    /**
+     * Return at most the 30 first chars of the label, logging a warning message about the truncation
+     * @return at most the 30 first chars of the label
+     */
+    private String getShortLabel() {
+    	String shortLabel;
+        if (label !=  null && label.length() > 31) {
+        	shortLabel = this.label.substring(0, 30);
+            log("Label is longer than 31 characters, truncated to: " + shortLabel, Project.MSG_WARN);
+        } else {
+        	shortLabel = label;
+        }
+        return shortLabel;
+    }
+    /**
+     * Gets the style string. "-Lbuild1"
+     * @return An empty string if label is not set.
+     */
+    protected String getStyle() {
+        return style != null ? style : "";
+    }
+
+    /**
+     * Gets the version string. Returns the first specified of version "-V1.0",
+     * date "-Vd01.01.01", label "-Vlbuild1".
+     * @return An empty string if a version, date and label are not set.
+     */
+    protected String getVersionDateLabel() {
+    	String versionDateLabel = "";
+        if (version != null) {
+        	versionDateLabel = FLAG_VERSION + version;
+        } else if (date != null) {
+        	versionDateLabel = FLAG_VERSION_DATE + date;
+        } else {
+            // Use getShortLabel() so labels longer then 30 char are truncated
+            // and the user is warned
+            String shortLabel = getShortLabel();
+            if (shortLabel != null && !shortLabel.equals("")) {
+            	versionDateLabel = FLAG_VERSION_LABEL + shortLabel;
+            }
+        }
+        return versionDateLabel;
+    }
+
+    /**
+     * Gets the version string.
+     * @return An empty string if a version is not set.
+     */
+    protected String getVersion() {
+        return version != null ? FLAG_VERSION + version : "";
+    }
+
+    /**
+     * Gets the localpath string. "-GLc:\source" <p>
+     * The localpath is created if it didn't exist.
+     * @return An empty string if localpath is not set.
+     */
+    protected String getLocalpath() {
+    	String lclPath = ""; //set to empty str if no local path return
+    	if (localPath != null) {
+    		//make sure m_LocalDir exists, create it if it doesn't
+            File dir = getProject().resolveFile(localPath);
+            if (!dir.exists()) {
+                boolean done = dir.mkdirs();
+                if (!done) {
+                    String msg = "Directory " + localPath + " creation was not "
+                            + "successful for an unknown reason";
+                    throw new BuildException(msg, getLocation());
+                }
+                getProject().log("Created dir: " + dir.getAbsolutePath());
+            }
+            lclPath = FLAG_OVERRIDE_WORKING_DIR + localPath;
+    	}
+        return lclPath;
+    }
+
+    /**
+     * Gets the comment string. "-Ccomment text"
+     * @return A comment of "-" if comment is not set.
+     */
+    protected String getComment() {
+        return comment != null ? FLAG_COMMENT + comment : FLAG_COMMENT + "-";
+    }
+
+    /**
+     * Gets the auto response string. This can be Y "-I-Y" or N "-I-N".
+     * @return The default value "-I-" if autoresponse is not set.
+     */
+    protected String getAutoresponse() {
+        if (autoResponse == null) {
+            return FLAG_AUTORESPONSE_DEF;
+        } else if (autoResponse.equalsIgnoreCase("Y")) {
+            return FLAG_AUTORESPONSE_YES;
+        } else if (autoResponse.equalsIgnoreCase("N")) {
+            return FLAG_AUTORESPONSE_NO;
+        } else {
+            return FLAG_AUTORESPONSE_DEF;
+        }
+    }
+
+    /**
+     * Gets the login string. This can be user and password, "-Yuser,password"
+     * or just user "-Yuser".
+     * @return An empty string if login is not set.
+     */
+    protected String getLogin() {
+        return vssLogin != null ? FLAG_LOGIN + vssLogin : "";
+    }
+
+    /**
+     * Gets the output file string. "-Ooutput.file"
+     * @return An empty string if user is not set.
+     */
+    protected String getOutput() {
+        return outputFileName != null ? FLAG_OUTPUT + outputFileName : "";
+    }
+
+    /**
+     * Gets the user string. "-Uusername"
+     * @return An empty string if user is not set.
+     */
+    protected String getUser() {
+        return user != null ? FLAG_USER + user : "";
+    }
+
+    /**
+     * Gets the version string. This can be to-from "-VLbuild2~Lbuild1", from
+     * "~Lbuild1" or to "-VLbuild2".
+     * @return An empty string if neither tolabel or fromlabel are set.
+     */
+    protected String getVersionLabel() {
+        if (fromLabel == null && toLabel == null) {
+            return "";
+        }
+        if (fromLabel != null && toLabel != null) {
+            if (fromLabel.length() > 31) {
+                fromLabel = fromLabel.substring(0, 30);
+                log("FromLabel is longer than 31 characters, truncated to: "
+                    + fromLabel, Project.MSG_WARN);
+            }
+            if (toLabel.length() > 31) {
+                toLabel = toLabel.substring(0, 30);
+                log("ToLabel is longer than 31 characters, truncated to: "
+                    + toLabel, Project.MSG_WARN);
+            }
+            return FLAG_VERSION_LABEL + toLabel + VALUE_FROMLABEL + fromLabel;
+        } else if (fromLabel != null) {
+            if (fromLabel.length() > 31) {
+                fromLabel = fromLabel.substring(0, 30);
+                log("FromLabel is longer than 31 characters, truncated to: "
+                    + fromLabel, Project.MSG_WARN);
+            }
+            return FLAG_VERSION + VALUE_FROMLABEL + fromLabel;
+        } else {
+            if (toLabel.length() > 31) {
+                toLabel = toLabel.substring(0, 30);
+                log("ToLabel is longer than 31 characters, truncated to: "
+                    + toLabel, Project.MSG_WARN);
+            }
+            return FLAG_VERSION_LABEL + toLabel;
+        }
+    }
+
+    /**
+     * Gets the Version date string.
+     * @return An empty string if neither Todate or from date are set.
+     * @throws BuildException
+     */
+    protected String getVersionDate() throws BuildException {
+        if (fromDate == null && toDate == null
+            && numDays == Integer.MIN_VALUE) {
+            return "";
+        }
+        if (fromDate != null && toDate != null) {
+            return FLAG_VERSION_DATE + toDate + VALUE_FROMDATE + fromDate;
+        } else if (toDate != null && numDays != Integer.MIN_VALUE) {
+            try {
+                return FLAG_VERSION_DATE + toDate + VALUE_FROMDATE
+                        + calcDate(toDate, numDays);
+            } catch (ParseException ex) {
+                String msg = "Error parsing date: " + toDate;
+                throw new BuildException(msg, getLocation());
+            }
+        } else if (fromDate != null && numDays != Integer.MIN_VALUE) {
+            try {
+                return FLAG_VERSION_DATE + calcDate(fromDate, numDays)
+                        + VALUE_FROMDATE + fromDate;
+            } catch (ParseException ex) {
+                String msg = "Error parsing date: " + fromDate;
+                throw new BuildException(msg, getLocation());
+            }
+        } else {
+            return fromDate != null ? FLAG_VERSION + VALUE_FROMDATE
+                    + fromDate : FLAG_VERSION_DATE + toDate;
+        }
+    }
+
+    /**
+     * Builds and returns the -G- flag if required.
+     * @return An empty string if get local copy is true.
+     */
+    protected String getGetLocalCopy() {
+        return (!getLocalCopy) ? FLAG_NO_GET : "";
+    }
+
+    /**
+     * Gets the value of the fail on error flag.
+     * @return    True if the FailOnError flag has been set or if 'writablefiles=skip'.
+     */
+    private boolean getFailOnError() {
+        return getWritableFiles().equals(WRITABLE_SKIP) ? false : failOnError;
+    }
+
+
+    /**
+     * Gets the value set for the FileTimeStamp.
+     * if it equals "current" then we return -GTC
+     * if it equals "modified" then we return -GTM
+     * if it equals "updated" then we return -GTU
+     * otherwise we return -GTC
+     *
+     * @return The default file time flag, if not set.
+     */
+    public String getFileTimeStamp() {
+        if (timestamp == null) {
+            return "";
+        } else if (timestamp.getValue().equals(TIME_MODIFIED)) {
+            return FLAG_FILETIME_MODIFIED;
+        } else if (timestamp.getValue().equals(TIME_UPDATED)) {
+            return FLAG_FILETIME_UPDATED;
+        } else {
+            return FLAG_FILETIME_DEF;
+        }
+    }
+
+
+    /**
+     * Gets the value to determine the behaviour when encountering writable files.
+     * @return An empty String, if not set.
+     */
+    public String getWritableFiles() {
+        if (writableFiles == null) {
+            return "";
+        } else if (writableFiles.getValue().equals(WRITABLE_REPLACE)) {
+            return FLAG_REPLACE_WRITABLE;
+        } else if (writableFiles.getValue().equals(WRITABLE_SKIP)) {
+            // ss.exe exits with '100', when files have been skipped
+            // so we have to ignore the failure
+            failOnError = false;
+            return FLAG_SKIP_WRITABLE;
+        } else {
+            return "";
+        }
+    }
+
+    /**
+     *  Sets up the required environment and executes the command line.
+     *
+     * @param  cmd  The command line to execute.
+     * @return      The return code from the exec'd process.
+     */
+    private int run(Commandline cmd) {
+        try {
+            Execute exe = new Execute(new LogStreamHandler(this,
+                    Project.MSG_INFO,
+                    Project.MSG_WARN));
+
+            // If location of ss.ini is specified we need to set the
+            // environment-variable SSDIR to this value
+            if (serverPath != null) {
+                String[] env = exe.getEnvironment();
+                if (env == null) {
+                    env = new String[0];
+                }
+                String[] newEnv = new String[env.length + 1];
+                for (int i = 0; i < env.length; i++) {
+                    newEnv[i] = env[i];
+                }
+                newEnv[env.length] = "SSDIR=" + serverPath;
+
+                exe.setEnvironment(newEnv);
+            }
+
+            exe.setAntRun(getProject());
+            exe.setWorkingDirectory(getProject().getBaseDir());
+            exe.setCommandline(cmd.getCommandline());
+            // Use the OS launcher so we get environment variables
+            exe.setVMLauncher(false);
+            return exe.execute();
+        } catch (IOException e) {
+            throw new BuildException(e, getLocation());
+        }
+    }
+
+     /**
+     * Calculates the start date for version comparison.
+     * <p>
+     * Calculates the date numDay days earlier than startdate.
+     * @param   startDate    The start date.
+     * @param   daysToAdd     The number of days to add.
+     * @return The calculated date.
+     * @throws ParseException
+     */
+    private String calcDate(String startDate, int daysToAdd) throws ParseException {
+        Date currentDate = new Date();
+        Calendar calendar = new GregorianCalendar();
+        currentDate = dateFormat.parse(startDate);
+        calendar.setTime(currentDate);
+        calendar.add(Calendar.DATE, daysToAdd);
+        return dateFormat.format(calendar.getTime());
+    }
+
+    /**
+     * Changes the password to '***' so it isn't displayed on screen if the build fails
+     *
+     * @param cmd   The command line to clean
+     * @return The command line as a string with out the password
+     */
+    private String formatCommandLine(Commandline cmd) {
+        StringBuffer sBuff = new StringBuffer(cmd.toString());
+        int indexUser = sBuff.substring(0).indexOf(FLAG_LOGIN);
+        if (indexUser > 0) {
+            int indexPass = sBuff.substring(0).indexOf(",", indexUser);
+            int indexAfterPass = sBuff.substring(0).indexOf(" ", indexPass);
+
+            for (int i = indexPass + 1; i < indexAfterPass; i++) {
+                sBuff.setCharAt(i, '*');
+            }
+        }
+        return sBuff.toString();
+    }
+
+    /**
+     * Extention of EnumeratedAttribute to hold the values for file time stamp.
+     */
+    public static class CurrentModUpdated extends EnumeratedAttribute {
+        /**
+         * Gets the list of allowable values.
+         * @return The values.
+         */
+        public String[] getValues() {
+            return new String[] {TIME_CURRENT, TIME_MODIFIED, TIME_UPDATED};
+        }
+    }
+
+    /**
+     * Extention of EnumeratedAttribute to hold the values for writable filess.
+     */
+    public static class WritableFiles extends EnumeratedAttribute {
+        /**
+         * Gets the list of allowable values.
+         * @return The values.
+         */
+        public String[] getValues() {
+            return new String[] {WRITABLE_REPLACE, WRITABLE_SKIP, WRITABLE_FAIL};
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSADD.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSADD.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSADD.java	(revision 16139)
@@ -0,0 +1,122 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.vss;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Performs Add commands to Microsoft Visual SourceSafe.
+ *
+ *
+ * @ant.task name="vssadd" category="scm"
+ */
+public class MSVSSADD extends MSVSS {
+
+    private String localPath = null;
+
+    /**
+     * Builds a command line to execute ss.
+     * @return     The constructed commandline.
+     */
+    protected Commandline buildCmdLine() {
+        Commandline commandLine = new Commandline();
+
+        // first off, make sure that we've got a command and a localPath ...
+        if (getLocalpath() == null) {
+            String msg = "localPath attribute must be set!";
+            throw new BuildException(msg, getLocation());
+        }
+
+        // build the command line from what we got the format is
+        // ss Add VSS items [-B] [-C] [-D-] [-H] [-I-] [-K] [-N] [-O] [-R] [-W] [-Y] [-?]
+        // as specified in the SS.EXE help
+        commandLine.setExecutable(getSSCommand());
+        commandLine.createArgument().setValue(COMMAND_ADD);
+
+        // VSS items
+        commandLine.createArgument().setValue(getLocalpath());
+        // -I- or -I-Y or -I-N
+        commandLine.createArgument().setValue(getAutoresponse());
+        // -R
+        commandLine.createArgument().setValue(getRecursive());
+        // -W
+        commandLine.createArgument().setValue(getWritable());
+        // -Y
+        commandLine.createArgument().setValue(getLogin());
+        // -C
+        commandLine.createArgument().setValue(getComment());
+
+        return commandLine;
+    }
+
+    /**
+     * Returns the local path without the flag.; required
+     * @todo See why this returns the local path without the flag.
+     * @return The local path value.
+     */
+    protected String getLocalpath() {
+        return localPath;
+    }
+
+    /**
+     * Add files recursively. Defaults to false.
+     *
+     * @param recursive  The boolean value for recursive.
+     */
+    public void setRecursive(boolean recursive) {
+        super.setInternalRecursive(recursive);
+    }
+
+    /**
+     * Unset the READ-ONLY flag on local copies of files added to VSS. Defaults to false.
+     *
+     * @param   writable The boolean value for writable.
+     */
+    public final void setWritable(boolean writable) {
+        super.setInternalWritable(writable);
+    }
+
+    /**
+     * Autoresponce behaviour. Valid options are Y and N.
+     *
+     * @param response The auto response value.
+     */
+    public void setAutoresponse(String response) {
+        super.setInternalAutoResponse(response);
+    }
+
+    /**
+     * Comment to apply to files added to SourceSafe.
+     *
+     * @param comment The comment to apply in SourceSafe
+     */
+    public void setComment(String comment) {
+        super.setInternalComment(comment);
+    }
+
+    /**
+     * Override the project working directory.
+     *
+     * @param   localPath   The path on disk.
+     */
+    public void setLocalpath(Path localPath) {
+        this.localPath = localPath.toString();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKIN.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKIN.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKIN.java	(revision 16139)
@@ -0,0 +1,114 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.vss;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Performs CheckIn commands to Microsoft Visual SourceSafe.
+ *
+ *
+ * @ant.task name="vsscheckin" category="scm"
+ */
+public class MSVSSCHECKIN extends MSVSS {
+
+    /**
+     * Builds a command line to execute ss.
+     * @return     The constructed commandline.
+     */
+    protected Commandline buildCmdLine() {
+        Commandline commandLine = new Commandline();
+
+        // first off, make sure that we've got a command and a vssdir ...
+        if (getVsspath() == null) {
+            String msg = "vsspath attribute must be set!";
+            throw new BuildException(msg, getLocation());
+        }
+
+        // build the command line from what we got the format is
+        // ss Checkin VSS items [-H] [-C] [-I-] [-N] [-O] [-R] [-W] [-Y] [-?]
+        // as specified in the SS.EXE help
+        commandLine.setExecutable(getSSCommand());
+        commandLine.createArgument().setValue(COMMAND_CHECKIN);
+
+        // VSS items
+        commandLine.createArgument().setValue(getVsspath());
+        // -GL
+        commandLine.createArgument().setValue(getLocalpath());
+        // -I- or -I-Y or -I-N
+        commandLine.createArgument().setValue(getAutoresponse());
+        // -R
+        commandLine.createArgument().setValue(getRecursive());
+        // -W
+        commandLine.createArgument().setValue(getWritable());
+        // -Y
+        commandLine.createArgument().setValue(getLogin());
+        // -C
+        commandLine.createArgument().setValue(getComment());
+
+        return commandLine;
+    }
+
+    /**
+     * Override the project working directory.
+     *
+     * @param   localPath   The path on disk.
+     */
+    public void setLocalpath(Path localPath) {
+        super.setInternalLocalPath(localPath.toString());
+    }
+
+    /**
+     * Check-in files recursively. Defaults to false.
+     *
+     * @param recursive  The boolean value for recursive.
+     */
+    public void setRecursive(boolean recursive) {
+        super.setInternalRecursive(recursive);
+    }
+
+    /**
+     * Unset the READ-ONLY flag on local copies of files checked-in to VSS.
+     * Defaults to false.
+     *
+     * @param   writable The boolean value for writable.
+     */
+    public final void setWritable(boolean writable) {
+        super.setInternalWritable(writable);
+    }
+
+    /**
+     * Autoresponce behaviour. Valid options are Y and N.
+     *
+     * @param response The auto response value.
+     */
+    public void setAutoresponse(String response) {
+        super.setInternalAutoResponse(response);
+    }
+
+    /**
+     * Comment to apply to files checked-in to SourceSafe.
+     *
+     * @param comment The comment to apply in SourceSafe
+     */
+    public void setComment(String comment) {
+        super.setInternalComment(comment);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKOUT.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKOUT.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCHECKOUT.java	(revision 16139)
@@ -0,0 +1,165 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.vss;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Performs CheckOut commands to Microsoft Visual SourceSafe.
+ *
+ *
+ * @ant.task name="vsscheckout" category="scm"
+ * @ant.attribute.group name="vdl" description="Only one of version, date or label"
+ */
+public class MSVSSCHECKOUT extends MSVSS {
+
+    /**
+     * Builds a command line to execute ss.
+     * @return     The constructed commandline.
+     */
+    protected Commandline buildCmdLine() {
+        Commandline commandLine = new Commandline();
+
+        // first off, make sure that we've got a command and a vssdir ...
+        if (getVsspath() == null) {
+            String msg = "vsspath attribute must be set!";
+            throw new BuildException(msg, getLocation());
+        }
+
+        // build the command line from what we got the format is
+        // ss Checkout VSS items [-G] [-C] [-H] [-I-] [-N] [-O] [-R] [-V] [-Y] [-?]
+        // as specified in the SS.EXE help
+        commandLine.setExecutable(getSSCommand());
+        commandLine.createArgument().setValue(COMMAND_CHECKOUT);
+
+        // VSS items
+        commandLine.createArgument().setValue(getVsspath());
+        // -GL
+        commandLine.createArgument().setValue(getLocalpath());
+        // -I- or -I-Y or -I-N
+        commandLine.createArgument().setValue(getAutoresponse());
+        // -R
+        commandLine.createArgument().setValue(getRecursive());
+        // -V
+        commandLine.createArgument().setValue(getVersionDateLabel());
+        // -Y
+        commandLine.createArgument().setValue(getLogin());
+        // -G
+        commandLine.createArgument().setValue(getFileTimeStamp());
+        // -GWS or -GWR
+        commandLine.createArgument().setValue(getWritableFiles());
+        // -G-
+        commandLine.createArgument().setValue(getGetLocalCopy());
+
+        return commandLine;
+    }
+
+    /**
+     * Override the project working directory.
+     *
+     * @param   localPath   The path on disk.
+     */
+    public void setLocalpath(Path localPath) {
+        super.setInternalLocalPath(localPath.toString());
+    }
+
+    /**
+     * Check-out files recursively. Defaults to false.
+     *
+     * @param recursive  The boolean value for recursive.
+     */
+    public void setRecursive(boolean recursive) {
+        super.setInternalRecursive(recursive);
+    }
+
+    /**
+     * Version to check-out.
+     *
+     * @param  version The version to check-out.
+     *
+     * @ant.attribute group="vdl"
+     */
+    public void setVersion(String version) {
+        super.setInternalVersion(version);
+    }
+
+    /**
+     * Date to check-out.
+     *
+     * @param  date The date to check-out.
+     *
+     * @ant.attribute group="vdl"
+     */
+    public void setDate(String date) {
+        super.setInternalDate(date);
+    }
+
+    /**
+     * Label to check-out.
+     *
+     * @param  label The label to check-out.
+     *
+     * @ant.attribute group="vdl"
+     */
+    public void setLabel(String label) {
+        super.setInternalLabel(label);
+    }
+
+    /**
+     * Autoresponce behaviour. Valid options are Y and N.
+     *
+     * @param response The auto response value.
+     */
+    public void setAutoresponse(String response) {
+        super.setInternalAutoResponse(response);
+    }
+
+    /**
+     * Date and time stamp given to the local copy. Defaults to <code>current</code>.
+     *
+     * @param timestamp     The file time stamping behaviour.
+     */
+    public void setFileTimeStamp(CurrentModUpdated timestamp) {
+        super.setInternalFileTimeStamp(timestamp);
+    }
+
+    /**
+     * Action taken when local files are writable. Defaults to <code>fail</code>.
+     * <p>
+     * Due to ss.exe returning with an exit code of '100' for both errors and when
+     * a file has been skipped, <code>failonerror</code> is set to false when using
+     * the <code>skip</code> option.
+     * </p>
+     *
+     * @param files     The writable files behaviour
+     */
+    public void setWritableFiles(WritableFiles files) {
+        super.setInternalWritableFiles(files);
+    }
+
+    /**
+     * Retrieve a local copy during a checkout. Defaults to true.
+     *
+     * @param get   The get local copy behaviour
+     */
+    public void setGetLocalCopy(boolean get) {
+        super.setInternalGetLocalCopy(get);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCP.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCP.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCP.java	(revision 16139)
@@ -0,0 +1,68 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.vss;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Performs CP (Change Project) commands to Microsoft Visual SourceSafe.
+ * <p>This task is typically used before a VssAdd in order to set the target project</p>
+ *
+ * @ant.task name="vsscp" category="scm"
+ */
+public class MSVSSCP extends MSVSS {
+
+    /**
+     * Builds a command line to execute ss.
+     * @return     The constructed commandline.
+     */
+    protected Commandline buildCmdLine() {
+        Commandline commandLine = new Commandline();
+
+        // first off, make sure that we've got a command and a vssdir ...
+        if (getVsspath() == null) {
+            String msg = "vsspath attribute must be set!";
+            throw new BuildException(msg, getLocation());
+        }
+
+        // build the command line from what we got the format is
+        // ss CP VSS items [-H] [-I-] [-Y] [-?]
+        // as specified in the SS.EXE help
+        commandLine.setExecutable(getSSCommand());
+        commandLine.createArgument().setValue(COMMAND_CP);
+
+        // VSS items
+        commandLine.createArgument().setValue(getVsspath());
+        // -I- or -I-Y or -I-N
+        commandLine.createArgument().setValue(getAutoresponse());
+        // -Y
+        commandLine.createArgument().setValue(getLogin());
+
+        return commandLine;
+    }
+
+    /**
+     * Autoresponce behaviour. Valid options are Y and N.
+     *
+     * @param response The auto response value.
+     */
+    public void setAutoresponse(String response) {
+        super.setInternalAutoResponse(response);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCREATE.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCREATE.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSCREATE.java	(revision 16139)
@@ -0,0 +1,90 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.vss;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Creates a new project in Microsoft Visual SourceSafe.
+ *
+ * @ant.task name="vsscreate" category="scm"
+ */
+public class MSVSSCREATE extends MSVSS {
+
+    /**
+     * Builds a command line to execute ss.
+     * @return     The constructed commandline.
+     */
+    Commandline buildCmdLine() {
+        Commandline commandLine = new Commandline();
+
+        // first off, make sure that we've got a command and a vssdir...
+        if (getVsspath() == null) {
+            String msg = "vsspath attribute must be set!";
+            throw new BuildException(msg, getLocation());
+        }
+
+        // build the command line from what we got
+        // the format is:
+        // ss Create VSS items [-C] [-H] [-I-] [-N] [-O] [-S] [-Y] [-?]
+        // as specified in the SS.EXE help
+        commandLine.setExecutable(getSSCommand());
+        commandLine.createArgument().setValue(COMMAND_CREATE);
+
+        // VSS items
+        commandLine.createArgument().setValue(getVsspath());
+        // -C
+        commandLine.createArgument().setValue(getComment());
+        // -I- or -I-Y or -I-N
+        commandLine.createArgument().setValue(getAutoresponse());
+        // -O-
+        commandLine.createArgument().setValue(getQuiet());
+        // -Y
+        commandLine.createArgument().setValue(getLogin());
+
+        return commandLine;
+    }
+
+    /**
+     * Comment to apply to the project created in SourceSafe.
+     *
+     * @param comment The comment to apply in SourceSafe
+     */
+    public void setComment(String comment) {
+        super.setInternalComment(comment);
+    }
+
+    /**
+     * Enable quiet mode. Defaults to false.
+     *
+     * @param   quiet The boolean value for quiet.
+     */
+    public final void setQuiet (boolean quiet) {
+        super.setInternalQuiet(quiet);
+    }
+
+    /**
+     * Autoresponce behaviour. Valid options are Y and N.
+     *
+     * @param response The auto response value.
+     */
+    public void setAutoresponse(String response) {
+        super.setInternalAutoResponse(response);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSConstants.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSConstants.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSConstants.java	(revision 16139)
@@ -0,0 +1,97 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.vss;
+
+/**
+ *  Holds all the constants for the VSS tasks.
+ *
+ */
+public interface MSVSSConstants {
+    /**  Constant for the thing to execute  */
+    String SS_EXE = "ss";
+    /** Dollar Sigh to prefix the project path */
+    String PROJECT_PREFIX = "$";
+
+    /**  The 'CP' command  */
+    String COMMAND_CP = "CP";
+    /**  The 'Add' command  */
+    String COMMAND_ADD = "Add";
+    /**  The 'Get' command  */
+    String COMMAND_GET = "Get";
+    /**  The 'Checkout' command  */
+    String COMMAND_CHECKOUT = "Checkout";
+    /**  The 'Checkin' command  */
+    String COMMAND_CHECKIN = "Checkin";
+    /**  The 'Label' command  */
+    String COMMAND_LABEL = "Label";
+    /**  The 'History' command  */
+    String COMMAND_HISTORY = "History";
+    /**  The 'Create' command  */
+    String COMMAND_CREATE = "Create";
+
+    /**  The brief style flag  */
+    String STYLE_BRIEF = "brief";
+    /**  The codediff style flag  */
+    String STYLE_CODEDIFF = "codediff";
+    /**  The nofile style flag  */
+    String STYLE_NOFILE = "nofile";
+    /**  The default style flag  */
+    String STYLE_DEFAULT = "default";
+
+    /**  The text for  current (default) timestamp */
+    String TIME_CURRENT = "current";
+    /**  The text for  modified timestamp */
+    String TIME_MODIFIED = "modified";
+    /**  The text for  updated timestamp */
+    String TIME_UPDATED = "updated";
+
+    /**  The text for replacing writable files   */
+    String WRITABLE_REPLACE = "replace";
+    /**  The text for skiping writable files  */
+    String WRITABLE_SKIP = "skip";
+    /**  The text for failing on writable files  */
+    String WRITABLE_FAIL = "fail";
+
+    String FLAG_LOGIN = "-Y";
+    String FLAG_OVERRIDE_WORKING_DIR = "-GL";
+    String FLAG_AUTORESPONSE_DEF = "-I-";
+    String FLAG_AUTORESPONSE_YES = "-I-Y";
+    String FLAG_AUTORESPONSE_NO = "-I-N";
+    String FLAG_RECURSION = "-R";
+    String FLAG_VERSION = "-V";
+    String FLAG_VERSION_DATE = "-Vd";
+    String FLAG_VERSION_LABEL = "-VL";
+    String FLAG_WRITABLE = "-W";
+    String VALUE_NO = "-N";
+    String VALUE_YES = "-Y";
+    String FLAG_QUIET = "-O-";
+    String FLAG_COMMENT = "-C";
+    String FLAG_LABEL = "-L";
+    String VALUE_FROMDATE = "~d";
+    String VALUE_FROMLABEL = "~L";
+    String FLAG_OUTPUT = "-O";
+    String FLAG_USER = "-U";
+    String FLAG_NO_FILE = "-F-";
+    String FLAG_BRIEF = "-B";
+    String FLAG_CODEDIFF = "-D";
+    String FLAG_FILETIME_DEF = "-GTC";
+    String FLAG_FILETIME_MODIFIED = "-GTM";
+    String FLAG_FILETIME_UPDATED = "-GTU";
+    String FLAG_REPLACE_WRITABLE = "-GWR";
+    String FLAG_SKIP_WRITABLE = "-GWS";
+    String FLAG_NO_GET = "-G-";
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSGET.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSGET.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSGET.java	(revision 16139)
@@ -0,0 +1,171 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.vss;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * Perform Get commands from Microsoft Visual SourceSafe.
+ *
+ * @ant.task name="vssget" category="scm"
+ * @ant.attribute.group name="vdl" description="Only one of version, date or label"
+ */
+public class MSVSSGET extends MSVSS {
+
+    /**
+     * Builds a command line to execute ss.
+     * @return     The constructed commandline.
+     */
+    Commandline buildCmdLine() {
+        Commandline commandLine = new Commandline();
+
+        // build the command line from what we got the format is
+        // ss Get VSS items [-G] [-H] [-I-] [-N] [-O] [-R] [-V] [-W] [-Y] [-?]
+        // as specified in the SS.EXE help
+        commandLine.setExecutable(getSSCommand());
+        commandLine.createArgument().setValue(COMMAND_GET);
+
+        if (getVsspath() == null) {
+            throw new BuildException("vsspath attribute must be set!", getLocation());
+        }
+        commandLine.createArgument().setValue(getVsspath());
+
+        // -GL
+        commandLine.createArgument().setValue(getLocalpath());
+        // -I- or -I-Y or -I-N
+        commandLine.createArgument().setValue(getAutoresponse());
+        // -O-
+        commandLine.createArgument().setValue(getQuiet());
+        // -R
+        commandLine.createArgument().setValue(getRecursive());
+        // -V
+        commandLine.createArgument().setValue(getVersionDateLabel());
+        // -W
+        commandLine.createArgument().setValue(getWritable());
+        // -Y
+        commandLine.createArgument().setValue(getLogin());
+        // -G
+        commandLine.createArgument().setValue(getFileTimeStamp());
+        // -GWS or -GWR
+        commandLine.createArgument().setValue(getWritableFiles());
+
+        return commandLine;
+    }
+
+    /**
+     * Override the project working directory.
+     *
+     * @param   localPath   The path on disk.
+     */
+    public void setLocalpath(Path localPath) {
+        super.setInternalLocalPath(localPath.toString());
+    }
+
+    /**
+     * Get files recursively. Defaults to false.
+     *
+     * @param recursive  The boolean value for recursive.
+     */
+    public final void setRecursive(boolean recursive) {
+        super.setInternalRecursive(recursive);
+    }
+
+    /**
+     * Enable quiet mode. Defaults to false.
+     *
+     * @param   quiet The boolean value for quiet.
+     */
+    public final void setQuiet (boolean quiet) {
+        super.setInternalQuiet(quiet);
+    }
+
+    /**
+     * Unset the READ-ONLY flag on files retrieved from VSS. Defaults to false.
+     *
+     * @param   writable The boolean value for writable.
+     */
+    public final void setWritable(boolean writable) {
+        super.setInternalWritable(writable);
+    }
+
+    /**
+     * Version to get.
+     *
+     * @param  version The version to get.
+     *
+     * @ant.attribute group="vdl"
+     */
+    public void setVersion(String version) {
+        super.setInternalVersion(version);
+    }
+
+    /**
+     * Date to get.
+     *
+     * @param  date The date to get.
+     *
+     * @ant.attribute group="vdl"
+     */
+    public void setDate(String date) {
+        super.setInternalDate(date);
+    }
+
+    /**
+     * Label to get.
+     *
+     * @param  label The label to get.
+     *
+     * @ant.attribute group="vdl"
+     */
+    public void setLabel(String label) {
+        super.setInternalLabel(label);
+    }
+
+    /**
+     * Autoresponce behaviour. Valid options are Y and N.
+     *
+     * @param response The auto response value.
+     */
+    public void setAutoresponse(String response) {
+        super.setInternalAutoResponse(response);
+    }
+
+    /**
+     * Date and time stamp given to the local copy. Defaults to <code>current</code>.
+     *
+     * @param timestamp     The file time stamping behaviour.
+     */
+    public void setFileTimeStamp(CurrentModUpdated timestamp) {
+        super.setInternalFileTimeStamp(timestamp);
+    }
+
+    /**
+     * Action taken when local files are writable. Defaults to <code>fail</code>.
+     * <p>
+     * Due to ss.exe returning with an exit code of '100' for both errors and when
+     * a file has been skipped, <code>failonerror</code> is set to false when using
+     * the <code>skip</code> option.
+     *
+     * @param files The action to take.
+     */
+    public void setWritableFiles(WritableFiles files) {
+        super.setInternalWritableFiles(files);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSHISTORY.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSHISTORY.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSHISTORY.java	(revision 16139)
@@ -0,0 +1,198 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.vss;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+/**
+ * Performs History commands to Microsoft Visual SourceSafe.
+ *
+ * @ant.task name="vsshistory" category="scm"
+ */
+public class MSVSSHISTORY extends MSVSS {
+
+    /**
+     * Builds a command line to execute ss.
+     * @return     The constructed commandline.
+     */
+    Commandline buildCmdLine() {
+        Commandline commandLine = new Commandline();
+
+        // first off, make sure that we've got a command and a vssdir and a label ...
+        if (getVsspath() == null) {
+            String msg = "vsspath attribute must be set!";
+            throw new BuildException(msg, getLocation());
+        }
+
+        // build the command line from what we got the format is
+        // ss History elements [-H] [-L] [-N] [-O] [-V] [-Y] [-#] [-?]
+        // as specified in the SS.EXE help
+        commandLine.setExecutable(getSSCommand());
+        commandLine.createArgument().setValue(COMMAND_HISTORY);
+
+        // VSS items
+        commandLine.createArgument().setValue(getVsspath());
+        // -I-
+        commandLine.createArgument().setValue(FLAG_AUTORESPONSE_DEF);  // ignore all errors
+        // -Vd
+        commandLine.createArgument().setValue(getVersionDate());
+        // -VL
+        commandLine.createArgument().setValue(getVersionLabel());
+        // -R
+        commandLine.createArgument().setValue(getRecursive());
+        // -B / -D / -F-
+        commandLine.createArgument().setValue(getStyle());
+        // -Y
+        commandLine.createArgument().setValue(getLogin());
+        // -O
+        commandLine.createArgument().setValue(getOutput());
+
+        return commandLine;
+    }
+
+    /**
+     * Retrieve history recursively. Defaults to false.
+     *
+     * @param recursive  The boolean value for recursive.
+     */
+    public void setRecursive(boolean recursive) {
+        super.setInternalRecursive(recursive);
+    }
+
+    /**
+     * Name of the user whose change history is generated.
+     *
+     * @param   user The username.
+     */
+    public void setUser(String user) {
+        super.setInternalUser(user);
+    }
+
+    /**
+     * Date representing the 'start' of the range.
+     *
+     * @param   fromDate    The start date.
+     */
+    public void setFromDate(String fromDate) {
+        super.setInternalFromDate(fromDate);
+    }
+
+    /**
+     * Date representing the 'end' of the range.
+     *
+     * @param   toDate    The end date.
+     */
+    public void setToDate(String toDate) {
+        super.setInternalToDate(toDate);
+    }
+
+    /**
+     * Label representing the 'start' of the range.
+     *
+     * @param   fromLabel    The start label.
+     */
+    public void setFromLabel(String fromLabel) {
+        super.setInternalFromLabel(fromLabel);
+    }
+
+    /**
+     * Label representing the 'end' of the range.
+     *
+     * @param   toLabel    The end label.
+     */
+    public void setToLabel(String toLabel) {
+        super.setInternalToLabel(toLabel);
+    }
+
+    /**
+     * Number of days for comparison.
+     * Defaults to 2 days.
+     *
+     * @param   numd    The number of days.
+     */
+    public void setNumdays(int numd) {
+        super.setInternalNumDays(numd);
+    }
+
+    /**
+     * Output file name for the history.
+     *
+     * @param   outfile The output file name.
+     */
+    public void setOutput(File outfile) {
+        if (outfile != null) {
+            super.setInternalOutputFilename(outfile.getAbsolutePath());
+        }
+    }
+
+    /**
+     * Format of dates in <code>fromDate</code and <code>toDate</code>.
+     * Used when calculating dates with the numdays attribute.
+     * This string uses the formatting rules of <code>SimpleDateFormat</code>.
+     * Defaults to <code>DateFormat.SHORT</code>.
+     *
+     * @param   dateFormat  The date format.
+     */
+    public void setDateFormat(String dateFormat) {
+        super.setInternalDateFormat(new SimpleDateFormat(dateFormat));
+    }
+
+   /**
+     * Output style. Valid options are:
+     * <ul>
+     * <li>brief:    -B Display a brief history.
+     * <li>codediff: -D Display line-by-line file changes.
+     * <li>nofile:   -F- Do not display individual file updates in the project history.
+     * <li>default:  No option specified. Display in Source Safe's default format.
+     * </ul>
+     *
+     * @param attr The history style:
+     */
+    public void setStyle(BriefCodediffNofile attr) {
+        String option = attr.getValue();
+        if (option.equals(STYLE_BRIEF)) {
+            super.setInternalStyle(FLAG_BRIEF);
+        } else if (option.equals(STYLE_CODEDIFF)) {
+            super.setInternalStyle(FLAG_CODEDIFF);
+        } else if (option.equals(STYLE_DEFAULT)) {
+            super.setInternalStyle("");
+        } else if (option.equals(STYLE_NOFILE)) {
+            super.setInternalStyle(FLAG_NO_FILE);
+        } else {
+            throw new BuildException("Style " + attr + " unknown.", getLocation());
+        }
+    }
+
+    /**
+     * Extention of EnumeratedAttribute to hold the values for style.
+     */
+    public static class BriefCodediffNofile extends EnumeratedAttribute {
+        /**
+         * Gets the list of allowable values.
+         * @return The values.
+         */
+        public String[] getValues() {
+            return new String[] {STYLE_BRIEF, STYLE_CODEDIFF, STYLE_NOFILE, STYLE_DEFAULT};
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSLABEL.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSLABEL.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/vss/MSVSSLABEL.java	(revision 16139)
@@ -0,0 +1,107 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.vss;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Performs Label commands to Microsoft Visual SourceSafe.
+ *
+ * @ant.task name="vsslabel" category="scm"
+ */
+public class MSVSSLABEL extends MSVSS {
+
+    /**
+     * Builds a command line to execute ss.
+     * @return     The constructed commandline.
+     */
+    Commandline buildCmdLine() {
+        Commandline commandLine = new Commandline();
+
+        // first off, make sure that we've got a command and a vssdir and a label ...
+        if (getVsspath() == null) {
+            throw new BuildException("vsspath attribute must be set!", getLocation());
+        }
+
+        String label = getLabel();
+        if (label.equals("")) {
+            String msg = "label attribute must be set!";
+            throw new BuildException(msg, getLocation());
+        }
+
+        // build the command line from what we got the format is
+        // ss Label VSS items [-C] [-H] [-I-] [-Llabel] [-N] [-O] [-V] [-Y] [-?]
+        // as specified in the SS.EXE help
+        commandLine.setExecutable(getSSCommand());
+        commandLine.createArgument().setValue(COMMAND_LABEL);
+
+        // VSS items
+        commandLine.createArgument().setValue(getVsspath());
+        // -C
+        commandLine.createArgument().setValue(getComment());
+        // -I- or -I-Y or -I-N
+        commandLine.createArgument().setValue(getAutoresponse());
+        // -L Specify the new label on the command line (instead of being prompted)
+        commandLine.createArgument().setValue(label);
+        // -V Label an existing file or project version
+        commandLine.createArgument().setValue(getVersion());
+        // -Y
+        commandLine.createArgument().setValue(getLogin());
+
+        return commandLine;
+    }
+
+    /**
+     * Label to apply in SourceSafe.
+     *
+     * @param  label The label to apply.
+     *
+     * @ant.attribute group="required"
+     */
+    public void setLabel(String label) {
+        super.setInternalLabel(label);
+    }
+
+    /**
+     * Version to label.
+     *
+     * @param  version The version to label.
+     */
+    public void setVersion(String version) {
+        super.setInternalVersion(version);
+    }
+
+    /**
+     * Comment to apply to files labeled in SourceSafe.
+     *
+     * @param comment The comment to apply in SourceSafe
+     */
+    public void setComment(String comment) {
+        super.setInternalComment(comment);
+    }
+
+    /**
+     * Autoresponce behaviour. Valid options are Y and N.
+     *
+     * @param response The auto response value.
+     */
+    public void setAutoresponse(String response) {
+        super.setInternalAutoResponse(response);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/windows/Attrib.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/windows/Attrib.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/optional/windows/Attrib.java	(revision 16139)
@@ -0,0 +1,152 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.windows;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.ExecuteOn;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.FileSet;
+
+import java.io.File;
+
+/**
+ * Attrib equivalent for Win32 environments.
+ * Note: Attrib parameters /S and /D are not handled.
+ *
+ *
+ * @since Ant 1.6
+ */
+public class Attrib extends ExecuteOn {
+
+    private static final String ATTR_READONLY = "R";
+    private static final String ATTR_ARCHIVE  = "A";
+    private static final String ATTR_SYSTEM   = "S";
+    private static final String ATTR_HIDDEN   = "H";
+    private static final String SET    = "+";
+    private static final String UNSET  = "-";
+
+    private boolean haveAttr = false;
+
+    public Attrib() {
+        super.setExecutable("attrib");
+        super.setParallel(false);
+    }
+
+    public void setFile(File src) {
+        FileSet fs = new FileSet();
+        fs.setFile(src);
+        addFileset(fs);
+    }
+
+    /** set the ReadOnly file attribute */
+    public void setReadonly(boolean value) {
+        addArg(value, ATTR_READONLY);
+    }
+
+    /** set the Archive file attribute */
+    public void setArchive(boolean value) {
+        addArg(value, ATTR_ARCHIVE);
+    }
+
+    /** set the System file attribute */
+    public void setSystem(boolean value) {
+        addArg(value, ATTR_SYSTEM);
+    }
+
+    /** set the Hidden file attribute */
+    public void setHidden(boolean value) {
+        addArg(value, ATTR_HIDDEN);
+    }
+
+    protected void checkConfiguration() {
+        if (!haveAttr()) {
+            throw new BuildException("Missing attribute parameter",
+                                     getLocation());
+        }
+        super.checkConfiguration();
+    }
+
+    /**
+     * @ant.attribute ignore="true"
+     */
+    public void setExecutable(String e) {
+        throw new BuildException(getTaskType()
+            + " doesn\'t support the executable attribute", getLocation());
+    }
+
+    /**
+     * @ant.attribute ignore="true"
+     */
+    public void setCommand(String e) {
+        throw new BuildException(getTaskType()
+            + " doesn\'t support the command attribute", getLocation());
+    }
+
+    /**
+     * @ant.attribute ignore="true"
+     */
+    public void setAddsourcefile(boolean b) {
+        throw new BuildException(getTaskType()
+            + " doesn\'t support the addsourcefile attribute", getLocation());
+    }
+
+    /**
+     * @ant.attribute ignore="true"
+     */
+    public void setSkipEmptyFilesets(boolean skip) {
+        throw new BuildException(getTaskType() + " doesn\'t support the "
+                                 + "skipemptyfileset attribute",
+                                 getLocation());
+    }
+
+    /**
+     * @ant.attribute ignore="true"
+     */
+    public void setParallel(boolean parallel) {
+        throw new BuildException(getTaskType()
+                                 + " doesn\'t support the parallel attribute",
+                                 getLocation());
+    }
+
+    /**
+     * @ant.attribute ignore="true"
+     */
+    public void setMaxParallel(int max) {
+        throw new BuildException(getTaskType()
+                                 + " doesn\'t support the maxparallel attribute",
+                                 getLocation());
+    }
+
+    protected boolean isValidOs() {
+        return Os.isFamily("windows") && super.isValidOs();
+    }
+
+    private static String getSignString(boolean attr) {
+        return (attr == true ? SET : UNSET);
+    }
+
+    private void addArg(boolean sign, String attribute) {
+        createArg().setValue(getSignString(sign) + attribute);
+        haveAttr = true;
+    }
+
+    private boolean haveAttr() {
+        return haveAttr;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/DefaultRmicAdapter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/DefaultRmicAdapter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/DefaultRmicAdapter.java	(revision 16139)
@@ -0,0 +1,375 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.rmic;
+
+import java.io.File;
+import java.util.Random;
+import java.util.Vector;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Rmic;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * This is the default implementation for the RmicAdapter interface.
+ * Currently, this is a cut-and-paste of the original rmic task and
+ * DefaultCopmpilerAdapter.
+ *
+ * @since Ant 1.4
+ */
+public abstract class DefaultRmicAdapter implements RmicAdapter {
+
+    private Rmic attributes;
+    private FileNameMapper mapper;
+    private static final Random rand = new Random();
+
+    public DefaultRmicAdapter() {
+    }
+
+    public void setRmic(Rmic attributes) {
+        this.attributes = attributes;
+        mapper = new RmicFileNameMapper();
+    }
+
+    public Rmic getRmic() {
+        return attributes;
+    }
+
+    protected String getStubClassSuffix() {
+        return "_Stub";
+    }
+
+    protected String getSkelClassSuffix() {
+        return "_Skel";
+    }
+
+    protected String getTieClassSuffix() {
+        return "_Tie";
+    }
+
+    /**
+     * This implementation returns a mapper that may return up to two
+     * file names.
+     *
+     * <ul>
+     *   <li>for JRMP it will return *_getStubClassSuffix (and
+     *   *_getSkelClassSuffix if JDK 1.1 is used)</li>
+     *
+     *   <li>for IDL it will return a random name, causing &lt;rmic&gt; to
+     *     always recompile.</li>
+     *
+     *   <li>for IIOP it will return _*_getStubClassSuffix for
+     *   interfaces and _*_getStubClassSuffix for non-interfaces (and
+     *   determine the interface and create _*_Stub from that).</li>
+     * </ul>
+     */
+    public FileNameMapper getMapper() {
+        return mapper;
+    }
+
+    /**
+     * The CLASSPATH this rmic process will use.
+     */
+    public Path getClasspath() {
+        return getCompileClasspath();
+    }
+
+    /**
+     * Builds the compilation classpath.
+     */
+    protected Path getCompileClasspath() {
+        Path classpath = new Path(attributes.getProject());
+        // add dest dir to classpath so that previously compiled and
+        // untouched classes are on classpath
+        classpath.setLocation(attributes.getBase());
+
+        // Combine the build classpath with the system classpath, in an
+        // order determined by the value of build.sysclasspath
+
+        Path cp = attributes.getClasspath();
+        if (cp == null) {
+            cp = new Path(attributes.getProject());
+        }
+        if (attributes.getIncludeantruntime()) {
+            classpath.addExisting(cp.concatSystemClasspath("last"));
+        } else {
+            classpath.addExisting(cp.concatSystemClasspath("ignore"));
+        }
+
+        if (attributes.getIncludejavaruntime()) {
+            classpath.addJavaRuntime();
+        }
+        return classpath;
+    }
+
+    /**
+     * setup rmic argument for rmic.
+     */
+    protected Commandline setupRmicCommand() {
+        return setupRmicCommand(null);
+    }
+
+    /**
+     * setup rmic argument for rmic.
+     *
+     * @param options additional parameters needed by a specific
+     *                implementation.
+     */
+    protected Commandline setupRmicCommand(String[] options) {
+        Commandline cmd = new Commandline();
+
+        if (options != null) {
+            for (int i = 0; i < options.length; i++) {
+                cmd.createArgument().setValue(options[i]);
+            }
+        }
+
+        Path classpath = getCompileClasspath();
+
+        cmd.createArgument().setValue("-d");
+        cmd.createArgument().setFile(attributes.getBase());
+
+        if (attributes.getExtdirs() != null) {
+            if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
+                /*
+                 * XXX - This doesn't mix very well with build.systemclasspath,
+                 */
+                classpath.addExtdirs(attributes.getExtdirs());
+            } else {
+                cmd.createArgument().setValue("-extdirs");
+                cmd.createArgument().setPath(attributes.getExtdirs());
+            }
+        }
+
+        cmd.createArgument().setValue("-classpath");
+        cmd.createArgument().setPath(classpath);
+
+        String stubVersion = attributes.getStubVersion();
+        if (null != stubVersion) {
+            if ("1.1".equals(stubVersion)) {
+                cmd.createArgument().setValue("-v1.1");
+            } else if ("1.2".equals(stubVersion)) {
+                cmd.createArgument().setValue("-v1.2");
+            } else {
+                cmd.createArgument().setValue("-vcompat");
+            }
+        }
+
+        if (null != attributes.getSourceBase()) {
+            cmd.createArgument().setValue("-keepgenerated");
+        }
+
+        if (attributes.getIiop()) {
+            attributes.log("IIOP has been turned on.", Project.MSG_INFO);
+            cmd.createArgument().setValue("-iiop");
+            if (attributes.getIiopopts() != null) {
+                attributes.log("IIOP Options: " + attributes.getIiopopts(),
+                               Project.MSG_INFO);
+                cmd.createArgument().setValue(attributes.getIiopopts());
+            }
+        }
+
+        if (attributes.getIdl())  {
+            cmd.createArgument().setValue("-idl");
+            attributes.log("IDL has been turned on.", Project.MSG_INFO);
+            if (attributes.getIdlopts() != null) {
+                cmd.createArgument().setValue(attributes.getIdlopts());
+                attributes.log("IDL Options: " + attributes.getIdlopts(),
+                               Project.MSG_INFO);
+            }
+        }
+
+        if (attributes.getDebug()) {
+            cmd.createArgument().setValue("-g");
+        }
+
+        cmd.addArguments(attributes.getCurrentCompilerArgs());
+
+        logAndAddFilesToCompile(cmd);
+        return cmd;
+     }
+
+    /**
+     * Logs the compilation parameters, adds the files to compile and logs the
+     * &qout;niceSourceList&quot;
+     */
+    protected void logAndAddFilesToCompile(Commandline cmd) {
+        Vector compileList = attributes.getCompileList();
+
+        attributes.log("Compilation " + cmd.describeArguments(),
+                       Project.MSG_VERBOSE);
+
+        StringBuffer niceSourceList = new StringBuffer("File");
+        if (compileList.size() != 1) {
+            niceSourceList.append("s");
+        }
+        niceSourceList.append(" to be compiled:");
+
+        for (int i = 0; i < compileList.size(); i++) {
+            String arg = (String) compileList.elementAt(i);
+            cmd.createArgument().setValue(arg);
+            niceSourceList.append("    " + arg);
+        }
+
+        attributes.log(niceSourceList.toString(), Project.MSG_VERBOSE);
+    }
+
+    /**
+     * Mapper that may return up to two file names.
+     *
+     * <ul>
+     *   <li>for JRMP it will return *_getStubClassSuffix (and
+     *   *_getSkelClassSuffix if JDK 1.1 is used)</li>
+     *
+     *   <li>for IDL it will return a random name, causing <rmic> to
+     *     always recompile.</li>
+     *
+     *   <li>for IIOP it will return _*_getStubClassSuffix for
+     *   interfaces and _*_getStubClassSuffix for non-interfaces (and
+     *   determine the interface and create _*_Stub from that).</li>
+     * </ul>
+     */
+    private class RmicFileNameMapper implements FileNameMapper {
+
+        RmicFileNameMapper() {
+        }
+
+        /**
+         * Empty implementation.
+         */
+        public void setFrom(String s) {
+        }
+        /**
+         * Empty implementation.
+         */
+        public void setTo(String s) {
+        }
+
+        public String[] mapFileName(String name) {
+            if (name == null
+                || !name.endsWith(".class")
+                || name.endsWith(getStubClassSuffix() + ".class")
+                || name.endsWith(getSkelClassSuffix() + ".class")
+                || name.endsWith(getTieClassSuffix() + ".class")) {
+                // Not a .class file or the one we'd generate
+                return null;
+            }
+
+            // we know that name.endsWith(".class")
+            String base = name.substring(0, name.length() - 6);
+
+            String classname = base.replace(File.separatorChar, '.');
+            if (attributes.getVerify()
+                && !attributes.isValidRmiRemote(classname)) {
+                return null;
+            }
+
+            /*
+             * fallback in case we have trouble loading the class or
+             * don't know how to handle it (there is no easy way to
+             * know what IDL mode would generate.
+             *
+             * This is supposed to make Ant always recompile the
+             * class, as a file of that name should not exist.
+             */
+            String[] target = new String[] {name + ".tmp." + rand.nextLong()};
+
+            if (!attributes.getIiop() && !attributes.getIdl()) {
+                // JRMP with simple naming convention
+                if ("1.2".equals(attributes.getStubVersion())) {
+                    target = new String[] {
+                        base + getStubClassSuffix() + ".class"
+                    };
+                } else {
+                    target = new String[] {
+                        base + getStubClassSuffix() + ".class",
+                        base + getSkelClassSuffix() + ".class",
+                    };
+                }
+            } else if (!attributes.getIdl()) {
+                int lastSlash = base.lastIndexOf(File.separatorChar);
+
+                String dirname = "";
+                /*
+                 * I know, this is not necessary, but I prefer it explicit (SB)
+                 */
+                int index = -1;
+                if (lastSlash == -1) {
+                    // no package
+                    index = 0;
+                } else {
+                    index = lastSlash + 1;
+                    dirname = base.substring(0, index);
+                }
+
+                String filename = base.substring(index);
+
+                try {
+                    Class c = attributes.getLoader().loadClass(classname);
+
+                    if (c.isInterface()) {
+                        // only stub, no tie
+                        target = new String[] {
+                            dirname + "_" + filename + getStubClassSuffix()
+                            + ".class"
+                        };
+                    } else {
+                        /*
+                         * stub is derived from implementation,
+                         * tie from interface name.
+                         */
+                        Class interf = attributes.getRemoteInterface(c);
+                        String iName = interf.getName();
+                        String iDir = "";
+                        int iIndex = -1;
+                        int lastDot = iName.lastIndexOf(".");
+                        if (lastDot == -1) {
+                            // no package
+                            iIndex = 0;
+                        } else {
+                            iIndex = lastDot + 1;
+                            iDir = iName.substring(0, iIndex);
+                            iDir = iDir.replace('.', File.separatorChar);
+                        }
+
+                        target = new String[] {
+                            dirname + "_" + filename + getTieClassSuffix()
+                            + ".class",
+                            iDir + "_" + iName.substring(iIndex)
+                            + getStubClassSuffix() + ".class"
+                        };
+                    }
+                } catch (ClassNotFoundException e) {
+                    attributes.log("Unable to verify class " + classname
+                                   + ". It could not be found.",
+                                   Project.MSG_WARN);
+                } catch (NoClassDefFoundError e) {
+                    attributes.log("Unable to verify class " + classname
+                                   + ". It is not defined.", Project.MSG_WARN);
+                } catch (Throwable t) {
+                    attributes.log("Unable to verify class " + classname
+                                   + ". Loading caused Exception: "
+                                   + t.getMessage(), Project.MSG_WARN);
+                }
+            }
+            return target;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/KaffeRmic.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/KaffeRmic.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/KaffeRmic.java	(revision 16139)
@@ -0,0 +1,97 @@
+/*
+ * Copyright  2001-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.rmic;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.ExecuteJava;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * The implementation of the rmic for Kaffe
+ *
+ * @since Ant 1.4
+ */
+public class KaffeRmic extends DefaultRmicAdapter {
+    // sorted by newest Kaffe version first
+    private static final String[] RMIC_CLASSNAMES = new String[] {
+        "gnu.classpath.tools.rmi.rmic.RMIC",
+        // pre Kaffe 1.1.5
+        "gnu.java.rmi.rmic.RMIC",
+        // pre Kaffe 1.1.2
+        "kaffe.rmi.rmic.RMIC",
+    };
+
+    /**
+     * the name of this adapter for users to select
+     */
+    public static final String COMPILER_NAME = "kaffe";
+
+
+    public boolean execute() throws BuildException {
+        getRmic().log("Using Kaffe rmic", Project.MSG_VERBOSE);
+        Commandline cmd = setupRmicCommand();
+
+        Class c = getRmicClass();
+        if (c == null) {
+            StringBuffer buf = new StringBuffer("Cannot use Kaffe rmic, as it"
+                                                + " is not available.  None"
+                                                + " of ");
+            for (int i = 0; i < RMIC_CLASSNAMES.length; i++) {
+                if (i != 0) {
+                    buf.append(", ");
+                }
+                
+                buf.append(RMIC_CLASSNAMES[i]);
+            }
+            buf.append(" have been found. A common solution is to set the"
+                       + " environment variable JAVA_HOME or CLASSPATH.");
+            throw new BuildException(buf.toString(),
+                                     getRmic().getLocation());
+        }
+
+        cmd.setExecutable(c.getName());
+        ExecuteJava ej = new ExecuteJava();
+        ej.setJavaCommand(cmd);
+        return ej.fork(getRmic()) == 0;
+    }
+
+    /**
+     * test for kaffe being on the system
+     * @return true if kaffe is on the current classpath
+     */
+    public static boolean isAvailable() {
+        return getRmicClass() != null;
+    }
+
+    /**
+     * tries to load Kaffe RMIC and falls back to the older class name
+     * if necessary.
+     *
+     * @return null if neither class can get loaded.
+     */
+    private static Class getRmicClass() {
+        for (int i = 0; i < RMIC_CLASSNAMES.length; i++) {
+            try {
+                return Class.forName(RMIC_CLASSNAMES[i]);
+            } catch (ClassNotFoundException cnfe) {
+            }
+        }
+        return null;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/RmicAdapter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/RmicAdapter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/RmicAdapter.java	(revision 16139)
@@ -0,0 +1,62 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.rmic;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Rmic;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileNameMapper;
+
+/**
+ * The interface that all rmic adapters must adher to.
+ *
+ * <p>A rmic adapter is an adapter that interprets the rmic's
+ * parameters in preperation to be passed off to the compiler this
+ * adapter represents.  As all the necessary values are stored in the
+ * Rmic task itself, the only thing all adapters need is the rmic
+ * task, the execute command and a parameterless constructor (for
+ * reflection).</p>
+ *
+ * @since Ant 1.4
+ */
+
+public interface RmicAdapter {
+
+    /**
+     * Sets the rmic attributes, which are stored in the Rmic task.
+     */
+    void setRmic(Rmic attributes);
+
+    /**
+     * Executes the task.
+     *
+     * @return has the compilation been successful
+     */
+    boolean execute() throws BuildException;
+
+    /**
+     * Maps source class files to the files generated by this rmic
+     * implementation.
+     */
+    FileNameMapper getMapper();
+
+    /**
+     * The CLASSPATH this rmic process will use.
+     */
+    Path getClasspath();
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/RmicAdapterFactory.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/RmicAdapterFactory.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/RmicAdapterFactory.java	(revision 16139)
@@ -0,0 +1,89 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.rmic;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+
+/**
+ * Creates the necessary rmic adapter, given basic criteria.
+ *
+ * @since 1.4
+ */
+public class RmicAdapterFactory {
+
+    /** This is a singleton -- can't create instances!! */
+    private RmicAdapterFactory() {
+    }
+
+    /**
+     * Based on the parameter passed in, this method creates the necessary
+     * factory desired.
+     *
+     * <p>The current mapping for rmic names are as follows:</p>
+     * <ul><li>sun = SUN's rmic
+     * <li>kaffe = Kaffe's rmic
+     * <li><i>a fully quallified classname</i> = the name of a rmic
+     * adapter
+     * </ul>
+     *
+     * @param rmicType either the name of the desired rmic, or the
+     * full classname of the rmic's adapter.
+     * @param task a task to log through.
+     * @throws BuildException if the rmic type could not be resolved into
+     * a rmic adapter.
+     */
+    public static RmicAdapter getRmic(String rmicType, Task task)
+        throws BuildException {
+        if (rmicType.equalsIgnoreCase("sun")) {
+            return new SunRmic();
+        } else if (rmicType.equalsIgnoreCase("kaffe")) {
+            return new KaffeRmic();
+        } else if (rmicType.equalsIgnoreCase("weblogic")) {
+            return new WLRmic();
+        }
+        return resolveClassName(rmicType);
+    }
+
+    /**
+     * Tries to resolve the given classname into a rmic adapter.
+     * Throws a fit if it can't.
+     *
+     * @param className The fully qualified classname to be created.
+     * @throws BuildException This is the fit that is thrown if className
+     * isn't an instance of RmicAdapter.
+     */
+    private static RmicAdapter resolveClassName(String className)
+        throws BuildException {
+        try {
+            Class c = Class.forName(className);
+            Object o = c.newInstance();
+            return (RmicAdapter) o;
+        } catch (ClassNotFoundException cnfe) {
+            throw new BuildException(className + " can\'t be found.", cnfe);
+        } catch (ClassCastException cce) {
+            throw new BuildException(className + " isn\'t the classname of "
+                                     + "a rmic adapter.", cce);
+        } catch (Throwable t) {
+            // for all other possibilities
+            throw new BuildException(className + " caused an interesting "
+                                     + "exception.", t);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/SunRmic.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/SunRmic.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/SunRmic.java	(revision 16139)
@@ -0,0 +1,78 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.rmic;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.LogOutputStream;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * The implementation of the rmic for SUN's JDK.
+ *
+ * @since Ant 1.4
+ */
+public class SunRmic extends DefaultRmicAdapter {
+
+    public boolean execute() throws BuildException {
+        getRmic().log("Using SUN rmic compiler", Project.MSG_VERBOSE);
+        Commandline cmd = setupRmicCommand();
+
+        // Create an instance of the rmic, redirecting output to
+        // the project log
+        LogOutputStream logstr = new LogOutputStream(getRmic(),
+                                                     Project.MSG_WARN);
+
+        try {
+            Class c = Class.forName("sun.rmi.rmic.Main");
+            Constructor cons
+                = c.getConstructor(new Class[]  {OutputStream.class, String.class});
+            Object rmic = cons.newInstance(new Object[] {logstr, "rmic"});
+
+            Method doRmic = c.getMethod("compile",
+                                        new Class [] {String[].class});
+            Boolean ok =
+                (Boolean) doRmic.invoke(rmic,
+                                       (new Object[] {cmd.getArguments()}));
+            return ok.booleanValue();
+        } catch (ClassNotFoundException ex) {
+            throw new BuildException("Cannot use SUN rmic, as it is not "
+                                     + "available.  A common solution is to "
+                                     + "set the environment variable "
+                                     + "JAVA_HOME or CLASSPATH.",
+                                     getRmic().getLocation());
+        } catch (Exception ex) {
+            if (ex instanceof BuildException) {
+                throw (BuildException) ex;
+            } else {
+                throw new BuildException("Error starting SUN rmic: ",
+                                         ex, getRmic().getLocation());
+            }
+        } finally {
+            try {
+                logstr.close();
+            } catch (IOException e) {
+                throw new BuildException(e);
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/WLRmic.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/WLRmic.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/taskdefs/rmic/WLRmic.java	(revision 16139)
@@ -0,0 +1,84 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.rmic;
+
+import java.lang.reflect.Method;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * The implementation of the rmic for WebLogic
+ *
+ * @since Ant 1.4
+ */
+public class WLRmic extends DefaultRmicAdapter {
+
+    public boolean execute() throws BuildException {
+        getRmic().log("Using WebLogic rmic", Project.MSG_VERBOSE);
+        Commandline cmd = setupRmicCommand(new String[] {"-noexit"});
+
+        AntClassLoader loader = null;
+        try {
+            // Create an instance of the rmic
+            Class c = null;
+            if (getRmic().getClasspath() == null) {
+                c = Class.forName("weblogic.rmic");
+            } else {
+                loader
+                    = getRmic().getProject().createClassLoader(getRmic().getClasspath());
+                c = Class.forName("weblogic.rmic", true, loader);
+            }
+            Method doRmic = c.getMethod("main",
+                                        new Class [] {String[].class});
+            doRmic.invoke(null, new Object[] {cmd.getArguments()});
+            return true;
+        } catch (ClassNotFoundException ex) {
+            throw new BuildException("Cannot use WebLogic rmic, as it is not "
+                                     + "available.  A common solution is to "
+                                     + "set the environment variable "
+                                     + "CLASSPATH.", getRmic().getLocation());
+        } catch (Exception ex) {
+            if (ex instanceof BuildException) {
+                throw (BuildException) ex;
+            } else {
+                throw new BuildException("Error starting WebLogic rmic: ", ex,
+                                         getRmic().getLocation());
+            }
+        } finally {
+            if (loader != null) {
+                loader.cleanup();
+            }
+        }
+    }
+
+    /**
+     * Get the suffix for the rmic stub classes
+     */
+    public String getStubClassSuffix() {
+        return "_WLStub";
+    }
+
+    /**
+     * Get the suffix for the rmic skeleton classes
+     */
+    public String getSkelClassSuffix() {
+        return "_WLSkel";
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/AbstractFileSet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/AbstractFileSet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/AbstractFileSet.java	(revision 16139)
@@ -0,0 +1,695 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Stack;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.FileScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.selectors.AndSelector;
+import org.apache.tools.ant.types.selectors.ContainsSelector;
+import org.apache.tools.ant.types.selectors.DateSelector;
+import org.apache.tools.ant.types.selectors.DependSelector;
+import org.apache.tools.ant.types.selectors.DepthSelector;
+import org.apache.tools.ant.types.selectors.ExtendSelector;
+import org.apache.tools.ant.types.selectors.FileSelector;
+import org.apache.tools.ant.types.selectors.DifferentSelector;
+import org.apache.tools.ant.types.selectors.FilenameSelector;
+import org.apache.tools.ant.types.selectors.TypeSelector;
+import org.apache.tools.ant.types.selectors.MajoritySelector;
+import org.apache.tools.ant.types.selectors.NoneSelector;
+import org.apache.tools.ant.types.selectors.NotSelector;
+import org.apache.tools.ant.types.selectors.OrSelector;
+import org.apache.tools.ant.types.selectors.PresentSelector;
+import org.apache.tools.ant.types.selectors.ContainsRegexpSelector;
+import org.apache.tools.ant.types.selectors.SelectSelector;
+import org.apache.tools.ant.types.selectors.SelectorContainer;
+import org.apache.tools.ant.types.selectors.SelectorScanner;
+import org.apache.tools.ant.types.selectors.SizeSelector;
+import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector;
+
+/**
+ * Class that holds an implicit patternset and supports nested
+ * patternsets and creates a DirectoryScanner using these patterns.
+ *
+ * <p>Common base class for DirSet and FileSet.</p>
+ *
+ */
+public abstract class AbstractFileSet extends DataType
+    implements Cloneable, SelectorContainer {
+
+    private PatternSet defaultPatterns = new PatternSet();
+    private Vector additionalPatterns = new Vector();
+    private Vector selectors = new Vector();
+
+    private File dir;
+    private boolean useDefaultExcludes = true;
+    private boolean isCaseSensitive = true;
+    private boolean followSymlinks = true;
+
+    /**
+     * Construct a new <code>AbstractFileSet</code>.
+     */
+    public AbstractFileSet() {
+        super();
+    }
+
+    /**
+     * Construct a new <code>AbstractFileSet</code>, shallowly cloned
+     * from the specified <code>AbstractFileSet</code>.
+     * @param fileset the <code>AbstractFileSet</code> to use as a template.
+     */
+    protected AbstractFileSet(AbstractFileSet fileset) {
+        this.dir = fileset.dir;
+        this.defaultPatterns = fileset.defaultPatterns;
+        this.additionalPatterns = fileset.additionalPatterns;
+        this.selectors = fileset.selectors;
+        this.useDefaultExcludes = fileset.useDefaultExcludes;
+        this.isCaseSensitive = fileset.isCaseSensitive;
+        this.followSymlinks = fileset.followSymlinks;
+        setProject(fileset.getProject());
+    }
+
+    /**
+     * Makes this instance in effect a reference to another instance.
+     *
+     * <p>You must not set another attribute or nest elements inside
+     * this element if you make it a reference.</p>
+     * @param r the <code>Reference</code> to use.
+     */
+    public void setRefid(Reference r) throws BuildException {
+        if (dir != null || defaultPatterns.hasPatterns(getProject())) {
+            throw tooManyAttributes();
+        }
+        if (!additionalPatterns.isEmpty()) {
+            throw noChildrenAllowed();
+        }
+        if (!selectors.isEmpty()) {
+            throw noChildrenAllowed();
+        }
+        super.setRefid(r);
+    }
+
+    /**
+     * Sets the base-directory for this instance.
+     * @param dir the directory's <code>File</code> instance.
+     */
+    public void setDir(File dir) throws BuildException {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.dir = dir;
+    }
+
+    /**
+     * Retrieves the base-directory for this instance.
+     * @param p the <code>Project</code> against which the
+     *          reference is resolved, if set.
+     * @return <code>File</code>.
+     */
+    public File getDir(Project p) {
+        return (isReference()) ? getRef(p).getDir(p) : dir;
+    }
+
+    /**
+     * Creates a nested patternset.
+     * @return <code>PatternSet</code>.
+     */
+    public PatternSet createPatternSet() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        PatternSet patterns = new PatternSet();
+        additionalPatterns.addElement(patterns);
+        return patterns;
+    }
+
+    /**
+     * Add a name entry to the include list.
+     * @return <code>PatternSet.NameEntry</code>.
+     */
+    public PatternSet.NameEntry createInclude() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        return defaultPatterns.createInclude();
+    }
+
+    /**
+     * Add a name entry to the include files list.
+     * @return <code>PatternSet.NameEntry</code>.
+     */
+    public PatternSet.NameEntry createIncludesFile() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        return defaultPatterns.createIncludesFile();
+    }
+
+    /**
+     * Add a name entry to the exclude list.
+     * @return <code>PatternSet.NameEntry</code>.
+     */
+    public PatternSet.NameEntry createExclude() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        return defaultPatterns.createExclude();
+    }
+
+    /**
+     * Add a name entry to the excludes files list.
+     * @return <code>PatternSet.NameEntry</code>.
+     */
+    public PatternSet.NameEntry createExcludesFile() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        return defaultPatterns.createExcludesFile();
+    }
+
+    /**
+     * Creates a single file fileset.
+     * @param file the single <code>File</code> included in this
+     *             <code>AbstractFileSet</code>.
+     */
+    public void setFile(File file) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        setDir(file.getParentFile());
+        createInclude().setName(file.getName());
+    }
+
+    /**
+     * Appends <code>includes</code> to the current list of include
+     * patterns.
+     *
+     * <p>Patterns may be separated by a comma or a space.</p>
+     *
+     * @param includes the <code>String</code> containing the include patterns.
+     */
+    public void setIncludes(String includes) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        defaultPatterns.setIncludes(includes);
+    }
+
+    /**
+     * Appends <code>excludes</code> to the current list of exclude
+     * patterns.
+     *
+     * <p>Patterns may be separated by a comma or a space.</p>
+     *
+     * @param excludes the <code>String</code> containing the exclude patterns.
+     */
+    public void setExcludes(String excludes) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        defaultPatterns.setExcludes(excludes);
+    }
+
+    /**
+     * Sets the <code>File</code> containing the includes patterns.
+     *
+     * @param incl <code>File</code> instance.
+     */
+     public void setIncludesfile(File incl) throws BuildException {
+         if (isReference()) {
+             throw tooManyAttributes();
+         }
+         defaultPatterns.setIncludesfile(incl);
+     }
+
+    /**
+     * Sets the <code>File</code> containing the excludes patterns.
+     *
+     * @param excl <code>File</code> instance.
+     */
+     public void setExcludesfile(File excl) throws BuildException {
+         if (isReference()) {
+             throw tooManyAttributes();
+         }
+         defaultPatterns.setExcludesfile(excl);
+     }
+
+    /**
+     * Sets whether default exclusions should be used or not.
+     *
+     * @param useDefaultExcludes <code>boolean</code>.
+     */
+    public void setDefaultexcludes(boolean useDefaultExcludes) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.useDefaultExcludes = useDefaultExcludes;
+    }
+
+    /**
+     * Whether default exclusions should be used or not.
+     * @since Ant 1.6.3
+     */
+    public boolean getDefaultexcludes() {
+        return (isReference())
+            ? getRef(getProject()).getDefaultexcludes() : useDefaultExcludes;
+    }
+
+    /**
+     * Sets case sensitivity of the file system.
+     *
+     * @param isCaseSensitive <code>boolean</code>.
+     */
+    public void setCaseSensitive(boolean isCaseSensitive) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.isCaseSensitive = isCaseSensitive;
+    }
+
+    /**
+     * Sets whether or not symbolic links should be followed.
+     *
+     * @param followSymlinks whether or not symbolic links should be followed.
+     */
+    public void setFollowSymlinks(boolean followSymlinks) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.followSymlinks = followSymlinks;
+    }
+
+    /**
+     * Find out if the fileset wants to follow symbolic links.
+     *
+     * @return <code>boolean</code> indicating whether symbolic links
+     *         should be followed.
+     *
+     * @since Ant 1.6
+     */
+    public boolean isFollowSymlinks() {
+        return (isReference())
+            ? getRef(getProject()).isFollowSymlinks() : followSymlinks;
+    }
+
+    /**
+     * Gets as descriptive as possible a name used for this datatype instance.
+     * @return <code>String</code> name.
+     */
+    protected String getDataTypeName() {
+        // look up the types in project and see if they match this class
+        Project p = getProject();
+        if (p != null) {
+            Hashtable typedefs = p.getDataTypeDefinitions();
+            for (Enumeration e = typedefs.keys(); e.hasMoreElements();) {
+                String typeName = (String) e.nextElement();
+                Class typeClass = (Class) typedefs.get(typeName);
+                if (typeClass == getClass()) {
+                    return typeName;
+                }
+            }
+        }
+        String classname = getClass().getName();
+        return classname.substring(classname.lastIndexOf('.') + 1);
+    }
+
+    /**
+     * Returns the directory scanner needed to access the files to process.
+     * @return a <code>DirectoryScanner</code> instance.
+     */
+    public DirectoryScanner getDirectoryScanner(Project p) {
+        if (isReference()) {
+            return getRef(p).getDirectoryScanner(p);
+        }
+        if (dir == null) {
+            throw new BuildException("No directory specified for "
+                                     + getDataTypeName() + ".");
+        }
+        if (!dir.exists()) {
+            throw new BuildException(dir.getAbsolutePath() + " not found.");
+        }
+        if (!dir.isDirectory()) {
+            throw new BuildException(dir.getAbsolutePath()
+                                     + " is not a directory.");
+        }
+        DirectoryScanner ds = new DirectoryScanner();
+        setupDirectoryScanner(ds, p);
+        ds.setFollowSymlinks(followSymlinks);
+        ds.scan();
+        return ds;
+    }
+
+    /**
+     * Set up the specified directory scanner against the specified project.
+     * @param ds a <code>FileScanner</code> instance.
+     * @param p an Ant <code>Project</code> instance.
+     */
+    public void setupDirectoryScanner(FileScanner ds, Project p) {
+        if (isReference()) {
+            getRef(p).setupDirectoryScanner(ds, p);
+            return;
+        }
+        if (ds == null) {
+            throw new IllegalArgumentException("ds cannot be null");
+        }
+        ds.setBasedir(dir);
+
+        final int count = additionalPatterns.size();
+        for (int i = 0; i < count; i++) {
+            Object o = additionalPatterns.elementAt(i);
+            defaultPatterns.append((PatternSet) o, p);
+        }
+        p.log(getDataTypeName() + ": Setup scanner in dir " + dir
+            + " with " + defaultPatterns, Project.MSG_DEBUG);
+
+        ds.setIncludes(defaultPatterns.getIncludePatterns(p));
+        ds.setExcludes(defaultPatterns.getExcludePatterns(p));
+        if (ds instanceof SelectorScanner) {
+            SelectorScanner ss = (SelectorScanner) ds;
+            ss.setSelectors(getSelectors(p));
+        }
+        if (useDefaultExcludes) {
+            ds.addDefaultExcludes();
+        }
+        ds.setCaseSensitive(isCaseSensitive);
+    }
+
+    /**
+     * Performs the check for circular references and returns the
+     * referenced FileSet.
+     */
+    protected AbstractFileSet getRef(Project p) {
+        if (!isChecked()) {
+            Stack stk = new Stack();
+            stk.push(this);
+            dieOnCircularReference(stk, p);
+        }
+        Object o = getRefid().getReferencedObject(p);
+        if (!getClass().isAssignableFrom(o.getClass())) {
+            throw new BuildException(getRefid().getRefId()
+                + " doesn\'t denote a " + getDataTypeName());
+        }
+        return (AbstractFileSet) o;
+    }
+
+    // SelectorContainer methods
+
+    /**
+     * Indicates whether there are any selectors here.
+     *
+     * @return whether any selectors are in this container.
+     */
+    public boolean hasSelectors() {
+        return (isReference() && getProject() != null)
+            ? getRef(getProject()).hasSelectors() : !(selectors.isEmpty());
+    }
+
+    /**
+     * Indicates whether there are any patterns here.
+     *
+     * @return whether any patterns are in this container.
+     */
+    public boolean hasPatterns() {
+        if (isReference() && getProject() != null) {
+            return getRef(getProject()).hasPatterns();
+        }
+        if (defaultPatterns.hasPatterns(getProject())) {
+            return true;
+        }
+        Enumeration e = additionalPatterns.elements();
+        while (e.hasMoreElements()) {
+            PatternSet ps = (PatternSet) e.nextElement();
+            if (ps.hasPatterns(getProject())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Gives the count of the number of selectors in this container.
+     *
+     * @return the number of selectors in this container as an <code>int</code>.
+     */
+    public int selectorCount() {
+        return (isReference() && getProject() != null)
+            ? getRef(getProject()).selectorCount() : selectors.size();
+    }
+
+    /**
+     * Returns the set of selectors as an array.
+     *
+     * @return a <code>FileSelector[]</code> of the selectors in this container.
+     */
+    public FileSelector[] getSelectors(Project p) {
+        return (isReference())
+            ? getRef(p).getSelectors(p) : (FileSelector[])(selectors.toArray(
+            new FileSelector[selectors.size()]));
+    }
+
+    /**
+     * Returns an enumerator for accessing the set of selectors.
+     *
+     * @return an <code>Enumeration</code> of selectors.
+     */
+    public Enumeration selectorElements() {
+        return (isReference() && getProject() != null)
+            ? getRef(getProject()).selectorElements() : selectors.elements();
+    }
+
+    /**
+     * Add a new selector into this container.
+     *
+     * @param selector the new <code>FileSelector</code> to add.
+     */
+    public void appendSelector(FileSelector selector) {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        selectors.addElement(selector);
+    }
+
+    /* Methods below all add specific selectors */
+
+    /**
+     * Add a "Select" selector entry on the selector list.
+     * @param selector the <code>SelectSelector</code> to add.
+     */
+    public void addSelector(SelectSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add an "And" selector entry on the selector list.
+     * @param selector the <code>AndSelector</code> to add.
+     */
+    public void addAnd(AndSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add an "Or" selector entry on the selector list.
+     * @param selector the <code>OrSelector</code> to add.
+     */
+    public void addOr(OrSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add a "Not" selector entry on the selector list.
+     * @param selector the <code>NotSelector</code> to add.
+     */
+    public void addNot(NotSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add a "None" selector entry on the selector list.
+     * @param selector the <code>NoneSelector</code> to add.
+     */
+    public void addNone(NoneSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add a majority selector entry on the selector list.
+     * @param selector the <code>MajoritySelector</code> to add.
+     */
+    public void addMajority(MajoritySelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add a selector date entry on the selector list.
+     * @param selector the <code>DateSelector</code> to add.
+     */
+    public void addDate(DateSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add a selector size entry on the selector list.
+     * @param selector the <code>SizeSelector</code> to add.
+     */
+    public void addSize(SizeSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add a DifferentSelector entry on the selector list.
+     * @param selector the <code>DifferentSelector</code> to add.
+     */
+    public void addDifferent(DifferentSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add a selector filename entry on the selector list.
+     * @param selector the <code>FilenameSelector</code> to add.
+     */
+    public void addFilename(FilenameSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add a selector type entry on the selector list.
+     * @param selector the <code>TypeSelector</code> to add.
+     */
+    public void addType(TypeSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add an extended selector entry on the selector list.
+     * @param selector the <code>ExtendSelector</code> to add.
+     */
+    public void addCustom(ExtendSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add a contains selector entry on the selector list.
+     * @param selector the <code>ContainsSelector</code> to add.
+     */
+    public void addContains(ContainsSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add a present selector entry on the selector list.
+     * @param selector the <code>PresentSelector</code> to add.
+     */
+    public void addPresent(PresentSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add a depth selector entry on the selector list.
+     * @param selector the <code>DepthSelector</code> to add.
+     */
+    public void addDepth(DepthSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add a depends selector entry on the selector list.
+     * @param selector the <code>DependSelector</code> to add.
+     */
+    public void addDepend(DependSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add a regular expression selector entry on the selector list.
+     * @param selector the <code>ContainsRegexpSelector</code> to add.
+     */
+    public void addContainsRegexp(ContainsRegexpSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add the modified selector.
+     * @param selector the <code>ModifiedSelector</code> to add.
+     * @since ant 1.6
+     */
+    public void addModified(ModifiedSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Add an arbitary selector.
+     * @param selector the <code>FileSelector</code> to add.
+     * @since Ant 1.6
+     */
+    public void add(FileSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * Returns included files as a list of semicolon-separated filenames.
+     *
+     * @return a <code>String</code> of included filenames.
+     */
+    public String toString() {
+        DirectoryScanner ds = getDirectoryScanner(getProject());
+        String[] files = ds.getIncludedFiles();
+        StringBuffer sb = new StringBuffer();
+
+        for (int i = 0; i < files.length; i++) {
+            if (i > 0) {
+                sb.append(';');
+            }
+            sb.append(files[i]);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Creates a deep clone of this instance, except for the nested
+     * selectors (the list of selectors is a shallow clone of this
+     * instance's list).
+     *
+     * @since Ant 1.6
+     */
+    public Object clone() {
+        if (isReference()) {
+            return (getRef(getProject())).clone();
+        } else {
+            try {
+                AbstractFileSet fs = (AbstractFileSet) super.clone();
+                fs.defaultPatterns = (PatternSet) defaultPatterns.clone();
+                fs.additionalPatterns = new Vector(additionalPatterns.size());
+                Enumeration e = additionalPatterns.elements();
+                while (e.hasMoreElements()) {
+                    fs.additionalPatterns
+                        .addElement(((PatternSet) e.nextElement()).clone());
+                }
+                fs.selectors = (Vector) fs.selectors.clone();
+                return fs;
+            } catch (CloneNotSupportedException e) {
+                throw new BuildException(e);
+            }
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/AntFilterReader.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/AntFilterReader.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/AntFilterReader.java	(revision 16139)
@@ -0,0 +1,134 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * An AntFileReader is a wrapper class that encloses the classname
+ * and configuration of a Configurable FilterReader.
+ *
+ */
+public final class AntFilterReader
+    extends DataType
+    implements Cloneable {
+
+    private String className;
+
+    private final Vector parameters = new Vector();
+
+    private Path classpath;
+
+    public final void setClassName(final String className) {
+        this.className = className;
+    }
+
+    public final String getClassName() {
+        return className;
+    }
+
+    public final void addParam(final Parameter param) {
+        parameters.addElement(param);
+    }
+
+    /**
+     * Set the classpath to load the FilterReader through (attribute).
+     */
+    public final void setClasspath(Path classpath) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        if (this.classpath == null) {
+            this.classpath = classpath;
+        } else {
+            this.classpath.append(classpath);
+        }
+    }
+
+    /**
+     * Set the classpath to load the FilterReader through (nested element).
+     */
+    public final Path createClasspath() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        if (this.classpath == null) {
+            this.classpath = new Path(getProject());
+        }
+        return this.classpath.createPath();
+    }
+
+    /**
+     * Get the classpath
+     */
+    public final Path getClasspath() {
+        return classpath;
+    }
+
+    /**
+     * Set the classpath to load the FilterReader through via
+     * reference (attribute).
+     */
+    public void setClasspathRef(Reference r) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        createClasspath().setRefid(r);
+    }
+
+    public final Parameter[] getParams() {
+        Parameter[] params = new Parameter[parameters.size()];
+        parameters.copyInto(params);
+        return params;
+    }
+
+    /**
+     * Makes this instance in effect a reference to another AntFilterReader
+     * instance.
+     *
+     * <p>You must not set another attribute or nest elements inside
+     * this element if you make it a reference.</p>
+     *
+     * @param r the reference to which this instance is associated
+     * @exception BuildException if this instance already has been configured.
+     */
+    public void setRefid(Reference r) throws BuildException {
+        if (!parameters.isEmpty() || className != null
+                || classpath != null) {
+            throw tooManyAttributes();
+        }
+        // change this to get the objects from the other reference
+        Object o = r.getReferencedObject(getProject());
+        if (o instanceof AntFilterReader) {
+            AntFilterReader afr = (AntFilterReader) o;
+            setClassName(afr.getClassName());
+            setClasspath(afr.getClasspath());
+            Parameter[] p = afr.getParams();
+            if (p != null) {
+                for (int i = 0; i < p.length; i++) {
+                    addParam(p[i]);
+                }
+            }
+        } else {
+            String msg = r.getRefId() + " doesn\'t refer to a FilterReader";
+            throw new BuildException(msg);
+        }
+
+        super.setRefid(r);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Assertions.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Assertions.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Assertions.java	(revision 16139)
@@ -0,0 +1,362 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * The assertion datatype. This type describes
+ * assertion settings for the &lt;java&gt; task and others.
+ * One can set the system assertions, and enable/disable those in
+ * packages and classes.
+ * Assertions can only be enabled or disabled when forking Java.
+ *
+ * Example: set system assertions and all org.apache packages except
+ * for ant, and the class org.apache.tools.ant.Main.
+ * <pre>
+ * &lt;assertions enableSystemAssertions="true" &gt;
+ *   &lt;enable package="org.apache" /&gt;
+ *   &lt;disable package="org.apache.ant" /&gt;
+ *   &lt;enable class="org.apache.tools.ant.Main"/&gt;
+ * &lt;/assertions&gt;
+ *</pre>
+ * Disable system assertions; enable those in the anonymous package
+ * <pre>
+ * &lt;assertions enableSystemAssertions="false" &gt;
+ *   &lt;enable package="..." /&gt;
+ * &lt;/assertions&gt;
+ * </pre>
+ * enable assertions in a class called Test
+ * <pre>
+ * &lt;assertions &gt;
+ *   &lt;enable class="Test" /&gt;
+ * &lt;/assertions&gt;
+ * </pre>
+ * This type is a datatype, so you can declare assertions and use them later
+ *
+ * <pre>
+ * &lt;assertions id="project.assertions" &gt;
+ *   &lt;enable project="org.apache.test" /&gt;
+ * &lt;/assertions&gt;
+ *
+ * &lt;assertions refid="project.assertions" /&gt;
+ *
+ * </pre>
+ * @since Ant 1.6
+ */
+public class Assertions extends DataType implements Cloneable {
+
+    /**
+     * enable/disable sys assertions; null means undefined
+     */
+    private Boolean enableSystemAssertions;
+
+    /**
+     * list of type BaseAssertion
+     */
+    private ArrayList assertionList = new ArrayList();
+
+
+    /**
+     * enable assertions
+     * @param assertion
+     */
+    public void addEnable(EnabledAssertion assertion) {
+        checkChildrenAllowed();
+        assertionList.add(assertion);
+    }
+
+    /**
+     * disable assertions
+     * @param assertion
+     */
+    public void addDisable(DisabledAssertion assertion) {
+        checkChildrenAllowed();
+        assertionList.add(assertion);
+    }
+
+    /**
+     * enable or disable system assertions
+     * @param enableSystemAssertions
+     */
+    public void setEnableSystemAssertions(Boolean enableSystemAssertions) {
+        checkAttributesAllowed();
+        this.enableSystemAssertions = enableSystemAssertions;
+    }
+
+    /**
+     * Set the value of the refid attribute.
+     *
+     * <p>Subclasses may need to check whether any other attributes
+     * have been set as well or child elements have been created and
+     * thus override this method. if they do the must call
+     * <code>super.setRefid</code>.</p>
+     */
+    public void setRefid(Reference ref) {
+        if (assertionList.size() > 0 || enableSystemAssertions != null) {
+            throw tooManyAttributes();
+        }
+        super.setRefid(ref);
+    }
+
+    /**
+     * get whatever we are referencing to. This could be ourself.
+     * @return the object that contains the assertion info
+     */
+    private Assertions getFinalReference() {
+        if (getRefid() == null) {
+            return this;
+        } else {
+            Object o = getRefid().getReferencedObject(getProject());
+            if (!(o instanceof Assertions)) {
+                throw new BuildException("reference is of wrong type");
+            }
+            return (Assertions) o;
+        }
+    }
+
+    /**
+     * how many assertions are made...will resolve references before returning
+     * @return total # of commands to make
+     */
+    public int size() {
+        Assertions clause = getFinalReference();
+        return clause.getFinalSize();
+    }
+
+
+    /**
+     * what is the final size of this object
+     * @return
+     */
+    private int getFinalSize() {
+        return assertionList.size() + (enableSystemAssertions != null ? 1 : 0);
+    }
+
+    /**
+     * add the assertions to a list in a format suitable
+     * for adding to a command line
+     * @param commandList
+     */
+    public void applyAssertions(List commandList) {
+        getProject().log("Applying assertions",Project.MSG_DEBUG);
+        Assertions clause = getFinalReference();
+        //do the system assertions
+        if (Boolean.TRUE.equals(clause.enableSystemAssertions)) {
+            getProject().log("Enabling system assertions", Project.MSG_DEBUG);
+            commandList.add("-enablesystemassertions");
+        } else if (Boolean.FALSE.equals(clause.enableSystemAssertions)) {
+            getProject().log("disabling system assertions", Project.MSG_DEBUG);
+            commandList.add("-disablesystemassertions");
+        }
+
+        //now any inner assertions
+        Iterator it = clause.assertionList.iterator();
+        while (it.hasNext()) {
+            BaseAssertion assertion = (BaseAssertion) it.next();
+            String arg = assertion.toCommand();
+            getProject().log("adding assertion "+arg, Project.MSG_DEBUG);
+            commandList.add(arg);
+        }
+    }
+
+    /**
+     * apply all the assertions to the command.
+     * @param command
+     */
+    public void applyAssertions(CommandlineJava command) {
+        Assertions clause = getFinalReference();
+        //do the system assertions
+        if (Boolean.TRUE.equals(clause.enableSystemAssertions)) {
+            addVmArgument(command, "-enablesystemassertions");
+        } else if (Boolean.FALSE.equals(clause.enableSystemAssertions)) {
+            addVmArgument(command, "-disablesystemassertions");
+        }
+
+        //now any inner assertions
+        Iterator it = clause.assertionList.iterator();
+        while (it.hasNext()) {
+            BaseAssertion assertion = (BaseAssertion) it.next();
+            String arg = assertion.toCommand();
+            addVmArgument(command, arg);
+        }
+    }
+
+    /**
+     * add the assertions to a list in a format suitable
+     * for adding to a command line
+     * @param commandIterator list of commands
+     */
+    public void applyAssertions(final ListIterator commandIterator) {
+        getProject().log("Applying assertions", Project.MSG_DEBUG);
+        Assertions clause = getFinalReference();
+        //do the system assertions
+        if (Boolean.TRUE.equals(clause.enableSystemAssertions)) {
+            getProject().log("Enabling system assertions", Project.MSG_DEBUG);
+            commandIterator.add("-enablesystemassertions");
+        } else if (Boolean.FALSE.equals(clause.enableSystemAssertions)) {
+            getProject().log("disabling system assertions", Project.MSG_DEBUG);
+            commandIterator.add("-disablesystemassertions");
+        }
+
+        //now any inner assertions
+        Iterator it = clause.assertionList.iterator();
+        while (it.hasNext()) {
+            BaseAssertion assertion = (BaseAssertion) it.next();
+            String arg = assertion.toCommand();
+            getProject().log("adding assertion "+arg, Project.MSG_DEBUG);
+            commandIterator.add(arg);
+        }
+    }
+
+    /**
+     * helper method to add a string JVM argument to a command
+     * @param command
+     * @param arg
+     */
+    private static void addVmArgument(CommandlineJava command, String arg) {
+        Commandline.Argument argument;
+        argument = command.createVmArgument();
+        argument.setValue(arg);
+    }
+
+    /**
+     * clone the objects.
+     * This is not a full depth clone; the list of assertions is cloned,
+     * but it does not clone the underlying assertions.
+     * @return a cli
+     * @throws CloneNotSupportedException
+     */
+    public Object clone() throws CloneNotSupportedException {
+        Assertions that = (Assertions) super.clone();
+        that.assertionList = (ArrayList) assertionList.clone();
+        return that;
+    }
+
+    /**
+     * base class for our assertion elements.
+     */
+
+    public abstract static class BaseAssertion {
+        private String packageName;
+        private String className;
+
+        /**
+         * name a class
+         * @param className
+         */
+        public void setClass(String className) {
+            this.className = className;
+        }
+
+        /**
+         * name a package
+         * @param packageName
+         */
+        public void setPackage(String packageName) {
+            this.packageName = packageName;
+        }
+
+        /**
+         * what is the class name?
+         * @return classname or null
+         * @see #setClass
+         */
+        protected String getClassName() {
+            return className;
+        }
+
+        /**
+         * what is the package name?
+         * @return package name or null
+         * @see #setPackage
+         */
+        protected String getPackageName() {
+            return packageName;
+        }
+
+        /**
+         * get the prefix used to begin the command; -ea or -da.
+         * @return prefix
+         */
+        public abstract String getCommandPrefix();
+
+        /**
+         * create a full command string from this class
+         * @throws BuildException in case of trouble
+         * @return The command string
+         */
+        public String toCommand() {
+            //catch invalidness
+            if (getPackageName() != null && getClassName() != null) {
+                throw new BuildException("Both package and class have been set");
+            }
+            StringBuffer command = new StringBuffer(getCommandPrefix());
+            //see if it is a package or a class
+            if (getPackageName() != null) {
+                //packages get a ... prefix
+                command.append(':');
+                command.append(getPackageName());
+                if (!command.toString().endsWith("...")) {
+                    //append the ... suffix if not there already
+                    command.append("...");
+                }
+            } else if (getClassName() != null) {
+                //classes just get the classname
+                command.append(':');
+                command.append(getClassName());
+            }
+            return command.toString();
+        }
+    }
+
+
+    /**
+     * an enabled assertion enables things
+     */
+    public static class EnabledAssertion extends BaseAssertion {
+        /**
+         * get the prefix used to begin the command; -ea or -da.
+         * @return prefix
+         */
+        public String getCommandPrefix() {
+            return "-ea";
+        }
+
+    }
+
+    /**
+     * A disabled assertion disables things
+     */
+    public static class DisabledAssertion extends BaseAssertion {
+        /**
+         * get the prefix used to begin the command; -ea or -da.
+         * @return prefix
+         */
+        public String getCommandPrefix() {
+            return "-da";
+        }
+
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Commandline.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Commandline.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Commandline.java	(revision 16139)
@@ -0,0 +1,602 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import java.io.File;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.LinkedList;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.util.StringUtils;
+
+
+/**
+ * Commandline objects help handling command lines specifying processes to
+ * execute.
+ *
+ * The class can be used to define a command line as nested elements or as a
+ * helper to define a command line by an application.
+ * <p>
+ * <code>
+ * &lt;someelement&gt;<br>
+ * &nbsp;&nbsp;&lt;acommandline executable="/executable/to/run"&gt;<br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 1" /&gt;<br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument line="argument_1 argument_2 argument_3" /&gt;<br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&lt;argument value="argument 4" /&gt;<br>
+ * &nbsp;&nbsp;&lt;/acommandline&gt;<br>
+ * &lt;/someelement&gt;<br>
+ * </code>
+ * The element <code>someelement</code> must provide a method
+ * <code>createAcommandline</code> which returns an instance of this class.
+ *
+ */
+public class Commandline implements Cloneable {
+
+    /**
+     * The arguments of the command
+     */
+    private Vector arguments = new Vector();
+
+    /**
+     * the program to execute
+     */
+    private String executable = null;
+
+    protected static final String DISCLAIMER =
+        StringUtils.LINE_SEP
+        + "The \' characters around the executable and arguments are"
+        + StringUtils.LINE_SEP
+        + "not part of the command."
+        + StringUtils.LINE_SEP;
+
+    /**
+     * create a command line from a string
+     * @param toProcess the line: the first element becomes the executable, the rest
+     * the arguments
+     */
+    public Commandline(String toProcess) {
+        super();
+        String[] tmp = translateCommandline(toProcess);
+        if (tmp != null && tmp.length > 0) {
+            setExecutable(tmp[0]);
+            for (int i = 1; i < tmp.length; i++) {
+                createArgument().setValue(tmp[i]);
+            }
+        }
+    }
+
+    /**
+     *  Create an empty command line
+     */
+    public Commandline() {
+        super();
+    }
+
+    /**
+     * Used for nested xml command line definitions.
+     */
+    public static class Argument extends ProjectComponent {
+
+        private String[] parts;
+
+        /**
+         * Sets a single commandline argument.
+         *
+         * @param value a single commandline argument.
+         */
+        public void setValue(String value) {
+            parts = new String[] {value};
+        }
+
+        /**
+         * Line to split into several commandline arguments.
+         *
+         * @param line line to split into several commandline arguments
+         */
+        public void setLine(String line) {
+            if (line == null) {
+                return;
+            }
+            parts = translateCommandline(line);
+        }
+
+        /**
+         * Sets a single commandline argument and treats it like a
+         * PATH - ensures the right separator for the local platform
+         * is used.
+         *
+         * @param value a single commandline argument.
+         */
+        public void setPath(Path value) {
+            parts = new String[] {value.toString()};
+        }
+
+        /**
+         * Sets a single commandline argument from a reference to a
+         * path - ensures the right separator for the local platform
+         * is used.
+         *
+         * @param value a single commandline argument.
+         */
+        public void setPathref(Reference value) {
+            Path p = new Path(getProject());
+            p.setRefid(value);
+            parts = new String[] {p.toString()};
+        }
+
+        /**
+         * Sets a single commandline argument to the absolute filename
+         * of the given file.
+         *
+         * @param value a single commandline argument.
+         */
+        public void setFile(File value) {
+            parts = new String[] {value.getAbsolutePath()};
+        }
+
+        /**
+         * Returns the parts this Argument consists of.
+         */
+        public String[] getParts() {
+            return parts;
+        }
+    }
+
+    /**
+     * Class to keep track of the position of an Argument.
+     <p>This class is there to support the srcfile and targetfile
+     elements of &lt;execon&gt; and &lt;transform&gt; - don't know
+     whether there might be additional use cases.</p> --SB
+     */
+    public class Marker {
+
+        private int position;
+        private int realPos = -1;
+
+        Marker(int position) {
+            this.position = position;
+        }
+
+        /**
+         * Return the number of arguments that preceeded this marker.
+         *
+         * <p>The name of the executable - if set - is counted as the
+         * very first argument.</p>
+         */
+        public int getPosition() {
+            if (realPos == -1) {
+                realPos = (executable == null ? 0 : 1);
+                for (int i = 0; i < position; i++) {
+                    Argument arg = (Argument) arguments.elementAt(i);
+                    realPos += arg.getParts().length;
+                }
+            }
+            return realPos;
+        }
+    }
+
+    /**
+     * Creates an argument object.
+     *
+     * <p>Each commandline object has at most one instance of the
+     * argument class.  This method calls
+     * <code>this.createArgument(false)</code>.</p>
+     *
+     * @see #createArgument(boolean)
+     * @return the argument object.
+     */
+    public Argument createArgument() {
+        return this.createArgument(false);
+    }
+
+    /**
+     * Creates an argument object and adds it to our list of args.
+     *
+     * <p>Each commandline object has at most one instance of the
+     * argument class.</p>
+     *
+     * @param insertAtStart if true, the argument is inserted at the
+     * beginning of the list of args, otherwise it is appended.
+     */
+    public Argument createArgument(boolean insertAtStart) {
+        Argument argument = new Argument();
+        if (insertAtStart) {
+            arguments.insertElementAt(argument, 0);
+        } else {
+            arguments.addElement(argument);
+        }
+        return argument;
+    }
+
+    /**
+     * Sets the executable to run. All file separators in the string
+     * are converted to the platform specific value
+     */
+    public void setExecutable(String executable) {
+        if (executable == null || executable.length() == 0) {
+            return;
+        }
+        this.executable = executable.replace('/', File.separatorChar)
+            .replace('\\', File.separatorChar);
+    }
+
+
+    /**
+     * get the executable
+     * @return the program to run -null if not yet set
+     */
+    public String getExecutable() {
+        return executable;
+    }
+
+
+    /**
+     * append the arguments to the existing command
+     * @param line an array of arguments to append
+     */
+    public void addArguments(String[] line) {
+        for (int i = 0; i < line.length; i++) {
+            createArgument().setValue(line[i]);
+        }
+    }
+
+    /**
+     * Returns the executable and all defined arguments.
+     */
+    public String[] getCommandline() {
+        List commands = new LinkedList();
+        ListIterator list = commands.listIterator();
+        addCommandToList(list);
+        final String[] result = new String[commands.size()];
+        return (String[]) commands.toArray(result);
+    }
+
+    /**
+     * add the entire command, including (optional) executable to a list
+     * @param list
+     * @since Ant 1.6
+     */
+    public void addCommandToList(ListIterator list) {
+        if (executable != null) {
+            list.add(executable);
+        }
+        addArgumentsToList(list);
+    }
+
+
+    /**
+     * Returns all arguments defined by <code>addLine</code>,
+     * <code>addValue</code> or the argument object.
+     */
+    public String[] getArguments() {
+        List result = new ArrayList(arguments.size() * 2);
+        addArgumentsToList(result.listIterator());
+        String [] res = new String[result.size()];
+        return (String[]) result.toArray(res);
+    }
+
+    /**
+     * append all the arguments to the tail of a supplied list
+     * @param list
+     * @since Ant 1.6
+     */
+    public void addArgumentsToList(ListIterator list) {
+        for (int i = 0; i < arguments.size(); i++) {
+            Argument arg = (Argument) arguments.elementAt(i);
+            String[] s = arg.getParts();
+            if (s != null) {
+                for (int j = 0; j < s.length; j++) {
+                    list.add(s[j]);
+                }
+            }
+        }
+    }
+
+
+    /**
+     * stringify operator returns the command line as a string
+     * @return the command line
+     */
+    public String toString() {
+        return toString(getCommandline());
+    }
+
+    /**
+     * Put quotes around the given String if necessary.
+     *
+     * <p>If the argument doesn't include spaces or quotes, return it
+     * as is. If it contains double quotes, use single quotes - else
+     * surround the argument by double quotes.</p>
+     *
+     * @exception BuildException if the argument contains both, single
+     *                           and double quotes.
+     */
+    public static String quoteArgument(String argument) {
+        if (argument.indexOf("\"") > -1) {
+            if (argument.indexOf("\'") > -1) {
+                throw new BuildException("Can\'t handle single and double"
+                        + " quotes in same argument");
+            } else {
+                return '\'' + argument + '\'';
+            }
+        } else if (argument.indexOf("\'") > -1 || argument.indexOf(" ") > -1) {
+            return '\"' + argument + '\"';
+        } else {
+            return argument;
+        }
+    }
+
+    /**
+     * Quotes the parts of the given array in way that makes them
+     * usable as command line arguments.
+     * @return empty string for null or no command, else every argument split
+     * by spaces and quoted by quoting rules
+     */
+    public static String toString(String [] line) {
+        // empty path return empty string
+        if (line == null || line.length == 0) {
+            return "";
+        }
+
+        // path containing one or more elements
+        final StringBuffer result = new StringBuffer();
+        for (int i = 0; i < line.length; i++) {
+            if (i > 0) {
+                result.append(' ');
+            }
+            result.append(quoteArgument(line[i]));
+        }
+        return result.toString();
+    }
+
+    /**
+     * crack a command line
+     * @param toProcess the command line to process
+     * @return the command line broken into strings.
+     * An empty or null toProcess parameter results in a zero sized array
+     */
+    public static String[] translateCommandline(String toProcess) {
+        if (toProcess == null || toProcess.length() == 0) {
+            //no command? no string
+            return new String[0];
+        }
+
+        // parse with a simple finite state machine
+
+        final int normal = 0;
+        final int inQuote = 1;
+        final int inDoubleQuote = 2;
+        int state = normal;
+        StringTokenizer tok = new StringTokenizer(toProcess, "\"\' ", true);
+        Vector v = new Vector();
+        StringBuffer current = new StringBuffer();
+        boolean lastTokenHasBeenQuoted = false;
+
+        while (tok.hasMoreTokens()) {
+            String nextTok = tok.nextToken();
+            switch (state) {
+            case inQuote:
+                if ("\'".equals(nextTok)) {
+                    lastTokenHasBeenQuoted = true;
+                    state = normal;
+                } else {
+                    current.append(nextTok);
+                }
+                break;
+            case inDoubleQuote:
+                if ("\"".equals(nextTok)) {
+                    lastTokenHasBeenQuoted = true;
+                    state = normal;
+                } else {
+                    current.append(nextTok);
+                }
+                break;
+            default:
+                if ("\'".equals(nextTok)) {
+                    state = inQuote;
+                } else if ("\"".equals(nextTok)) {
+                    state = inDoubleQuote;
+                } else if (" ".equals(nextTok)) {
+                    if (lastTokenHasBeenQuoted || current.length() != 0) {
+                        v.addElement(current.toString());
+                        current = new StringBuffer();
+                    }
+                } else {
+                    current.append(nextTok);
+                }
+                lastTokenHasBeenQuoted = false;
+                break;
+            }
+        }
+
+        if (lastTokenHasBeenQuoted || current.length() != 0) {
+            v.addElement(current.toString());
+        }
+
+        if (state == inQuote || state == inDoubleQuote) {
+            throw new BuildException("unbalanced quotes in " + toProcess);
+        }
+
+        String[] args = new String[v.size()];
+        v.copyInto(args);
+        return args;
+    }
+
+    /**
+     * size operator. This actually creates the command line, so it is not
+     * a zero cost operation.
+     * @return number of elements in the command, including the executable
+     */
+    public int size() {
+        return getCommandline().length;
+    }
+
+    /**
+     * Generate a deep clone of the contained object.
+     * @return a clone of the contained object
+     */
+    public Object clone() {
+        try {
+            Commandline c = (Commandline) super.clone();
+            c.arguments = (Vector) arguments.clone();
+            return c;
+        } catch (CloneNotSupportedException e) {
+            throw new BuildException(e);
+        }
+    }
+
+    /**
+     * Clear out the whole command line.  */
+    public void clear() {
+        executable = null;
+        arguments.removeAllElements();
+    }
+
+    /**
+     * Clear out the arguments but leave the executable in place for
+     * another operation.
+     */
+    public void clearArgs() {
+        arguments.removeAllElements();
+    }
+
+    /**
+     * Return a marker.
+     *
+     * <p>This marker can be used to locate a position on the
+     * commandline - to insert something for example - when all
+     * parameters have been set.</p>
+     */
+    public Marker createMarker() {
+        return new Marker(arguments.size());
+    }
+
+    /**
+     * Returns a String that describes the command and arguments
+     * suitable for verbose output before a call to
+     * <code>Runtime.exec(String[])<code>
+     *
+     * @since Ant 1.5
+     */
+    public String describeCommand() {
+        return describeCommand(this);
+    }
+
+    /**
+     * Returns a String that describes the arguments suitable for
+     * verbose output before a call to
+     * <code>Runtime.exec(String[])<code>
+     *
+     * @since Ant 1.5
+     */
+    public String describeArguments() {
+        return describeArguments(this);
+    }
+
+    /**
+     * Returns a String that describes the command and arguments
+     * suitable for verbose output before a call to
+     * <code>Runtime.exec(String[])<code>
+     *
+     * @since Ant 1.5
+     */
+    public static String describeCommand(Commandline line) {
+        return describeCommand(line.getCommandline());
+    }
+
+    /**
+     * Returns a String that describes the arguments suitable for
+     * verbose output before a call to
+     * <code>Runtime.exec(String[])<code>
+     *
+     * @since Ant 1.5
+     */
+    public static String describeArguments(Commandline line) {
+        return describeArguments(line.getArguments());
+    }
+
+    /**
+     * Returns a String that describes the command and arguments
+     * suitable for verbose output before a call to
+     * <code>Runtime.exec(String[])<code>.
+     *
+     * <p>This method assumes that the first entry in the array is the
+     * executable to run.</p>
+     *
+     * @since Ant 1.5
+     */
+    public static String describeCommand(String[] args) {
+        if (args == null || args.length == 0) {
+            return "";
+        }
+
+        StringBuffer buf = new StringBuffer("Executing \'");
+        buf.append(args[0]);
+        buf.append("\'");
+        if (args.length > 0) {
+            buf.append(" with ");
+            buf.append(describeArguments(args, 1));
+        } else {
+            buf.append(DISCLAIMER);
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Returns a String that describes the arguments suitable for
+     * verbose output before a call to
+     * <code>Runtime.exec(String[])<code>
+     *
+     * @since Ant 1.5
+     */
+    public static String describeArguments(String[] args) {
+        return describeArguments(args, 0);
+    }
+
+    /**
+     * Returns a String that describes the arguments suitable for
+     * verbose output before a call to
+     * <code>Runtime.exec(String[])<code>
+     *
+     * @param offset ignore entries before this index
+     *
+     * @since Ant 1.5
+     */
+    protected static String describeArguments(String[] args, int offset) {
+        if (args == null || args.length <= offset) {
+            return "";
+        }
+
+        StringBuffer buf = new StringBuffer("argument");
+        if (args.length > offset) {
+            buf.append("s");
+        }
+        buf.append(":").append(StringUtils.LINE_SEP);
+        for (int i = offset; i < args.length; i++) {
+            buf.append("\'").append(args[i]).append("\'")
+                .append(StringUtils.LINE_SEP);
+        }
+        buf.append(DISCLAIMER);
+        return buf.toString();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/CommandlineJava.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/CommandlineJava.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/CommandlineJava.java	(revision 16139)
@@ -0,0 +1,647 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Properties;
+import java.util.Vector;
+
+/**
+ * A representation of a Java command line that is
+ * a composite of 2 <tt>Commandline</tt>. One is used for the
+ * vm/options and one for the classname/arguments. It provides
+ * specific methods for a java command line.
+ *
+ */
+public class CommandlineJava implements Cloneable {
+
+    /**
+     * commands to the JVM
+     */
+    private Commandline vmCommand = new Commandline();
+    /**
+     * actual java commands
+     */
+    private Commandline javaCommand = new Commandline();
+    /**
+     * properties to add using -D
+     */
+    private SysProperties sysProperties = new SysProperties();
+    private Path classpath = null;
+    private Path bootclasspath = null;
+    private String vmVersion;
+    private String maxMemory = null;
+    /**
+     *  any assertions to make? Currently only supported in forked JVMs
+     */
+    private Assertions assertions = null;
+
+    /**
+     * Indicate whether it will execute a jar file or not, in this case
+     * the first vm option must be a -jar and the 'executable' is a jar file.
+     */
+     private boolean executeJar = false;
+
+    /**
+     * Specialized Environment class for System properties
+     */
+    public static class SysProperties extends Environment implements Cloneable {
+        Properties sys = null;
+        private Vector propertySets = new Vector();
+
+        /**
+         * get the properties as an array; this is an override of the
+         * superclass, as it evaluates all the properties
+         * @return the array of definitions; may be null
+         * @throws BuildException on error
+         */
+        public String[] getVariables() throws BuildException {
+
+            List definitions = new LinkedList();
+            ListIterator list = definitions.listIterator();
+            addDefinitionsToList(list);
+            if (definitions.size() == 0) {
+                return null;
+            } else {
+                return (String[]) definitions.toArray(new String[0]);
+            }
+        }
+
+        /**
+         * add all definitions (including property sets) to a list
+         * @param listIt list iterator supporting add method
+         */
+        public void addDefinitionsToList(ListIterator listIt) {
+            String[] props = super.getVariables();
+            if (props != null) {
+                for (int i = 0; i < props.length; i++) {
+                    listIt.add("-D" + props[i]);
+                }
+            }
+            Properties propertySetProperties = mergePropertySets();
+            for (Enumeration e = propertySetProperties.keys();
+                 e.hasMoreElements();) {
+                String key = (String) e.nextElement();
+                String value = propertySetProperties.getProperty(key);
+                listIt.add("-D" + key + "=" + value);
+            }
+        }
+
+        /**
+         * This method gets the size of the sysproperties instance. This merges all
+         * property sets, so is not an O(1) operation.
+         * @return the size of the sysproperties instance
+         */
+        public int size() {
+            Properties p = mergePropertySets();
+            return variables.size() + p.size();
+        }
+
+        /**
+         * cache the system properties and set the system properties to the
+         * new values
+         * @throws BuildException if Security prevented this operation
+         */
+        public void setSystem() throws BuildException {
+            try {
+                sys = System.getProperties();
+                Properties p = new Properties();
+                for (Enumeration e = sys.propertyNames(); e.hasMoreElements();) {
+                    String name = (String) e.nextElement();
+                    p.put(name, sys.getProperty(name));
+                }
+                p.putAll(mergePropertySets());
+                for (Enumeration e = variables.elements(); e.hasMoreElements();) {
+                    Environment.Variable v = (Environment.Variable) e.nextElement();
+                    v.validate();
+                    p.put(v.getKey(), v.getValue());
+                }
+                System.setProperties(p);
+            } catch (SecurityException e) {
+                throw new BuildException("Cannot modify system properties", e);
+            }
+        }
+
+        /**
+         * restore the system properties to the cached value
+         * @throws BuildException  if Security prevented this operation, or
+         * there was no system properties to restore
+         */
+        public void restoreSystem() throws BuildException {
+            if (sys == null) {
+                throw new BuildException("Unbalanced nesting of SysProperties");
+            }
+
+            try {
+                System.setProperties(sys);
+                sys = null;
+            } catch (SecurityException e) {
+                throw new BuildException("Cannot modify system properties", e);
+            }
+        }
+
+        /**
+         *  deep clone
+         * @return a cloned instance of SysProperties
+         * @exception CloneNotSupportedException for signature
+         */
+        public Object clone() throws CloneNotSupportedException {
+            try {
+                SysProperties c = (SysProperties) super.clone();
+                c.variables = (Vector) variables.clone();
+                c.propertySets = (Vector) propertySets.clone();
+                return c;
+            } catch (CloneNotSupportedException e) {
+                return null;
+            }
+        }
+
+        /**
+         * add a propertyset to the total set
+         * @param ps the new property set
+         */
+        public void addSyspropertyset(PropertySet ps) {
+            propertySets.addElement(ps);
+        }
+
+        /**
+         * add a propertyset to the total set
+         * @param ps the new property set
+         * @since Ant 1.6.3
+         */
+        public void addSysproperties(SysProperties ps) {
+            variables.addAll(ps.variables);
+            propertySets.addAll(ps.propertySets);
+        }        
+
+        /**
+         * merge all property sets into a single Properties object
+         * @return the merged object
+         */
+        private Properties mergePropertySets() {
+            Properties p = new Properties();
+            for (Enumeration e = propertySets.elements();
+                 e.hasMoreElements();) {
+                PropertySet ps = (PropertySet) e.nextElement();
+                p.putAll(ps.getProperties());
+            }
+            return p;
+        }
+
+    }
+
+    /**
+     * constructor uses the VM we are running on now.
+     */
+    public CommandlineJava() {
+        setVm(JavaEnvUtils.getJreExecutable("java"));
+        setVmversion(JavaEnvUtils.getJavaVersion());
+    }
+
+    /**
+     * create a new argument to the java program
+     * @return an argument to be configured
+     */
+    public Commandline.Argument createArgument() {
+        return javaCommand.createArgument();
+    }
+
+    /**
+     * create a new JVM argument
+     * @return an argument to be configured
+     */
+    public Commandline.Argument createVmArgument() {
+        return vmCommand.createArgument();
+    }
+
+    /**
+     * add a system property
+     * @param sysp a property to be set in the JVM
+     */
+    public void addSysproperty(Environment.Variable sysp) {
+        sysProperties.addVariable(sysp);
+    }
+
+    /**
+     * add a set of system properties
+     * @param sysp a set of properties
+     */
+    public void addSyspropertyset(PropertySet sysp) {
+        sysProperties.addSyspropertyset(sysp);
+    }
+
+    /**
+     * add a set of system properties
+     * @param sysp a set of properties
+     * @since Ant 1.6.3
+     */
+    public void addSysproperties(SysProperties sysp) {
+        sysProperties.addSysproperties(sysp);
+    }
+
+    /**
+     * Set the executable used to start the new JVM.
+     * @param vm the executable to use
+     */
+    public void setVm(String vm) {
+        vmCommand.setExecutable(vm);
+    }
+
+    /**
+     * Set the JVM version required.
+     * @param value the version required
+     */
+    public void setVmversion(String value) {
+        vmVersion = value;
+    }
+
+
+    /**
+     * get the current assertions
+     * @return assertions or null
+     */
+    public Assertions getAssertions() {
+        return assertions;
+    }
+
+    /**
+     *  add an assertion set to the command
+     * @param assertions assertions to make
+     */
+    public void setAssertions(Assertions assertions) {
+        this.assertions = assertions;
+    }
+
+    /**
+     * set a jar file to execute via the -jar option.
+     * @param jarpathname the pathname of the jar to execute
+     */
+    public void setJar(String jarpathname) {
+        javaCommand.setExecutable(jarpathname);
+        executeJar = true;
+    }
+
+    /**
+     * @return the pathname of the jar file to run via -jar option
+     * or <tt>null</tt> if there is no jar to run.
+     * @see #getClassname()
+     */
+    public String getJar() {
+        if (executeJar) {
+            return javaCommand.getExecutable();
+        }
+        return null;
+    }
+
+    /**
+     * set the classname to execute
+     * @param classname the fully qualified classname.
+     */
+    public void setClassname(String classname) {
+        javaCommand.setExecutable(classname);
+        executeJar = false;
+    }
+
+    /**
+     * @return the name of the class to run or <tt>null</tt> if there is no class.
+     * @see #getJar()
+     */
+    public String getClassname() {
+        if (!executeJar) {
+            return javaCommand.getExecutable();
+        }
+        return null;
+    }
+
+    /**
+     * Create a classpath.
+     * @param p the project to use to create the path in
+     * @return a path to be configured
+     */
+    public Path createClasspath(Project p) {
+        if (classpath == null) {
+            classpath = new Path(p);
+        }
+        return classpath;
+    }
+
+    /**
+     * Create a boot classpath.
+     * @param p the project to use to create the path in
+     * @return a path to be configured
+     * @since Ant 1.6
+     */
+    public Path createBootclasspath(Project p) {
+        if (bootclasspath == null) {
+            bootclasspath = new Path(p);
+        }
+        return bootclasspath;
+    }
+
+    /**
+     * Get the vm version.
+     * @return the vm version
+     */
+    public String getVmversion() {
+        return vmVersion;
+    }
+
+    /**
+     * get the command line to run a java vm.
+     * @return the list of all arguments necessary to run the vm.
+     */
+    public String[] getCommandline() {
+        //create the list
+        List commands = new LinkedList();
+        final ListIterator listIterator = commands.listIterator();
+        //fill it
+        addCommandsToList(listIterator);
+        //convert to an array
+        return (String[]) commands.toArray(new String[0]);
+    }
+
+    /**
+     * add all the commands to a list identified by the iterator passed in
+     * @param listIterator an iterator that supports the add method
+     * @since Ant1.6
+     */
+    private void addCommandsToList(final ListIterator listIterator) {
+        //create the command to run Java, including user specified options
+        getActualVMCommand().addCommandToList(listIterator);
+        // properties are part of the vm options...
+        sysProperties.addDefinitionsToList(listIterator);
+        //boot classpath
+        if (haveBootclasspath(true)) {
+            listIterator.add("-Xbootclasspath:" + bootclasspath.toString());
+        }
+
+        //main classpath
+        if (haveClasspath()) {
+            listIterator.add("-classpath");
+            listIterator.add(
+                    classpath.concatSystemClasspath("ignore").toString());
+        }
+
+        //now any assertions are added
+        if (getAssertions() != null) {
+            getAssertions().applyAssertions(listIterator);
+        }
+
+        // JDK usage command line says that -jar must be the first option, as there is
+        // a bug in JDK < 1.4 that forces the jvm type to be specified as the first
+        // option, it is appended here as specified in the docs even though there is
+        // in fact no order.
+        if (executeJar) {
+            listIterator.add("-jar");
+        }
+        // this is the classname to run as well as its arguments.
+        // in case of 'executeJar', the executable is a jar file.
+        javaCommand.addCommandToList(listIterator);
+    }
+
+    /**
+     * Specify max memory of the JVM
+     * -mx or -Xmx depending on VM version
+     * @param max the string to pass to the jvm to specifiy the max memory
+     */
+    public void setMaxmemory(String max) {
+        this.maxMemory = max;
+    }
+
+
+    /**
+     * get a string description.
+     * @return the command line as a string
+     */
+    public String toString() {
+        return Commandline.toString(getCommandline());
+    }
+
+    /**
+     * Returns a String that describes the command and arguments
+     * suitable for verbose output before a call to
+     * <code>Runtime.exec(String[])<code>
+     * @return the description string
+     * @since Ant 1.5
+     */
+    public String describeCommand() {
+        return Commandline.describeCommand(getCommandline());
+    }
+
+    /**
+     * Returns a String that describes the java command and arguments
+     * for in VM executions.
+     *
+     * <p>The class name is the executable in this context.</p>
+     * @return the description string
+     * @since Ant 1.5
+     */
+    public String describeJavaCommand() {
+        return Commandline.describeCommand(getJavaCommand());
+    }
+
+    /**
+     * Get the VM command parameters, including memory settings
+     * @return the VM command parameters
+     */
+    protected Commandline getActualVMCommand() {
+        Commandline actualVMCommand = (Commandline) vmCommand.clone();
+        if (maxMemory != null) {
+            if (vmVersion.startsWith("1.1")) {
+                actualVMCommand.createArgument().setValue("-mx" + maxMemory);
+            } else {
+                actualVMCommand.createArgument().setValue("-Xmx" + maxMemory);
+            }
+        }
+        return actualVMCommand;
+    }
+
+    /**
+     * The size of the java command line. This is a fairly intensive
+     * operation, as it has to evaluate the size of many components.
+     * @return the total number of arguments in the java command line.
+     * @see #getCommandline()
+     * @deprecated please dont use this -it effectively creates the entire command.
+     */
+    public int size() {
+        int size = getActualVMCommand().size() + javaCommand.size()
+            + sysProperties.size();
+        // classpath is "-classpath <classpath>" -> 2 args
+        if (haveClasspath()) {
+            size += 2;
+        }
+        // bootclasspath is "-Xbootclasspath:<classpath>" -> 1 arg
+        if (haveBootclasspath(true)) {
+            size++;
+        }
+        // jar execution requires an additional -jar option
+        if (executeJar) {
+            size++;
+        }
+        //assertions take up space too
+        if (getAssertions() != null) {
+            size += getAssertions().size();
+        }
+        return size;
+    }
+
+    /**
+     * get the Java command to be used.
+     * @return the java command -not a clone.
+     */
+    public Commandline getJavaCommand() {
+        return javaCommand;
+    }
+
+    /**
+     * Get the VM command, including memory.
+     * @return A deep clone of the instance's VM command, with memory settings added
+     */
+    public Commandline getVmCommand() {
+        return getActualVMCommand();
+    }
+
+    /**
+     * get the classpath for the command
+     * @return the classpath or null
+     */
+    public Path getClasspath() {
+        return classpath;
+    }
+
+    /**
+     * get the boot classpath
+     * @return boot classpath or null
+     */
+    public Path getBootclasspath() {
+        return bootclasspath;
+    }
+
+    /**
+     * cache current system properties and set them to those in this
+     * java command
+     * @throws BuildException  if Security prevented this operation
+     */
+    public void setSystemProperties() throws BuildException {
+        sysProperties.setSystem();
+    }
+
+    /**
+     * @throws BuildException  if Security prevented this operation, or
+     * there was no system properties to restore
+     */
+    public void restoreSystemProperties() throws BuildException {
+        sysProperties.restoreSystem();
+    }
+
+    /**
+     * get the system properties object
+     * @return The system properties object
+     */
+    public SysProperties getSystemProperties() {
+        return sysProperties;
+    }
+
+    /**
+     * clone the object; clone of all fields in the class
+     * @return a CommandlineJava object
+     * @throws BuildException if anything went wrong.
+     * @throws CloneNotSupportedException never
+     */
+    public Object clone() throws CloneNotSupportedException {
+        try {
+            CommandlineJava c = (CommandlineJava) super.clone();
+            c.vmCommand = (Commandline) vmCommand.clone();
+            c.javaCommand = (Commandline) javaCommand.clone();
+            c.sysProperties = (SysProperties) sysProperties.clone();
+            if (classpath != null) {
+                c.classpath = (Path) classpath.clone();
+            }
+            if (bootclasspath != null) {
+                c.bootclasspath = (Path) bootclasspath.clone();
+            }
+            if (assertions != null) {
+                c.assertions = (Assertions) assertions.clone();
+            }
+            return c;
+        } catch (CloneNotSupportedException e) {
+            throw new BuildException(e);
+        }
+    }
+
+    /**
+     * Clear out the java arguments.
+     */
+    public void clearJavaArgs() {
+        javaCommand.clearArgs();
+    }
+
+    /**
+     * Has the classpath been specified and shall it really be used or
+     * will build.sysclasspath null it?
+     * @return true if the classpath is to be used
+     * @since Ant 1.6
+     */
+    protected boolean haveClasspath() {
+        Path fullClasspath = classpath != null
+            ? classpath.concatSystemClasspath("ignore") : null;
+        return fullClasspath != null
+            && fullClasspath.toString().trim().length() > 0;
+    }
+
+    /**
+     * Has the bootclasspath been specified and shall it really be
+     * used (build.sysclasspath could be set or the VM may not support
+     * it)?
+     *
+     * @param log whether to log a warning if a bootclasspath has been
+     * specified but will be ignored.
+     * @return true if the bootclasspath is to be used
+     * @since Ant 1.6
+     */
+    protected boolean haveBootclasspath(boolean log) {
+        if (bootclasspath != null
+            && bootclasspath.toString().trim().length() > 0) {
+
+            if (!bootclasspath.toString()
+                .equals(bootclasspath.concatSystemClasspath("ignore")
+                        .toString())) {
+                if (log) {
+                    bootclasspath.log("Ignoring bootclasspath as "
+                                       + "build.sysclasspath has been set.");
+                }
+            } else if (vmVersion.startsWith("1.1")) {
+                if (log) {
+                    bootclasspath.log("Ignoring bootclasspath as "
+                                       + "the target VM doesn't support it.");
+                }
+            } else {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/DTDLocation.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/DTDLocation.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/DTDLocation.java	(revision 16139)
@@ -0,0 +1,32 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+/**
+ * <p>Helper class to handle the DTD nested element.  Instances of
+ * this class correspond to the <code>PUBLIC</code> catalog entry type
+ * of the <a
+ * href="http://oasis-open.org/committees/entity/spec-2001-08-06.html">
+ * OASIS "Open Catalog" standard</a>.</p>
+ *
+ * <p>Possible Future Enhancement: Bring the Ant element name into
+ * conformance with the OASIS standard.</p>
+ *
+ * @see org.apache.xml.resolver.Catalog
+ */
+public class DTDLocation extends ResourceLocation {
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/DataType.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/DataType.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/DataType.java	(revision 16139)
@@ -0,0 +1,226 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+
+import java.util.Stack;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * Base class for those classes that can appear inside the build file
+ * as stand alone data types.
+ *
+ * <p>This class handles the common description attribute and provides
+ * a default implementation for reference handling and checking for
+ * circular references that is appropriate for types that can not be
+ * nested inside elements of the same type (i.e. &lt;patternset&gt;
+ * but not &lt;path&gt;).</p>
+ *
+ */
+public abstract class DataType extends ProjectComponent {
+    /**
+     * The description the user has set.
+     *
+     * @deprecated The user should not be directly referencing
+     *   variable. Please use {@link #setDescription} or
+     *   {@link #getDescription} instead.
+     */
+    protected String description;
+
+    /**
+     * Value to the refid attribute.
+     *
+     * @deprecated The user should not be directly referencing
+     *   variable. Please use {@link #getRefid} instead.
+     */
+    protected Reference ref;
+
+    /**
+     * Are we sure we don't hold circular references?
+     *
+     * <p>Subclasses are responsible for setting this value to false
+     * if we'd need to investigate this condition (usually because a
+     * child element has been added that is a subclass of
+     * DataType).</p>
+     *
+     * @deprecated The user should not be directly referencing
+     *   variable. Please use {@link #setChecked} or
+     *   {@link #isChecked} instead.
+     */
+    protected boolean checked = true;
+
+    /**
+     * Sets a description of the current data type. It will be useful
+     * in commenting what we are doing.
+     */
+    public void setDescription(final String desc) {
+        description = desc;
+    }
+
+    /**
+     * Return the description for the current data type.
+     */
+    public String getDescription() {
+        return description;
+    }
+
+    /**
+     * Has the refid attribute of this element been set?
+     */
+    public boolean isReference() {
+        return ref != null;
+    }
+
+    /**
+     * Set the value of the refid attribute.
+     *
+     * <p>Subclasses may need to check whether any other attributes
+     * have been set as well or child elements have been created and
+     * thus override this method. if they do the must call
+     * <code>super.setRefid</code>.</p>
+     */
+    public void setRefid(final Reference ref) {
+        this.ref = ref;
+        checked = false;
+    }
+
+    /**
+     * Check to see whether any DataType we hold references to is
+     * included in the Stack (which holds all DataType instances that
+     * directly or indirectly reference this instance, including this
+     * instance itself).
+     *
+     * <p>If one is included, throw a BuildException created by {@link
+     * #circularReference circularReference}.</p>
+     *
+     * <p>This implementation is appropriate only for a DataType that
+     * cannot hold other DataTypes as children.</p>
+     *
+     * <p>The general contract of this method is that it shouldn't do
+     * anything if {@link #checked <code>checked</code>} is true and
+     * set it to true on exit.</p>
+     */
+    protected void dieOnCircularReference(final Stack stack,
+                                          final Project project)
+        throws BuildException {
+
+        if (checked || !isReference()) {
+            return;
+        }
+        Object o = ref.getReferencedObject(project);
+
+        if (o instanceof DataType) {
+            if (stack.contains(o)) {
+                throw circularReference();
+            } else {
+                stack.push(o);
+                ((DataType) o).dieOnCircularReference(stack, project);
+                stack.pop();
+            }
+        }
+        checked = true;
+    }
+
+    /**
+     * Performs the check for circular references and returns the
+     * referenced object.
+     */
+    protected Object getCheckedRef(final Class requiredClass,
+                                   final String dataTypeName) {
+        if (!checked) {
+            Stack stk = new Stack();
+            stk.push(this);
+            dieOnCircularReference(stk, getProject());
+        }
+
+        Object o = ref.getReferencedObject(getProject());
+        if (!(requiredClass.isAssignableFrom(o.getClass()))) {
+            String msg = ref.getRefId() + " doesn\'t denote a " + dataTypeName;
+            throw new BuildException(msg);
+        } else {
+            return o;
+        }
+    }
+
+    /**
+     * Creates an exception that indicates that refid has to be the
+     * only attribute if it is set.
+     */
+    protected BuildException tooManyAttributes() {
+        return new BuildException("You must not specify more than one "
+            + "attribute when using refid");
+    }
+
+    /**
+     * Creates an exception that indicates that this XML element must
+     * not have child elements if the refid attribute is set.
+     */
+    protected BuildException noChildrenAllowed() {
+        return new BuildException("You must not specify nested elements "
+            + "when using refid");
+    }
+
+    /**
+     * Creates an exception that indicates the user has generated a
+     * loop of data types referencing each other.
+     */
+    protected BuildException circularReference() {
+        return new BuildException("This data type contains a circular "
+            + "reference.");
+    }
+
+    protected boolean isChecked() {
+        return checked;
+    }
+
+    protected void setChecked(final boolean checked) {
+        this.checked = checked;
+    }
+
+    /**
+     * get the reference set on this object
+     * @return the reference or null
+     */
+    protected Reference getRefid() {
+        return ref;
+    }
+
+    /**
+     * check that it is ok to set attributes, i.e that no reference is defined
+     * @since Ant 1.6
+     * @throws BuildException if not allowed
+     */
+    protected void checkAttributesAllowed() {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+    }
+
+    /**
+     * check that it is ok to add children, i.e that no reference is defined
+     * @since Ant 1.6
+     * @throws BuildException if not allowed
+     */
+    protected void checkChildrenAllowed() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Description.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Description.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Description.java	(revision 16139)
@@ -0,0 +1,121 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.UnknownElement;
+import org.apache.tools.ant.Target;
+import org.apache.tools.ant.helper.ProjectHelperImpl;
+
+import java.util.Vector;
+
+
+/**
+ * Description is used to provide a project-wide description element
+ * (that is, a description that applies to a buildfile as a whole).
+ * If present, the &lt;description&gt; element is printed out before the
+ * target descriptions.
+ *
+ * Description has no attributes, only text.  There can only be one
+ * project description per project.  A second description element will
+ * overwrite the first.
+ *
+ *
+ * @ant.datatype ignore="true"
+ */
+public class Description extends DataType {
+
+    /**
+     * Adds descriptive text to the project.
+     *
+     * @param text the descriptive text
+     */
+    public void addText(String text) {
+
+        ProjectHelper ph = ProjectHelper.getProjectHelper();
+        if (!(ph instanceof ProjectHelperImpl)) {
+            // New behavior for delayed task creation. Description
+            // will be evaluated in Project.getDescription()
+            return;
+        }
+        String currentDescription = getProject().getDescription();
+        if (currentDescription == null) {
+            getProject().setDescription(text);
+        } else {
+            getProject().setDescription(currentDescription + text);
+        }
+    }
+
+    /**
+     * Return the descriptions from all the targets of
+     * a project.
+     *
+     * @param project the project to get the descriptions for.
+     * @return a string containing the concatenated descriptions of
+     *         the targets.
+     */
+    public static String getDescription(Project project) {
+        Vector targets = (Vector) project.getReference("ant.targets");
+        if (targets == null) {
+            return null;
+        }
+        StringBuffer description = new StringBuffer();
+        for (int i = 0; i < targets.size(); i++) {
+            Target t = (Target) targets.elementAt(i);
+            concatDescriptions(project, t, description);
+        }
+        return description.toString();
+    }
+
+    private static void concatDescriptions(Project project, Target t,
+                                           StringBuffer description) {
+        if (t == null) {
+            return;
+        }
+        Vector tasks = findElementInTarget(project, t, "description");
+        if (tasks == null) {
+            return;
+        }
+        for (int i = 0; i < tasks.size(); i++) {
+            Task task = (Task) tasks.elementAt(i);
+            if (!(task instanceof UnknownElement)) {
+                continue;
+            }
+            UnknownElement ue = ((UnknownElement) task);
+            StringBuffer descComp = ue.getWrapper().getText();
+            if (descComp != null) {
+                description.append((Object) descComp);
+            }
+        }
+    }
+
+    private static Vector findElementInTarget(Project project,
+                                              Target t, String name) {
+        Task[] tasks = t.getTasks();
+        Vector elems = new Vector();
+        for (int i = 0; i < tasks.length; i++) {
+            if (name.equals(tasks[i].getTaskName())) {
+                elems.addElement(tasks[i]);
+            }
+        }
+        return elems;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/DirSet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/DirSet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/DirSet.java	(revision 16139)
@@ -0,0 +1,48 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+/**
+ * Subclass as hint for supporting tasks that the included directories
+ * instead of files should be used.
+ *
+ * @since Ant 1.5
+ */
+public class DirSet extends AbstractFileSet {
+
+    public DirSet() {
+        super();
+    }
+
+    protected DirSet(DirSet dirset) {
+        super(dirset);
+    }
+
+    /**
+     * Return a DirSet that has the same basedir and same patternsets
+     * as this one.
+     */
+    public Object clone() {
+        if (isReference()) {
+            return ((DirSet) getRef(getProject())).clone();
+        } else {
+            return super.clone();
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/EnumeratedAttribute.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/EnumeratedAttribute.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/EnumeratedAttribute.java	(revision 16139)
@@ -0,0 +1,119 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Helper class for attributes that can only take one of a fixed list
+ * of values.
+ *
+ * <p>See {@link org.apache.tools.ant.taskdefs.FixCRLF FixCRLF} for an
+ * example.
+ *
+ */
+public abstract class EnumeratedAttribute {
+
+    /**
+     * The selected value in this enumeration.
+     */
+    protected String value;
+
+    /**
+     * the index of the selected value in the array.
+     */
+    private int index = -1;
+
+    /**
+     * This is the only method a subclass needs to implement.
+     *
+     * @return an array holding all possible values of the enumeration.
+     * The order of elements must be fixed so that <tt>indexOfValue(String)</tt>
+     * always return the same index for the same value.
+     */
+    public abstract String[] getValues();
+
+    /** bean constructor */
+    protected EnumeratedAttribute() {
+    }
+
+    /**
+     * Invoked by {@link org.apache.tools.ant.IntrospectionHelper IntrospectionHelper}.
+     */
+    public final void setValue(String value) throws BuildException {
+        int index = indexOfValue(value);
+        if (index == -1) {
+            throw new BuildException(value + " is not a legal value for this attribute");
+        }
+        this.index = index;
+        this.value = value;
+    }
+
+    /**
+     * Is this value included in the enumeration?
+     */
+    public final boolean containsValue(String value) {
+        return (indexOfValue(value) != -1);
+    }
+
+    /**
+     * get the index of a value in this enumeration.
+     * @param value the string value to look for.
+     * @return the index of the value in the array of strings
+     * or -1 if it cannot be found.
+     * @see #getValues()
+     */
+    public final int indexOfValue(String value) {
+        String[] values = getValues();
+        if (values == null || value == null) {
+            return -1;
+        }
+        for (int i = 0; i < values.length; i++) {
+            if (value.equals(values[i])) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * @return the selected value.
+     */
+    public final String getValue() {
+        return value;
+    }
+
+    /**
+     * @return the index of the selected value in the array.
+     * @see #getValues()
+     */
+    public final int getIndex() {
+        return index;
+    }
+
+
+    /**
+     * Convert the value to its string form.
+     *
+     * @return the string form of the value.
+     */
+    public String toString() {
+        return getValue();
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Environment.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Environment.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Environment.java	(revision 16139)
@@ -0,0 +1,162 @@
+/*
+ * Copyright  2000,2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Wrapper for environment variables.
+ *
+ */
+public class Environment {
+
+    /**
+     * a vector of type Enviromment.Variable
+     * @see Variable
+     */
+    protected Vector variables;
+
+    /**
+     * representation of a single env value
+     */
+    public static class Variable {
+
+        /**
+         * env key and value pair; everything gets expanded to a string
+         * during assignment
+         */
+        private String key, value;
+
+        /**
+         * Constructor for variable
+         *
+         */
+        public Variable() {
+            super();
+        }
+
+        /**
+         * set the key
+         * @param key string
+         */
+        public void setKey(String key) {
+            this.key = key;
+        }
+
+        /**
+         * set the value
+         * @param value string value
+         */
+        public void setValue(String value) {
+            this.value = value;
+        }
+
+        /**
+         * key accessor
+         * @return key
+         */
+        public String getKey() {
+            return this.key;
+        }
+
+        /**
+         * value accessor
+         * @return value
+         */
+        public String getValue() {
+            return this.value;
+        }
+
+        /**
+         * stringify path and assign to the value.
+         * The value will contain all path elements separated by the appropriate
+         * separator
+         * @param path path
+         */
+        public void setPath(Path path) {
+            this.value = path.toString();
+        }
+
+        /**
+         * get the absolute path of a file and assign it to the value
+         * @param file file to use as the value
+         */
+        public void setFile(java.io.File file) {
+            this.value = file.getAbsolutePath();
+        }
+
+        /**
+         * get the assigment string
+         * This is not ready for insertion into a property file without following
+         * the escaping rules of the properties class.
+         * @return a string of the form key=value.
+         * @throws BuildException if key or value are unassigned
+         */
+        public String getContent() throws BuildException {
+            validate();
+            StringBuffer sb = new StringBuffer(key.trim());
+            sb.append("=").append(value.trim());
+            return sb.toString();
+        }
+
+        /**
+         * checks whether all required attributes have been specified.
+         * @throws BuildException if key or value are unassigned
+         */
+        public void validate() {
+            if (key == null || value == null) {
+                throw new BuildException("key and value must be specified "
+                    + "for environment variables.");
+            }
+        }
+    }
+
+    /**
+     * constructor
+     */
+    public Environment() {
+        variables = new Vector();
+    }
+
+    /**
+     * add a variable.
+     * Validity checking is <i>not</i> performed at this point. Duplicates
+     * are not caught either.
+     * @param var new variable.
+     */
+    public void addVariable(Variable var) {
+        variables.addElement(var);
+    }
+
+    /**
+     * get the variable list as an array
+     * @return array of key=value assignment strings
+     * @throws BuildException if any variable is misconfigured
+     */
+    public String[] getVariables() throws BuildException {
+        if (variables.size() == 0) {
+            return null;
+        }
+        String[] result = new String[variables.size()];
+        for (int i = 0; i < result.length; i++) {
+            result[i] = ((Variable) variables.elementAt(i)).getContent();
+        }
+        return result;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FileList.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FileList.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FileList.java	(revision 16139)
@@ -0,0 +1,197 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import java.io.File;
+import java.util.Stack;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * FileList represents an explicitly named list of files.  FileLists
+ * are useful when you want to capture a list of files regardless of
+ * whether they currently exist.  By contrast, FileSet operates as a
+ * filter, only returning the name of a matched file if it currently
+ * exists in the file system.
+ */
+public class FileList extends DataType {
+
+    private Vector filenames = new Vector();
+    private File dir;
+
+    /**
+     * The default constructor.
+     *
+     */
+    public FileList() {
+        super();
+    }
+
+    /**
+     * A copy constructor.
+     *
+     * @param filelist a <code>FileList</code> value
+     */
+    protected FileList(FileList filelist) {
+        this.dir       = filelist.dir;
+        this.filenames = filelist.filenames;
+        setProject(filelist.getProject());
+    }
+
+    /**
+     * Makes this instance in effect a reference to another FileList
+     * instance.
+     *
+     * <p>You must not set another attribute or nest elements inside
+     * this element if you make it a reference.</p>
+     * @param r the reference to another filelist.
+     * @exception BuildException if an error occurs.
+     */
+    public void setRefid(Reference r) throws BuildException {
+        if ((dir != null) || (filenames.size() != 0)) {
+            throw tooManyAttributes();
+        }
+        super.setRefid(r);
+    }
+
+    /**
+     * Set the dir attribute.
+     *
+     * @param dir the directory this filelist is relative to.
+     * @exception BuildException if an error occurs
+     */
+    public void setDir(File dir) throws BuildException {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.dir = dir;
+    }
+
+    /**
+     * @param p the current project
+     * @return the directory attribute
+     */
+    public File getDir(Project p) {
+        if (isReference()) {
+            return getRef(p).getDir(p);
+        }
+        return dir;
+    }
+
+    /**
+     * Set the filenames attribute.
+     *
+     * @param filenames a string contains filenames, separated by , or
+     *        by whitespace.
+     */
+    public void setFiles(String filenames) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        if (filenames != null && filenames.length() > 0) {
+            StringTokenizer tok = new StringTokenizer(
+                filenames, ", \t\n\r\f", false);
+            while (tok.hasMoreTokens()) {
+               this.filenames.addElement(tok.nextToken());
+            }
+        }
+    }
+
+    /**
+     * Returns the list of files represented by this FileList.
+     * @param p the current project
+     * @return the list of files represented by this FileList.
+     */
+    public String[] getFiles(Project p) {
+        if (isReference()) {
+            return getRef(p).getFiles(p);
+        }
+
+        if (dir == null) {
+            throw new BuildException("No directory specified for filelist.");
+        }
+
+        if (filenames.size() == 0) {
+            throw new BuildException("No files specified for filelist.");
+        }
+
+        String[] result = new String[filenames.size()];
+        filenames.copyInto(result);
+        return result;
+    }
+
+    /**
+     * Performs the check for circular references and returns the
+     * referenced FileList.
+     * @param p the current project
+     * @return the FileList represented by a referenced filelist.
+     */
+    protected FileList getRef(Project p) {
+        if (!isChecked()) {
+            Stack stk = new Stack();
+            stk.push(this);
+            dieOnCircularReference(stk, p);
+        }
+
+        Object o = getRefid().getReferencedObject(p);
+        if (!(o instanceof FileList)) {
+            String msg = getRefid().getRefId() + " doesn\'t denote a filelist";
+            throw new BuildException(msg);
+        } else {
+            return (FileList) o;
+        }
+    }
+
+    /**
+     * Inner class corresponding to the &lt;file&gt; nested element.
+     */
+    public static class FileName {
+        private String name;
+
+        /**
+         * The name attribute of the file element.
+         *
+         * @param name the name of a file to add to the file list.
+         */
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        /**
+         * @return the name of the file for this element.
+         */
+        public String getName() {
+            return name;
+        }
+    }
+
+    /**
+     * Add a nested &lt;file&gt; nested element.
+     *
+     * @param name a configured file element with a name.
+     */
+    public void addConfiguredFile(FileName name) {
+        if (name.getName() == null) {
+            throw new BuildException(
+                "No name specified in nested file element");
+        }
+        filenames.addElement(name.getName());
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FileSet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FileSet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FileSet.java	(revision 16139)
@@ -0,0 +1,47 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+/**
+ * Moved out of MatchingTask to make it a standalone object that could
+ * be referenced (by scripts for example).
+ *
+ */
+public class FileSet extends AbstractFileSet {
+
+    public FileSet() {
+        super();
+    }
+
+    protected FileSet(FileSet fileset) {
+        super(fileset);
+    }
+
+    /**
+     * Return a FileSet that has the same basedir and same patternsets
+     * as this one.
+     */
+    public Object clone() {
+        if (isReference()) {
+            return ((FileSet) getRef(getProject())).clone();
+        } else {
+            return super.clone();
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FilterChain.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FilterChain.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FilterChain.java	(revision 16139)
@@ -0,0 +1,209 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.filters.ChainableReader;
+import org.apache.tools.ant.filters.ClassConstants;
+import org.apache.tools.ant.filters.EscapeUnicode;
+import org.apache.tools.ant.filters.ExpandProperties;
+import org.apache.tools.ant.filters.HeadFilter;
+import org.apache.tools.ant.filters.LineContains;
+import org.apache.tools.ant.filters.LineContainsRegExp;
+import org.apache.tools.ant.filters.PrefixLines;
+import org.apache.tools.ant.filters.ReplaceTokens;
+import org.apache.tools.ant.filters.StripJavaComments;
+import org.apache.tools.ant.filters.StripLineBreaks;
+import org.apache.tools.ant.filters.StripLineComments;
+import org.apache.tools.ant.filters.TabsToSpaces;
+import org.apache.tools.ant.filters.TailFilter;
+import org.apache.tools.ant.filters.TokenFilter;
+
+
+/**
+ * FilterChain may contain a chained set of filter readers.
+ *
+ */
+public class FilterChain extends DataType
+    implements Cloneable {
+
+    private Vector filterReaders = new Vector();
+
+    public final void addFilterReader(final AntFilterReader filterReader) {
+        filterReaders.addElement(filterReader);
+    }
+
+    public final Vector getFilterReaders() {
+        return filterReaders;
+    }
+
+    public final void addClassConstants(final ClassConstants classConstants) {
+        filterReaders.addElement(classConstants);
+    }
+
+    public final void addExpandProperties(final ExpandProperties expandProperties) {
+        filterReaders.addElement(expandProperties);
+    }
+
+    public final void addHeadFilter(final HeadFilter headFilter) {
+        filterReaders.addElement(headFilter);
+    }
+
+    public final void addLineContains(final LineContains lineContains) {
+        filterReaders.addElement(lineContains);
+    }
+
+    public final void addLineContainsRegExp(final LineContainsRegExp
+                                                lineContainsRegExp) {
+        filterReaders.addElement(lineContainsRegExp);
+    }
+
+    public final void addPrefixLines(final PrefixLines prefixLines) {
+        filterReaders.addElement(prefixLines);
+    }
+
+    public final void addReplaceTokens(final ReplaceTokens replaceTokens) {
+        filterReaders.addElement(replaceTokens);
+    }
+
+    public final void addStripJavaComments(final StripJavaComments
+                                                stripJavaComments) {
+        filterReaders.addElement(stripJavaComments);
+    }
+
+    public final void addStripLineBreaks(final StripLineBreaks
+                                                stripLineBreaks) {
+        filterReaders.addElement(stripLineBreaks);
+    }
+
+    public final void addStripLineComments(final StripLineComments
+                                                stripLineComments) {
+        filterReaders.addElement(stripLineComments);
+    }
+
+    public final void addTabsToSpaces(final TabsToSpaces tabsToSpaces) {
+        filterReaders.addElement(tabsToSpaces);
+    }
+
+    public final void addTailFilter(final TailFilter tailFilter) {
+        filterReaders.addElement(tailFilter);
+    }
+
+    /**
+     * @since Ant 1.6
+     */
+    public final void addEscapeUnicode(final EscapeUnicode escapeUnicode) {
+        filterReaders.addElement(escapeUnicode);
+    }
+
+    /**
+     * @since Ant 1.6
+     */
+    public final void addTokenFilter(final TokenFilter tokenFilter) {
+        filterReaders.addElement(tokenFilter);
+    }
+
+    /**
+     * delete characters filter
+     * @since Ant 1.6
+     */
+    public void addDeleteCharacters(TokenFilter.DeleteCharacters filter) {
+        filterReaders.addElement(filter);
+    }
+
+    /**
+     * containsregex
+     * @since Ant 1.6
+     */
+    public void addContainsRegex(TokenFilter.ContainsRegex filter) {
+        filterReaders.addElement(filter);
+    }
+
+    /**
+     * replaceregex
+     * @since Ant 1.6
+     */
+    public void addReplaceRegex(TokenFilter.ReplaceRegex filter) {
+        filterReaders.addElement(filter);
+    }
+
+    /**
+     * trim
+     * @since Ant 1.6
+     */
+    public void addTrim(TokenFilter.Trim filter) {
+        filterReaders.addElement(filter);
+    }
+
+    /**
+     * replacestring
+     * @since Ant 1.6
+     */
+    public void addReplaceString(
+        TokenFilter.ReplaceString filter) {
+        filterReaders.addElement(filter);
+    }
+
+    /**
+     * ignoreBlank
+     * @since Ant 1.6
+     */
+    public void addIgnoreBlank(
+        TokenFilter.IgnoreBlank filter) {
+        filterReaders.addElement(filter);
+    }
+
+
+    /**
+     * Makes this instance in effect a reference to another FilterChain
+     * instance.
+     *
+     * <p>You must not set another attribute or nest elements inside
+     * this element if you make it a reference.</p>
+     *
+     * @param r the reference to which this instance is associated
+     * @exception BuildException if this instance already has been configured.
+     */
+    public void setRefid(Reference r) throws BuildException {
+        if (!filterReaders.isEmpty()) {
+            throw tooManyAttributes();
+        }
+        // change this to get the objects from the other reference
+        Object o = r.getReferencedObject(getProject());
+        if (o instanceof FilterChain) {
+            FilterChain fc = (FilterChain) o;
+            filterReaders = fc.getFilterReaders();
+        } else {
+            String msg = r.getRefId() + " doesn\'t refer to a FilterChain";
+            throw new BuildException(msg);
+        }
+
+        super.setRefid(r);
+    }
+
+    /**
+     * add a chainfilter
+     * @since Ant 1.6
+     */
+
+    public void add(ChainableReader filter) {
+        filterReaders.addElement(filter);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FilterSet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FilterSet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FilterSet.java	(revision 16139)
@@ -0,0 +1,529 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * A set of filters to be applied to something.
+ *
+ * A filter set may have begintoken and endtokens defined.
+ *
+ */
+public class FilterSet extends DataType implements Cloneable {
+
+    /**
+     * Individual filter component of filterset.
+     *
+     */
+    public static class Filter {
+        /** Token which will be replaced in the filter operation. */
+        String token;
+
+        /** The value which will replace the token in the filtering operation. */
+        String value;
+
+        /**
+         * Constructor for the Filter object.
+         *
+         * @param token  The token which will be replaced when filtering.
+         * @param value  The value which will replace the token when filtering.
+         */
+        public Filter(String token, String value) {
+           setToken(token);
+           setValue(value);
+        }
+
+        /**
+         * No-argument conmstructor.
+         */
+        public Filter() {
+        }
+
+        /**
+         * Sets the Token attribute of the Filter object.
+         *
+         * @param token  The new Token value.
+         */
+        public void setToken(String token) {
+           this.token = token;
+        }
+
+        /**
+         * Sets the Value attribute of the Filter object.
+         *
+         * @param value  The new Value value.
+         */
+        public void setValue(String value) {
+           this.value = value;
+        }
+
+        /**
+         * Gets the Token attribute of the Filter object.
+         *
+         * @return   The Token value.
+         */
+        public String getToken() {
+           return token;
+        }
+
+        /**
+         * Gets the Value attribute of the Filter object.
+         *
+         * @return   The Value value.
+         */
+        public String getValue() {
+           return value;
+        }
+     }
+
+    /**
+     * The filtersfile nested element.
+     *
+     */
+    public class FiltersFile {
+
+        /**
+         * Constructor for the FiltersFile object.
+         */
+        public FiltersFile() {
+        }
+
+        /**
+         * Sets the file from which filters will be read.
+         *
+         * @param file the file from which filters will be read.
+         */
+        public void setFile(File file) {
+           readFiltersFromFile(file);
+        }
+    }
+
+    /** The default token start string */
+    public static final String DEFAULT_TOKEN_START = "@";
+
+    /** The default token end string */
+    public static final String DEFAULT_TOKEN_END = "@";
+
+    private String startOfToken = DEFAULT_TOKEN_START;
+    private String endOfToken = DEFAULT_TOKEN_END;
+
+    /** Contains a list of parsed tokens */
+    private Vector passedTokens;
+    /** if a duplicate token is found, this is set to true */
+    private boolean duplicateToken = false;
+
+    private boolean recurse = true;
+    private Hashtable filterHash = null;
+
+    /**
+     * List of ordered filters and filter files.
+     */
+    private Vector filters = new Vector();
+
+    /**
+     * Default constructor.
+     */
+    public FilterSet() {
+    }
+
+    /**
+     * Create a Filterset from another filterset.
+     *
+     * @param filterset the filterset upon which this filterset will be based.
+     */
+    protected FilterSet(FilterSet filterset) {
+        super();
+        this.filters = (Vector) filterset.getFilters().clone();
+    }
+
+    /**
+     * Get the filters in the filter set.
+     *
+     * @return a Vector of Filter instances.
+     */
+    protected synchronized Vector getFilters() {
+        if (isReference()) {
+            return getRef().getFilters();
+        }
+        return filters;
+    }
+
+    /**
+     * Get the referenced filter set.
+     *
+     * @return the filterset from the reference.
+     */
+    protected FilterSet getRef() {
+        return (FilterSet) getCheckedRef(FilterSet.class, "filterset");
+    }
+
+    /**
+     * Gets the filter hash of the FilterSet.
+     *
+     * @return   The hash of the tokens and values for quick lookup.
+     */
+    public synchronized Hashtable getFilterHash() {
+        if (filterHash == null) {
+            filterHash = new Hashtable(getFilters().size());
+            for (Enumeration e = getFilters().elements(); e.hasMoreElements();) {
+               Filter filter = (Filter) e.nextElement();
+               filterHash.put(filter.getToken(), filter.getValue());
+            }
+        }
+        return filterHash;
+    }
+
+    /**
+     * Set the file containing the filters for this filterset.
+     *
+     * @param filtersFile sets the filter file from which to read filters
+     *        for this filter set.
+     * @exception BuildException if there is a problem reading the filters.
+     */
+    public void setFiltersfile(File filtersFile) throws BuildException {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        readFiltersFromFile(filtersFile);
+    }
+
+    /**
+     * Set the string used to id the beginning of a token.
+     *
+     * @param startOfToken  The new Begintoken value.
+     */
+    public void setBeginToken(String startOfToken) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        if (startOfToken == null || "".equals(startOfToken)) {
+            throw new BuildException("beginToken must not be empty");
+        }
+        this.startOfToken = startOfToken;
+    }
+
+    /**
+     * Get the begin token for this filterset.
+     *
+     * @return the filter set's begin token for filtering.
+     */
+    public String getBeginToken() {
+        if (isReference()) {
+            return getRef().getBeginToken();
+        }
+        return startOfToken;
+    }
+
+    /**
+     * Set the string used to id the end of a token.
+     *
+     * @param endOfToken  The new Endtoken value.
+     */
+    public void setEndToken(String endOfToken) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        if (endOfToken == null || "".equals(endOfToken)) {
+            throw new BuildException("endToken must not be empty");
+        }
+        this.endOfToken = endOfToken;
+    }
+
+    /**
+     * Get the end token for this filterset.
+     *
+     * @return the filter set's end token for replacement delimiting.
+     */
+    public String getEndToken() {
+        if (isReference()) {
+            return getRef().getEndToken();
+        }
+        return endOfToken;
+    }
+
+    /**
+     * Set whether recursive token expansion is enabled.
+     * @param recurse <code>boolean</code> whether to recurse.
+     */
+    public void setRecurse(boolean recurse) {
+        this.recurse = recurse;
+    }
+
+    /**
+     * Get whether recursive token expansion is enabled.
+     * @return <code>boolean</code> whether enabled.
+     */
+    public boolean isRecurse() {
+        return recurse;
+    }
+
+    /**
+     * Read the filters from the given file.
+     *
+     * @param filtersFile        the file from which filters are read.
+     * @exception BuildException when the file cannot be read.
+     */
+    public synchronized void readFiltersFromFile(File filtersFile) throws BuildException {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        if (!filtersFile.exists()) {
+            throw new BuildException("Could not read filters from file "
+                                     + filtersFile + " as it doesn't exist.");
+        }
+        if (filtersFile.isFile()) {
+           log("Reading filters from " + filtersFile, Project.MSG_VERBOSE);
+           FileInputStream in = null;
+           try {
+              Properties props = new Properties();
+              in = new FileInputStream(filtersFile);
+              props.load(in);
+
+              Enumeration e = props.propertyNames();
+              Vector filts = getFilters();
+              while (e.hasMoreElements()) {
+                 String strPropName = (String) e.nextElement();
+                 String strValue = props.getProperty(strPropName);
+                 filts.addElement(new Filter(strPropName, strValue));
+              }
+           } catch (Exception ex) {
+              throw new BuildException("Could not read filters from file: "
+                + filtersFile);
+           } finally {
+              if (in != null) {
+                 try {
+                    in.close();
+                 } catch (IOException ioex) {
+                     // ignore
+                 }
+              }
+           }
+        } else {
+           throw new BuildException("Must specify a file not a directory in "
+            + "the filtersfile attribute:" + filtersFile);
+        }
+    }
+
+    /**
+     * Does replacement on the given string with token matching.
+     * This uses the defined begintoken and endtoken values which default
+     * to @ for both.
+     * This resets the passedTokens and calls iReplaceTokens to
+     * do the actual replacements.
+     *
+     * @param line  The line in which to process embedded tokens.
+     * @return      The input string after token replacement.
+     */
+    public synchronized String replaceTokens(String line) {
+        passedTokens = null; // reset for new line
+        return iReplaceTokens(line);
+    }
+
+    /**
+     * Does replacement on the given string with token matching.
+     * This uses the defined begintoken and endtoken values which default
+     * to @ for both.
+     *
+     * @param line  The line to process the tokens in.
+     * @return      The string with the tokens replaced.
+     */
+    private synchronized String iReplaceTokens(String line) {
+        String beginToken = getBeginToken();
+        String endToken = getEndToken();
+        int index = line.indexOf(beginToken);
+
+        if (index > -1) {
+            Hashtable tokens = getFilterHash();
+            try {
+                StringBuffer b = new StringBuffer();
+                int i = 0;
+                String token = null;
+                String value = null;
+
+                do {
+                    //can't have zero-length token
+                    int endIndex = line.indexOf(endToken,
+                        index + beginToken.length() + 1);
+                    if (endIndex == -1) {
+                        break;
+                    }
+                    token
+                        = line.substring(index + beginToken.length(), endIndex);
+                    b.append(line.substring(i, index));
+                    if (tokens.containsKey(token)) {
+                        value = (String) tokens.get(token);
+                        if (recurse && !value.equals(token)) {
+                            // we have another token, let's parse it.
+                            value = replaceTokens(value, token);
+                        }
+                        log("Replacing: " + beginToken + token + endToken
+                            + " -> " + value, Project.MSG_VERBOSE);
+                        b.append(value);
+                        i = index + beginToken.length() + token.length()
+                            + endToken.length();
+                    } else {
+                        // just append beginToken and search further
+                        b.append(beginToken);
+                        i = index + beginToken.length();
+                    }
+                } while ((index = line.indexOf(beginToken, i)) > -1);
+
+                b.append(line.substring(i));
+                return b.toString();
+            } catch (StringIndexOutOfBoundsException e) {
+                return line;
+            }
+        } else {
+           return line;
+        }
+    }
+
+    /**
+     * This parses tokens which point to tokens.
+     * It also maintains a list of currently used tokens, so we cannot
+     * get into an infinite loop.
+     * @param line the value / token to parse.
+     * @param parent the parent token (= the token it was parsed from).
+     */
+    private synchronized String replaceTokens(String line, String parent)
+        throws BuildException {
+        String beginToken = getBeginToken();
+        String endToken = getEndToken();
+        if (passedTokens == null) {
+            passedTokens = new Vector();
+        }
+        if (passedTokens.contains(parent) && !duplicateToken) {
+            duplicateToken = true;
+            System.out.println(
+                "Infinite loop in tokens. Currently known tokens : "
+                + passedTokens.toString() + "\nProblem token : " + beginToken
+                + parent + endToken + " called from " + beginToken
+                + passedTokens.lastElement().toString() + endToken);
+            return parent;
+        }
+        passedTokens.addElement(parent);
+        String value = iReplaceTokens(line);
+        if (value.indexOf(beginToken) == -1 && !duplicateToken) {
+            duplicateToken = false;
+            passedTokens = null;
+        } else if (duplicateToken) {
+            // should always be the case...
+            if (passedTokens.size() > 0) {
+                value = (String) passedTokens.remove(passedTokens.size() - 1);
+                if (passedTokens.size() == 0) {
+                    value = beginToken + value + endToken;
+                    duplicateToken = false;
+                }
+            }
+        }
+        return value;
+    }
+
+    /**
+     * Add a new filter.
+     *
+     * @param filter the filter to be added.
+     */
+    public synchronized void addFilter(Filter filter) {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        filters.addElement(filter);
+        filterHash = null;
+    }
+
+    /**
+     * Create a new FiltersFile.
+     *
+     * @return The filtersfile that was created.
+     */
+    public FiltersFile createFiltersfile() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        return new FiltersFile();
+    }
+
+    /**
+     * Add a new filter made from the given token and value.
+     *
+     * @param token The token for the new filter.
+     * @param value The value for the new filter.
+     */
+    public synchronized void addFilter(String token, String value) {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        addFilter(new Filter(token, value));
+    }
+
+    /**
+     * Add a Filterset to this filter set.
+     *
+     * @param filterSet the filterset to be added to this filterset
+     */
+    public synchronized void addConfiguredFilterSet(FilterSet filterSet) {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        for (Enumeration e = filterSet.getFilters().elements(); e.hasMoreElements();) {
+            addFilter((Filter) e.nextElement());
+        }
+    }
+
+    /**
+    * Test to see if this filter set has filters.
+    *
+    * @return Return true if there are filters in this set.
+    */
+    public synchronized boolean hasFilters() {
+        return getFilters().size() > 0;
+    }
+
+    /**
+     * Clone the filterset.
+     *
+     * @return a deep clone of this filterset.
+     *
+     * @throws BuildException if the clone cannot be performed.
+     */
+    public synchronized Object clone() throws BuildException {
+        if (isReference()) {
+            return ((FilterSet) getRef()).clone();
+        } else {
+            try {
+                FilterSet fs = (FilterSet) super.clone();
+                fs.filters = (Vector) getFilters().clone();
+                fs.setProject(getProject());
+                return fs;
+            } catch (CloneNotSupportedException e) {
+                throw new BuildException(e);
+            }
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FilterSetCollection.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FilterSetCollection.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FilterSetCollection.java	(revision 16139)
@@ -0,0 +1,87 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+// java io classes
+
+
+
+
+// java util classes
+import java.util.Enumeration;
+import java.util.Vector;
+
+// ant classes
+
+
+
+
+/**
+ * A FilterSetCollection is a collection of filtersets each of which may have
+ * a different start/end token settings.
+ *
+ */
+public class FilterSetCollection {
+
+    private Vector filterSets = new Vector();
+
+    public FilterSetCollection() {
+    }
+
+    public FilterSetCollection(FilterSet filterSet) {
+        addFilterSet(filterSet);
+    }
+
+
+    public void addFilterSet(FilterSet filterSet) {
+        filterSets.addElement(filterSet);
+    }
+
+    /**
+     * Does replacement on the given string with token matching.
+     * This uses the defined begintoken and endtoken values which default to @ for both.
+     *
+     * @param line  The line to process the tokens in.
+     * @return      The string with the tokens replaced.
+     */
+    public String replaceTokens(String line) {
+        String replacedLine = line;
+        for (Enumeration e = filterSets.elements(); e.hasMoreElements();) {
+            FilterSet filterSet = (FilterSet) e.nextElement();
+            replacedLine = filterSet.replaceTokens(replacedLine);
+        }
+        return replacedLine;
+    }
+
+    /**
+    * Test to see if this filter set it empty.
+    *
+    * @return   Return true if there are filter in this set otherwise false.
+    */
+    public boolean hasFilters() {
+        for (Enumeration e = filterSets.elements(); e.hasMoreElements();) {
+            FilterSet filterSet = (FilterSet) e.nextElement();
+            if (filterSet.hasFilters()) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
+
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FlexInteger.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FlexInteger.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/FlexInteger.java	(revision 16139)
@@ -0,0 +1,50 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+/**
+ * Helper class which can be used for Ant task attribute setter methods to allow
+ * the build file to specify an integer in either decimal, octal, or hexadecimal
+ * format.
+ *
+ * @see java.lang.Integer#decode(String)
+ */
+public class FlexInteger {
+    private Integer value;
+
+    /**
+     * Constructor used by Ant's introspection mechanism for attribute population
+     */
+    public FlexInteger(String value) {
+        this.value = Integer.decode(value);
+    }
+
+    /**
+     * Returns the decimal integer value
+     */
+    public int intValue() {
+        return value.intValue();
+    }
+
+    /**
+     * Overridden method to return the decimal value for display
+     */
+    public String toString() {
+        return value.toString();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Mapper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Mapper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Mapper.java	(revision 16139)
@@ -0,0 +1,288 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import java.util.Properties;
+import java.util.Stack;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.CompositeMapper;
+import org.apache.tools.ant.util.ContainerMapper;
+
+/**
+ * Element to define a FileNameMapper.
+ *
+ */
+public class Mapper extends DataType implements Cloneable {
+
+    protected MapperType type = null;
+    protected String classname = null;
+    protected Path classpath = null;
+    protected String from = null;
+    protected String to = null;
+
+    private ContainerMapper container = null;
+
+    /**
+     * Construct a new <CODE>Mapper</CODE> element.
+     * @param p   the owning Ant <CODE>Project</CODE>.
+     */
+    public Mapper(Project p) {
+        setProject(p);
+    }
+
+    /**
+     * Set the type of <code>FileNameMapper</code> to use.
+     * @param type   the <CODE>MapperType</CODE> enumerated attribute.
+     */
+    public void setType(MapperType type) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.type = type;
+    }
+
+    /**
+     * Add a nested <CODE>FileNameMapper</CODE>.
+     * @param fileNameMapper   the <CODE>FileNameMapper</CODE> to add.
+     */
+    public void add(FileNameMapper fileNameMapper) {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        if (container == null) {
+            if (type == null && classname == null) {
+                container = new CompositeMapper();
+            } else {
+                FileNameMapper m = getImplementation();
+                if (m instanceof ContainerMapper) {
+                    container = (ContainerMapper)m;
+                } else {
+                    throw new BuildException(String.valueOf(m)
+                        + " mapper implementation does not support nested mappers!");
+                }
+            }
+        }
+        container.add(fileNameMapper);
+    }
+
+    /**
+     * Add a Mapper
+     * @param mapper the mapper to add
+     */
+    public void addConfiguredMapper(Mapper mapper) {
+        add(mapper.getImplementation());
+    }
+
+    /**
+     * Set the class name of the FileNameMapper to use.
+     */
+    public void setClassname(String classname) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.classname = classname;
+    }
+
+    /**
+     * Set the classpath to load the FileNameMapper through (attribute).
+     */
+    public void setClasspath(Path classpath) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        if (this.classpath == null) {
+            this.classpath = classpath;
+        } else {
+            this.classpath.append(classpath);
+        }
+    }
+
+    /**
+     * Set the classpath to load the FileNameMapper through (nested element).
+     */
+    public Path createClasspath() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        if (this.classpath == null) {
+            this.classpath = new Path(getProject());
+        }
+        return this.classpath.createPath();
+    }
+
+    /**
+     * Set the classpath to load the FileNameMapper through via
+     * reference (attribute).
+     */
+    public void setClasspathRef(Reference r) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        createClasspath().setRefid(r);
+    }
+
+    /**
+     * Set the argument to FileNameMapper.setFrom
+     */
+    public void setFrom(String from) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.from = from;
+    }
+
+    /**
+     * Set the argument to FileNameMapper.setTo
+     */
+    public void setTo(String to) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.to = to;
+    }
+
+    /**
+     * Make this Mapper instance a reference to another Mapper.
+     *
+     * <p>You must not set any other attribute if you make it a
+     * reference.</p>
+     */
+    public void setRefid(Reference r) throws BuildException {
+        if (type != null || from != null || to != null) {
+            throw tooManyAttributes();
+        }
+        super.setRefid(r);
+    }
+
+    /**
+     * Returns a fully configured FileNameMapper implementation.
+     */
+    public FileNameMapper getImplementation() throws BuildException {
+        if (isReference()) {
+            return getRef().getImplementation();
+        }
+
+        if (type == null && classname == null && container == null) {
+            throw new BuildException(
+                "nested mapper or "
+                + "one of the attributes type or classname is required");
+        }
+
+        if (container != null) {
+            return container;
+        }
+
+        if (type != null && classname != null) {
+            throw new BuildException(
+                "must not specify both type and classname attribute");
+        }
+
+        try {
+            FileNameMapper m
+                = (FileNameMapper)(getImplementationClass().newInstance());
+            final Project project = getProject();
+            if (project != null) {
+                project.setProjectReference(m);
+            }
+            m.setFrom(from);
+            m.setTo(to);
+
+            return m;
+        } catch (BuildException be) {
+            throw be;
+        } catch (Throwable t) {
+            throw new BuildException(t);
+        }
+    }
+
+     /**
+     * Gets the Class object associated with the mapper implementation.
+     * @return <CODE>Class</CODE>.
+     */
+    protected Class getImplementationClass() throws ClassNotFoundException {
+
+        String classname = this.classname;
+        if (type != null) {
+            classname = type.getImplementation();
+        }
+
+        ClassLoader loader = (classpath == null)
+            ? getClass().getClassLoader()
+            : getProject().createClassLoader(classpath);
+
+        return Class.forName(classname, true, loader);
+    }
+
+    /**
+     * Performs the check for circular references and returns the
+     * referenced Mapper.
+     */
+    protected Mapper getRef() {
+        if (!isChecked()) {
+            Stack stk = new Stack();
+            stk.push(this);
+            dieOnCircularReference(stk, getProject());
+        }
+
+        Object o = getRefid().getReferencedObject(getProject());
+        if (!(o instanceof Mapper)) {
+            String msg = getRefid().getRefId() + " doesn\'t denote a mapper";
+            throw new BuildException(msg);
+        } else {
+            return (Mapper) o;
+        }
+    }
+
+    /**
+     * Class as Argument to FileNameMapper.setType.
+     */
+    public static class MapperType extends EnumeratedAttribute {
+        private Properties implementations;
+
+        public MapperType() {
+            implementations = new Properties();
+            implementations.put("identity",
+                                "org.apache.tools.ant.util.IdentityMapper");
+            implementations.put("flatten",
+                                "org.apache.tools.ant.util.FlatFileNameMapper");
+            implementations.put("glob",
+                                "org.apache.tools.ant.util.GlobPatternMapper");
+            implementations.put("merge",
+                                "org.apache.tools.ant.util.MergingMapper");
+            implementations.put("regexp",
+                                "org.apache.tools.ant.util.RegexpPatternMapper");
+            implementations.put("package",
+                                "org.apache.tools.ant.util.PackageNameMapper");
+            implementations.put("unpackage",
+                                "org.apache.tools.ant.util.UnPackageNameMapper");
+        }
+
+        public String[] getValues() {
+            return new String[] {"identity", "flatten", "glob",
+                                 "merge", "regexp", "package", "unpackage"};
+        }
+
+        public String getImplementation() {
+            return implementations.getProperty(getValue());
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Parameter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Parameter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Parameter.java	(revision 16139)
@@ -0,0 +1,51 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+/**
+ * A parameter is composed of a name, type and value.
+ *
+ */
+public final class Parameter {
+    private String name = null;
+    private String type = null;
+    private String value = null;
+
+    public final void setName(final String name) {
+        this.name = name;
+    }
+
+    public final void setType(final String type) {
+        this.type = type;
+    }
+
+    public final void setValue(final String value) {
+        this.value = value;
+    }
+
+    public final String getName() {
+        return name;
+    }
+
+    public final String getType() {
+        return type;
+    }
+
+    public final String getValue() {
+        return value;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Parameterizable.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Parameterizable.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Parameterizable.java	(revision 16139)
@@ -0,0 +1,30 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+/**
+ * Parameterizable objects take genric key value pairs.
+ *
+ */
+public interface Parameterizable {
+    /**
+     * Set the parameters
+     *
+     * @param parameters an array of name/type/value parameters.
+     */
+    void setParameters(Parameter[] parameters);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Path.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Path.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Path.java	(revision 16139)
@@ -0,0 +1,687 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.Stack;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.PathTokenizer;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+
+
+/**
+ * This object represents a path as used by CLASSPATH or PATH
+ * environment variable.
+ * <p>
+ * <code>
+ * &lt;sometask&gt;<br>
+ * &nbsp;&nbsp;&lt;somepath&gt;<br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/path/to/file.jar" /&gt;<br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement path="/path/to/file2.jar:/path/to/class2;/path/to/class3" /&gt;<br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/path/to/file3.jar" /&gt;<br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/path/to/file4.jar" /&gt;<br>
+ * &nbsp;&nbsp;&lt;/somepath&gt;<br>
+ * &lt;/sometask&gt;<br>
+ * </code>
+ * <p>
+ * The object implemention <code>sometask</code> must provide a method called
+ * <code>createSomepath</code> which returns an instance of <code>Path</code>.
+ * Nested path definitions are handled by the Path object and must be labeled
+ * <code>pathelement</code>.<p>
+ *
+ * The path element takes a parameter <code>path</code> which will be parsed
+ * and split into single elements. It will usually be used
+ * to define a path from an environment variable.
+ *
+ */
+
+public class Path extends DataType implements Cloneable {
+
+    private Vector elements;
+
+    /** The system classspath as a Path object */
+    public static Path systemClasspath =
+        new Path(null, System.getProperty("java.class.path"));
+
+
+    /**
+     * The system bootclassspath as a Path object.
+     *
+     * @since Ant 1.6.2
+     */
+    public static Path systemBootClasspath =
+        new Path(null, System.getProperty("sun.boot.class.path"));
+
+
+    /**
+     * Helper class, holds the nested <code>&lt;pathelement&gt;</code> values.
+     */
+    public class PathElement {
+        private String[] parts;
+
+        public void setLocation(File loc) {
+            parts = new String[] {translateFile(loc.getAbsolutePath())};
+        }
+
+        public void setPath(String path) {
+            parts = Path.translatePath(getProject(), path);
+        }
+
+        public String[] getParts() {
+            return parts;
+        }
+    }
+
+    /**
+     * Invoked by IntrospectionHelper for <code>setXXX(Path p)</code>
+     * attribute setters.
+     * @param project the <CODE>Project</CODE> for this path.
+     * @param path the <CODE>String</CODE> path definition.
+     */
+    public Path(Project p, String path) {
+        this(p);
+        createPathElement().setPath(path);
+    }
+
+    /**
+     * Construct an empty <CODE>Path</CODE>.
+     * @param project the <CODE>Project</CODE> for this path.
+     */
+    public Path(Project project) {
+        setProject(project);
+        elements = new Vector();
+    }
+
+    /**
+     * Adds a element definition to the path.
+     * @param location the location of the element to add (must not be
+     * <code>null</code> nor empty.
+     */
+    public void setLocation(File location) throws BuildException {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        createPathElement().setLocation(location);
+    }
+
+
+    /**
+     * Parses a path definition and creates single PathElements.
+     * @param path the <CODE>String</CODE> path definition.
+     */
+    public void setPath(String path) throws BuildException {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        createPathElement().setPath(path);
+    }
+
+    /**
+     * Makes this instance in effect a reference to another Path instance.
+     *
+     * <p>You must not set another attribute or nest elements inside
+     * this element if you make it a reference.</p>
+     */
+    public void setRefid(Reference r) throws BuildException {
+        if (!elements.isEmpty()) {
+            throw tooManyAttributes();
+        }
+        elements.addElement(r);
+        super.setRefid(r);
+    }
+
+    /**
+     * Creates the nested <code>&lt;pathelement&gt;</code> element.
+     */
+    public PathElement createPathElement() throws BuildException {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        PathElement pe = new PathElement();
+        elements.addElement(pe);
+        return pe;
+    }
+
+    /**
+     * Adds a nested <code>&lt;fileset&gt;</code> element.
+     */
+    public void addFileset(FileSet fs) throws BuildException {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        elements.addElement(fs);
+        setChecked(false);
+    }
+
+    /**
+     * Adds a nested <code>&lt;filelist&gt;</code> element.
+     */
+    public void addFilelist(FileList fl) throws BuildException {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        elements.addElement(fl);
+        setChecked(false);
+    }
+
+    /**
+     * Adds a nested <code>&lt;dirset&gt;</code> element.
+     */
+    public void addDirset(DirSet dset) throws BuildException {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        elements.addElement(dset);
+        setChecked(false);
+    }
+
+    /**
+     * Adds a nested path
+     * @since Ant 1.6
+     */
+    public void add(Path path) throws BuildException {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        elements.addElement(path);
+        setChecked(false);
+
+    }
+
+    /**
+     * Creates a nested <code>&lt;path&gt;</code> element.
+     */
+    public Path createPath() throws BuildException {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        Path p = new Path(getProject());
+        elements.addElement(p);
+        setChecked(false);
+        return p;
+    }
+
+    /**
+     * Append the contents of the other Path instance to this.
+     */
+    public void append(Path other) {
+        if (other == null) {
+            return;
+        }
+        String[] l = other.list();
+        for (int i = 0; i < l.length; i++) {
+            if (elements.indexOf(l[i]) == -1) {
+                elements.addElement(l[i]);
+            }
+        }
+    }
+
+     /**
+     * Adds the components on the given path which exist to this
+     * Path. Components that don't exist, aren't added.
+     *
+     * @param source - source path whose components are examined for existence
+     */
+     public void addExisting(Path source) {
+         addExisting(source, false);
+     }
+
+    /** Same as addExisting, but support classpath behavior if tryUserDir
+     * is true. Classpaths are relative to user dir, not the project base.
+     * That used to break jspc test
+     *
+     * @param source
+     * @param tryUserDir
+     */
+    public void addExisting(Path source, boolean tryUserDir) {
+        String[] list = source.list();
+        File userDir = (tryUserDir) ? new File(System.getProperty("user.dir"))
+                : null;
+
+        for (int i = 0; i < list.length; i++) {
+            File f = null;
+            if (getProject() != null) {
+                f = getProject().resolveFile(list[i]);
+            } else {
+                f = new File(list[i]);
+            }
+            // probably not the best choice, but it solves the problem of
+            // relative paths in CLASSPATH
+            if (tryUserDir && !f.exists()) {
+                f = new File(userDir, list[i]);
+            }
+            if (f.exists()) {
+                setLocation(f);
+            } else {
+                log("dropping " + f + " from path as it doesn't exist",
+                    Project.MSG_VERBOSE);
+            }
+        }
+    }
+
+    /**
+     * Returns all path elements defined by this and nested path objects.
+     * @return list of path elements.
+     */
+    public String[] list() {
+        if (!isChecked()) {
+            // make sure we don't have a circular reference here
+            Stack stk = new Stack();
+            stk.push(this);
+            dieOnCircularReference(stk, getProject());
+        }
+
+        Vector result = new Vector(2 * elements.size());
+        for (int i = 0; i < elements.size(); i++) {
+            Object o = elements.elementAt(i);
+            if (o instanceof Reference) {
+                Reference r = (Reference) o;
+                o = r.getReferencedObject(getProject());
+                // we only support references to paths right now
+                if (!(o instanceof Path)) {
+                    String msg = r.getRefId() + " doesn\'t denote a path " + o;
+                    throw new BuildException(msg);
+                }
+            }
+
+            if (o instanceof String) {
+                // obtained via append
+                addUnlessPresent(result, (String) o);
+            } else if (o instanceof PathElement) {
+                String[] parts = ((PathElement) o).getParts();
+                if (parts == null) {
+                    throw new BuildException("You must either set location or"
+                        + " path on <pathelement>");
+                }
+                for (int j = 0; j < parts.length; j++) {
+                    addUnlessPresent(result, parts[j]);
+                }
+            } else if (o instanceof Path) {
+                Path p = (Path) o;
+                if (p.getProject() == null) {
+                    p.setProject(getProject());
+                }
+                String[] parts = p.list();
+                for (int j = 0; j < parts.length; j++) {
+                    addUnlessPresent(result, parts[j]);
+                }
+            } else if (o instanceof DirSet) {
+                DirSet dset = (DirSet) o;
+                addUnlessPresent(result, dset.getDir(getProject()),
+                    dset.getDirectoryScanner(getProject()).getIncludedDirectories());
+            } else if (o instanceof FileSet) {
+                FileSet fs = (FileSet) o;
+                addUnlessPresent(result, fs.getDir(getProject()),
+                    fs.getDirectoryScanner(getProject()).getIncludedFiles());
+            } else if (o instanceof FileList) {
+                FileList fl = (FileList) o;
+                addUnlessPresent(result,
+                    fl.getDir(getProject()), fl.getFiles(getProject()));
+            }
+        }
+        String[] res = new String[result.size()];
+        result.copyInto(res);
+        return res;
+    }
+
+
+    /**
+     * Returns a textual representation of the path, which can be used as
+     * CLASSPATH or PATH environment variable definition.
+     * @return a textual representation of the path.
+     */
+    public String toString() {
+        final String[] list = list();
+
+        // empty path return empty string
+        if (list.length == 0) {
+            return "";
+        }
+
+        // path containing one or more elements
+        final StringBuffer result = new StringBuffer(list[0].toString());
+        for (int i = 1; i < list.length; i++) {
+            result.append(File.pathSeparatorChar);
+            result.append(list[i]);
+        }
+
+        return result.toString();
+    }
+
+    /**
+     * Splits a PATH (with : or ; as separators) into its parts.
+     */
+    public static String[] translatePath(Project project, String source) {
+        final Vector result = new Vector();
+        if (source == null) {
+            return new String[0];
+        }
+
+        PathTokenizer tok = new PathTokenizer(source);
+        StringBuffer element = new StringBuffer();
+        while (tok.hasMoreTokens()) {
+            String pathElement = tok.nextToken();
+            try {
+                element.append(resolveFile(project, pathElement));
+            } catch (BuildException e) {
+                project.log("Dropping path element " + pathElement
+                    + " as it is not valid relative to the project",
+                    Project.MSG_VERBOSE);
+            }
+            for (int i = 0; i < element.length(); i++) {
+                translateFileSep(element, i);
+            }
+            result.addElement(element.toString());
+            element = new StringBuffer();
+        }
+        String[] res = new String[result.size()];
+        result.copyInto(res);
+        return res;
+    }
+
+    /**
+     * Returns its argument with all file separator characters
+     * replaced so that they match the local OS conventions.
+     */
+    public static String translateFile(String source) {
+        if (source == null) {
+          return "";
+        }
+
+        final StringBuffer result = new StringBuffer(source);
+        for (int i = 0; i < result.length(); i++) {
+            translateFileSep(result, i);
+        }
+
+        return result.toString();
+    }
+
+    /**
+     * Translates all occurrences of / or \ to correct separator of the
+     * current platform and returns whether it had to do any
+     * replacements.
+     */
+    protected static boolean translateFileSep(StringBuffer buffer, int pos) {
+        if (buffer.charAt(pos) == '/' || buffer.charAt(pos) == '\\') {
+            buffer.setCharAt(pos, File.separatorChar);
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * How many parts does this Path instance consist of.
+     */
+    public int size() {
+        return list().length;
+    }
+
+    /**
+     * Return a Path that holds the same elements as this instance.
+     */
+    public Object clone() {
+        try {
+            Path p = (Path) super.clone();
+            p.elements = (Vector) elements.clone();
+            return p;
+        } catch (CloneNotSupportedException e) {
+            throw new BuildException(e);
+        }
+    }
+
+    /**
+     * Overrides the version of DataType to recurse on all DataType
+     * child elements that may have been added.
+     */
+    protected void dieOnCircularReference(Stack stk, Project p)
+        throws BuildException {
+
+        if (isChecked()) {
+            return;
+        }
+
+        Enumeration e = elements.elements();
+        while (e.hasMoreElements()) {
+            Object o = e.nextElement();
+            if (o instanceof Reference) {
+                o = ((Reference) o).getReferencedObject(p);
+            }
+
+            if (o instanceof DataType) {
+                if (stk.contains(o)) {
+                    throw circularReference();
+                } else {
+                    stk.push(o);
+                    ((DataType) o).dieOnCircularReference(stk, p);
+                    stk.pop();
+                }
+            }
+        }
+        setChecked(true);
+    }
+
+    /**
+     * Resolve a filename with Project's help - if we know one that is.
+     *
+     * <p>Assume the filename is absolute if project is null.</p>
+     */
+    private static String resolveFile(Project project, String relativeName) {
+        if (project != null) {
+            File f = project.resolveFile(relativeName);
+            return f.getAbsolutePath();
+        }
+        return relativeName;
+    }
+
+    /**
+     * Adds a String to the Vector if it isn't already included.
+     */
+    private static void addUnlessPresent(Vector v, String s) {
+        if (v.indexOf(s) == -1) {
+            v.addElement(s);
+        }
+    }
+
+    /**
+     * Adds absolute path names of listed files in the given directory
+     * to the Vector if they are not already included.
+     */
+    private static void addUnlessPresent(Vector v, File dir, String[] s) {
+        for (int j = 0; j < s.length; j++) {
+            File d = new File(dir, s[j]);
+            String absolutePath = d.getAbsolutePath();
+            addUnlessPresent(v, translateFile(absolutePath));
+        }
+    }
+
+    /**
+     * Concatenates the system class path in the order specified by
+     * the ${build.sysclasspath} property - using &quot;last&quot; as
+     * default value.
+     */
+    public Path concatSystemClasspath() {
+        return concatSystemClasspath("last");
+    }
+
+    /**
+     * Concatenates the system class path in the order specified by
+     * the ${build.sysclasspath} property - using the supplied value
+     * if ${build.sysclasspath} has not been set.
+     */
+    public Path concatSystemClasspath(String defValue) {
+
+        Path result = new Path(getProject());
+
+        String order = defValue;
+        if (getProject() != null) {
+            String o = getProject().getProperty("build.sysclasspath");
+            if (o != null) {
+                order = o;
+            }
+        }
+
+        if (order.equals("only")) {
+            // only: the developer knows what (s)he is doing
+            result.addExisting(Path.systemClasspath, true);
+
+        } else if (order.equals("first")) {
+            // first: developer could use a little help
+            result.addExisting(Path.systemClasspath, true);
+            result.addExisting(this);
+
+        } else if (order.equals("ignore")) {
+            // ignore: don't trust anyone
+            result.addExisting(this);
+
+        } else {
+            // last: don't trust the developer
+            if (!order.equals("last")) {
+                log("invalid value for build.sysclasspath: " + order,
+                    Project.MSG_WARN);
+            }
+
+            result.addExisting(this);
+            result.addExisting(Path.systemClasspath, true);
+        }
+
+
+        return result;
+
+    }
+
+    /**
+     * Add the Java Runtime classes to this Path instance.
+     */
+    public void addJavaRuntime() {
+        if ("Kaffe".equals(System.getProperty("java.vm.name"))) {
+            // newer versions of Kaffe (1.1.1+) won't have this,
+            // but this will be sorted by FileSet anyway.
+            File kaffeShare = new File(System.getProperty("java.home")
+                                       + File.separator + "share"
+                                       + File.separator + "kaffe");
+            if (kaffeShare.isDirectory()) {
+                FileSet kaffeJarFiles = new FileSet();
+                kaffeJarFiles.setDir(kaffeShare);
+                kaffeJarFiles.setIncludes("*.jar");
+                addFileset(kaffeJarFiles);
+            }
+        } else if ("GNU libgcj".equals(System.getProperty("java.vm.name"))) {
+            addExisting(systemBootClasspath);
+        }
+
+        if (System.getProperty("java.vendor").toLowerCase(Locale.US).indexOf("microsoft") >= 0) {
+            // Pull in *.zip from packages directory
+            FileSet msZipFiles = new FileSet();
+            msZipFiles.setDir(new File(System.getProperty("java.home")
+                + File.separator + "Packages"));
+            msZipFiles.setIncludes("*.ZIP");
+            addFileset(msZipFiles);
+        } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
+            addExisting(new Path(null,
+                                 System.getProperty("java.home")
+                                 + File.separator + "lib"
+                                 + File.separator
+                                 + "classes.zip"));
+        } else {
+            // JDK > 1.1 seems to set java.home to the JRE directory.
+            addExisting(new Path(null,
+                                 System.getProperty("java.home")
+                                 + File.separator + "lib"
+                                 + File.separator + "rt.jar"));
+            // Just keep the old version as well and let addExisting
+            // sort it out.
+            addExisting(new Path(null,
+                                 System.getProperty("java.home")
+                                 + File.separator + "jre"
+                                 + File.separator + "lib"
+                                 + File.separator + "rt.jar"));
+
+            // Sun's and Apple's 1.4 have JCE and JSSE in separate jars.
+            String[] secJars = {"jce", "jsse"};
+            for (int i = 0; i < secJars.length; i++) {
+                addExisting(new Path(null,
+                                     System.getProperty("java.home")
+                                     + File.separator + "lib"
+                                     + File.separator + secJars[i] + ".jar"));
+                addExisting(new Path(null,
+                                     System.getProperty("java.home")
+                                     + File.separator + ".."
+                                     + File.separator + "Classes"
+                                     + File.separator + secJars[i] + ".jar"));
+            }
+
+            // IBM's 1.4 has rt.jar split into 4 smaller jars and a combined
+            // JCE/JSSE in security.jar.
+            String[] ibmJars
+                = {"core", "graphics", "security", "server", "xml"};
+            for (int i = 0; i < ibmJars.length; i++) {
+                addExisting(new Path(null,
+                                     System.getProperty("java.home")
+                                     + File.separator + "lib"
+                                     + File.separator + ibmJars[i] + ".jar"));
+            }
+
+            // Added for MacOS X
+            addExisting(new Path(null,
+                                 System.getProperty("java.home")
+                                 + File.separator + ".."
+                                 + File.separator + "Classes"
+                                 + File.separator + "classes.jar"));
+            addExisting(new Path(null,
+                                 System.getProperty("java.home")
+                                 + File.separator + ".."
+                                 + File.separator + "Classes"
+                                 + File.separator + "ui.jar"));
+        }
+    }
+
+    /**
+     * Emulation of extdirs feature in java >= 1.2.
+     * This method adds all files in the given
+     * directories (but not in sub-directories!) to the classpath,
+     * so that you don't have to specify them all one by one.
+     * @param extdirs - Path to append files to
+     */
+    public void addExtdirs(Path extdirs) {
+        if (extdirs == null) {
+            String extProp = System.getProperty("java.ext.dirs");
+            if (extProp != null) {
+                extdirs = new Path(getProject(), extProp);
+            } else {
+                return;
+            }
+        }
+
+        String[] dirs = extdirs.list();
+        for (int i = 0; i < dirs.length; i++) {
+            File dir = getProject().resolveFile(dirs[i]);
+            if (dir.exists() && dir.isDirectory()) {
+                FileSet fs = new FileSet();
+                fs.setDir(dir);
+                fs.setIncludes("*");
+                addFileset(fs);
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/PatternSet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/PatternSet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/PatternSet.java	(revision 16139)
@@ -0,0 +1,524 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Stack;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Named collection of include/exclude tags.
+ *
+ * <p>Moved out of MatchingTask to make it a standalone object that
+ * could be referenced (by scripts for example).
+ *
+ */
+public class PatternSet extends DataType implements Cloneable {
+    private Vector includeList = new Vector();
+    private Vector excludeList = new Vector();
+    private Vector includesFileList = new Vector();
+    private Vector excludesFileList = new Vector();
+
+    /**
+     * inner class to hold a name on list.  "If" and "Unless" attributes
+     * may be used to invalidate the entry based on the existence of a
+     * property (typically set thru the use of the Available task).
+     */
+    public class NameEntry {
+        private String name;
+        private String ifCond;
+        private String unlessCond;
+
+        /**
+         * Sets the name pattern.
+         *
+         * @param name The pattern string.
+         */
+        public void setName(String name) {
+            this.name = name;
+        }
+
+        /**
+         * Sets the if attribute. This attribute and the "unless"
+         * attribute are used to validate the name, based in the
+         * existence of the property.
+         *
+         * @param cond A property name. If this property is not
+         *             present, the name is invalid.
+         */
+        public void setIf(String cond) {
+            ifCond = cond;
+        }
+
+        /**
+         * Sets the unless attribute. This attribute and the "if"
+         * attribute are used to validate the name, based in the
+         * existence of the property.
+         *
+         * @param cond A property name. If this property is
+         *             present, the name is invalid.
+         */
+        public void setUnless(String cond) {
+            unlessCond = cond;
+        }
+
+        /**
+         * @return the name attribute.
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * This validates the name - checks the if and unless
+         * properties.
+         *
+         * @param p the current project, used to check the presence or
+         *          absence of a property.
+         * @return  the name attribute or null if the "if" or "unless"
+         *          properties are not/are set.
+         */
+        public String evalName(Project p) {
+            return valid(p) ? name : null;
+        }
+
+        private boolean valid(Project p) {
+            if (ifCond != null && p.getProperty(ifCond) == null) {
+                return false;
+            } else if (unlessCond != null && p.getProperty(unlessCond) != null) {
+                return false;
+            }
+            return true;
+        }
+
+        /**
+         * @return a printable form of this object.
+         */
+        public String toString() {
+            if (name == null) {
+                throw new BuildException(
+                    "Missing attribute \"name\" for a pattern");
+            }
+            StringBuffer buf = new StringBuffer(name);
+            if ((ifCond != null) || (unlessCond != null)) {
+                buf.append(":");
+                String connector = "";
+
+                if (ifCond != null) {
+                    buf.append("if->");
+                    buf.append(ifCond);
+                    connector = ";";
+                }
+                if (unlessCond != null) {
+                    buf.append(connector);
+                    buf.append("unless->");
+                    buf.append(unlessCond);
+                }
+            }
+
+            return buf.toString();
+        }
+    }
+
+    /**
+     * Creates a new <code>PatternSet</code> instance.
+     */
+    public PatternSet() {
+        super();
+    }
+
+    /**
+     * Makes this instance in effect a reference to another PatternSet
+     * instance.
+     *
+     * <p>You must not set another attribute or nest elements inside
+     * this element if you make it a reference.</p>
+     * @param r the reference to another patternset.
+     * @throws BuildException on error.
+     */
+    public void setRefid(Reference r) throws BuildException {
+        if (!includeList.isEmpty() || !excludeList.isEmpty()) {
+            throw tooManyAttributes();
+        }
+        super.setRefid(r);
+    }
+
+    /**
+     * This is a patternset nested element.
+     *
+     * @param p a configured patternset nested element.
+     */
+    public void addConfiguredPatternset(PatternSet p) {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+
+        String[] nestedIncludes = p.getIncludePatterns(getProject());
+        String[] nestedExcludes = p.getExcludePatterns(getProject());
+
+        if (nestedIncludes != null) {
+            for (int i = 0; i < nestedIncludes.length; i++) {
+                createInclude().setName(nestedIncludes[i]);
+            }
+        }
+
+        if (nestedExcludes != null) {
+            for (int i = 0; i < nestedExcludes.length; i++) {
+                createExclude().setName(nestedExcludes[i]);
+            }
+        }
+    }
+
+    /**
+     * add a name entry on the include list
+     * @return a nested include element to be configured.
+     */
+    public NameEntry createInclude() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        return addPatternToList(includeList);
+    }
+
+    /**
+     * add a name entry on the include files list
+     * @return a nested includesfile element to be configured.
+     */
+    public NameEntry createIncludesFile() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        return addPatternToList(includesFileList);
+    }
+
+    /**
+     * add a name entry on the exclude list
+     * @return a nested exclude element to be configured.
+     */
+    public NameEntry createExclude() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        return addPatternToList(excludeList);
+    }
+
+    /**
+     * add a name entry on the exclude files list
+     * @return a nested excludesfile element to be configured.
+     */
+    public NameEntry createExcludesFile() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        return addPatternToList(excludesFileList);
+    }
+
+    /**
+     * Appends <code>includes</code> to the current list of include patterns.
+     * Patterns may be separated by a comma or a space.
+     *
+     * @param includes the string containing the include patterns
+     */
+    public void setIncludes(String includes) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        if (includes != null && includes.length() > 0) {
+            StringTokenizer tok = new StringTokenizer(includes, ", ", false);
+            while (tok.hasMoreTokens()) {
+                createInclude().setName(tok.nextToken());
+            }
+        }
+    }
+
+    /**
+     * Appends <code>excludes</code> to the current list of exclude patterns.
+     * Patterns may be separated by a comma or a space.
+     *
+     * @param excludes the string containing the exclude patterns
+     */
+    public void setExcludes(String excludes) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        if (excludes != null && excludes.length() > 0) {
+            StringTokenizer tok = new StringTokenizer(excludes, ", ", false);
+            while (tok.hasMoreTokens()) {
+                createExclude().setName(tok.nextToken());
+            }
+        }
+    }
+
+    /**
+     * add a name entry to the given list
+     */
+    private NameEntry addPatternToList(Vector list) {
+        NameEntry result = new NameEntry();
+        list.addElement(result);
+        return result;
+    }
+
+    /**
+     * Sets the name of the file containing the includes patterns.
+     *
+     * @param includesFile The file to fetch the include patterns from.
+     * @throws BuildException on error.
+     */
+     public void setIncludesfile(File includesFile) throws BuildException {
+         if (isReference()) {
+             throw tooManyAttributes();
+         }
+         createIncludesFile().setName(includesFile.getAbsolutePath());
+     }
+
+    /**
+     * Sets the name of the file containing the excludes patterns.
+     *
+     * @param excludesFile The file to fetch the exclude patterns from.
+     * @throws BuildException on error.
+     */
+     public void setExcludesfile(File excludesFile) throws BuildException {
+         if (isReference()) {
+             throw tooManyAttributes();
+         }
+         createExcludesFile().setName(excludesFile.getAbsolutePath());
+     }
+
+    /**
+     *  Reads path matching patterns from a file and adds them to the
+     *  includes or excludes list (as appropriate).
+     */
+    private void readPatterns(File patternfile, Vector patternlist, Project p)
+        throws BuildException {
+
+        BufferedReader patternReader = null;
+        try {
+            // Get a FileReader
+            patternReader =
+                new BufferedReader(new FileReader(patternfile));
+
+            // Create one NameEntry in the appropriate pattern list for each
+            // line in the file.
+            String line = patternReader.readLine();
+            while (line != null) {
+                if (line.length() > 0) {
+                    line = p.replaceProperties(line);
+                    addPatternToList(patternlist).setName(line);
+                }
+                line = patternReader.readLine();
+            }
+        } catch (IOException ioe)  {
+            String msg = "An error occurred while reading from pattern file: "
+                + patternfile;
+            throw new BuildException(msg, ioe);
+        } finally {
+            if (null != patternReader) {
+                try {
+                    patternReader.close();
+                } catch (IOException ioe) {
+                    //Ignore exception
+                }
+            }
+        }
+    }
+
+    /**
+     * Adds the patterns of the other instance to this set.
+     * @param other the other PatternSet instance.
+     * @param p the current project.
+     */
+    public void append(PatternSet other, Project p) {
+        if (isReference()) {
+            throw new BuildException("Cannot append to a reference");
+        }
+
+        String[] incl = other.getIncludePatterns(p);
+        if (incl != null) {
+            for (int i = 0; i < incl.length; i++) {
+                createInclude().setName(incl[i]);
+            }
+        }
+
+        String[] excl = other.getExcludePatterns(p);
+        if (excl != null) {
+            for (int i = 0; i < excl.length; i++) {
+                createExclude().setName(excl[i]);
+            }
+        }
+    }
+
+    /**
+     * Returns the filtered include patterns.
+     * @param p the current project.
+     * @return the filtered included patterns.
+     */
+    public String[] getIncludePatterns(Project p) {
+        if (isReference()) {
+            return getRef(p).getIncludePatterns(p);
+        } else {
+            readFiles(p);
+            return makeArray(includeList, p);
+        }
+    }
+
+    /**
+     * Returns the filtered include patterns.
+     * @param p the current project.
+     * @return the filtered excluded patterns.
+     */
+    public String[] getExcludePatterns(Project p) {
+        if (isReference()) {
+            return getRef(p).getExcludePatterns(p);
+        } else {
+            readFiles(p);
+            return makeArray(excludeList, p);
+        }
+    }
+
+    /**
+     * helper for FileSet.
+     */
+    boolean hasPatterns(Project p) {
+        if (isReference()) {
+            return getRef(p).hasPatterns(p);
+        } else {
+            return includesFileList.size() > 0 || excludesFileList.size() > 0
+                || includeList.size() > 0 || excludeList.size() > 0;
+        }
+    }
+
+    /**
+     * Performs the check for circular references and returns the
+     * referenced PatternSet.
+     */
+    private PatternSet getRef(Project p) {
+        if (!isChecked()) {
+            Stack stk = new Stack();
+            stk.push(this);
+            dieOnCircularReference(stk, p);
+        }
+
+        Object o = getRefid().getReferencedObject(p);
+        if (!(o instanceof PatternSet)) {
+            String msg = getRefid().getRefId() + " doesn\'t denote a patternset";
+            throw new BuildException(msg);
+        } else {
+            return (PatternSet) o;
+        }
+    }
+
+    /**
+     * Convert a vector of NameEntry elements into an array of Strings.
+     */
+    private String[] makeArray(Vector list, Project p) {
+        if (list.size() == 0) {
+          return null;
+        }
+
+        Vector tmpNames = new Vector();
+        for (Enumeration e = list.elements(); e.hasMoreElements();) {
+            NameEntry ne = (NameEntry) e.nextElement();
+            String pattern = ne.evalName(p);
+            if (pattern != null && pattern.length() > 0) {
+                tmpNames.addElement(pattern);
+            }
+        }
+
+        String[] result = new String[tmpNames.size()];
+        tmpNames.copyInto(result);
+        return result;
+    }
+
+    /**
+     * Read includesfile ot excludesfile if not already done so.
+     */
+    private void readFiles(Project p) {
+        if (includesFileList.size() > 0) {
+            Enumeration e = includesFileList.elements();
+            while (e.hasMoreElements()) {
+                NameEntry ne = (NameEntry) e.nextElement();
+                String fileName = ne.evalName(p);
+                if (fileName != null) {
+                    File inclFile = p.resolveFile(fileName);
+                    if (!inclFile.exists()) {
+                        throw new BuildException("Includesfile "
+                                                 + inclFile.getAbsolutePath()
+                                                 + " not found.");
+                    }
+                    readPatterns(inclFile, includeList, p);
+                }
+            }
+            includesFileList.removeAllElements();
+        }
+
+        if (excludesFileList.size() > 0) {
+            Enumeration e = excludesFileList.elements();
+            while (e.hasMoreElements()) {
+                NameEntry ne = (NameEntry) e.nextElement();
+                String fileName = ne.evalName(p);
+                if (fileName != null) {
+                    File exclFile = p.resolveFile(fileName);
+                    if (!exclFile.exists()) {
+                        throw new BuildException("Excludesfile "
+                                                 + exclFile.getAbsolutePath()
+                                                 + " not found.");
+                    }
+                    readPatterns(exclFile, excludeList, p);
+                }
+            }
+            excludesFileList.removeAllElements();
+        }
+    }
+
+    /**
+     * @return a printable form of this object.
+     */
+    public String toString() {
+        return "patternSet{ includes: " + includeList
+                + " excludes: " + excludeList + " }";
+    }
+
+    /**
+     * @since Ant 1.6
+     * @return a clone of this patternset.
+     */
+    public Object clone() {
+        if (isReference()) {
+            return getRef(getProject()).clone();
+        } else {
+            try {
+                PatternSet ps = (PatternSet) super.clone();
+                ps.includeList = (Vector) includeList.clone();
+                ps.excludeList = (Vector) excludeList.clone();
+                ps.includesFileList = (Vector) includesFileList.clone();
+                ps.excludesFileList = (Vector) excludesFileList.clone();
+                return ps;
+            } catch (CloneNotSupportedException e) {
+                throw new BuildException(e);
+            }
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Permissions.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Permissions.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Permissions.java	(revision 16139)
@@ -0,0 +1,329 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import java.security.UnresolvedPermission;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.ExitException;
+
+/**
+ * This class implements a security manager meant for useage by tasks that run inside the
+ * ant VM. An examples are the Java Task and JUnitTask.
+ *
+ * The basic functionality is that nothing (except for a base set of permissions) is allowed, unless
+ * the permission is granted either explicitly or implicitly.
+ * If an permission is granted this can be overruled by explicitly revoking the permission.
+ *
+ * It is not permissible to add permissions (either granted or revoked) while the Security Manager
+ * is active (after calling setSecurityManager() but before calling restoreSecurityManager()).
+ *
+ * @since Ant 1.6
+ */
+public class Permissions {
+
+    private List grantedPermissions = new LinkedList();
+    private List revokedPermissions = new LinkedList();
+    private java.security.Permissions granted = null;
+    private SecurityManager origSm = null;
+    private boolean active = false;
+    private boolean delegateToOldSM = false;
+
+    /**
+     * default constructor
+     */
+    public Permissions() {
+    }
+    /**
+     * create a new set of permissions
+     * @param delegateToOldSM  if <code>true</code> the old security manager
+     * will be used if the permission has not been explicitly granted or revoked
+     * in this instance
+     * if false, it behaves like the default constructor
+     */
+    public Permissions(boolean delegateToOldSM) {
+        this.delegateToOldSM = delegateToOldSM;
+    }
+    /**
+     * Adds a permission to be granted.
+     * @param perm The Permissions.Permission to be granted.
+     */
+    public void addConfiguredGrant(Permissions.Permission perm) {
+        grantedPermissions.add(perm);
+    }
+
+    /**
+     * Adds a permission to be revoked.
+     * @param perm The Permissions.Permission to be revoked
+     */
+    public void addConfiguredRevoke(Permissions.Permission perm) {
+        revokedPermissions.add(perm);
+    }
+
+    /**
+     * To be used by tasks wishing to use this security model before executing the part to be
+     * subject to these Permissions. Note that setting the SecurityManager too early may
+     * prevent your part from starting, as for instance changing classloaders may be prohibited.
+     * The classloader for the new situation is supposed to be present.
+     */
+    public void setSecurityManager() throws BuildException{
+        origSm = System.getSecurityManager();
+        init();
+        System.setSecurityManager(new MySM());
+        active = true;
+    }
+
+    /**
+     * Initializes the list of granted permissions, checks the list of revoked permissions.
+     */
+    private void init() throws BuildException {
+        granted = new java.security.Permissions();
+        for (Iterator i = revokedPermissions.listIterator(); i.hasNext();) {
+            Permissions.Permission p = (Permissions.Permission) i.next();
+            if (p.getClassName() == null) {
+                throw new BuildException("Revoked permission " + p + " does not contain a class.");
+            }
+        }
+        for (Iterator i = grantedPermissions.listIterator(); i.hasNext();) {
+            Permissions.Permission p = (Permissions.Permission) i.next();
+            if (p.getClassName() == null) {
+                throw new BuildException("Granted permission " + p + " does not contain a class.");
+            } else {
+                java.security.Permission perm =  new UnresolvedPermission(p.getClassName(),p.getName(),p.getActions(),null);
+                granted.add(perm);
+            }
+        }
+        // Add base set of permissions
+        granted.add(new java.net.SocketPermission("localhost:1024-", "listen"));
+        granted.add(new java.util.PropertyPermission("java.version", "read"));
+        granted.add(new java.util.PropertyPermission("java.vendor", "read"));
+        granted.add(new java.util.PropertyPermission("java.vendor.url", "read"));
+        granted.add(new java.util.PropertyPermission("java.class.version", "read"));
+        granted.add(new java.util.PropertyPermission("os.name", "read"));
+        granted.add(new java.util.PropertyPermission("os.version", "read"));
+        granted.add(new java.util.PropertyPermission("os.arch", "read"));
+        granted.add(new java.util.PropertyPermission("file.encoding", "read"));
+        granted.add(new java.util.PropertyPermission("file.separator", "read"));
+        granted.add(new java.util.PropertyPermission("path.separator", "read"));
+        granted.add(new java.util.PropertyPermission("line.separator", "read"));
+        granted.add(new java.util.PropertyPermission("java.specification.version", "read"));
+        granted.add(new java.util.PropertyPermission("java.specification.vendor", "read"));
+        granted.add(new java.util.PropertyPermission("java.specification.name", "read"));
+        granted.add(new java.util.PropertyPermission("java.vm.specification.version", "read"));
+        granted.add(new java.util.PropertyPermission("java.vm.specification.vendor", "read"));
+        granted.add(new java.util.PropertyPermission("java.vm.specification.name", "read"));
+        granted.add(new java.util.PropertyPermission("java.vm.version", "read"));
+        granted.add(new java.util.PropertyPermission("java.vm.vendor", "read"));
+        granted.add(new java.util.PropertyPermission("java.vm.name", "read"));
+    }
+
+    /**
+     * To be used by tasks that just finished executing the parts subject to these permissions.
+     */
+    public void restoreSecurityManager() {
+        active = false;
+        System.setSecurityManager(origSm);
+    }
+
+    /**
+     * This inner class implements the actual SecurityManager that can be used by tasks
+     * supporting Permissions.
+     */
+    private class MySM extends SecurityManager {
+
+        /**
+         * Exit is treated in a special way in order to be able to return the exit code towards tasks.
+         * An ExitException is thrown instead of a simple SecurityException to indicate the exit
+         * code.
+         * Overridden from java.lang.SecurityManager
+         * @param status The exit status requested.
+         */
+        public void checkExit(int status) {
+            java.security.Permission perm = new java.lang.RuntimePermission("exitVM",null);
+            try {
+                checkPermission(perm);
+            } catch (SecurityException e) {
+                throw new ExitException(e.getMessage(), status);
+            }
+        }
+
+        /**
+         * The central point in checking permissions.
+         * Overridden from java.lang.SecurityManager
+         *
+         * @param perm The permission requested.
+         */
+        public void checkPermission(java.security.Permission perm) {
+            if (active) {
+                if (delegateToOldSM && !perm.getName().equals("exitVM")) {
+                    boolean permOK = false;
+                    if (granted.implies(perm)) {
+                        permOK = true;
+                    }
+                    checkRevoked(perm);
+                    /*
+                     if the permission was not explicitly granted or revoked
+                     the original security manager will do its work
+                    */
+                    if (!permOK && origSm != null) {
+                        origSm.checkPermission(perm);
+                    }
+                }  else {
+                    if (!granted.implies(perm)) {
+                        throw new SecurityException("Permission " + perm + " was not granted.");
+                    }
+                    checkRevoked(perm);
+                }
+            }
+        }
+        /**
+         * throws an exception if this permission is revoked
+         * @param perm the permission being checked
+         */
+        private void checkRevoked(java.security.Permission perm) {
+            for (Iterator i = revokedPermissions.listIterator(); i.hasNext();) {
+                if (((Permissions.Permission)i.next()).matches(perm)) {
+                    throw new SecurityException("Permission " + perm + " was revoked.");
+                }
+            }
+
+        }
+    }
+
+    /** Represents a permission. */
+    public static class Permission {
+        private String className;
+        private String name;
+        private String actionString;
+        private Set actions;
+
+        /**
+         * Sets the class, mandatory.
+         * @param aClass The class name of the permission.
+         */
+        public void setClass(String aClass) {
+                className = aClass.trim();
+        }
+
+        /** Get the class of the permission
+         * @return The class name of the permission.
+         */
+        public String getClassName() {
+            return className;
+        }
+
+        /**
+         * Sets the name of the permission.
+         * @param aName The name of the permission.
+         */
+        public void setName(String aName) {
+            name = aName.trim();
+        }
+
+        /**
+         * Get the name of the permission.
+         * @return  The name of the permission.
+         */
+        public String getName() {
+            return name;
+        }
+
+        /**
+         * Sets the actions.
+         * @param actions The actions of the permission.
+         */
+        public void setActions(String actions) {
+            actionString = actions;
+            if (actions.length() > 0) {
+                this.actions = parseActions(actions);
+            }
+        }
+
+        /**
+         * Gets the actions.
+         * @return The actions of the permission.
+         */
+        public String getActions() {
+            return actionString;
+        }
+
+        /**
+         *  Checks if the permission matches in case of a revoked permission.
+         * @param perm The permission to check against.
+         */
+        boolean matches(java.security.Permission perm) {
+
+            if (!className.equals(perm.getClass().getName())) {
+                return false;
+            }
+
+            if (name != null) {
+                if (name.endsWith("*")) {
+                    if (!perm.getName().startsWith(name.substring(0, name.length() - 1))) {
+                        return false;
+                    }
+                } else {
+                    if (!name.equals(perm.getName())) {
+                        return false;
+                    }
+                }
+            }
+
+            if (actions != null) {
+                Set as = parseActions(perm.getActions());
+                int size = as.size();
+                as.removeAll(actions);
+                if (as.size() == size) {
+                    // None of the actions revoked, so all allowed.
+                    return false;
+                }
+            }
+
+            return true;
+        }
+
+        /**
+         * Parses the actions into a set of separate strings.
+         * @param actions The actions to be parsed.
+         */
+        private Set parseActions(String actions) {
+            Set result = new HashSet();
+            StringTokenizer tk = new StringTokenizer(actions, ",");
+            while (tk.hasMoreTokens()) {
+                String item = tk.nextToken().trim();
+                if (!item.equals("")) {
+                    result.add(item);
+                }
+            }
+            return result;
+        }
+        /**
+         * get a string description of the permissions
+         * @return  string description of the permissions
+         */
+        public String toString() {
+            return ("Permission: " + className + " (\"" + name + "\", \"" + actions + "\")");
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/PropertySet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/PropertySet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/PropertySet.java	(revision 16139)
@@ -0,0 +1,466 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Stack;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.regexp.RegexpMatcher;
+import org.apache.tools.ant.util.regexp.RegexpMatcherFactory;
+
+/**
+ * A set of properties.
+ *
+ * @since Ant 1.6
+ */
+public class PropertySet extends DataType {
+
+    private boolean dynamic = true;
+    private boolean negate = false;
+    private Set cachedNames;
+    private Vector ptyRefs = new Vector();
+    private Vector setRefs = new Vector();
+    private Mapper mapper;
+
+    /**
+     * This is a nested class containing a reference to some properties
+     * and optionally a source of properties.
+     */
+    public static class PropertyRef {
+
+        private int count;
+        private String name;
+        private String regex;
+        private String prefix;
+        private String builtin;
+
+        /**
+         * Set the name.
+         * @param name a <code>String</code> value.
+         */
+        public void setName(String name) {
+            assertValid("name", name);
+            this.name = name;
+        }
+
+        /**
+         * Set the regular expression to use to filter the properties.
+         * @param regex a regular expression.
+         */
+        public void setRegex(String regex) {
+            assertValid("regex", regex);
+            this.regex = regex;
+        }
+
+        /**
+         * Set the prefix to use.
+         * @param prefix a <code>String</code> value.
+         */
+        public void setPrefix(String prefix) {
+            assertValid("prefix", prefix);
+            this.prefix = prefix;
+        }
+
+        /**
+         * Builtin property names - all, system or commandline.
+         * @param b an enumerated <code>BuildinPropertySetName</code> value.
+         */
+        public void setBuiltin(BuiltinPropertySetName b) {
+            String pBuiltIn = b.getValue();
+            assertValid("builtin", pBuiltIn);
+            this.builtin = pBuiltIn;
+        }
+
+        private void assertValid(String attr, String value) {
+            if (value == null || value.length() < 1) {
+                throw new BuildException("Invalid attribute: " + attr);
+            }
+
+            if (++count != 1) {
+                throw new BuildException("Attributes name, regex, and "
+                    + "prefix are mutually exclusive");
+            }
+        }
+
+        /**
+         * A debug toString().
+         * @return a string version of this object.
+         */
+        public String toString() {
+            return "name=" + name + ", regex=" + regex + ", prefix=" + prefix
+                + ", builtin=" + builtin;
+        }
+
+    } //end nested class
+
+    /**
+     * Allow properties of a particular name in the set.
+     * @param name the property name to allow.
+     */
+    public void appendName(String name) {
+        PropertyRef r = new PropertyRef();
+        r.setName(name);
+        addPropertyref(r);
+    }
+
+    /**
+     * Allow properties whose names match a regex in the set.
+     * @param regex the regular expression to use.
+     */
+    public void appendRegex(String regex) {
+        PropertyRef r = new PropertyRef();
+        r.setRegex(regex);
+        addPropertyref(r);
+    }
+
+    /**
+     * Allow properties whose names start with a prefix in the set.
+     * @param prefix the prefix to use.
+     */
+    public void appendPrefix(String prefix) {
+        PropertyRef r = new PropertyRef();
+        r.setPrefix(prefix);
+        addPropertyref(r);
+    }
+
+    /**
+     * Allow builtin (all, system or commandline) properties in the set.
+     * @param b the type of builtin properties.
+     */
+    public void appendBuiltin(BuiltinPropertySetName b) {
+        PropertyRef r = new PropertyRef();
+        r.setBuiltin(b);
+        addPropertyref(r);
+    }
+
+    /**
+     * Set a mapper to change property names.
+     * @param type mapper type.
+     * @param from source pattern.
+     * @param to output pattern.
+     */
+    public void setMapper(String type, String from, String to) {
+        Mapper m = createMapper();
+        Mapper.MapperType mapperType = new Mapper.MapperType();
+        mapperType.setValue(type);
+        m.setFrom(from);
+        m.setTo(to);
+    }
+
+    /**
+     * Add a property reference (nested element) to the references to be used.
+     * @param ref a property reference.
+     */
+    public void addPropertyref(PropertyRef ref) {
+        assertNotReference();
+        ptyRefs.addElement(ref);
+    }
+
+    /**
+     * Add another property set to this set.
+     * @param ref another property set.
+     */
+    public void addPropertyset(PropertySet ref) {
+        assertNotReference();
+        setRefs.addElement(ref);
+    }
+
+    /**
+     * Create a mapper to map the property names.
+     * @return a mapper to be configured.
+     */
+    public Mapper createMapper() {
+        assertNotReference();
+        if (mapper != null) {
+            throw new BuildException("Too many <mapper>s!");
+        }
+        mapper = new Mapper(getProject());
+        return mapper;
+    }
+
+    /**
+     * Add a nested FileNameMapper.
+     * @param fileNameMapper the mapper to add.
+     * @since Ant 1.6.3
+     */
+    public void add(FileNameMapper fileNameMapper) {
+        createMapper().add(fileNameMapper);
+    }
+
+    /**
+     * Set whether to reevaluate the set everytime the set is used.
+     * Default is true.
+     *
+     * @param dynamic if true, reevaluate the property set each time
+     *                the set is used. if false cache the property set
+     *                the first time and use the cached set on subsequent
+     *                occasions.
+     */
+    public void setDynamic(boolean dynamic) {
+        assertNotReference();
+        this.dynamic = dynamic;
+    }
+
+    /**
+     * Set whether to negate results.
+     * If "true", all properties not selected by nested elements will be returned.
+     *  Default is "false".
+     * @param negate if true, negate the selection criteria.
+     */
+    public void setNegate(boolean negate) {
+        assertNotReference();
+        this.negate = negate;
+    }
+
+    /**
+     * Get the dynamic attribute.
+     * @return true if the property set is to be evalulated each time it is used.
+     */
+    public boolean getDynamic() {
+        return isReference() ? getRef().dynamic : dynamic;
+    }
+
+    /**
+     * Get the mapper attribute.
+     * @return the mapper attribute.
+     */
+    public Mapper getMapper() {
+        return isReference() ? getRef().mapper : mapper;
+    }
+
+    /**
+     * Convert the system properties to a hashtable.
+     * Use propertynames to get the list of properties (including
+     * default ones).
+     */
+    private Hashtable getAllSystemProperties() {
+        Hashtable ret = new Hashtable();
+        for (Enumeration e = System.getProperties().propertyNames();
+             e.hasMoreElements();) {
+            String name = (String) e.nextElement();
+            ret.put(name, System.getProperties().getProperty(name));
+        }
+        return ret;
+    }
+
+    /**
+     * This is the operation to get the existing or recalculated properties.
+     * @return the properties for this propertyset.
+     */
+    public Properties getProperties() {
+        if (isReference()) {
+            return getRef().getProperties();
+        }
+        Set names = null;
+        Project prj = getProject();
+        Hashtable props =
+            prj == null ? getAllSystemProperties() : prj.getProperties();
+
+        //quick & dirty, to make nested mapped p-sets work:
+        for (Enumeration e = setRefs.elements(); e.hasMoreElements();) {
+            PropertySet set = (PropertySet) e.nextElement();
+            props.putAll(set.getProperties());
+        }
+
+        if (getDynamic() || cachedNames == null) {
+            names = new HashSet();
+            addPropertyNames(names, props);
+            // Add this PropertySet's nested PropertySets' property names.
+            for (Enumeration e = setRefs.elements(); e.hasMoreElements();) {
+                PropertySet set = (PropertySet) e.nextElement();
+                names.addAll(set.getProperties().keySet());
+            }
+            if (negate) {
+                //make a copy...
+                HashSet complement = new HashSet(props.keySet());
+                complement.removeAll(names);
+                names = complement;
+            }
+            if (!getDynamic()) {
+                cachedNames = names;
+            }
+        } else {
+            names = cachedNames;
+        }
+        FileNameMapper m = null;
+        Mapper myMapper = getMapper();
+        if (myMapper != null) {
+            m = myMapper.getImplementation();
+        }
+        Properties properties = new Properties();
+        //iterate through the names, get the matching values
+        for (Iterator iter = names.iterator(); iter.hasNext();) {
+            String name = (String) iter.next();
+            String value = (String) props.get(name);
+            if (m != null) {
+                //map the names
+                String[] newname = m.mapFileName(name);
+                if (newname != null) {
+                    name = newname[0];
+                }
+            }
+            properties.setProperty(name, value);
+        }
+        return properties;
+    }
+
+    /**
+     * @param  names the output Set to fill with the property names
+     *         matching this PropertySet selection criteria.
+     * @param  properties the current Project properties, passed in to
+     *         avoid needless duplication of the Hashtable during recursion.
+     */
+    private void addPropertyNames(Set names, Hashtable properties) {
+        Project prj = getProject();
+
+        // Add this PropertySet's property names.
+        for (Enumeration e = ptyRefs.elements(); e.hasMoreElements();) {
+            PropertyRef r = (PropertyRef) e.nextElement();
+            if (r.name != null) {
+                if (prj != null && prj.getProperty(r.name) != null) {
+                    names.add(r.name);
+                }
+            } else if (r.prefix != null) {
+                for (Enumeration p = properties.keys(); p.hasMoreElements();) {
+                    String name = (String) p.nextElement();
+                    if (name.startsWith(r.prefix)) {
+                        names.add(name);
+                    }
+                }
+            } else if (r.regex != null) {
+                RegexpMatcherFactory matchMaker = new RegexpMatcherFactory();
+                RegexpMatcher matcher = matchMaker.newRegexpMatcher();
+                matcher.setPattern(r.regex);
+                for (Enumeration p = properties.keys(); p.hasMoreElements();) {
+                    String name = (String) p.nextElement();
+                    if (matcher.matches(name)) {
+                        names.add(name);
+                    }
+                }
+            } else if (r.builtin != null) {
+
+                if (r.builtin.equals(BuiltinPropertySetName.ALL)) {
+                    names.addAll(properties.keySet());
+                } else if (r.builtin.equals(BuiltinPropertySetName.SYSTEM)) {
+                    names.addAll(System.getProperties().keySet());
+                } else if (r.builtin.equals(BuiltinPropertySetName
+                                              .COMMANDLINE)) {
+                    names.addAll(getProject().getUserProperties().keySet());
+                } else {
+                    throw new BuildException("Impossible: Invalid builtin "
+                                             + "attribute!");
+                }
+            } else {
+                throw new BuildException("Impossible: Invalid PropertyRef!");
+            }
+        }
+    }
+
+    /**
+     * Performs the check for circular references and returns the
+     * referenced PropertySet.
+     * @return the referenced PropertySet.
+     */
+    protected PropertySet getRef() {
+        return (PropertySet) getCheckedRef(PropertySet.class, "propertyset");
+    }
+
+    /**
+     * Sets the value of the refid attribute.
+     *
+     * @param  r the reference this datatype should point to.
+     * @throws BuildException if another attribute was set, since
+     *         refid and all other attributes are mutually exclusive.
+     */
+    public final void setRefid(Reference r) {
+        if (!noAttributeSet) {
+            throw tooManyAttributes();
+        }
+        super.setRefid(r);
+    }
+
+    /**
+     * Ensures this data type is not a reference.
+     *
+     * <p>Calling this method as the first line of every bean method of
+     * this data type (setXyz, addXyz, createXyz) ensure proper handling
+     * of the refid attribute.</p>
+     *
+     * @throws BuildException if the refid attribute was already set, since
+     *         refid and all other attributes are mutually exclusive.
+     */
+    protected final void assertNotReference() {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        noAttributeSet = false;
+    }
+
+    /**
+     * Flag which tracks whether any attribute has been set; used by
+     * {@link #assertNotReference()} and {@link #setRefid(Reference)}.
+     */
+    private boolean noAttributeSet = true;
+
+    /**
+     * Used for propertyref's builtin attribute.
+     */
+    public static class BuiltinPropertySetName extends EnumeratedAttribute {
+        static final String ALL = "all";
+        static final String SYSTEM = "system";
+        static final String COMMANDLINE = "commandline";
+        /** @see EnumeratedAttribute#getValues() */
+        public String[] getValues() {
+            return new String[] {ALL, SYSTEM, COMMANDLINE};
+        }
+    }
+
+    /**
+     * A debug toString.
+     * This gets a comma separated list of key=value pairs for
+     * the properties in the set.
+     * The output order is sorted according to the keys' <i>natural order</i>.
+     * @return a string rep of this object.
+     */
+    public String toString() {
+        StringBuffer b = new StringBuffer();
+        TreeMap sorted = new TreeMap(getProperties());
+        for (Iterator i = sorted.entrySet().iterator(); i.hasNext();) {
+            Map.Entry e = (Map.Entry) i.next();
+            if (b.length() != 0) {
+                b.append(", ");
+            }
+            b.append(e.getKey().toString());
+            b.append("=");
+            b.append(e.getValue().toString());
+        }
+        return b.toString();
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/RedirectorElement.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/RedirectorElement.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/RedirectorElement.java	(revision 16139)
@@ -0,0 +1,604 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+import java.io.File;
+import java.util.Stack;
+import java.util.Vector;
+import java.util.Iterator;
+import java.util.ArrayList;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Redirector;
+
+/**
+ * Element representation of a <CODE>Redirector</CODE>.
+ */
+public class RedirectorElement extends DataType {
+
+    /**
+     * Whether the input mapper was set via <CODE>setOutput</CODE>.
+     */
+    private boolean usingInput = false;
+
+    /**
+     * Whether the output mapper was set via <CODE>setOutput</CODE>.
+     */
+    private boolean usingOutput = false;
+
+    /**
+     * Whether the error mapper was set via <CODE>setError</CODE>.
+     */
+    private boolean usingError = false;
+
+    /**
+     * Indicates if standard error should be logged to Ant's log system
+     * rather than the output. This has no effect if standard error is
+     * redirected to a file or property.
+     */
+    private Boolean logError;
+
+    /** The name of the property into which output is to be stored */
+    private String outputProperty;
+
+    /** The name of the property into which error output is to be stored */
+    private String errorProperty;
+
+    /** String from which input is taken */
+    private String inputString;
+
+    /** Flag which indicates if error and output files are to be appended. */
+    private Boolean append;
+
+    /** Flag which indicates that output should be always sent to the log */
+    private Boolean alwaysLog;
+
+    /** Flag which indicates whether files should be created even if empty. */
+    private Boolean createEmptyFiles;
+
+    /** Input file mapper. */
+    private Mapper inputMapper;
+
+    /** Output file mapper. */
+    private Mapper outputMapper;
+
+    /** Error file mapper. */
+    private Mapper errorMapper;
+
+    /** input filter chains. */
+    private Vector inputFilterChains = new Vector();
+
+    /** output filter chains. */
+    private Vector outputFilterChains = new Vector();
+
+    /** error filter chains. */
+    private Vector errorFilterChains = new Vector();
+
+    /** The output encoding */
+    private String outputEncoding;
+
+    /** The error encoding */
+    private String errorEncoding;
+
+    /** The input encoding */
+    private String inputEncoding;
+
+    /**
+     * Add the input file mapper.
+     * @param inputMapper   <CODE>Mapper</CODE>.
+     */
+    public void addConfiguredInputMapper(Mapper inputMapper) {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        if (this.inputMapper != null) {
+            if (usingInput) {
+                throw new BuildException("attribute \"input\""
+                    + " cannot coexist with a nested <inputmapper>");
+            } else {
+                throw new BuildException("Cannot have > 1 <inputmapper>");
+            }
+        }
+        this.inputMapper = inputMapper;
+    }
+
+    /**
+     * Add the output file mapper.
+     * @param outputMapper   <CODE>Mapper</CODE>.
+     */
+    public void addConfiguredOutputMapper(Mapper outputMapper) {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        if (this.outputMapper != null) {
+            if (usingOutput) {
+                throw new BuildException("attribute \"output\""
+                    + " cannot coexist with a nested <outputmapper>");
+            } else {
+                throw new BuildException("Cannot have > 1 <outputmapper>");
+            }
+        }
+        this.outputMapper = outputMapper;
+    }
+
+    /**
+     * Add the error file mapper.
+     * @param errorMapper   <CODE>Mapper</CODE>.
+     */
+    public void addConfiguredErrorMapper(Mapper errorMapper) {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        if (this.errorMapper != null) {
+            if (usingError) {
+                throw new BuildException("attribute \"error\""
+                    + " cannot coexist with a nested <errormapper>");
+            } else {
+                throw new BuildException("Cannot have > 1 <errormapper>");
+            }
+        }
+        this.errorMapper = errorMapper;
+    }
+
+    /**
+     * Makes this instance in effect a reference to another instance.
+     *
+     * <p>You must not set another attribute or nest elements inside
+     * this element if you make it a reference.</p>
+     * @param r the reference to use
+     * @throws BuildException on error
+     */
+    public void setRefid(Reference r) throws BuildException {
+        if (usingInput
+            || usingOutput
+            || usingError
+            || inputString != null
+            || logError != null
+            || append != null
+            || createEmptyFiles != null
+            || inputEncoding != null
+            || outputEncoding != null
+            || errorEncoding != null
+            || outputProperty != null
+            || errorProperty != null) {
+            throw tooManyAttributes();
+        }
+        super.setRefid(r);
+    }
+
+    /**
+     * Set the input to use for the task
+     * @param input the file from which input is read.
+     */
+    public void setInput(File input) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        if (inputString != null) {
+            throw new BuildException("The \"input\" and \"inputstring\" "
+                + "attributes cannot both be specified");
+        }
+        usingInput = true;
+        inputMapper = createMergeMapper(input);
+    }
+
+    /**
+     * Set the string to use as input
+     * @param inputString the string which is used as the input source
+     */
+    public void setInputString(String inputString) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        if (usingInput) {
+            throw new BuildException("The \"input\" and \"inputstring\" "
+                + "attributes cannot both be specified");
+        }
+        this.inputString = inputString;
+    }
+
+    /**
+     * File the output of the process is redirected to. If error is not
+     * redirected, it too will appear in the output
+     *
+     * @param out the file to which output stream is written
+     */
+    public void setOutput(File out) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        if (out == null) {
+            throw new IllegalArgumentException("output file specified as null");
+        }
+        usingOutput = true;
+        outputMapper = createMergeMapper(out);
+    }
+
+    /**
+     * Set the output encoding.
+     * @param outputEncoding   <CODE>String</CODE>.
+     */
+    public void setOutputEncoding(String outputEncoding) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.outputEncoding = outputEncoding;
+    }
+
+    /**
+     * Set the error encoding.
+     *
+     * @param errorEncoding   <CODE>String</CODE>.
+     */
+    public void setErrorEncoding(String errorEncoding) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.errorEncoding = errorEncoding;
+    }
+
+    /**
+     * Set the input encoding.
+     * @param inputEncoding   <CODE>String</CODE>.
+     */
+    public void setInputEncoding(String inputEncoding) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.inputEncoding = inputEncoding;
+    }
+
+    /**
+     * Controls whether error output of exec is logged. This is only useful
+     * when output is being redirected and error output is desired in the
+     * Ant log
+     * @param logError if true the standard error is sent to the Ant log system
+     *        and not sent to output.
+     */
+    public void setLogError(boolean logError) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.logError = ((logError) ? Boolean.TRUE : Boolean.FALSE);
+    }
+
+    /**
+     * Set the file to which standard error is to be redirected.
+     * @param error the file to which error is to be written
+     */
+    public void setError(File error) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        if (error == null) {
+            throw new IllegalArgumentException("error file specified as null");
+        }
+        usingError = true;
+        errorMapper = createMergeMapper(error);
+    }
+
+    /**
+     * Property name whose value should be set to the output of
+     * the process.
+     * @param outputProperty the name of the property to be set with the
+     *        task's output.
+     */
+    public void setOutputProperty(String outputProperty) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.outputProperty = outputProperty;
+    }
+
+    /**
+     * Whether output should be appended to or overwrite an existing file.
+     * Defaults to false.
+     * @param append if true output and error streams are appended to their
+     *        respective files, if specified.
+     */
+    public void setAppend(boolean append) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.append = ((append) ? Boolean.TRUE : Boolean.FALSE);
+    }
+
+    /**
+     * If true, (error and non-error) output will be "teed", redirected
+     * as specified while being sent to Ant's logging mechanism as if no
+     * redirection had taken place.  Defaults to false.
+     * @param alwaysLog <code>boolean</code>
+     * @since Ant 1.6.3
+     */
+    public void setAlwaysLog(boolean alwaysLog) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.alwaysLog = ((alwaysLog) ? Boolean.TRUE : Boolean.FALSE);
+    }
+
+    /**
+     * Whether output and error files should be created even when empty.
+     * Defaults to true.
+     * @param createEmptyFiles <CODE>boolean</CODE>.
+     */
+    public void setCreateEmptyFiles(boolean createEmptyFiles) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.createEmptyFiles = ((createEmptyFiles)
+            ? Boolean.TRUE : Boolean.FALSE);
+    }
+
+    /**
+     * Property name whose value should be set to the error of
+     * the process.
+     * @param errorProperty the name of the property to be set
+     *        with the error output.
+     */
+    public void setErrorProperty(String errorProperty) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        this.errorProperty = errorProperty;
+    }
+
+    /**
+     * Create a nested input <CODE>FilterChain</CODE>.
+     * @return <CODE>FilterChain</CODE>.
+     */
+    public FilterChain createInputFilterChain() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        FilterChain result = new FilterChain();
+        result.setProject(getProject());
+        inputFilterChains.add(result);
+        return result;
+    }
+
+    /**
+     * Create a nested output <CODE>FilterChain</CODE>.
+     * @return <CODE>FilterChain</CODE>.
+     */
+    public FilterChain createOutputFilterChain() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        FilterChain result = new FilterChain();
+        result.setProject(getProject());
+        outputFilterChains.add(result);
+        return result;
+    }
+
+    /**
+     * Create a nested error <CODE>FilterChain</CODE>.
+     * @return <CODE>FilterChain</CODE>.
+     */
+    public FilterChain createErrorFilterChain() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        FilterChain result = new FilterChain();
+        result.setProject(getProject());
+        errorFilterChains.add(result);
+        return result;
+    }
+
+    /**
+     * Configure the specified <CODE>Redirector</CODE>.
+     * @param redirector   <CODE>Redirector</CODE>.
+     */
+    public void configure(Redirector redirector) {
+        configure(redirector, null);
+    }
+
+    /**
+     * Configure the specified <CODE>Redirector</CODE>
+     * for the specified sourcefile.
+     * @param redirector   <CODE>Redirector</CODE>.
+     * @param sourcefile   <CODE>String</CODE>.
+     */
+    public void configure(Redirector redirector, String sourcefile) {
+        if (isReference()) {
+            getRef().configure(redirector, sourcefile);
+            return;
+        }
+        if (alwaysLog != null) {
+            redirector.setAlwaysLog(alwaysLog.booleanValue());
+        }
+        if (logError != null) {
+            redirector.setLogError(logError.booleanValue());
+        }
+        if (append != null) {
+            redirector.setAppend(append.booleanValue());
+        }
+        if (createEmptyFiles != null) {
+            redirector.setCreateEmptyFiles(createEmptyFiles.booleanValue());
+        }
+        if (outputProperty != null) {
+            redirector.setOutputProperty(outputProperty);
+        }
+        if (errorProperty != null) {
+            redirector.setErrorProperty(errorProperty);
+        }
+        if (inputString != null) {
+            redirector.setInputString(inputString);
+        }
+        if (inputMapper != null) {
+            String[] inputTargets = null;
+            try {
+                inputTargets =
+                    inputMapper.getImplementation().mapFileName(sourcefile);
+            } catch (NullPointerException enPeaEx) {
+                if (sourcefile != null) {
+                    throw enPeaEx;
+                }
+            }
+            if (inputTargets != null && inputTargets.length > 0) {
+                redirector.setInput(toFileArray(inputTargets));
+            }
+        }
+        if (outputMapper != null) {
+            String[] outputTargets = null;
+            try {
+                outputTargets =
+                    outputMapper.getImplementation().mapFileName(sourcefile);
+            } catch (NullPointerException enPeaEx) {
+                if (sourcefile != null) {
+                    throw enPeaEx;
+                }
+            }
+            if (outputTargets != null && outputTargets.length > 0) {
+                redirector.setOutput(toFileArray(outputTargets));
+            }
+        }
+        if (errorMapper != null) {
+            String[] errorTargets = null;
+            try {
+                errorTargets =
+                    errorMapper.getImplementation().mapFileName(sourcefile);
+            } catch (NullPointerException enPeaEx) {
+                if (sourcefile != null) {
+                    throw enPeaEx;
+                }
+            }
+            if (errorTargets != null && errorTargets.length > 0) {
+                redirector.setError(toFileArray(errorTargets));
+            }
+        }
+        if (inputFilterChains.size() > 0) {
+            redirector.setInputFilterChains(inputFilterChains);
+        }
+        if (outputFilterChains.size() > 0) {
+            redirector.setOutputFilterChains(outputFilterChains);
+        }
+        if (errorFilterChains.size() > 0) {
+            redirector.setErrorFilterChains(errorFilterChains);
+        }
+        if (inputEncoding != null) {
+            redirector.setInputEncoding(inputEncoding);
+        }
+        if (outputEncoding != null) {
+            redirector.setOutputEncoding(outputEncoding);
+        }
+        if (errorEncoding != null) {
+            redirector.setErrorEncoding(errorEncoding);
+        }
+    }
+
+    /**
+     * Create a merge mapper pointing to the specified destination file.
+     * @param destfile   <CODE>File</CODE>
+     * @return <CODE>Mapper</CODE>.
+     */
+    protected Mapper createMergeMapper(File destfile) {
+        Mapper result = new Mapper(getProject());
+        result.setClassname(
+            org.apache.tools.ant.util.MergingMapper.class.getName());
+        result.setTo(destfile.getAbsolutePath());
+        return result;
+    }
+
+    /**
+     * Return a <CODE>File[]</CODE> from the specified set of filenames.
+     * @param name   <CODE>String[]</CODE>
+     * @return <CODE>File[]</CODE>.
+     */
+    protected File[] toFileArray(String[] name) {
+        if (name == null) {
+            return null;
+        }
+        //remove any null elements
+        ArrayList list = new ArrayList(name.length);
+        for (int i = 0; i < name.length; i++) {
+            if (name[i] != null) {
+                list.add(getProject().resolveFile(name[i]));
+            }
+        }
+        return (File[]) (list.toArray(new File[list.size()]));
+    }
+
+    /**
+     * Convenience method.
+     * @throws BuildException on error.
+     */
+    protected void dieOnCircularReference() throws BuildException {
+        if (isChecked()) {
+            return;
+        }
+        Stack s = new Stack();
+        s.push(this);
+        dieOnCircularReference(s, getProject());
+    }
+
+    /**
+     * Overrides the version of DataType to recurse on all DataType
+     * child elements that may have been added.
+     * @param stk the stack of data types to use (recursively).
+     * @param p   the project to use to dereference the references.
+     * @throws BuildException on error.
+     */
+    protected void dieOnCircularReference(Stack stk, Project p)
+        throws BuildException {
+        if (isChecked()) {
+            return;
+        }
+        if (isReference()) {
+            super.dieOnCircularReference(stk, p);
+        } else {
+            Mapper[] m = new Mapper[] {inputMapper, outputMapper, errorMapper};
+            for (int i = 0; i < m.length; i++) {
+                if (m[i] != null) {
+                    stk.push(m[i]);
+                    m[i].dieOnCircularReference(stk, p);
+                    stk.pop();
+                }
+            }
+            Vector[] v = new Vector[]
+                {inputFilterChains, outputFilterChains, errorFilterChains};
+            for (int i = 0; i < v.length; i++) {
+                if (v[i] != null) {
+                    for (Iterator fci = v[i].iterator(); fci.hasNext();) {
+                        FilterChain fc = (FilterChain) fci.next();
+                        stk.push(fc);
+                        fc.dieOnCircularReference(stk, p);
+                        stk.pop();
+                    }
+                }
+            }
+            setChecked(true);
+        }
+    }
+
+    /**
+     * Perform the check for circular references, returning the
+     * referenced RedirectorElement
+     * @return the referenced RedirectorElement.
+     */
+    private RedirectorElement getRef() {
+        dieOnCircularReference();
+        Object o = getRefid().getReferencedObject(getProject());
+        if (!(o instanceof RedirectorElement)) {
+            throw new BuildException(getRefid().getRefId()
+                + " doesn\'t denote a RedirectorElement");
+        }
+        return (RedirectorElement) o;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Reference.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Reference.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Reference.java	(revision 16139)
@@ -0,0 +1,129 @@
+/*
+ * Copyright  2000-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Class to hold a reference to another object in the project.
+ *
+ */
+public class Reference {
+
+    private String refid;
+    private Project project;
+
+    /**
+     * Create a reference.
+     * @deprecated Please use {@link Reference#Reference(Project,String)} instead.
+     */
+    public Reference() {
+    }
+
+    /**
+     * Create a reference to a named ID.
+     * @param id the name of this reference
+     * @deprecated Please use {@link Reference#Reference(Project,String)} instead.
+     */
+    public Reference(String id) {
+        setRefId(id);
+    }
+
+    /**
+     * Create a reference to a named ID in a particular project.
+     * @param p the project this reference is associated with
+     * @param id the name of this reference
+     * @since Ant 1.6.3
+     */
+    public Reference(Project p, String id) {
+        setRefId(id);
+        setProject(p);
+    }
+
+    /**
+     * Set the reference id. Should not normally be necessary;
+     * use {@link Reference#Reference(Project, String)}.
+     * @param id the reference id to use
+     */
+    public void setRefId(String id) {
+        refid = id;
+    }
+
+    /**
+     * Get the reference id of this reference.
+     * @return the reference id
+     */
+    public String getRefId() {
+        return refid;
+    }
+
+    /**
+     * Set the associated project. Should not normally be necessary;
+     * use {@link Reference#Reference(Project,String)}.
+     * @param p the project to use
+     * @since Ant 1.6.3
+     */
+    public void setProject(Project p) {
+        this.project = p;
+    }
+
+    /**
+     * Get the associated project, if any; may be null.
+     * @return the associated project
+     * @since Ant 1.6.3
+     */
+    public Project getProject() {
+        return project;
+    }
+
+    /**
+     * Resolve the reference, using the associated project if
+     * it set, otherwise use the passed in project.
+     * @param fallback the fallback project to use if the project attribute of
+     *                 reference is not set.
+     * @return the dereferenced object.
+     * @throws BuildException if the reference cannot be dereferenced.
+     */
+    public Object getReferencedObject(Project fallback) throws BuildException {
+        if (refid == null) {
+            throw new BuildException("No reference specified");
+        }
+
+        Object o = project == null ? fallback.getReference(refid) : project.getReference(refid);
+        if (o == null) {
+            throw new BuildException("Reference " + refid + " not found.");
+        }
+        return o;
+    }
+
+    /**
+     * Resolve the reference, looking in the associated project.
+     * @see Project#getReference
+     * @return the dereferenced object.
+     * @throws BuildException if the project is null or the reference cannot be dereferenced
+     * @since Ant 1.6.3
+     */
+    public Object getReferencedObject() throws BuildException {
+        if (project == null) {
+            throw new BuildException("No project set on reference to " + refid);
+        }
+        return getReferencedObject(project);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/RegularExpression.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/RegularExpression.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/RegularExpression.java	(revision 16139)
@@ -0,0 +1,157 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+
+import java.util.Stack;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.regexp.Regexp;
+import org.apache.tools.ant.util.regexp.RegexpFactory;
+
+/***
+ * A regular expression datatype.  Keeps an instance of the
+ * compiled expression for speed purposes.  This compiled
+ * expression is lazily evaluated (it is compiled the first
+ * time it is needed).  The syntax is the dependent on which
+ * regular expression type you are using.  The system property
+ * "ant.regexp.regexpimpl" will be the classname of the implementation
+ * that will be used.
+ *
+ * <pre>
+ * For jdk  &lt;= 1.3, there are two available implementations:
+ *   org.apache.tools.ant.util.regexp.JakartaOroRegexp (the default)
+ *        Based on the jakarta-oro package
+ *
+ *   org.apache.tools.ant.util.regexp.JakartaRegexpRegexp
+ *        Based on the jakarta-regexp package
+ *
+ * For jdk &gt;= 1.4 an additional implementation is available:
+ *   org.apache.tools.ant.util.regexp.Jdk14RegexpRegexp
+ *        Based on the jdk 1.4 built in regular expression package.
+ * </pre>
+ *
+ * <pre>
+ *   &lt;regexp [ [id="id"] pattern="expression" | refid="id" ]
+ *   /&gt;
+ * </pre>
+ *
+ * @see org.apache.oro.text.regex.Perl5Compiler
+ * @see org.apache.regexp.RE
+ * @see java.util.regex.Pattern
+ *
+ * @see org.apache.tools.ant.util.regexp.Regexp
+ *
+ * @ant.datatype name="regexp"
+ */
+public class RegularExpression extends DataType {
+    /** Name of this data type */
+    public static final String DATA_TYPE_NAME = "regexp";
+    private boolean alreadyInit = false;
+
+    // The regular expression factory
+    private static final RegexpFactory FACTORY = new RegexpFactory();
+
+    private Regexp regexp = null;
+    // temporary variable
+    private String myPattern;
+    private boolean setPatternPending = false;
+
+    /**
+     * default constructor
+     */
+    public RegularExpression() {
+    }
+
+    private void init(Project p) {
+        if (!alreadyInit) {
+            this.regexp = FACTORY.newRegexp(p);
+            alreadyInit = true;
+        }
+    }
+    private void setPattern() {
+        if (setPatternPending) {
+            regexp.setPattern(myPattern);
+            setPatternPending = false;
+        }
+    }
+    /**
+     * sets the regular expression pattern
+     * @param pattern regular expression pattern
+     */
+    public void setPattern(String pattern) {
+        if (regexp == null) {
+            myPattern = pattern;
+            setPatternPending = true;
+        } else {
+            regexp.setPattern(pattern);
+        }
+    }
+
+    /***
+     * Gets the pattern string for this RegularExpression in the
+     * given project.
+     * @param p project
+     * @return pattern
+     */
+    public String getPattern(Project p) {
+        init(p);
+        if (isReference()) {
+            return getRef(p).getPattern(p);
+        }
+        setPattern();
+        return regexp.getPattern();
+    }
+
+    /**
+     * provides a reference to the Regexp contained in this
+     * @param p  project
+     * @return   Regexp instance associated with this RegularExpression instance
+     */
+    public Regexp getRegexp(Project p) {
+        init(p);
+        if (isReference()) {
+            return getRef(p).getRegexp(p);
+        }
+        setPattern();
+        return this.regexp;
+    }
+
+    /***
+     * Get the RegularExpression this reference refers to in
+     * the given project.  Check for circular references too
+     * @param p project
+     * @return resolved RegularExpression instance
+     */
+    public RegularExpression getRef(Project p) {
+        if (!isChecked()) {
+            Stack stk = new Stack();
+            stk.push(this);
+            dieOnCircularReference(stk, p);
+        }
+
+
+        Object o = getRefid().getReferencedObject(p);
+        if (!(o instanceof RegularExpression)) {
+            String msg = getRefid().getRefId() + " doesn\'t denote a "
+                    + DATA_TYPE_NAME;
+            throw new BuildException(msg);
+        } else {
+            return (RegularExpression) o;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Resource.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Resource.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Resource.java	(revision 16139)
@@ -0,0 +1,225 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+/**
+ * Describes a File or a ZipEntry.
+ *
+ * This class is meant to be used by classes needing to record path
+ * and date/time information about a file, a zip entry or some similar
+ * resource (URL, archive in a version control repository, ...).
+ *
+ * @since Ant 1.5.2
+ */
+public class Resource implements Cloneable, Comparable {
+    /** Constant unknown size */
+    public static final long UNKNOWN_SIZE = -1;
+
+    private String name = null;
+    private boolean exists = true;
+    private long lastmodified = 0;
+    private boolean directory = false;
+    private long size = UNKNOWN_SIZE;
+
+    /**
+     * Default constructor.
+     */
+    public Resource() {
+    }
+
+    /**
+     * Only sets the name.
+     *
+     * <p>This is a dummy, used for not existing resources.</p>
+     *
+     * @param name relative path of the resource.  Expects
+     * &quot;/&quot; to be used as the directory separator.
+     */
+    public Resource(String name) {
+        this(name, false, 0, false);
+    }
+
+    /**
+     * Sets the name, lastmodified flag, and exists flag.
+     *
+     * @param name relative path of the resource.  Expects
+     * &quot;/&quot; to be used as the directory separator.
+     * @param exists if true, this resource exists.
+     * @param lastmodified the last modification time of this resource.
+     */
+    public Resource(String name, boolean exists, long lastmodified) {
+        this(name, exists, lastmodified, false);
+    }
+
+    /**
+     * Sets the name, lastmodified flag, exists flag, and directory flag.
+     *
+     * @param name relative path of the resource.  Expects
+     * &quot;/&quot; to be used as the directory separator.
+     * @param exists if true the resource exists
+     * @param lastmodified the last modification time of the resource
+     * @param directory    if true, this resource is a directory
+     */
+    public Resource(String name, boolean exists, long lastmodified,
+                    boolean directory) {
+        this(name, exists, lastmodified, directory, UNKNOWN_SIZE);
+    }
+
+    /**
+     * Sets the name, lastmodified flag, exists flag, directory flag, and size.
+     *
+     * @param name relative path of the resource.  Expects
+     * &quot;/&quot; to be used as the directory separator.
+     * @param exists if true the resource exists
+     * @param lastmodified the last modification time of the resource
+     * @param directory    if true, this resource is a directory
+     * @param size the size of this resource.
+     */
+    public Resource(String name, boolean exists, long lastmodified,
+                    boolean directory, long size) {
+        this.name = name;
+        setName(name);
+        setExists(exists);
+        setLastModified(lastmodified);
+        setDirectory(directory);
+        setSize(size);
+    }
+
+    /**
+     * Name attribute will contain the path of a file relative to the
+     * root directory of its fileset or the recorded path of a zip
+     * entry.
+     *
+     * <p>example for a file with fullpath /var/opt/adm/resource.txt
+     * in a file set with root dir /var/opt it will be
+     * adm/resource.txt.</p>
+     *
+     * <p>&quot;/&quot; will be used as the directory separator.</p>
+     * @return the name of this resource.
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Set the name of this Resource.
+     * @param name relative path of the resource.  Expects
+     * &quot;/&quot; to be used as the directory separator.
+     */
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * The exists attribute tells whether a file exists.
+     * @return true if this resource exists.
+     */
+    public boolean isExists() {
+        return exists;
+    }
+
+    /**
+     * Set the exists attribute.
+     * @param exists if true, this resource exists.
+     */
+    public void setExists(boolean exists) {
+        this.exists = exists;
+    }
+
+    /**
+     * Tells the modification time in milliseconds since 01.01.1970 .
+     *
+     * @return 0 if the resource does not exist to mirror the behavior
+     * of {@link java.io.File File}.
+     */
+    public long getLastModified() {
+        return !exists || lastmodified < 0 ? 0L : lastmodified;
+    }
+
+    /**
+     * Set the last modification attribute.
+     * @param lastmodified the modification time in milliseconds since 01.01.1970.
+     */
+    public void setLastModified(long lastmodified) {
+        this.lastmodified = lastmodified;
+    }
+
+    /**
+     * Tells if the resource is a directory.
+     * @return boolean flag indicating if the resource is a directory.
+     */
+    public boolean isDirectory() {
+        return directory;
+    }
+
+    /**
+     * Set the directory attribute.
+     * @param directory if true, this resource is a directory.
+     */
+    public void setDirectory(boolean directory) {
+        this.directory = directory;
+    }
+
+    /**
+     * Set the size of this Resource.
+     * @param size the size, as a long.
+     * @since Ant 1.6.3
+     */
+    public void setSize(long size) {
+        this.size = (size > UNKNOWN_SIZE) ? size : UNKNOWN_SIZE;
+    }
+
+    /**
+     * Get the size of this Resource.
+     * @return the size, as a long, 0 if the Resource does not exist (for
+     *         compatibility with java.io.File), or UNKNOWN_SIZE if not known.
+     * @since Ant 1.6.3
+     */
+    public long getSize() {
+        return (exists) ? size : 0L;
+    }
+
+    /**
+     * Clone this Resource.
+     * @return copy of this.
+     */
+    public Object clone() {
+        try {
+            return super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new Error("CloneNotSupportedException for a "
+                            + "Clonable Resource caught?");
+        }
+    }
+
+    /**
+     * Delegates to a comparison of names.
+     * @param other the object to compare to.
+     * @return a negative integer, zero, or a positive integer as this Resource
+     *         is less than, equal to, or greater than the specified Resource.
+     * @since Ant 1.6
+     */
+    public int compareTo(Object other) {
+        if (!(other instanceof Resource)) {
+            throw new IllegalArgumentException("Can only be compared with "
+                                               + "Resources");
+        }
+        Resource r = (Resource) other;
+        return getName().compareTo(r.getName());
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/ResourceFactory.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/ResourceFactory.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/ResourceFactory.java	(revision 16139)
@@ -0,0 +1,37 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+/**
+ * this interface should be implemented by classes (Scanners) needing
+ * to deliver information about resources.
+ *
+ * @since Ant 1.5.2
+ */
+public interface ResourceFactory {
+
+    /**
+     * Query a resource (file, zipentry, ...) by name
+     *
+     * @param name relative path of the resource about which
+     * information is sought.  Expects &quot;/&quot; to be used as the
+     * directory separator.
+     * @return instance of Resource; the exists attribute of Resource
+     * will tell whether the sought resource exists
+     */
+    Resource getResource(String name);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/ResourceLocation.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/ResourceLocation.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/ResourceLocation.java	(revision 16139)
@@ -0,0 +1,111 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+import java.net.URL;
+
+/**
+ * <p>Helper class to handle the <code>&lt;dtd&gt;</code> and
+ * <code>&lt;entity&gt;</code> nested elements.  These correspond to
+ * the <code>PUBLIC</code> and <code>URI</code> catalog entry types,
+ * respectively, as defined in the <a
+ * href="http://oasis-open.org/committees/entity/spec-2001-08-06.html">
+ * OASIS "Open Catalog" standard</a>.</p>
+ *
+ * <p>Possible Future Enhancements:
+ * <ul>
+ * <li>Bring the Ant element names into conformance with the OASIS standard</li>
+ * <li>Add support for additional OASIS catalog entry types</li>
+ * </ul>
+ * </p>
+ *
+ * @see org.apache.xml.resolver.Catalog
+ * @since Ant 1.6
+ */
+public class ResourceLocation {
+
+    //-- Fields ----------------------------------------------------------------
+
+    /**
+     * name of the catalog entry type, as per OASIS spec.
+     */
+    private String name = null;
+
+    /** publicId of the dtd/entity. */
+    private String publicId = null;
+
+    /** location of the dtd/entity - a file/resource/URL. */
+    private String location = null;
+
+    /**
+     * base URL of the dtd/entity, or null. If null, the Ant project
+     * basedir is assumed.  If the location specifies a relative
+     * URL/pathname, it is resolved using the base.  The default base
+     * for an external catalog file is the directory in which it is
+     * located.
+     */
+    private URL base = null;
+
+    //-- Methods ---------------------------------------------------------------
+
+    /**
+     * @param publicId uniquely identifies the resource.
+     */
+    public void setPublicId(String publicId) {
+        this.publicId = publicId;
+    }
+
+    /**
+     * @param location the location of the resource associated with the
+     *      publicId.
+     */
+    public void setLocation(String location) {
+        this.location = location;
+    }
+
+    /**
+     * @param base the base URL of the resource associated with the
+     * publicId.  If the location specifies a relative URL/pathname,
+     * it is resolved using the base.  The default base for an
+     * external catalog file is the directory in which it is located.
+     */
+    public void setBase(URL base) {
+        this.base = base;
+    }
+
+    /**
+     * @return the publicId of the resource.
+     */
+    public String getPublicId() {
+        return publicId;
+    }
+
+    /**
+     * @return the location of the resource identified by the publicId.
+     */
+    public String getLocation() {
+        return location;
+    }
+
+    /**
+     * @return the base of the resource identified by the publicId.
+     */
+    public URL getBase() {
+        return base;
+    }
+
+} //-- ResourceLocation
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Substitution.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Substitution.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/Substitution.java	(revision 16139)
@@ -0,0 +1,81 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+
+import java.util.Stack;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/***
+ * A regular expression substitution datatype.  It is an expression
+ * that is meant to replace a regular expression.
+ *
+ * <pre>
+ *   &lt;substitition [ [id="id"] expression="expression" | refid="id" ]
+ *   /&gt;
+ * </pre>
+ *
+ * @see org.apache.oro.text.regex.Perl5Substitution
+ */
+public class Substitution extends DataType {
+    /** The name of this data type */
+    public static final String DATA_TYPE_NAME = "substitition";
+
+    private String expression;
+
+    public Substitution() {
+        this.expression = null;
+    }
+
+    public void setExpression(String expression) {
+        this.expression = expression;
+    }
+
+    /***
+     * Gets the pattern string for this RegularExpression in the
+     * given project.
+     */
+    public String getExpression(Project p) {
+        if (isReference()) {
+            return getRef(p).getExpression(p);
+        }
+
+        return expression;
+    }
+
+    /***
+     * Get the RegularExpression this reference refers to in
+     * the given project.  Check for circular references too
+     */
+    public Substitution getRef(Project p) {
+        if (!isChecked()) {
+            Stack stk = new Stack();
+            stk.push(this);
+            dieOnCircularReference(stk, p);
+        }
+
+
+        Object o = getRefid().getReferencedObject(p);
+        if (!(o instanceof Substitution)) {
+            String msg = getRefid().getRefId() + " doesn\'t denote a substitution";
+            throw new BuildException(msg);
+        } else {
+            return (Substitution) o;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/XMLCatalog.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/XMLCatalog.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/XMLCatalog.java	(revision 16139)
@@ -0,0 +1,1124 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import java.lang.reflect.Method;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Stack;
+import java.util.Vector;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import javax.xml.transform.Source;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.URIResolver;
+import javax.xml.transform.sax.SAXSource;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JAXPUtils;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+
+
+/**
+ * <p>This data type provides a catalog of resource locations (such as
+ * DTDs and XML entities), based on the <a
+ * href="http://oasis-open.org/committees/entity/spec-2001-08-06.html">
+ * OASIS "Open Catalog" standard</a>.  The catalog entries are used
+ * both for Entity resolution and URI resolution, in accordance with
+ * the {@link org.xml.sax.EntityResolver EntityResolver} and {@link
+ * javax.xml.transform.URIResolver URIResolver} interfaces as defined
+ * in the <a href="http://java.sun.com/xml/jaxp">Java API for XML
+ * Processing Specification</a>.</p>
+ *
+ * <p>Resource locations can be specified either in-line or in
+ * external catalog file(s), or both.  In order to use an external
+ * catalog file, the xml-commons resolver library ("resolver.jar")
+ * must be in your classpath.  External catalog files may be either <a
+ * href="http://oasis-open.org/committees/entity/background/9401.html">
+ * plain text format</a> or <a
+ * href="http://www.oasis-open.org/committees/entity/spec-2001-08-06.html">
+ * XML format</a>.  If the xml-commons resolver library is not found
+ * in the classpath, external catalog files, specified in
+ * <code>&lt;catalogpath&gt;</code> paths, will be ignored and a warning will
+ * be logged.  In this case, however, processing of inline entries will proceed
+ * normally.</p>
+ *
+ * <p>Currently, only <code>&lt;dtd&gt;</code> and
+ * <code>&lt;entity&gt;</code> elements may be specified inline; these
+ * correspond to OASIS catalog entry types <code>PUBLIC</code> and
+ * <code>URI</code> respectively.</p>
+ *
+ * <p>The following is a usage example:</p>
+ *
+ * <code>
+ * &lt;xmlcatalog&gt;<br>
+ * &nbsp;&nbsp;&lt;dtd publicId="" location="/path/to/file.jar" /&gt;<br>
+ * &nbsp;&nbsp;&lt;dtd publicId="" location="/path/to/file2.jar" /&gt;<br>
+ * &nbsp;&nbsp;&lt;entity publicId="" location="/path/to/file3.jar" /&gt;<br>
+ * &nbsp;&nbsp;&lt;entity publicId="" location="/path/to/file4.jar" /&gt;<br>
+ * &nbsp;&nbsp;&lt;catalogpath&gt;<br>
+ * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/etc/sgml/catalog"/&gt;<br>
+ * &nbsp;&nbsp;&lt;/catalogpath&gt;<br>
+ * &nbsp;&nbsp;&lt;catalogfiles dir="/opt/catalogs/" includes="**\catalog.xml" /&gt;<br>
+ * &lt;/xmlcatalog&gt;<br>
+ * </code>
+ * <p>
+ * Tasks wishing to use <code>&lt;xmlcatalog&gt;</code> must provide a method called
+ * <code>createXMLCatalog</code> which returns an instance of
+ * <code>XMLCatalog</code>. Nested DTD and entity definitions are handled by
+ * the XMLCatalog object and must be labeled <code>dtd</code> and
+ * <code>entity</code> respectively.</p>
+ *
+ * <p>The following is a description of the resolution algorithm:
+ * entities/URIs/dtds are looked up in each of the following contexts,
+ * stopping when a valid and readable resource is found:
+ * <ol>
+ * <li>In the local filesystem</li>
+ * <li>In the classpath</li>
+ * <li>Using the Apache xml-commons resolver (if it is available)</li>
+ * <li>In URL-space</li>
+ * </ol>
+ * </p>
+ *
+ * <p>See {@link
+ * org.apache.tools.ant.taskdefs.optional.XMLValidateTask
+ * XMLValidateTask} for an example of a task that has integrated
+ * support for XMLCatalogs.</p>
+ *
+ * <p>Possible future extension could provide for additional OASIS
+ * entry types to be specified inline.</p>
+ *
+ */
+public class XMLCatalog extends DataType
+    implements Cloneable, EntityResolver, URIResolver {
+
+    /** helper for some File.toURL connversions */
+    private static FileUtils fileUtils = FileUtils.newFileUtils();
+
+    //-- Fields ----------------------------------------------------------------
+
+    /** Holds dtd/entity objects until needed. */
+    private Vector elements = new Vector();
+
+    /**
+     * Classpath in which to attempt to resolve resources.
+     */
+    private Path classpath;
+
+    /**
+     * Path listing external catalog files to search when resolving entities
+     */
+    private Path catalogPath;
+
+    /**
+     * The name of the bridge to the Apache xml-commons resolver
+     * class, used to determine whether resolver.jar is present in the
+     * classpath.
+     */
+    public static final String APACHE_RESOLVER
+        = "org.apache.tools.ant.types.resolver.ApacheCatalogResolver";
+
+    /**
+     * Resolver base class
+     */
+    public static final String CATALOG_RESOLVER
+        = "org.apache.xml.resolver.tools.CatalogResolver";
+
+        //-- Methods ---------------------------------------------------------------
+
+    /**
+     * Default constructor
+     */
+    public XMLCatalog() {
+        setChecked(false);
+    }
+
+    /**
+     * Returns the elements of the catalog - ResourceLocation objects.
+     *
+     * @return the elements of the catalog - ResourceLocation objects
+     */
+    private Vector getElements() {
+        return getRef().elements;
+    }
+
+    /**
+     * Returns the classpath in which to attempt to resolve resources.
+     *
+     * @return the classpath
+     */
+    private Path getClasspath() {
+        return getRef().classpath;
+    }
+
+    /**
+     * Set the list of ResourceLocation objects in the catalog.
+     * Not allowed if this catalog is itself a reference to another catalog --
+     * that is, a catalog cannot both refer to another <em>and</em> contain
+     * elements or other attributes.
+     *
+     * @param aVector the new list of ResourceLocations
+     * to use in the catalog.
+     */
+    private void setElements(Vector aVector) {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        elements = aVector;
+    }
+
+    /**
+     * Allows nested classpath elements. Not allowed if this catalog
+     * is itself a reference to another catalog -- that is, a catalog
+     * cannot both refer to another <em>and</em> contain elements or
+     * other attributes.
+     *
+     * @return a Path instance to be configured.
+     */
+    public Path createClasspath() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        if (this.classpath == null) {
+            this.classpath = new Path(getProject());
+        }
+        setChecked(false);
+        return this.classpath.createPath();
+    }
+
+    /**
+     * Allows simple classpath string.  Not allowed if this catalog is
+     * itself a reference to another catalog -- that is, a catalog
+     * cannot both refer to another <em>and</em> contain elements or
+     * other attributes.
+     *
+     * @param classpath the classpath to use to look up entities.
+     */
+    public void setClasspath(Path classpath) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        if (this.classpath == null) {
+            this.classpath = classpath;
+        } else {
+            this.classpath.append(classpath);
+        }
+        setChecked(false);
+    }
+
+    /**
+     * Allows classpath reference.  Not allowed if this catalog is
+     * itself a reference to another catalog -- that is, a catalog
+     * cannot both refer to another <em>and</em> contain elements or
+     * other attributes.
+     *
+     * @param r an Ant reference containing a classpath.
+     */
+    public void setClasspathRef(Reference r) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        createClasspath().setRefid(r);
+        setChecked(false);
+    }
+
+    /** Creates a nested <code>&lt;catalogpath&gt;</code> element.
+     * Not allowed if this catalog is itself a reference to another
+     * catalog -- that is, a catalog cannot both refer to another
+     * <em>and</em> contain elements or other attributes.
+     *
+     * @return a path to be configured as the catalog path.
+     * @exception BuildException
+     * if this is a reference and no nested elements are allowed.
+     */
+    public Path createCatalogPath() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        if (this.catalogPath == null) {
+            this.catalogPath = new Path(getProject());
+        }
+        setChecked(false);
+        return this.catalogPath.createPath();
+    }
+
+    /**
+     * Allows catalogpath reference.  Not allowed if this catalog is
+     * itself a reference to another catalog -- that is, a catalog
+     * cannot both refer to another <em>and</em> contain elements or
+     * other attributes.
+     *
+     * @param r an Ant reference containing a classpath to be used as
+     * the catalog path.
+     */
+    public void setCatalogPathRef(Reference r) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        createCatalogPath().setRefid(r);
+        setChecked(false);
+    }
+
+
+    /**
+     * Returns the catalog path in which to attempt to resolve DTDs.
+     *
+     * @return the catalog path
+     */
+    public Path getCatalogPath() {
+        return getRef().catalogPath;
+    }
+
+
+    /**
+     * Creates the nested <code>&lt;dtd&gt;</code> element.  Not
+     * allowed if this catalog is itself a reference to another
+     * catalog -- that is, a catalog cannot both refer to another
+     * <em>and</em> contain elements or other attributes.
+     *
+     * @param dtd the information about the PUBLIC resource mapping to
+     *            be added to the catalog
+     * @exception BuildException if this is a reference and no nested
+     *       elements are allowed.
+     */
+    public void addDTD(ResourceLocation dtd) throws BuildException {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+
+        getElements().addElement(dtd);
+        setChecked(false);
+    }
+
+    /**
+     * Creates the nested <code>&lt;entity&gt;</code> element.    Not
+     * allowed if this catalog is itself a reference to another
+     * catalog -- that is, a catalog cannot both refer to another
+     * <em>and</em> contain elements or other attributes.
+     *
+     * @param entity the information about the URI resource mapping to be
+     *       added to the catalog.
+     * @exception BuildException if this is a reference and no nested
+     *       elements are allowed.
+     */
+    public void addEntity(ResourceLocation entity) throws BuildException {
+        addDTD(entity);
+    }
+
+    /**
+     * Loads a nested <code>&lt;xmlcatalog&gt;</code> into our
+     * definition.  Not allowed if this catalog is itself a reference
+     * to another catalog -- that is, a catalog cannot both refer to
+     * another <em>and</em> contain elements or other attributes.
+     *
+     * @param catalog Nested XMLCatalog
+     */
+    public void addConfiguredXMLCatalog(XMLCatalog catalog) {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+
+        // Add all nested elements to our catalog
+        Vector newElements = catalog.getElements();
+        Vector ourElements = getElements();
+        Enumeration e = newElements.elements();
+        while (e.hasMoreElements()) {
+            ourElements.addElement(e.nextElement());
+        }
+
+        // Append the classpath of the nested catalog
+        Path nestedClasspath = catalog.getClasspath();
+        createClasspath().append(nestedClasspath);
+
+        // Append the catalog path of the nested catalog
+        Path nestedCatalogPath = catalog.getCatalogPath();
+        createCatalogPath().append(nestedCatalogPath);
+        setChecked(false);
+    }
+
+    /**
+     * Makes this instance in effect a reference to another XMLCatalog
+     * instance.
+     *
+     * <p>You must not set another attribute or nest elements inside
+     * this element if you make it a reference.  That is, a catalog
+     * cannot both refer to another <em>and</em> contain elements or
+     * attributes.</p>
+     *
+     * @param r the reference to which this catalog instance is associated
+     * @exception BuildException if this instance already has been configured.
+     */
+    public void setRefid(Reference r) throws BuildException {
+        if (!elements.isEmpty()) {
+            throw tooManyAttributes();
+        }
+        super.setRefid(r);
+    }
+
+    /**
+     * Implements the EntityResolver.resolveEntity() interface method.
+     *
+     * @see org.xml.sax.EntityResolver#resolveEntity
+     */
+    public InputSource resolveEntity(String publicId, String systemId)
+        throws SAXException, IOException {
+
+        if (isReference()) {
+            return getRef().resolveEntity(publicId, systemId);
+        }
+
+        if (!isChecked()) {
+            // make sure we don't have a circular reference here
+            Stack stk = new Stack();
+            stk.push(this);
+            dieOnCircularReference(stk, getProject());
+        }
+
+        log("resolveEntity: '" + publicId + "': '" + systemId + "'",
+            Project.MSG_DEBUG);
+
+        InputSource inputSource =
+            getCatalogResolver().resolveEntity(publicId, systemId);
+
+        if (inputSource == null) {
+            log("No matching catalog entry found, parser will use: '"
+                + systemId + "'", Project.MSG_DEBUG);
+        }
+
+        return inputSource;
+    }
+
+    /**
+     * Implements the URIResolver.resolve() interface method.
+     *
+     * @see javax.xml.transform.URIResolver#resolve
+     */
+    public Source resolve(String href, String base)
+        throws TransformerException {
+
+        if (isReference()) {
+            return getRef().resolve(href, base);
+        }
+
+        if (!isChecked()) {
+            // make sure we don't have a circular reference here
+            Stack stk = new Stack();
+            stk.push(this);
+            dieOnCircularReference(stk, getProject());
+        }
+
+        SAXSource source = null;
+
+        String uri = removeFragment(href);
+
+        log("resolve: '" + uri + "' with base: '" + base + "'", Project.MSG_DEBUG);
+
+        source = (SAXSource) getCatalogResolver().resolve(uri, base);
+
+        if (source == null) {
+            log("No matching catalog entry found, parser will use: '"
+                + href + "'", Project.MSG_DEBUG);
+            //
+            // Cannot return a null source, because we have to call
+            // setEntityResolver (see setEntityResolver javadoc comment)
+            //
+            source = new SAXSource();
+            URL baseURL = null;
+            try {
+                if (base == null) {
+                    baseURL = fileUtils.getFileURL(getProject().getBaseDir());
+                } else {
+                    baseURL = new URL(base);
+                }
+                URL url = (uri.length() == 0 ? baseURL : new URL(baseURL, uri));
+                source.setInputSource(new InputSource(url.toString()));
+            } catch (MalformedURLException ex) {
+                // At this point we are probably in failure mode, but
+                // try to use the bare URI as a last gasp
+                source.setInputSource(new InputSource(uri));
+            }
+        }
+
+        setEntityResolver(source);
+        return source;
+    }
+
+    /**
+     * @since Ant 1.6
+     */
+    private XMLCatalog getRef() {
+        if (!isReference()) {
+            return this;
+        }
+        return (XMLCatalog) getCheckedRef(XMLCatalog.class, "xmlcatalog");
+    }
+
+    /**
+     * The instance of the CatalogResolver strategy to use.
+     */
+    private CatalogResolver catalogResolver = null;
+
+    /**
+     * Factory method for creating the appropriate CatalogResolver
+     * strategy implementation.
+     * <p> Until we query the classpath, we don't know whether the Apache
+     * resolver (Norm Walsh's library from xml-commons) is available or not.
+     * This method determines whether the library is available and creates the
+     * appropriate implementation of CatalogResolver based on the answer.</p>
+     * <p>This is an application of the Gang of Four Strategy Pattern
+     * combined with Template Method.</p>
+     */
+    private CatalogResolver getCatalogResolver() {
+
+        if (catalogResolver == null) {
+
+            AntClassLoader loader = null;
+
+            loader = getProject().createClassLoader(Path.systemClasspath);
+
+            try {
+                Class clazz = Class.forName(APACHE_RESOLVER, true, loader);
+
+                // The Apache resolver is present - Need to check if it can
+                // be seen by the catalog resolver class. Start by getting
+                // the actual loader
+                ClassLoader apacheResolverLoader = clazz.getClassLoader();
+
+                // load the base class through this loader.
+                Class baseResolverClass
+                    = Class.forName(CATALOG_RESOLVER, true, apacheResolverLoader);
+
+                // and find its actual loader
+                ClassLoader baseResolverLoader
+                    = baseResolverClass.getClassLoader();
+
+                // We have the loader which is being used to load the
+                // CatalogResolver. Can it see the ApacheResolver? The
+                // base resolver will only be able to create the ApacheResolver
+                // if it can see it - doesn't use the context loader.
+                clazz = Class.forName(APACHE_RESOLVER, true, baseResolverLoader);
+
+                Object obj  = clazz.newInstance();
+                //
+                // Success!  The xml-commons resolver library is
+                // available, so use it.
+                //
+                catalogResolver = new ExternalResolver(clazz, obj);
+            } catch (Throwable ex) {
+                //
+                // The xml-commons resolver library is not
+                // available, so we can't use it.
+                //
+                catalogResolver = new InternalResolver();
+                if (getCatalogPath() != null
+                    && getCatalogPath().list().length != 0) {
+                        log("Warning: catalogpath listing external catalogs"
+                            + " will be ignored", Project.MSG_WARN);
+                    }
+                log("Failed to load Apache resolver: " + ex, Project.MSG_DEBUG);
+            }
+        }
+        return catalogResolver;
+    }
+
+    /**
+     * <p>This is called from the URIResolver to set an EntityResolver
+     * on the SAX parser to be used for new XML documents that are
+     * encountered as a result of the document() function, xsl:import,
+     * or xsl:include.  This is done because the XSLT processor calls
+     * out to the SAXParserFactory itself to create a new SAXParser to
+     * parse the new document.  The new parser does not automatically
+     * inherit the EntityResolver of the original (although arguably
+     * it should).  See below:</p>
+     *
+     * <tt>"If an application wants to set the ErrorHandler or
+     * EntityResolver for an XMLReader used during a transformation,
+     * it should use a URIResolver to return the SAXSource which
+     * provides (with getXMLReader) a reference to the XMLReader"</tt>
+     *
+     * <p>...quoted from page 118 of the Java API for XML
+     * Processing 1.1 specification</p>
+     *
+     */
+    private void setEntityResolver(SAXSource source) throws TransformerException {
+
+        XMLReader reader = source.getXMLReader();
+        if (reader == null) {
+            SAXParserFactory spFactory = SAXParserFactory.newInstance();
+            spFactory.setNamespaceAware(true);
+            try {
+                reader = spFactory.newSAXParser().getXMLReader();
+            } catch (ParserConfigurationException ex) {
+                throw new TransformerException(ex);
+            } catch (SAXException ex) {
+                throw new TransformerException(ex);
+            }
+        }
+        reader.setEntityResolver(this);
+        source.setXMLReader(reader);
+    }
+
+    /**
+     * Find a ResourceLocation instance for the given publicId.
+     *
+     * @param publicId the publicId of the Resource for which local information
+     *        is required.
+     * @return a ResourceLocation instance with information on the local location
+     *         of the Resource or null if no such information is available.
+     */
+    private ResourceLocation findMatchingEntry(String publicId) {
+        Enumeration e = getElements().elements();
+        ResourceLocation element = null;
+        while (e.hasMoreElements()) {
+            Object o = e.nextElement();
+            if (o instanceof ResourceLocation) {
+                element = (ResourceLocation) o;
+                if (element.getPublicId().equals(publicId)) {
+                    return element;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Utility method to remove trailing fragment from a URI.
+     * For example,
+     * <code>http://java.sun.com/index.html#chapter1</code>
+     * would return <code>http://java.sun.com/index.html</code>.
+     *
+     * @param uri The URI to process.  It may or may not contain a
+     *            fragment.
+     * @return The URI sans fragment.
+     */
+    private String removeFragment(String uri) {
+        String result = uri;
+        int hashPos = uri.indexOf("#");
+        if (hashPos >= 0) {
+            result = uri.substring(0, hashPos);
+        }
+        return result;
+    }
+
+    /**
+     * Utility method to lookup a ResourceLocation in the filesystem.
+     *
+     * @return An InputSource for reading the file, or <code>null</code>
+     *     if the file does not exist or is not readable.
+     */
+    private InputSource filesystemLookup(ResourceLocation matchingEntry) {
+
+        String uri = matchingEntry.getLocation();
+        // the following line seems to be necessary on Windows under JDK 1.2
+        uri = uri.replace(File.separatorChar, '/');
+        URL baseURL = null;
+
+        //
+        // The ResourceLocation may specify a relative path for its
+        // location attribute.  This is resolved using the appropriate
+        // base.
+        //
+        if (matchingEntry.getBase() != null) {
+            baseURL = matchingEntry.getBase();
+        } else {
+            try {
+                baseURL = fileUtils.getFileURL(getProject().getBaseDir());
+            } catch (MalformedURLException ex) {
+                throw new BuildException("Project basedir cannot be converted to a URL");
+            }
+        }
+
+        InputSource source = null;
+        URL url = null;
+        try {
+            url = new URL(baseURL, uri);
+        } catch (MalformedURLException ex) {
+            // this processing is useful under Windows when the location of the DTD has been given as an absolute path
+            // see Bugzilla Report 23913
+            File testFile = new File(uri);
+            if (testFile.exists() && testFile.canRead()) {
+                log("uri : '"
+                    + uri + "' matches a readable file", Project.MSG_DEBUG);
+                try {
+                    url = fileUtils.getFileURL(testFile);
+                } catch (MalformedURLException ex1) {
+                    throw new BuildException("could not find an URL for :" + testFile.getAbsolutePath());
+                }
+            } else {
+                log("uri : '"
+                    + uri + "' does not match a readable file", Project.MSG_DEBUG);
+
+            }
+        }
+
+        if (url != null) {
+            String fileName = fileUtils.fromURI(url.toString());
+            if (fileName != null) {
+                log("fileName " + fileName, Project.MSG_DEBUG);
+                File resFile = new File(fileName);
+                if (resFile.exists() && resFile.canRead()) {
+                    try {
+                        source = new InputSource(new FileInputStream(resFile));
+                        String sysid = JAXPUtils.getSystemId(resFile);
+                        source.setSystemId(sysid);
+                        log("catalog entry matched a readable file: '"
+                            + sysid + "'", Project.MSG_DEBUG);
+                    } catch (IOException ex) {
+                        // ignore
+                    }
+                }
+            }
+        }
+        return source;
+    }
+
+    /**
+     * Utility method to lookup a ResourceLocation in the classpath.
+     *
+     * @return An InputSource for reading the resource, or <code>null</code>
+     *    if the resource does not exist in the classpath or is not readable.
+     */
+    private InputSource classpathLookup(ResourceLocation matchingEntry) {
+
+        InputSource source = null;
+
+        AntClassLoader loader = null;
+        Path cp = classpath;
+        if (cp != null) {
+            cp = classpath.concatSystemClasspath("ignore");
+        } else {
+            cp = (new Path(getProject())).concatSystemClasspath("last");
+        }
+        loader = getProject().createClassLoader(cp);
+
+        //
+        // for classpath lookup we ignore the base directory
+        //
+        InputStream is
+            = loader.getResourceAsStream(matchingEntry.getLocation());
+
+        if (is != null) {
+            source = new InputSource(is);
+            URL entryURL = loader.getResource(matchingEntry.getLocation());
+            String sysid = entryURL.toExternalForm();
+            source.setSystemId(sysid);
+            log("catalog entry matched a resource in the classpath: '"
+                + sysid + "'", Project.MSG_DEBUG);
+        }
+
+        return source;
+    }
+
+    /**
+     * Utility method to lookup a ResourceLocation in URL-space.
+     *
+     * @return An InputSource for reading the resource, or <code>null</code>
+     *    if the resource does not identify a valid URL or is not readable.
+     */
+    private InputSource urlLookup(ResourceLocation matchingEntry) {
+
+        String uri = matchingEntry.getLocation();
+        URL baseURL = null;
+
+        //
+        // The ResourceLocation may specify a relative url for its
+        // location attribute.  This is resolved using the appropriate
+        // base.
+        //
+        if (matchingEntry.getBase() != null) {
+            baseURL = matchingEntry.getBase();
+        } else {
+            try {
+                baseURL = fileUtils.getFileURL(getProject().getBaseDir());
+            } catch (MalformedURLException ex) {
+                throw new BuildException("Project basedir cannot be converted to a URL");
+            }
+        }
+
+        InputSource source = null;
+        URL url = null;
+
+        try {
+            url = new URL(baseURL, uri);
+        } catch (MalformedURLException ex) {
+            // ignore
+        }
+
+        if (url != null) {
+            try {
+                InputStream is = url.openStream();
+                if (is != null) {
+                    source = new InputSource(is);
+                    String sysid = url.toExternalForm();
+                    source.setSystemId(sysid);
+                    log("catalog entry matched as a URL: '"
+                        + sysid + "'", Project.MSG_DEBUG);
+                }
+            } catch (IOException ex) {
+                // ignore
+            }
+        }
+
+        return source;
+
+    }
+
+    /**
+     * Interface implemented by both the InternalResolver strategy and
+     * the ExternalResolver strategy.
+     */
+    private interface CatalogResolver extends URIResolver, EntityResolver {
+
+        InputSource resolveEntity(String publicId, String systemId);
+
+        Source resolve(String href, String base) throws TransformerException;
+    }
+
+    /**
+     * The InternalResolver strategy is used if the Apache resolver
+     * library (Norm Walsh's library from xml-commons) is not
+     * available.  In this case, external catalog files will be
+     * ignored.
+     *
+     */
+    private class InternalResolver implements CatalogResolver {
+
+        public InternalResolver() {
+            log("Apache resolver library not found, internal resolver will be used",
+                Project.MSG_VERBOSE);
+        }
+
+        public InputSource resolveEntity(String publicId,
+                                         String systemId) {
+            InputSource result = null;
+            ResourceLocation matchingEntry = findMatchingEntry(publicId);
+
+            if (matchingEntry != null) {
+
+                log("Matching catalog entry found for publicId: '"
+                    + matchingEntry.getPublicId() + "' location: '"
+                    + matchingEntry.getLocation() + "'",
+                    Project.MSG_DEBUG);
+
+                result = filesystemLookup(matchingEntry);
+
+                if (result == null) {
+                    result = classpathLookup(matchingEntry);
+                }
+
+                if (result == null) {
+                    result = urlLookup(matchingEntry);
+                }
+            }
+            return result;
+        }
+
+        public Source resolve(String href, String base)
+            throws TransformerException {
+
+            SAXSource result = null;
+            InputSource source = null;
+
+            ResourceLocation matchingEntry = findMatchingEntry(href);
+
+            if (matchingEntry != null) {
+
+                log("Matching catalog entry found for uri: '"
+                    + matchingEntry.getPublicId() + "' location: '"
+                    + matchingEntry.getLocation() + "'",
+                    Project.MSG_DEBUG);
+
+                //
+                // Use the passed in base in preference to the base
+                // from matchingEntry, which is either null or the
+                // directory in which the external catalog file from
+                // which it was obtained is located.  We make a copy
+                // so matchingEntry's original base is untouched.
+                //
+                // This is the standard behavior as per my reading of
+                // the JAXP and XML Catalog specs.  CKS 11/7/2002
+                //
+                ResourceLocation entryCopy = matchingEntry;
+                if (base != null) {
+                    try {
+                        URL baseURL = new URL(base);
+                        entryCopy = new ResourceLocation();
+                        entryCopy.setBase(baseURL);
+                    } catch (MalformedURLException ex) {
+                        // ignore
+                    }
+                }
+                entryCopy.setPublicId(matchingEntry.getPublicId());
+                entryCopy.setLocation(matchingEntry.getLocation());
+
+                source = filesystemLookup(entryCopy);
+
+                if (source == null) {
+                    source = classpathLookup(entryCopy);
+                }
+
+                if (source == null) {
+                    source = urlLookup(entryCopy);
+                }
+
+                if (source != null) {
+                    result = new SAXSource(source);
+                }
+            }
+            return result;
+        }
+    }
+
+    /**
+     * The ExternalResolver strategy is used if the Apache resolver
+     * library (Norm Walsh's library from xml-commons) is available in
+     * the classpath.  The ExternalResolver is a essentially a superset
+     * of the InternalResolver.
+     *
+     */
+    private class ExternalResolver implements CatalogResolver {
+
+        private Method setXMLCatalog = null;
+        private Method parseCatalog = null;
+        private Method resolveEntity = null;
+        private Method resolve = null;
+
+        /** The instance of the ApacheCatalogResolver bridge class */
+        private Object resolverImpl = null;
+
+        private boolean externalCatalogsProcessed = false;
+
+        public ExternalResolver(Class resolverImplClass,
+                              Object resolverImpl) {
+
+            this.resolverImpl = resolverImpl;
+
+            //
+            // Get Method instances for each of the methods we need to
+            // call on the resolverImpl using reflection.  We can't
+            // call them directly, because they require on the
+            // xml-commons resolver library which may not be available
+            // in the classpath.
+            //
+            try {
+                setXMLCatalog =
+                    resolverImplClass.getMethod("setXMLCatalog",
+                                                new Class[] {XMLCatalog.class});
+
+                parseCatalog =
+                    resolverImplClass.getMethod("parseCatalog",
+                                                new Class[] {String.class});
+
+                resolveEntity =
+                    resolverImplClass.getMethod("resolveEntity",
+                                                new Class[] {String.class, String.class});
+
+                resolve =
+                    resolverImplClass.getMethod("resolve",
+                                                new Class[] {String.class, String.class});
+            } catch (NoSuchMethodException ex) {
+                throw new BuildException(ex);
+            }
+
+            log("Apache resolver library found, xml-commons resolver will be used",
+                Project.MSG_VERBOSE);
+        }
+
+        public InputSource resolveEntity(String publicId,
+                                         String systemId) {
+            InputSource result = null;
+
+            processExternalCatalogs();
+
+            ResourceLocation matchingEntry = findMatchingEntry(publicId);
+
+            if (matchingEntry != null) {
+
+                log("Matching catalog entry found for publicId: '"
+                    + matchingEntry.getPublicId() + "' location: '"
+                    + matchingEntry.getLocation() + "'",
+                    Project.MSG_DEBUG);
+
+                result = filesystemLookup(matchingEntry);
+
+                if (result == null) {
+                    result = classpathLookup(matchingEntry);
+                }
+
+                if (result == null) {
+                    try {
+                        result =
+                            (InputSource) resolveEntity.invoke(resolverImpl,
+                                                              new Object[] {publicId, systemId});
+                    } catch (Exception ex) {
+                        throw new BuildException(ex);
+                    }
+                }
+            } else {
+                //
+                // We didn't match a ResourceLocation, but since we
+                // only support PUBLIC and URI entry types internally,
+                // it is still possible that there is another entry in
+                // an external catalog that will match.  We call
+                // Apache resolver's resolveEntity method to cover
+                // this possibility.
+                //
+                try {
+                    result =
+                        (InputSource) resolveEntity.invoke(resolverImpl,
+                                                          new Object[] {publicId, systemId});
+                } catch (Exception ex) {
+                    throw new BuildException(ex);
+                }
+            }
+
+            return result;
+        }
+
+        public Source resolve(String href, String base)
+            throws TransformerException {
+
+            SAXSource result = null;
+            InputSource source = null;
+
+            processExternalCatalogs();
+
+            ResourceLocation matchingEntry = findMatchingEntry(href);
+
+            if (matchingEntry != null) {
+
+                log("Matching catalog entry found for uri: '"
+                    + matchingEntry.getPublicId() + "' location: '"
+                    + matchingEntry.getLocation() + "'",
+                    Project.MSG_DEBUG);
+
+                //
+                // Use the passed in base in preference to the base
+                // from matchingEntry, which is either null or the
+                // directory in which the external catalog file from
+                // which it was obtained is located.  We make a copy
+                // so matchingEntry's original base is untouched.  Of
+                // course, if there is no base, no need to make a
+                // copy...
+                //
+                // This is the standard behavior as per my reading of
+                // the JAXP and XML Catalog specs.  CKS 11/7/2002
+                //
+                ResourceLocation entryCopy = matchingEntry;
+                if (base != null) {
+                    try {
+                        URL baseURL = new URL(base);
+                        entryCopy = new ResourceLocation();
+                        entryCopy.setBase(baseURL);
+                    } catch (MalformedURLException ex) {
+                        // ignore
+                    }
+                }
+                entryCopy.setPublicId(matchingEntry.getPublicId());
+                entryCopy.setLocation(matchingEntry.getLocation());
+
+                source = filesystemLookup(entryCopy);
+
+                if (source == null) {
+                    source = classpathLookup(entryCopy);
+                }
+
+                if (source != null) {
+                    result = new SAXSource(source);
+                } else {
+                    try {
+                        result =
+                            (SAXSource) resolve.invoke(resolverImpl,
+                                                      new Object[] {href, base});
+                    } catch (Exception ex) {
+                        throw new BuildException(ex);
+                    }
+                }
+            } else {
+                //
+                // We didn't match a ResourceLocation, but since we
+                // only support PUBLIC and URI entry types internally,
+                // it is still possible that there is another entry in
+                // an external catalog that will match.  We call
+                // Apache resolver's resolveEntity method to cover
+                // this possibility.
+                //
+                try {
+                    result =
+                        (SAXSource) resolve.invoke(resolverImpl,
+                                                  new Object[] {href, base});
+                } catch (Exception ex) {
+                    throw new BuildException(ex);
+                }
+            }
+            return result;
+        }
+
+        /**
+         * Process each external catalog file specified in a
+         * <code>&lt;catalogpath&gt;</code>.  It will be
+         * parsed by the resolver library, and the individual elements
+         * will be added back to us (that is, the controlling
+         * XMLCatalog instance) via a callback mechanism.
+         */
+        private void processExternalCatalogs() {
+
+            if (!externalCatalogsProcessed) {
+
+                try {
+                    setXMLCatalog.invoke(resolverImpl,
+                                         new Object[] {XMLCatalog.this});
+                } catch (Exception ex) {
+                    throw new BuildException(ex);
+                }
+
+                // Parse each catalog listed in nested <catalogpath> elements
+                Path catPath = getCatalogPath();
+                if (catPath != null) {
+                    log("Using catalogpath '" + getCatalogPath() + "'",
+                        Project.MSG_DEBUG);
+                    String[] catPathList = getCatalogPath().list();
+
+                    for (int i = 0; i < catPathList.length; i++) {
+                        File catFile = new File(catPathList[i]);
+                        log("Parsing " + catFile, Project.MSG_DEBUG);
+                        try {
+                            parseCatalog.invoke(resolverImpl,
+                                    new Object[] {catFile.getPath()});
+                        } catch (Exception ex) {
+                            throw new BuildException(ex);
+                        }
+                    }
+                }
+            }
+            externalCatalogsProcessed = true;
+        }
+    }
+} //-- XMLCatalog
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/ZipFileSet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/ZipFileSet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/ZipFileSet.java	(revision 16139)
@@ -0,0 +1,305 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+import java.io.File;
+import java.util.Stack;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.zip.UnixStat;
+
+/**
+ * A ZipFileSet is a FileSet with extra attributes useful in the context of
+ * Zip/Jar tasks.
+ *
+ * A ZipFileSet extends FileSets with the ability to extract a subset of the
+ * entries of a Zip file for inclusion in another Zip file.  It also includes
+ * a prefix attribute which is prepended to each entry in the output Zip file.
+ *
+ * Since ant 1.6 ZipFileSet can be defined with an id and referenced in packaging tasks
+ *
+ */
+public class ZipFileSet extends FileSet {
+
+    /**
+     * Default value for the dirmode attribute.
+     *
+     * @since Ant 1.5.2
+     */
+    public static final int DEFAULT_DIR_MODE =
+        UnixStat.DIR_FLAG  | UnixStat.DEFAULT_DIR_PERM;
+
+    /**
+     * Default value for the filemode attribute.
+     *
+     * @since Ant 1.5.2
+     */
+    public static final int DEFAULT_FILE_MODE =
+        UnixStat.FILE_FLAG | UnixStat.DEFAULT_FILE_PERM;
+
+    private File srcFile          = null;
+    private String prefix         = "";
+    private String fullpath       = "";
+    private boolean hasDir        = false;
+    private int fileMode          = DEFAULT_FILE_MODE;
+    private int dirMode           = DEFAULT_DIR_MODE;
+
+    private boolean fileModeHasBeenSet = false;
+    private boolean dirModeHasBeenSet  = false;
+
+    public ZipFileSet() {
+        super();
+    }
+
+    protected ZipFileSet(FileSet fileset) {
+        super(fileset);
+    }
+
+    protected ZipFileSet(ZipFileSet fileset) {
+        super(fileset);
+        srcFile = fileset.srcFile;
+        prefix = fileset.prefix;
+        fullpath = fileset.fullpath;
+        hasDir = fileset.hasDir;
+        fileMode = fileset.fileMode;
+        dirMode = fileset.dirMode;
+        fileModeHasBeenSet = fileset.fileModeHasBeenSet;
+        dirModeHasBeenSet = fileset.dirModeHasBeenSet;
+    }
+
+    /**
+     * Set the directory for the fileset.  Prevents both "dir" and "src"
+     * from being specified.
+     */
+    public void setDir(File dir) throws BuildException {
+        if (isReference()) {
+             throw tooManyAttributes();
+         }
+        if (srcFile != null) {
+            throw new BuildException("Cannot set both dir and src attributes");
+        } else {
+            super.setDir(dir);
+            hasDir = true;
+        }
+    }
+
+    /**
+     * Set the source Zip file for the zipfileset.  Prevents both
+     * "dir" and "src" from being specified.
+     *
+     * @param srcFile The zip file from which to extract entries.
+     */
+    public void setSrc(File srcFile) {
+        if (isReference()) {
+             throw tooManyAttributes();
+         }
+        if (hasDir) {
+            throw new BuildException("Cannot set both dir and src attributes");
+        }
+        this.srcFile = srcFile;
+    }
+
+    /**
+     * Get the zip file from which entries will be extracted.
+     * References are not followed, since it is not possible
+     * to have a reference to a ZipFileSet, only to a FileSet.
+     */
+    public File getSrc(Project p) {
+        if (isReference()) {
+            return ((ZipFileSet) getRef(p)).getSrc(p);
+        }
+        return srcFile;
+    }
+
+    /**
+     * Prepend this prefix to the path for each zip entry.
+     * Prevents both prefix and fullpath from being specified
+     *
+     * @param prefix The prefix to prepend to entries in the zip file.
+     */
+    public void setPrefix(String prefix) {
+        if (!prefix.equals("") && !fullpath.equals("")) {
+            throw new BuildException("Cannot set both fullpath and prefix attributes");
+        }
+        this.prefix = prefix;
+    }
+
+    /**
+     * Return the prefix prepended to entries in the zip file.
+     */
+    public String getPrefix(Project p) {
+        if (isReference()) {
+            return ((ZipFileSet) getRef(p)).getPrefix(p);
+        }
+        return prefix;
+    }
+
+    /**
+     * Set the full pathname of the single entry in this fileset.
+     * Prevents both prefix and fullpath from being specified
+     *
+     * @param fullpath the full pathname of the single entry in this fileset.
+     */
+    public void setFullpath(String fullpath) {
+        if (!prefix.equals("") && !fullpath.equals("")) {
+            throw new BuildException("Cannot set both fullpath and prefix attributes");
+        }
+        this.fullpath = fullpath;
+    }
+
+    /**
+     * Return the full pathname of the single entry in this fileset.
+     */
+    public String getFullpath(Project p) {
+        if (isReference()) {
+            return ((ZipFileSet) getRef(p)).getFullpath(p);
+        }
+        return fullpath;
+    }
+
+    /**
+     * Return the DirectoryScanner associated with this FileSet.
+     * If the ZipFileSet defines a source Zip file, then a ZipScanner
+     * is returned instead.
+     */
+    public DirectoryScanner getDirectoryScanner(Project p) {
+        if (isReference()) {
+            return getRef(p).getDirectoryScanner(p);
+        }
+        if (srcFile != null) {
+            ZipScanner zs = new ZipScanner();
+            zs.setSrc(srcFile);
+            super.setDir(p.getBaseDir());
+            setupDirectoryScanner(zs, p);
+            zs.init();
+            return zs;
+        } else {
+            return super.getDirectoryScanner(p);
+        }
+    }
+
+    /**
+     * A 3 digit octal string, specify the user, group and
+     * other modes in the standard Unix fashion;
+     * optional, default=0644
+     *
+     * @since Ant 1.5.2
+     */
+    public void setFileMode(String octalString) {
+        fileModeHasBeenSet = true;
+        this.fileMode =
+            UnixStat.FILE_FLAG | Integer.parseInt(octalString, 8);
+    }
+
+    /**
+     * @since Ant 1.5.2
+     */
+    public int getFileMode(Project p) {
+        if (isReference()) {
+            return ((ZipFileSet) getRef(p)).getFileMode(p);
+        }
+        return fileMode;
+    }
+
+    /**
+     * Whether the user has specified the mode explicitly.
+     *
+     * @since Ant 1.6
+     */
+    public boolean hasFileModeBeenSet() {
+        if (isReference()) {
+            return ((ZipFileSet) getRef(getProject())).hasFileModeBeenSet();
+        }
+        return fileModeHasBeenSet;
+    }
+
+    /**
+     * A 3 digit octal string, specify the user, group and
+     * other modes in the standard Unix fashion;
+     * optional, default=0755
+     *
+     * @since Ant 1.5.2
+     */
+    public void setDirMode(String octalString) {
+        dirModeHasBeenSet = true;
+        this.dirMode =
+            UnixStat.DIR_FLAG | Integer.parseInt(octalString, 8);
+    }
+
+    /**
+     * @since Ant 1.5.2
+     */
+    public int getDirMode(Project p) {
+        if (isReference()) {
+            return ((ZipFileSet) getRef(p)).getDirMode(p);
+        }
+        return dirMode;
+    }
+
+    /**
+     * Whether the user has specified the mode explicitly.
+     *
+     * @since Ant 1.6
+     */
+    public boolean hasDirModeBeenSet() {
+        if (isReference()) {
+            return ((ZipFileSet) getRef(getProject())).hasDirModeBeenSet();
+        }
+        return dirModeHasBeenSet;
+    }
+
+    /**
+     * A ZipFileset accepts another ZipFileSet or a FileSet as reference
+     * FileSets are often used by the war task for the lib attribute
+     */
+    protected AbstractFileSet getRef(Project p) {
+        if (!isChecked()) {
+            Stack stk = new Stack();
+            stk.push(this);
+            dieOnCircularReference(stk, p);
+        }
+        Object o = getRefid().getReferencedObject(p);
+        if (o instanceof ZipFileSet) {
+            return (AbstractFileSet) o;
+        } else if (o instanceof FileSet) {
+            ZipFileSet zfs = new ZipFileSet((FileSet) o);
+            zfs.setPrefix(prefix);
+            zfs.setFullpath(fullpath);
+            zfs.fileModeHasBeenSet = fileModeHasBeenSet;
+            zfs.fileMode = fileMode;
+            zfs.dirModeHasBeenSet = dirModeHasBeenSet;
+            zfs.dirMode = dirMode;
+            return zfs;
+        } else {
+            String msg = getRefid().getRefId() + " doesn\'t denote a zipfileset or a fileset";
+            throw new BuildException(msg);
+        }
+    }
+    /**
+     * Return a ZipFileSet that has the same properties
+     * as this one.
+     * @since Ant 1.6
+     */
+    public Object clone() {
+        if (isReference()) {
+            return ((ZipFileSet) getRef(getProject())).clone();
+        } else {
+            return super.clone();
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/ZipScanner.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/ZipScanner.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/ZipScanner.java	(revision 16139)
@@ -0,0 +1,244 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Vector;
+import java.util.Hashtable;
+import java.util.Enumeration;
+import java.util.zip.ZipException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.zip.ZipEntry;
+import org.apache.tools.zip.ZipFile;
+
+/**
+ * ZipScanner accesses the pattern matching algorithm in DirectoryScanner,
+ * which are protected methods that can only be accessed by subclassing.
+ *
+ * This implementation of FileScanner defines getIncludedFiles to return
+ * the matching Zip entries.
+ *
+ */
+public class ZipScanner extends DirectoryScanner {
+
+    /**
+     * The zip file which should be scanned.
+     */
+    protected File srcFile;
+    /**
+     * to record the last scanned zip file with its modification date
+     */
+    private Resource lastScannedResource;
+    /**
+     * record list of all zip entries
+     */
+    private Hashtable myentries;
+
+    /**
+     * encoding of file names.
+     *
+     * @since Ant 1.6
+     */
+    private String encoding;
+
+    /**
+     * Sets the srcFile for scanning. This is the jar or zip file that
+     * is scanned for matching entries.
+     *
+     * @param srcFile the (non-null) zip file name for scanning
+     */
+    public void setSrc(File srcFile) {
+        this.srcFile = srcFile;
+    }
+
+    /**
+     * Sets encoding of file names.
+     *
+     * @since Ant 1.6
+     */
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * Returns the names of the files which matched at least one of the
+     * include patterns and none of the exclude patterns.
+     * The names are relative to the base directory.
+     *
+     * @return the names of the files which matched at least one of the
+     *         include patterns and none of the exclude patterns.
+     */
+    public String[] getIncludedFiles() {
+        if (srcFile != null) {
+            Vector myvector = new Vector();
+            // first check if the archive needs to be scanned again
+            scanme();
+            for (Enumeration e = myentries.elements(); e.hasMoreElements();) {
+                Resource myresource = (Resource) e.nextElement();
+                if (!myresource.isDirectory() && match(myresource.getName())) {
+                    myvector.addElement(myresource.getName());
+                }
+            }
+            String[] files = new String[myvector.size()];
+            myvector.copyInto(files);
+            Arrays.sort(files);
+            return files;
+        } else {
+            return super.getIncludedFiles();
+        }
+    }
+
+    /**
+     * Returns the names of the directories which matched at least one of the
+     * include patterns and none of the exclude patterns.
+     * The names are relative to the base directory.
+     *
+     * @return the names of the directories which matched at least one of the
+     * include patterns and none of the exclude patterns.
+     */
+    public String[] getIncludedDirectories() {
+        if (srcFile != null) {
+            Vector myvector = new Vector();
+            // first check if the archive needs to be scanned again
+            scanme();
+            for (Enumeration e = myentries.elements(); e.hasMoreElements();) {
+                Resource myresource = (Resource) e.nextElement();
+                if (myresource.isDirectory() && match(myresource.getName())) {
+                    myvector.addElement(myresource.getName());
+                }
+            }
+            String[] files = new String[myvector.size()];
+            myvector.copyInto(files);
+            Arrays.sort(files);
+            return files;
+        } else {
+            return super.getIncludedDirectories();
+        }
+    }
+
+    /**
+     * Initialize DirectoryScanner data structures.
+     */
+    public void init() {
+        if (includes == null) {
+            // No includes supplied, so set it to 'matches all'
+            includes = new String[1];
+            includes[0] = "**";
+        }
+        if (excludes == null) {
+            excludes = new String[0];
+        }
+    }
+
+    /**
+     * Matches a jar entry against the includes/excludes list,
+     * normalizing the path separator.
+     *
+     * @param path the (non-null) path name to test for inclusion
+     *
+     * @return <code>true</code> if the path should be included
+     *         <code>false</code> otherwise.
+     */
+    public boolean match(String path) {
+        String vpath = path.replace('/', File.separatorChar).
+            replace('\\', File.separatorChar);
+        return isIncluded(vpath) && !isExcluded(vpath);
+    }
+
+    /**
+     * @param name path name of the file sought in the archive
+     *
+     * @since Ant 1.5.2
+     */
+    public Resource getResource(String name) {
+        if (srcFile == null) {
+            return super.getResource(name);
+        } else if (name.equals("")) {
+            // special case in ZIPs, we do not want this thing included
+            return new Resource("", true, Long.MAX_VALUE, true);
+        }
+
+        // first check if the archive needs to be scanned again
+        scanme();
+        if (myentries.containsKey(name)) {
+            return (Resource) myentries.get(name);
+        } else if (myentries.containsKey(name + "/")) {
+            return (Resource) myentries.get(name + "/");
+        } else {
+            return new Resource(name);
+        }
+    }
+
+    /**
+     * if the datetime of the archive did not change since
+     * lastScannedResource was initialized returns immediately else if
+     * the archive has not been scanned yet, then all the zip entries
+     * are put into the vector myentries as a vector of the resource
+     * type
+     */
+    private void scanme() {
+        Resource thisresource = new Resource(srcFile.getAbsolutePath(),
+                                             srcFile.exists(),
+                                             srcFile.lastModified());
+
+        // spare scanning again and again
+        if (lastScannedResource != null
+            && lastScannedResource.getName().equals(thisresource.getName())
+            && lastScannedResource.getLastModified()
+            == thisresource.getLastModified()) {
+            return;
+        }
+
+        ZipEntry entry = null;
+        ZipFile zf = null;
+        myentries = new Hashtable();
+        try {
+            try {
+                zf = new ZipFile(srcFile, encoding);
+            } catch (ZipException ex) {
+                throw new BuildException("problem reading " + srcFile, ex);
+            } catch (IOException ex) {
+                throw new BuildException("problem opening " + srcFile, ex);
+            }
+
+            Enumeration e = zf.getEntries();
+            while (e.hasMoreElements()) {
+                entry = (ZipEntry) e.nextElement();
+                myentries.put(new String(entry.getName()),
+                              new Resource(entry.getName(), true,
+                                           entry.getTime(),
+                                           entry.isDirectory(),
+                                           entry.getSize()));
+            }
+        } finally {
+            if (zf != null) {
+                try {
+                    zf.close();
+                } catch (IOException ex) {
+                    // swallow
+                }
+            }
+        }
+        // record data about the last scanned resource
+        lastScannedResource = thisresource;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/defaults.properties
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/defaults.properties	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/defaults.properties	(revision 16139)
@@ -0,0 +1,37 @@
+classfileset=org.apache.tools.ant.types.optional.depend.ClassfileSet
+description=org.apache.tools.ant.types.Description
+dirset=org.apache.tools.ant.types.DirSet
+filelist=org.apache.tools.ant.types.FileList
+fileset=org.apache.tools.ant.types.FileSet
+filterchain=org.apache.tools.ant.types.FilterChain
+filterreader=org.apache.tools.ant.types.AntFilterReader
+filterset=org.apache.tools.ant.types.FilterSet
+mapper=org.apache.tools.ant.types.Mapper
+redirector=org.apache.tools.ant.types.RedirectorElement
+# different filename mappers
+identitymapper=org.apache.tools.ant.util.IdentityMapper
+flattenmapper=org.apache.tools.ant.util.FlatFileNameMapper
+globmapper=org.apache.tools.ant.util.GlobPatternMapper
+mergemapper=org.apache.tools.ant.util.MergingMapper
+regexpmapper=org.apache.tools.ant.util.RegexpPatternMapper
+packagemapper=org.apache.tools.ant.util.PackageNameMapper
+unpackagemapper=org.apache.tools.ant.util.UnPackageNameMapper
+compositemapper=org.apache.tools.ant.util.CompositeMapper
+chainedmapper=org.apache.tools.ant.util.ChainedMapper
+filtermapper=org.apache.tools.ant.types.mappers.FilterMapper
+
+path=org.apache.tools.ant.types.Path
+patternset=org.apache.tools.ant.types.PatternSet
+regexp=org.apache.tools.ant.types.RegularExpression
+substitution=org.apache.tools.ant.types.Substitution
+xmlcatalog=org.apache.tools.ant.types.XMLCatalog
+extensionSet=org.apache.tools.ant.taskdefs.optional.extension.ExtensionSet
+extension=org.apache.tools.ant.taskdefs.optional.extension.ExtensionAdapter
+libfileset=org.apache.tools.ant.taskdefs.optional.extension.LibFileSet
+selector=org.apache.tools.ant.types.selectors.SelectSelector
+zipfileset=org.apache.tools.ant.types.ZipFileSet
+scriptfilter=org.apache.tools.ant.types.optional.ScriptFilter
+propertyset=org.apache.tools.ant.types.PropertySet
+assertions=org.apache.tools.ant.types.Assertions
+concatfilter=org.apache.tools.ant.filters.ConcatFilter
+isfileselected=org.apache.tools.ant.taskdefs.condition.IsFileSelected
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/mappers/FilterMapper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/mappers/FilterMapper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/mappers/FilterMapper.java	(revision 16139)
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.mappers;
+
+import java.io.StringReader;
+import java.io.Reader;
+
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.UnsupportedAttributeException;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
+import org.apache.tools.ant.types.FilterChain;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * This is a FileNameMapper based on a FilterChain.
+ */
+public class FilterMapper extends FilterChain implements FileNameMapper {
+    /**
+     * From attribute not supported.
+     * @param from a string
+     * @throws BuildException always
+     */
+    public void setFrom(String from) {
+        throw new UnsupportedAttributeException(
+            "filtermapper doesn't support the \"from\" attribute.", "from");
+    }
+
+    /**
+     * From attribute not supported.
+     * @param to a string
+     * @throws BuildException always
+     */
+    public void setTo(String to) {
+        throw new UnsupportedAttributeException(
+            "filtermapper doesn't support the \"to\" attribute.", "to");
+    }
+
+    /**
+     * Return the result of the filters on the sourcefilename.
+     * @param sourceFileName the filename to map
+     * @return  a one-element array of converted filenames, or null if
+     *          the filterchain returns an empty string.
+     */
+    public String[] mapFileName(String sourceFileName) {
+        try {
+            Reader stringReader = new StringReader(sourceFileName);
+            ChainReaderHelper helper = new ChainReaderHelper();
+            helper.setBufferSize(8192);
+            helper.setPrimaryReader(stringReader);
+            helper.setProject(getProject());
+            Vector filterChains = new Vector();
+            filterChains.add(this);
+            helper.setFilterChains(filterChains);
+            String result = FileUtils.readFully(helper.getAssembledReader());
+            if (result.length() == 0) {
+                return null;
+            } else {
+                return new String[] {result};
+            }
+        } catch (BuildException ex) {
+            throw ex;
+        } catch (Exception ex) {
+            throw new BuildException(ex);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/ScriptFilter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/ScriptFilter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/ScriptFilter.java	(revision 16139)
@@ -0,0 +1,124 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.optional;
+
+import org.apache.tools.ant.filters.TokenFilter;
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.ScriptRunner;
+
+
+/**
+ * Most of this is CAP (Cut And Paste) from the Script task
+ * ScriptFilter class, implements TokenFilter.Filter
+ * for scripts to use.
+ * This provides the same beans as the Script Task
+ * to a script.
+ * The script is meant to use get self.token and
+ * set self.token in the reply.
+ *
+ *
+ * @since Ant 1.6
+ */
+public class ScriptFilter extends TokenFilter.ChainableReaderFilter {
+    /** Has this object been initialized ? */
+    private boolean initialized = false;
+    /** the token used by the script */
+    private String token;
+
+    private ScriptRunner runner = new ScriptRunner();
+
+    /**
+     * Defines the language (required).
+     *
+     * @param language the scripting language name for the script.
+     */
+    public void setLanguage(String language) {
+        runner.setLanguage(language);
+    }
+
+    /**
+     * Initialize.
+     *
+     * @exception BuildException if someting goes wrong
+     */
+    private void init() throws BuildException {
+        if (initialized) {
+            return;
+        }
+        initialized = true;
+
+        runner.addBeans(getProject().getProperties());
+        runner.addBeans(getProject().getUserProperties());
+        runner.addBeans(getProject().getTargets());
+        runner.addBeans(getProject().getReferences());
+
+        runner.addBean("project", getProject());
+        runner.addBean("self", this);
+    }
+
+    /**
+     * The current token
+     *
+     * @param token the string filtered by the script
+     */
+    public void setToken(String token) {
+        this.token = token;
+    }
+
+    /**
+     * The current token
+     *
+     * @return the string filtered by the script
+     */
+    public String getToken() {
+        return token;
+    }
+
+    /**
+     * Called filter the token.
+     * This sets the token in this object, calls
+     * the script and returns the token.
+     *
+     * @param token the token to be filtered
+     * @return the filtered token
+     */
+    public String filter(String token) {
+        init();
+        setToken(token);
+        runner.executeScript("<ANT-Filter>");
+        return getToken();
+    }
+
+    /**
+     * Load the script from an external file ; optional.
+     *
+     * @param file the file containing the script source.
+     */
+    public void setSrc(File file) {
+        runner.setSrc(file);
+    }
+
+    /**
+     * The script text.
+     *
+     * @param text a component of the script text to be added.
+     */
+    public void addText(String text) {
+        runner.addText(text);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/depend/ClassfileSet.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/depend/ClassfileSet.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/depend/ClassfileSet.java	(revision 16139)
@@ -0,0 +1,162 @@
+/*
+ * Copyright  2001-2002, 2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.optional.depend;
+
+import java.util.Vector;
+import java.util.Enumeration;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ * A ClassfileSet is a FileSet that enlists all classes that depend on a
+ * certain set of root classes.
+ *
+ * ClassfileSet extends FileSet, its inherited properties
+ * defining the domain searched for dependent classes.
+ *
+ */
+public class ClassfileSet extends FileSet {
+    /**
+     * The list of root classes for this class file set. These are the
+     * classes which must be included in the fileset and which are the
+     * starting point for the dependency search.
+     */
+    private Vector rootClasses = new Vector();
+
+    /**
+     * The list of filesets which contain root classes.
+     */
+    private Vector rootFileSets = new Vector();
+
+    /**
+     * Inner class used to contain info about root classes.
+     */
+    public static class ClassRoot {
+        /** The name of the root class */
+        private String rootClass;
+
+        /**
+         * Set the root class name.
+         *
+         * @param name the name of the root class.
+         */
+        public void setClassname(String name) {
+            this.rootClass = name;
+        }
+
+        /**
+         * Get the name of the root class.
+         *
+         * @return the name of the root class.
+         */
+        public String getClassname() {
+            return rootClass;
+        }
+    }
+
+    /**
+     * Default constructor.
+     */
+    public ClassfileSet() {
+    }
+
+    /**
+     * Add a fileset to which contains a collection of root classes used to
+     * drive the search from classes.
+     *
+     * @param rootFileSet a root file set to be used to search for dependent
+     * classes.
+     */
+    public void addRootFileset(FileSet rootFileSet) {
+        rootFileSets.addElement(rootFileSet);
+    }
+
+    /**
+     * Create a ClassfileSet from another ClassfileSet.
+     *
+     * @param s the other classfileset.
+     */
+    protected ClassfileSet(ClassfileSet s) {
+        super(s);
+        rootClasses = (Vector) s.rootClasses.clone();
+    }
+
+    /**
+     * Set the root class attribute.
+     *
+     * @param rootClass the name of the root class.
+     */
+    public void setRootClass(String rootClass) {
+        rootClasses.addElement(rootClass);
+    }
+
+    /**
+     * Return the DirectoryScanner associated with this FileSet.
+     *
+     * @param p the project used to resolve dirs, etc.
+     *
+     * @return a dependency scanner.
+     */
+    public DirectoryScanner getDirectoryScanner(Project p) {
+        if (isReference()) {
+            return getRef(p).getDirectoryScanner(p);
+        }
+        Vector allRootClasses = (Vector) rootClasses.clone();
+        for (Enumeration e = rootFileSets.elements(); e.hasMoreElements();) {
+            FileSet additionalRootSet = (FileSet) e.nextElement();
+            DirectoryScanner additionalScanner
+                = additionalRootSet.getDirectoryScanner(p);
+            String[] files = additionalScanner.getIncludedFiles();
+            for (int i = 0; i < files.length; ++i) {
+                if (files[i].endsWith(".class")) {
+                    String classFilePath
+                        = files[i].substring(0, files[i].length() - 6);
+                    String className
+                        = classFilePath.replace('/', '.').replace('\\', '.');
+                    allRootClasses.addElement(className);
+                }
+            }
+        }
+        DirectoryScanner parentScanner = super.getDirectoryScanner(p);
+        DependScanner scanner = new DependScanner(parentScanner);
+        scanner.setBasedir(getDir(p));
+        scanner.setRootClasses(allRootClasses);
+        scanner.scan();
+        return scanner;
+    }
+
+    /**
+     * Add a nested root class definition to this class file set.
+     *
+     * @param root the configured class root.
+     */
+    public void addConfiguredRoot(ClassRoot root) {
+        rootClasses.addElement(root.getClassname());
+    }
+
+    /**
+     * Clone this data type.
+     *
+     * @return a clone of the class file set.
+     */
+    public Object clone() {
+        return new ClassfileSet(isReference()
+            ? (ClassfileSet) (getRef(getProject())) : this);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/depend/DependScanner.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/depend/DependScanner.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/depend/DependScanner.java	(revision 16139)
@@ -0,0 +1,205 @@
+/*
+ * Copyright  2001-2002, 2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.optional.depend;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.depend.DependencyAnalyzer;
+
+
+/**
+ * DirectoryScanner for finding class dependencies.
+ */
+public class DependScanner extends DirectoryScanner {
+    /**
+     * The name of the analyzer to use by default.
+     */
+    public static final String DEFAULT_ANALYZER_CLASS
+        = "org.apache.tools.ant.util.depend.bcel.FullAnalyzer";
+
+    /**
+     * The root classes to drive the search for dependent classes.
+     */
+    private Vector rootClasses;
+
+    /**
+     * The names of the classes to include in the fileset.
+     */
+    private Vector included;
+
+    /**
+     * The parent scanner which gives the basic set of files. Only files which
+     * are in this set and which can be reached from a root class will end
+     * up being included in the result set.
+     */
+    private DirectoryScanner parentScanner;
+
+    /**
+     * Create a DependScanner, using the given scanner to provide the basic
+     * set of files from which class files come.
+     *
+     * @param parentScanner the DirectoryScanner which returns the files from
+     *        which class files must come.
+     */
+    public DependScanner(DirectoryScanner parentScanner) {
+        this.parentScanner = parentScanner;
+    }
+
+    /**
+     * Sets the root classes to be used to drive the scan.
+     *
+     * @param rootClasses the rootClasses to be used for this scan.
+     */
+    public synchronized void setRootClasses(Vector rootClasses) {
+        this.rootClasses = rootClasses;
+    }
+
+    /**
+     * Get the names of the class files on which baseClass depends.
+     *
+     * @return the names of the files.
+     */
+    public String[] getIncludedFiles() {
+        String[] files = new String[getIncludedFilesCount()];
+        for (int i = 0; i < files.length; i++) {
+            files[i] = (String) included.elementAt(i);
+        }
+        return files;
+    }
+
+    /**
+     * @see DirectoryScanner#getIncludedFilesCount
+     */
+    public synchronized int getIncludedFilesCount() {
+        if (included == null) {
+            throw new IllegalStateException();
+        }
+        return included.size();
+    }
+
+    /**
+     * Scans the base directory for files on which baseClass depends.
+     *
+     * @exception IllegalStateException when basedir was set incorrectly.
+     */
+    public synchronized void scan() throws IllegalStateException {
+        included = new Vector();
+        String analyzerClassName = DEFAULT_ANALYZER_CLASS;
+        DependencyAnalyzer analyzer = null;
+        try {
+            Class analyzerClass = Class.forName(analyzerClassName);
+            analyzer = (DependencyAnalyzer) analyzerClass.newInstance();
+        } catch (Exception e) {
+            throw new BuildException("Unable to load dependency analyzer: "
+                + analyzerClassName, e);
+        }
+        analyzer.addClassPath(new Path(null, basedir.getPath()));
+
+        for (Enumeration e = rootClasses.elements(); e.hasMoreElements();) {
+            String rootClass = (String) e.nextElement();
+            analyzer.addRootClass(rootClass);
+        }
+        Enumeration e = analyzer.getClassDependencies();
+
+        String[] parentFiles = parentScanner.getIncludedFiles();
+        Hashtable parentSet = new Hashtable();
+        for (int i = 0; i < parentFiles.length; ++i) {
+            parentSet.put(parentFiles[i], parentFiles[i]);
+        }
+        while (e.hasMoreElements()) {
+            String classname = (String) e.nextElement();
+            String filename = classname.replace('.', File.separatorChar);
+            filename = filename + ".class";
+            File depFile = new File(basedir, filename);
+            if (depFile.exists() && parentSet.containsKey(filename)) {
+                // This is included
+                included.addElement(filename);
+            }
+        }
+    }
+
+    /**
+     * @see DirectoryScanner#addDefaultExcludes
+     */
+    public void addDefaultExcludes() {
+    }
+
+    /**
+     * @see DirectoryScanner#getExcludedDirectories
+     */
+    public String[] getExcludedDirectories() {
+        return null;
+    }
+
+    /**
+     * @see DirectoryScanner#getExcludedFiles
+     */
+    public String[] getExcludedFiles() {
+        return null;
+    }
+
+    /**
+     * @see DirectoryScanner#getIncludedDirectories
+     */
+    public String[] getIncludedDirectories() {
+        return new String[0];
+    }
+
+    /**
+     * @see DirectoryScanner#getIncludedDirsCount
+     */
+    public int getIncludedDirsCount() {
+        return 0;
+    }
+
+    /**
+     * @see DirectoryScanner#getNotIncludedDirectories
+     */
+    public String[] getNotIncludedDirectories() {
+        return null;
+    }
+
+    /**
+     * @see DirectoryScanner#getNotIncludedFiles
+     */
+    public String[] getNotIncludedFiles() {
+        return null;
+    }
+
+    /**
+     * @see DirectoryScanner#setExcludes
+     */
+    public void setExcludes(String[] excludes) {
+    }
+
+    /**
+     * @see DirectoryScanner#setIncludes
+     */
+    public void setIncludes(String[] includes) {
+    }
+
+    /**
+     * @see DirectoryScanner#setCaseSensitive
+     */
+    public void setCaseSensitive(boolean isCaseSensitive) {
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Arc.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Arc.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Arc.java	(revision 16139)
@@ -0,0 +1,95 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.optional.image;
+
+import javax.media.jai.PlanarImage;
+import java.awt.BasicStroke;
+import java.awt.Graphics2D;
+import java.awt.geom.Arc2D;
+import java.awt.image.BufferedImage;
+
+public class Arc extends BasicShape implements DrawOperation {
+    protected int width = 0;
+    protected int height = 0;
+    protected int start = 0;
+    protected int stop = 0;
+    protected int type = Arc2D.OPEN;
+
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    public void setHeight(int height) {
+        this.height = height;
+    }
+
+    public void setStart(int start) {
+        this.start = start;
+    }
+
+    public void setStop(int stop) {
+        this.stop = stop;
+    }
+
+    /**
+     * @todo refactor using an EnumeratedAttribute
+     */
+    public void setType(String str_type) {
+        if (str_type.toLowerCase().equals("open")) {
+            type = Arc2D.OPEN;
+        } else if (str_type.toLowerCase().equals("pie")) {
+            type = Arc2D.PIE;
+        } else if (str_type.toLowerCase().equals("chord")) {
+            type = Arc2D.CHORD;
+        }
+    }
+
+    public PlanarImage executeDrawOperation() {
+        BufferedImage bi = new BufferedImage(width + (stroke_width * 2),
+            height + (stroke_width * 2), BufferedImage.TYPE_4BYTE_ABGR_PRE);
+
+        Graphics2D graphics = (Graphics2D) bi.getGraphics();
+
+        if (!stroke.equals("transparent")) {
+            BasicStroke b_stroke = new BasicStroke(stroke_width);
+            graphics.setColor(ColorMapper.getColorByName(stroke));
+            graphics.setStroke(b_stroke);
+            graphics.draw(new Arc2D.Double(stroke_width, stroke_width, width,
+                height, start, stop, type));
+        }
+
+        if (!fill.equals("transparent")) {
+            graphics.setColor(ColorMapper.getColorByName(fill));
+            graphics.fill(new Arc2D.Double(stroke_width, stroke_width,
+                width, height, start, stop, type));
+        }
+
+
+        for (int i = 0; i < instructions.size(); i++) {
+            ImageOperation instr = ((ImageOperation) instructions.elementAt(i));
+            if (instr instanceof DrawOperation) {
+                PlanarImage img = ((DrawOperation) instr).executeDrawOperation();
+                graphics.drawImage(img.getAsBufferedImage(), null, 0, 0);
+            } else if (instr instanceof TransformOperation) {
+                graphics = (Graphics2D) bi.getGraphics();
+                PlanarImage image = ((TransformOperation) instr).executeTransformOperation(PlanarImage.wrapRenderedImage(bi));
+                bi = image.getAsBufferedImage();
+            }
+        }
+        return PlanarImage.wrapRenderedImage(bi);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/BasicShape.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/BasicShape.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/BasicShape.java	(revision 16139)
@@ -0,0 +1,36 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.optional.image;
+
+public abstract class BasicShape extends ImageOperation implements DrawOperation {
+    protected int stroke_width = 0;
+    protected String fill = "transparent";
+    protected String stroke = "black";
+
+
+    public void setFill(String col) {
+        fill = col;
+    }
+
+    public void setStroke(String col) {
+        stroke = col;
+    }
+
+    public void setStrokewidth(int width) {
+        stroke_width = width;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/ColorMapper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/ColorMapper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/ColorMapper.java	(revision 16139)
@@ -0,0 +1,80 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.optional.image;
+
+import java.awt.Color;
+
+/**
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public final class ColorMapper {
+    public static final String COLOR_BLACK = "black";
+    public static final String COLOR_BLUE = "blue";
+    public static final String COLOR_CYAN = "cyan";
+    public static final String COLOR_DARKGRAY = "darkgray";
+    public static final String COLOR_GRAY = "gray";
+    public static final String COLOR_LIGHTGRAY = "lightgray";
+    // Gotta atleast put in the proper spelling :-P
+    public static final String COLOR_DARKGREY = "darkgrey";
+    public static final String COLOR_GREY = "grey";
+    public static final String COLOR_LIGHTGREY = "lightgrey";
+    public static final String COLOR_GREEN = "green";
+    public static final String COLOR_MAGENTA = "magenta";
+    public static final String COLOR_ORANGE = "orange";
+    public static final String COLOR_PINK = "pink";
+    public static final String COLOR_RED = "red";
+    public static final String COLOR_WHITE = "white";
+    public static final String COLOR_YELLOW = "yellow";
+
+    /**
+     * @todo refactor to use an EnumeratedAttribute (maybe?)
+     */
+    public static final Color getColorByName(String color_name) {
+        color_name = color_name.toLowerCase();
+
+        if (color_name.equals(COLOR_BLACK)) {
+            return Color.black;
+        } else if (color_name.equals(COLOR_BLUE)) {
+            return Color.blue;
+        } else if (color_name.equals(COLOR_CYAN)) {
+            return Color.cyan;
+        } else if (color_name.equals(COLOR_DARKGRAY) || color_name.equals(COLOR_DARKGREY)) {
+            return Color.darkGray;
+        } else if (color_name.equals(COLOR_GRAY) || color_name.equals(COLOR_GREY)) {
+            return Color.gray;
+        } else if (color_name.equals(COLOR_LIGHTGRAY) || color_name.equals(COLOR_LIGHTGREY)) {
+            return Color.lightGray;
+        } else if (color_name.equals(COLOR_GREEN)) {
+            return Color.green;
+        } else if (color_name.equals(COLOR_MAGENTA)) {
+            return Color.magenta;
+        } else if (color_name.equals(COLOR_ORANGE)) {
+            return Color.orange;
+        } else if (color_name.equals(COLOR_PINK)) {
+            return Color.pink;
+        } else if (color_name.equals(COLOR_RED)) {
+            return Color.red;
+        } else if (color_name.equals(COLOR_WHITE)) {
+            return Color.white;
+        } else if (color_name.equals(COLOR_YELLOW)) {
+            return Color.yellow;
+        }
+        return Color.black;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Draw.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Draw.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Draw.java	(revision 16139)
@@ -0,0 +1,79 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.optional.image;
+
+import javax.media.jai.PlanarImage;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+/**
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public class Draw extends TransformOperation {
+    protected int xloc = 0;
+    protected int yloc = 0;
+
+    public void setXloc(int x) {
+        xloc = x;
+    }
+
+    public void setYloc(int y) {
+        yloc = y;
+    }
+
+    public void addRectangle(Rectangle rect) {
+        instructions.add(rect);
+    }
+
+    public void addText(Text text) {
+        instructions.add(text);
+    }
+
+    public void addEllipse(Ellipse elip) {
+        instructions.add(elip);
+    }
+
+    public void addArc(Arc arc) {
+        instructions.add(arc);
+    }
+
+
+    public PlanarImage executeTransformOperation(PlanarImage image) {
+        BufferedImage bi = image.getAsBufferedImage();
+        Graphics2D graphics = (Graphics2D) bi.getGraphics();
+
+        for (int i = 0; i < instructions.size(); i++) {
+            ImageOperation instr = ((ImageOperation) instructions.elementAt(i));
+            if (instr instanceof DrawOperation) {
+                PlanarImage op = ((DrawOperation) instr).executeDrawOperation();
+                log("\tDrawing to x=" + xloc + " y=" + yloc);
+                graphics.drawImage(op.getAsBufferedImage(), null, xloc, yloc);
+            } else if (instr instanceof TransformOperation) {
+                PlanarImage op
+                    = ((TransformOperation) instr).executeTransformOperation(null);
+                BufferedImage child = op.getAsBufferedImage();
+                log("\tDrawing to x=" + xloc + " y=" + yloc);
+                graphics.drawImage(child, null, xloc, yloc);
+                PlanarImage.wrapRenderedImage(bi);
+            }
+        }
+        image = PlanarImage.wrapRenderedImage(bi);
+
+        return image;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/DrawOperation.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/DrawOperation.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/DrawOperation.java	(revision 16139)
@@ -0,0 +1,37 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.optional.image;
+
+import javax.media.jai.PlanarImage;
+
+/**
+ * Interface which represents an Operation which is "drawable", such
+ * as a Rectangle, Circle or Text.  The Operation is responsible for
+ * creating its own image buffer and drawing itself into it, then
+ * wrapping and returning it as a PlanarImage.  This allows multible
+ * "drawable" objects to be nested.
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public interface DrawOperation {
+    /**
+     * Abstract method which is intended to create an image buffer
+     * and return it so it can be drawn into another object.  Use
+     * an Alpha channel for a "transparent" background.
+     */
+    public PlanarImage executeDrawOperation();
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Ellipse.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Ellipse.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Ellipse.java	(revision 16139)
@@ -0,0 +1,72 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.optional.image;
+
+import javax.media.jai.PlanarImage;
+import java.awt.BasicStroke;
+import java.awt.Graphics2D;
+import java.awt.geom.Ellipse2D;
+import java.awt.image.BufferedImage;
+
+/**
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public class Ellipse extends BasicShape implements DrawOperation {
+    protected int width = 0;
+    protected int height = 0;
+
+    public void setWidth(int width) {
+        this.width = width;
+    }
+
+    public void setHeight(int height) {
+        this.height = height;
+    }
+
+    public PlanarImage executeDrawOperation() {
+        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR_PRE);
+
+        Graphics2D graphics = (Graphics2D) bi.getGraphics();
+
+        if (!stroke.equals("transparent")) {
+            BasicStroke b_stroke = new BasicStroke(stroke_width);
+            graphics.setColor(ColorMapper.getColorByName(stroke));
+            graphics.setStroke(b_stroke);
+            graphics.draw(new Ellipse2D.Double(0, 0, width, height));
+        }
+
+        if (!fill.equals("transparent")) {
+            graphics.setColor(ColorMapper.getColorByName(fill));
+            graphics.fill(new Ellipse2D.Double(0, 0, width, height));
+        }
+
+
+        for (int i = 0; i < instructions.size(); i++) {
+            ImageOperation instr = ((ImageOperation) instructions.elementAt(i));
+            if (instr instanceof DrawOperation) {
+                PlanarImage img = ((DrawOperation) instr).executeDrawOperation();
+                graphics.drawImage(img.getAsBufferedImage(), null, 0, 0);
+            } else if (instr instanceof TransformOperation) {
+                graphics = (Graphics2D) bi.getGraphics();
+                PlanarImage image = ((TransformOperation) instr).executeTransformOperation(PlanarImage.wrapRenderedImage(bi));
+                bi = image.getAsBufferedImage();
+            }
+        }
+        return PlanarImage.wrapRenderedImage(bi);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/ImageOperation.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/ImageOperation.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/ImageOperation.java	(revision 16139)
@@ -0,0 +1,48 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.optional.image;
+
+import org.apache.tools.ant.types.DataType;
+import java.util.Vector;
+
+/**
+ *
+ * @see org.apache.tools.ant.tasks.optional.image.Image
+ */
+public abstract class ImageOperation extends DataType {
+    protected Vector instructions = new Vector();
+
+    public void addRotate(Rotate instr) {
+        instructions.add(instr);
+    }
+
+    public void addDraw(Draw instr) {
+        instructions.add(instr);
+    }
+
+    public void addRectangle(Rectangle instr) {
+        instructions.add(instr);
+    }
+
+    public void addText(Text instr) {
+        instructions.add(instr);
+    }
+
+    public void addScale(Scale instr) {
+        instructions.add(instr);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Rectangle.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Rectangle.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Rectangle.java	(revision 16139)
@@ -0,0 +1,96 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.optional.image;
+
+import javax.media.jai.PlanarImage;
+import java.awt.BasicStroke;
+import java.awt.Graphics2D;
+import java.awt.image.BufferedImage;
+
+/**
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public class Rectangle extends BasicShape implements DrawOperation {
+    protected int width = 0;
+    protected int height = 0;
+    protected int arcwidth = 0;
+    protected int archeight = 0;
+
+    public void setWidth(int w) {
+        width = w;
+    }
+
+    public void setHeight(int h) {
+        height = h;
+    }
+
+    public void setArcwidth(int w) {
+        arcwidth = w;
+    }
+
+    public void setArcheight(int h) {
+        archeight = h;
+    }
+
+    public PlanarImage executeDrawOperation() {
+        log("\tCreating Rectangle w=" + width + " h=" + height + " arcw="
+            + arcwidth + " arch=" + archeight);
+        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR_PRE);
+
+        Graphics2D graphics = (Graphics2D) bi.getGraphics();
+
+        if (!stroke.equals("transparent")) {
+            BasicStroke b_stroke = new BasicStroke(stroke_width);
+            graphics.setColor(ColorMapper.getColorByName(stroke));
+            graphics.setStroke(b_stroke);
+
+            if ((arcwidth != 0) || (archeight != 0)) {
+                graphics.drawRoundRect(0, 0, width, height, arcwidth, archeight);
+            } else {
+                graphics.drawRect(0, 0, width, height);
+            }
+        }
+
+        if (!fill.equals("transparent")) {
+            graphics.setColor(ColorMapper.getColorByName(fill));
+            if ((arcwidth != 0) || (archeight != 0)) {
+                graphics.fillRoundRect(stroke_width, stroke_width,
+                    width - (stroke_width * 2), height - (stroke_width * 2),
+                    arcwidth, archeight);
+            } else {
+                graphics.fillRect(stroke_width, stroke_width,
+                    width - (stroke_width * 2), height - (stroke_width * 2));
+            }
+        }
+
+
+        for (int i = 0; i < instructions.size(); i++) {
+            ImageOperation instr = ((ImageOperation) instructions.elementAt(i));
+            if (instr instanceof DrawOperation) {
+                PlanarImage img = ((DrawOperation) instr).executeDrawOperation();
+                graphics.drawImage(img.getAsBufferedImage(), null, 0, 0);
+            } else if (instr instanceof TransformOperation) {
+                graphics = (Graphics2D) bi.getGraphics();
+                PlanarImage image
+                    = ((TransformOperation) instr).executeTransformOperation(PlanarImage.wrapRenderedImage(bi));
+                bi = image.getAsBufferedImage();
+            }
+        }
+        return PlanarImage.wrapRenderedImage(bi);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Rotate.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Rotate.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Rotate.java	(revision 16139)
@@ -0,0 +1,105 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.optional.image;
+
+import javax.media.jai.PlanarImage;
+import javax.media.jai.InterpolationNearest;
+import javax.media.jai.JAI;
+import java.awt.image.renderable.ParameterBlock;
+import java.awt.image.BufferedImage;
+import java.awt.Graphics2D;
+
+/**
+ * ImageOperation to rotate an image by a certain degree
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public class Rotate extends TransformOperation implements DrawOperation {
+    protected float angle = 0.0F;
+
+    /**
+     * Sets the angle of rotation in degrees.
+     * @param ang The angle at which to rotate the image
+     */
+    public void setAngle(String ang) {
+        angle = Float.parseFloat(ang);
+    }
+
+
+    public PlanarImage performRotate(PlanarImage image) {
+        float t_angle = (float) (angle * (Math.PI / 180.0F));
+        ParameterBlock pb = new ParameterBlock();
+        pb.addSource(image);
+        pb.add(0.0F);
+        pb.add(0.0F);
+        pb.add(t_angle);
+        pb.add(new InterpolationNearest());
+        return JAI.create("Rotate", pb, null);
+    }
+
+
+    /**
+     *  Performs the image rotation when being handled as a TransformOperation.
+     * @param image The image to perform the transformation on.
+     */
+    public PlanarImage executeTransformOperation(PlanarImage image) {
+        BufferedImage bi = null;
+        Graphics2D graphics = null;
+        for (int i = 0; i < instructions.size(); i++) {
+            ImageOperation instr = ((ImageOperation) instructions.elementAt(i));
+            if (instr instanceof DrawOperation) {
+                // If this TransformOperation has DrawOperation children
+                // then Rotate the first child and return.
+                System.out.println("Execing Draws");
+                PlanarImage op = ((DrawOperation) instr).executeDrawOperation();
+                image = performRotate(op);
+                return image;
+            } else if (instr instanceof TransformOperation) {
+                bi = image.getAsBufferedImage();
+                graphics = (Graphics2D) bi.getGraphics();
+                System.out.println("Execing Transforms");
+                image = ((TransformOperation) instr).executeTransformOperation(PlanarImage.wrapRenderedImage(bi));
+                bi = image.getAsBufferedImage();
+            }
+        }
+        System.out.println("Execing as TransformOperation");
+        image = performRotate(image);
+        System.out.println(image);
+        return image;
+    }
+
+    /**
+     *  Performs the image rotation when being handled as a DrawOperation.
+     *  It absolutely requires that there be a DrawOperation nested beneath it,
+     *  but only the FIRST DrawOperation will be handled since it can only return
+     *  ONE image.
+     */
+    public PlanarImage executeDrawOperation() {
+        for (int i = 0; i < instructions.size(); i++) {
+            ImageOperation instr = ((ImageOperation) instructions.elementAt(i));
+            if (instr instanceof DrawOperation) {
+                // If this TransformOperation has DrawOperation children
+                // then Rotate the first child and return.
+                PlanarImage op = ((DrawOperation) instr).executeDrawOperation();
+                op = performRotate(op);
+                return op;
+            }
+        }
+        return null;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Scale.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Scale.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Scale.java	(revision 16139)
@@ -0,0 +1,151 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.optional.image;
+
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+import javax.media.jai.JAI;
+import javax.media.jai.PlanarImage;
+import java.awt.image.BufferedImage;
+import java.awt.image.renderable.ParameterBlock;
+
+/**
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public class Scale extends TransformOperation implements DrawOperation {
+
+    private String width_str = "100%";
+    private String height_str = "100%";
+    private boolean x_percent = true;
+    private boolean y_percent = true;
+    private String proportions = "ignore";
+
+    public static class ProportionsAttribute extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[] {"ignore", "width", "height", "cover", "fit"};
+        }
+    }
+
+    /**
+     *  Sets the behaviour regarding the image proportions.
+     */
+    public void setProportions(ProportionsAttribute pa) {
+        proportions = pa.getValue();
+    }
+
+    /**
+     *  Sets the width of the image, either as an integer or a %.  Defaults to 100%.
+     */
+    public void setWidth(String width) {
+        width_str = width;
+    }
+
+    /**
+     *  Sets the height of the image, either as an integer or a %.  Defaults to 100%.
+     */
+    public void setHeight(String height) {
+        height_str = height;
+    }
+
+    public float getWidth() {
+        float width = 0.0F;
+        int perc_index = width_str.indexOf('%');
+        if (perc_index > 0) {
+            width = Float.parseFloat(width_str.substring(0, perc_index));
+            x_percent = true;
+            return width / 100;
+        } else {
+            x_percent = false;
+            return Float.parseFloat(width_str);
+        }
+    }
+
+    public float getHeight() {
+        int perc_index = height_str.indexOf('%');
+        if (perc_index > 0) {
+            float height = Float.parseFloat(height_str.substring(0, perc_index));
+            y_percent = true;
+            return height / 100;
+        } else {
+            y_percent = false;
+            return Float.parseFloat(height_str);
+        }
+    }
+
+    public PlanarImage performScale(PlanarImage image) {
+        ParameterBlock pb = new ParameterBlock();
+        pb.addSource(image);
+        float x_fl = getWidth();
+        float y_fl = getHeight();
+
+        if (!x_percent) {
+            x_fl = (x_fl / image.getWidth());
+        }
+        if (!y_percent) {
+            y_fl = (y_fl / image.getHeight());
+        }
+
+        if ("width".equals(proportions)) {
+            y_fl = x_fl;
+        } else if ("height".equals(proportions)) {
+            x_fl = y_fl;
+        } else if ("fit".equals(proportions)) {
+            x_fl = y_fl = Math.min(x_fl, y_fl);
+        } else if ("cover".equals(proportions)) {
+            x_fl = y_fl = Math.max(x_fl, y_fl);
+        }
+
+        pb.add(new Float(x_fl));
+        pb.add(new Float(y_fl));
+
+        log("\tScaling to " + (x_fl * 100) + "% x " + (y_fl * 100) + "%");
+
+        return JAI.create("scale", pb);
+    }
+
+
+    public PlanarImage executeTransformOperation(PlanarImage image) {
+        BufferedImage bi = null;
+        for (int i = 0; i < instructions.size(); i++) {
+            ImageOperation instr = ((ImageOperation) instructions.elementAt(i));
+            if (instr instanceof DrawOperation) {
+                return performScale(image);
+            } else if (instr instanceof TransformOperation) {
+                bi = image.getAsBufferedImage();
+                image = ((TransformOperation) instr).executeTransformOperation(PlanarImage.wrapRenderedImage(bi));
+                bi = image.getAsBufferedImage();
+            }
+        }
+        return performScale(image);
+    }
+
+
+    public PlanarImage executeDrawOperation() {
+        for (int i = 0; i < instructions.size(); i++) {
+            ImageOperation instr = ((ImageOperation) instructions.elementAt(i));
+            if (instr instanceof DrawOperation) {
+                PlanarImage image = null;
+                // If this TransformOperation has DrawOperation children
+                // then Rotate the first child and return.
+                performScale(image);
+                return image;
+            }
+        }
+        return null;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Text.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Text.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/Text.java	(revision 16139)
@@ -0,0 +1,98 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.optional.image;
+
+import javax.media.jai.PlanarImage;
+import java.awt.Color;
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics2D;
+import java.awt.RenderingHints;
+import java.awt.image.BufferedImage;
+
+/**
+ *
+ * @see org.apache.tools.ant.taskdefs.optional.image.Image
+ */
+public class Text extends ImageOperation implements DrawOperation {
+    private String str_text = "";
+    private String font = "Arial";
+    private int point = 10;
+    private boolean bold = false;
+    private boolean italic = false;
+    private String color = "black";
+
+    public void setString(String str) {
+        str_text = str;
+    }
+
+    public void setFont(String f) {
+        font = f;
+    }
+
+    public void setPoint(String p) {
+        point = Integer.parseInt(p);
+    }
+
+    public void setColor(String c) {
+        color = c;
+    }
+
+    /**
+     * @todo is this used?
+     */
+    public void setBold(boolean state) {
+        bold = state;
+    }
+
+    /**
+     * @todo is this used?
+     */
+    public void setItalic(boolean state) {
+        italic = state;
+    }
+
+    public PlanarImage executeDrawOperation() {
+        log("\tCreating Text \"" + str_text + "\"");
+
+        Color couloir = ColorMapper.getColorByName(color);
+        int width = 1;
+        int height = 1;
+
+        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR_PRE);
+        Graphics2D graphics = (Graphics2D) bi.getGraphics();
+        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+        Font f = new Font(font, Font.PLAIN, point);
+        FontMetrics fmetrics = graphics.getFontMetrics(f);
+        height = fmetrics.getMaxAscent() + fmetrics.getMaxDescent();
+        width = fmetrics.stringWidth(str_text);
+
+
+        bi = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR_PRE);
+        graphics = (Graphics2D) bi.getGraphics();
+
+        graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+        graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
+
+        graphics.setFont(f);
+        graphics.setColor(couloir);
+        graphics.drawString(str_text, 0, height - fmetrics.getMaxDescent());
+        PlanarImage image = PlanarImage.wrapRenderedImage(bi);
+        return image;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/TransformOperation.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/TransformOperation.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/optional/image/TransformOperation.java	(revision 16139)
@@ -0,0 +1,31 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.optional.image;
+import javax.media.jai.PlanarImage;
+
+/**
+ *
+ * @see org.apache.tools.ant.tasks.optional.image.Image
+ */
+public abstract class TransformOperation extends ImageOperation {
+    public abstract PlanarImage executeTransformOperation(PlanarImage img);
+
+    public void addRectangle(Rectangle instr) {
+        instructions.add(instr);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/resolver/ApacheCatalog.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/resolver/ApacheCatalog.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/resolver/ApacheCatalog.java	(revision 16139)
@@ -0,0 +1,116 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.resolver;
+
+import org.apache.xml.resolver.Catalog;
+import org.apache.xml.resolver.CatalogEntry;
+
+import org.apache.xml.resolver.helpers.PublicId;
+
+/**
+ * This class extends the Catalog class provided by Norman Walsh's
+ * resolver library in xml-commons in order to add classpath entity
+ * and URI resolution.  Since XMLCatalog already does classpath
+ * resolution, we simply add all CatalogEntry instances back to the
+ * controlling XMLCatalog instance.  This is done via a callback
+ * mechanism.  ApacheCatalog is <em>only</em> used for external
+ * catalog files.  Inline entries (currently <code>&lt;dtd&gt;</code>
+ * and <code>&lt;entity&gt;</code>) are not added to ApacheCatalog.
+ * See XMLCatalog.java for the details of the entity and URI
+ * resolution algorithms.
+ *
+ * @see org.apache.tools.ant.types.XMLCatalog.CatalogResolver
+ * @since Ant 1.6
+ */
+public class ApacheCatalog extends Catalog {
+
+    /** The resolver object to callback. */
+    private ApacheCatalogResolver resolver = null;
+
+    /**
+     * <p>Create a new ApacheCatalog instance.</p>
+     *
+     * <p>This method overrides the superclass method of the same name
+     *  in order to set the resolver object for callbacks.  The reason
+     *  we have to do this is that internally Catalog creates a new
+     *  instance of itself for each external catalog file processed.
+     *  That is, if two external catalog files are processed, there
+     *  will be a total of two ApacheCatalog instances, and so on.</p>
+     */
+    protected Catalog newCatalog() {
+        ApacheCatalog cat = (ApacheCatalog) super.newCatalog();
+        cat.setResolver(resolver);
+        return cat;
+    }
+
+    /** Set the resolver object to callback. */
+    public void setResolver(ApacheCatalogResolver resolver) {
+        this.resolver = resolver;
+    }
+
+    /**
+     * <p>This method overrides the superclass method of the same name
+     * in order to add catalog entries back to the controlling
+     * XMLCatalog instance.  In this way, we can add classpath lookup
+     * for these entries.</p>
+     *
+     * <p>When we add an external catalog file, the entries inside it
+     * get parsed by this method.  Therefore, we override it to add
+     * each of them back to the controlling XMLCatalog instance.  This
+     * is done by performing a callback to the ApacheCatalogResolver,
+     * which in turn calls the XMLCatalog.</p>
+     *
+     * <p>XMLCatalog currently only understands <code>PUBLIC</code>
+     * and <code>URI</code> entry types, so we ignore the other types.</p>
+     *
+     * @param entry The CatalogEntry to process.
+     */
+    public void addEntry(CatalogEntry entry) {
+
+        int type = entry.getEntryType();
+
+        if (type == PUBLIC) {
+
+            String publicid = PublicId.normalize(entry.getEntryArg(0));
+            String systemid = normalizeURI(entry.getEntryArg(1));
+
+            if (resolver == null) {
+                catalogManager.debug
+                    .message(1, "Internal Error: null ApacheCatalogResolver");
+            } else {
+                resolver.addPublicEntry(publicid, systemid, base);
+            }
+
+        } else if (type == URI) {
+
+            String uri = normalizeURI(entry.getEntryArg(0));
+            String altURI = normalizeURI(entry.getEntryArg(1));
+
+            if (resolver == null) {
+                catalogManager.debug
+                    .message(1, "Internal Error: null ApacheCatalogResolver");
+            } else {
+                resolver.addURIEntry(uri, altURI, base);
+            }
+
+        }
+
+        super.addEntry(entry);
+    }
+
+} //- ApacheCatalog
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/resolver/ApacheCatalogResolver.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/resolver/ApacheCatalogResolver.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/resolver/ApacheCatalogResolver.java	(revision 16139)
@@ -0,0 +1,172 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.resolver;
+
+import java.io.IOException;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.apache.tools.ant.BuildException;
+
+import org.apache.tools.ant.types.XMLCatalog;
+import org.apache.tools.ant.types.ResourceLocation;
+
+import org.apache.xml.resolver.Catalog;
+import org.apache.xml.resolver.CatalogManager;
+
+import org.apache.xml.resolver.tools.CatalogResolver;
+
+/**
+ * <p>This class extends the CatalogResolver class provided by Norman
+ * Walsh's resolver library in xml-commons.  It provides the bridge
+ * between the Ant XMLCatalog datatype and the xml-commons Catalog
+ * class.  XMLCatalog calls methods in this class using Reflection in
+ * order to avoid requiring the xml-commons resolver library in the
+ * path.</p>
+ *
+ * <p>The {@link org.apache.tools.ant.types.resolver.ApacheCatalog
+ * ApacheCatalog} class is used to parse external catalog files, which
+ * can be in either <a
+ * href="http://oasis-open.org/committees/entity/background/9401.html">
+ * plain text format</a> or <a
+ * href="http://www.oasis-open.org/committees/entity/spec-2001-08-06.html">
+ * XML format</a>.</p>
+ *
+ * <p>For each entry found in an external catalog file, if any, an
+ * instance of {@link org.apache.tools.ant.types.ResourceLocation
+ * ResourceLocation} is created and added to the controlling
+ * XMLCatalog datatype.  In this way, these entries will be included
+ * in XMLCatalog's lookup algorithm.  See XMLCatalog.java for more
+ * details.</p>
+ *
+ * @see org.apache.tools.ant.types.XMLCatalog.CatalogResolver
+ * @see org.apache.xml.resolver.CatalogManager
+ * @since Ant 1.6
+ */
+
+public class ApacheCatalogResolver extends CatalogResolver {
+
+    /** The XMLCatalog object to callback. */
+    private XMLCatalog xmlCatalog = null;
+
+    static {
+        //
+        // If you don't do this, you get all sorts of annoying
+        // warnings about a missing properties file.  However, it
+        // seems to work just fine with default values.  Ultimately,
+        // we should probably include a "CatalogManager.properties"
+        // file in the ant jarfile with some default property
+        // settings.  See CatalogManager.java for more details.
+        //
+        CatalogManager.getStaticManager().setIgnoreMissingProperties(true);
+
+        //
+        // Make sure CatalogResolver instantiates ApacheCatalog,
+        // rather than a plain Catalog
+        //
+        System.getProperties().put("xml.catalog.className",
+                                   ApacheCatalog.class.getName());
+
+        CatalogManager.getStaticManager().setUseStaticCatalog(false);
+
+        // debug
+        // CatalogManager.getStaticManager().setVerbosity(4);
+    }
+
+    /** Set the XMLCatalog object to callback. */
+    public void setXMLCatalog(XMLCatalog xmlCatalog) {
+        this.xmlCatalog = xmlCatalog;
+    }
+
+    /**
+     * XMLCatalog calls this to add an external catalog file for each
+     * file within a <code>&lt;catalogfiles&gt;</code> fileset.
+     */
+    public void parseCatalog(String file) {
+
+        Catalog _catalog = getCatalog();
+        if (!(_catalog instanceof ApacheCatalog)) {
+            throw new BuildException("Wrong catalog type found: " + _catalog.getClass().getName());
+        }
+        ApacheCatalog catalog = (ApacheCatalog) _catalog;
+
+        // Pass in reference to ourselves so we can be called back.
+        catalog.setResolver(this);
+
+        try {
+            catalog.parseCatalog(file);
+        } catch (MalformedURLException ex) {
+            throw new BuildException(ex);
+        } catch (IOException ex) {
+            throw new BuildException(ex);
+        }
+    }
+
+    /**
+     * <p>Add a PUBLIC catalog entry to the controlling XMLCatalog instance.
+     * ApacheCatalog calls this for each PUBLIC entry found in an external
+     * catalog file.</p>
+     *
+     * @param publicid The public ID of the resource
+     * @param systemid The system ID (aka location) of the resource
+     * @param base The base URL of the resource.  If the systemid
+     * specifies a relative URL/pathname, it is resolved using the
+     * base.  The default base for an external catalog file is the
+     * directory in which the catalog is located.
+     *
+     */
+    public void addPublicEntry(String publicid,
+                               String systemid,
+                               URL base) {
+
+        ResourceLocation dtd = new ResourceLocation();
+        dtd.setBase(base);
+        dtd.setPublicId(publicid);
+        dtd.setLocation(systemid);
+
+        xmlCatalog.addDTD(dtd);
+    }
+
+    /**
+     * <p>Add a URI catalog entry to the controlling XMLCatalog instance.
+     * ApacheCatalog calls this for each URI entry found in an external
+     * catalog file.</p>
+     *
+     * @param uri The URI of the resource
+     * @param altURI The URI to which the resource should be mapped
+     * (aka the location)
+     * @param base The base URL of the resource.  If the altURI
+     * specifies a relative URL/pathname, it is resolved using the
+     * base.  The default base for an external catalog file is the
+     * directory in which the catalog is located.
+     *
+     */
+    public void addURIEntry(String uri,
+                            String altURI,
+                            URL base) {
+
+        ResourceLocation entity = new ResourceLocation();
+        entity.setBase(base);
+        entity.setPublicId(uri);
+        entity.setLocation(altURI);
+
+        xmlCatalog.addEntity(entity);
+    }
+
+} //-- ApacheCatalogResolver
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/resolver/package.html
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/resolver/package.html	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/resolver/package.html	(revision 16139)
@@ -0,0 +1,23 @@
+<body>
+Ant integration with xml-commons resolver.
+
+<p>These classes enhance the <code>&lt;xmlcatalog&gt;</code> datatype
+to support external catalog files using the xml-commons resolver, in
+accordance with the
+<a href="http://oasis-open.org/committees/entity/spec-2001-08-06.html">
+OASIS "Open Catalog" standard</a>.  They will be used if and only if
+the xml-commons resolver library is available on the classpath.</p>
+
+@see <A HREF="http://xml.apache.org/commons">Apache xml-commons Project</A>
+
+@see org.apache.tools.ant.types.XMLCatalog
+@see org.apache.tools.ant.types.resolver.ApacheCatalogResolver
+@see org.apache.tools.ant.types.resolver.ApacheCatalog
+
+@author <A HREF="mailto:cstrong@arielpartners.com">Craeg Strong</A>
+
+
+<hr>
+<p align="center">Copyright &copy; 2002 Apache Software Foundation. All rights
+Reserved.</p>
+</body>
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/AbstractSelectorContainer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/AbstractSelectorContainer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/AbstractSelectorContainer.java	(revision 16139)
@@ -0,0 +1,287 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector;
+
+/**
+ * This is the a base class a container of selectors - it does
+ * not need do be a selector itself.
+ *
+ * @since 1.7
+ */
+public abstract class AbstractSelectorContainer extends DataType
+        implements SelectorContainer {
+
+    private Vector selectorsList = new Vector();
+
+    /**
+     * Indicates whether there are any selectors here.
+     * @return true if there are selectors
+     */
+    public boolean hasSelectors() {
+        return !(selectorsList.isEmpty());
+    }
+
+    /**
+     * Gives the count of the number of selectors in this container
+     * @return the number of selectors
+     */
+    public int selectorCount() {
+        return selectorsList.size();
+    }
+
+    /**
+     * Returns the set of selectors as an array.
+     * @param p the current project
+     * @return an array of selectors
+     */
+    public FileSelector[] getSelectors(Project p) {
+        FileSelector[] result = new FileSelector[selectorsList.size()];
+        selectorsList.copyInto(result);
+        return result;
+    }
+
+    /**
+     * Returns an enumerator for accessing the set of selectors.
+     * @return an enumerator for the selectors
+     */
+    public Enumeration selectorElements() {
+        return selectorsList.elements();
+    }
+
+    /**
+     * Convert the Selectors within this container to a string. This will
+     * just be a helper class for the subclasses that put their own name
+     * around the contents listed here.
+     *
+     * @return comma separated list of Selectors contained in this one
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        Enumeration e = selectorElements();
+        if (e.hasMoreElements()) {
+            while (e.hasMoreElements()) {
+                buf.append(e.nextElement().toString());
+                if (e.hasMoreElements()) {
+                    buf.append(", ");
+                }
+            }
+        }
+
+        return buf.toString();
+    }
+
+    /**
+     * Add a new selector into this container.
+     *
+     * @param selector the new selector to add
+     */
+    public void appendSelector(FileSelector selector) {
+        selectorsList.addElement(selector);
+    }
+
+    /**
+     * <p>
+     * This validates each contained selector
+     * provided that the selector implements the validate interface.
+     * </p>
+     * <p>Ordinarily, this will validate all the elements of a selector
+     * container even if the isSelected() method of some elements is
+     * never called. This has two effects:</p>
+     * <ul>
+     * <li>Validation will often occur twice.
+     * <li>Since it is not required that selectors derive from
+     * BaseSelector, there could be selectors in the container whose
+     * error conditions are not detected if their isSelected() call
+     * is never made.
+     * </ul>
+     */
+    public void validate() {
+        Enumeration e = selectorElements();
+        while (e.hasMoreElements()) {
+            Object o = e.nextElement();
+            if (o instanceof BaseSelector) {
+                ((BaseSelector) o).validate();
+            }
+        }
+    }
+
+
+    /* Methods below all add specific selectors */
+
+    /**
+     * add a "Select" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addSelector(SelectSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add an "And" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addAnd(AndSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add an "Or" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addOr(OrSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a "Not" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addNot(NotSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a "None" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addNone(NoneSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a majority selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addMajority(MajoritySelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a selector date entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addDate(DateSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a selector size entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addSize(SizeSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a selector filename entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addFilename(FilenameSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add an extended selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addCustom(ExtendSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a contains selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addContains(ContainsSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a present selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addPresent(PresentSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a depth selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addDepth(DepthSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a depends selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addDepend(DependSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * adds a different selector to the selector list
+     * @param selector the selector to add
+     */
+    public void addDifferent(DifferentSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * adds a type selector to the selector list
+     * @param selector the selector to add
+     */
+    public void addType(TypeSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a regular expression selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addContainsRegexp(ContainsRegexpSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add the modified selector
+     * @param selector the selector to add
+     * @since ant 1.6
+     */
+    public void addModified(ModifiedSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add an arbitary selector
+     * @param selector the selector to add
+     * @since Ant 1.6
+     */
+    public void add(FileSelector selector) {
+        appendSelector(selector);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/AndSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/AndSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/AndSelector.java	(revision 16139)
@@ -0,0 +1,76 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.Enumeration;
+
+/**
+ * This selector has a collection of other selectors, all of which have to
+ * select a file in order for this selector to select it.
+ *
+ * @since 1.5
+ */
+public class AndSelector extends BaseSelectorContainer {
+
+    /**
+     * Default constructor.
+     */
+    public AndSelector() {
+    }
+
+    /**
+     * @return a string representation of the selector
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        if (hasSelectors()) {
+            buf.append("{andselect: ");
+            buf.append(super.toString());
+            buf.append("}");
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Returns true (the file is selected) only if all other selectors
+     * agree that the file should be selected.
+     *
+     * @param basedir the base directory the scan is being done from
+     * @param filename the name of the file to check
+     * @param file a java.io.File object for the filename that the selector
+     * can use
+     * @return whether the file should be selected or not
+     */
+    public boolean isSelected(File basedir, String filename, File file) {
+        validate();
+        Enumeration e = selectorElements();
+        boolean result;
+
+        while (e.hasMoreElements()) {
+            result = ((FileSelector) e.nextElement()).isSelected(basedir,
+                    filename, file);
+            if (!result) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/BaseExtendSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/BaseExtendSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/BaseExtendSelector.java	(revision 16139)
@@ -0,0 +1,82 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Convenience base class for all selectors accessed through ExtendSelector.
+ * It provides support for gathering the parameters together as well as for
+ * assigning an error message and throwing a build exception if an error is
+ * detected.
+ *
+ * @since 1.5
+ */
+public abstract class BaseExtendSelector
+        extends BaseSelector
+        implements ExtendFileSelector {
+
+    /** The passed in parameter array. */
+    protected Parameter[] parameters = null;
+
+    /**
+     * Default constructor.
+     */
+    public BaseExtendSelector() {
+    }
+
+    /**
+     * Set all the Parameters for this custom selector, collected by
+     * the ExtendSelector class.
+     *
+     * @param parameters the complete set of parameters for this selector
+     */
+    public void setParameters(Parameter[] parameters) {
+        this.parameters = parameters;
+    }
+
+    /**
+     * Allows access to the parameters gathered and set within the
+     * &lt;custom&gt; tag.
+     *
+     * @return the set of parameters defined for this selector
+     */
+    protected Parameter[] getParameters() {
+        return parameters;
+    }
+
+    /**
+     * Method that each selector will implement to create their
+     * selection behaviour. If there is a problem with the setup
+     * of a selector, it can throw a BuildException to indicate
+     * the problem.
+     *
+     * @param basedir A java.io.File object for the base directory
+     * @param filename The name of the file to check
+     * @param file A File object for this filename
+     * @return whether the file should be selected or not
+     * @exception BuildException if an error occurs
+     */
+    public abstract boolean isSelected(File basedir, String filename,
+                                       File file)
+            throws BuildException;
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/BaseSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/BaseSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/BaseSelector.java	(revision 16139)
@@ -0,0 +1,106 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.DataType;
+
+/**
+ * A convenience base class that you can subclass Selectors from. It
+ * provides some helpful common behaviour. Note that there is no need
+ * for Selectors to inherit from this class, it is only necessary that
+ * they implement FileSelector.
+ *
+ * @since 1.5
+ */
+public abstract class BaseSelector extends DataType implements FileSelector {
+
+    private String errmsg = null;
+
+
+    /**
+     * Do nothing constructor.
+     */
+    public BaseSelector() {
+    }
+
+    /**
+     * Allows all selectors to indicate a setup error. Note that only
+     * the first error message is recorded.
+     *
+     * @param msg The error message any BuildException should throw.
+     */
+    public void setError(String msg) {
+        if (errmsg == null) {
+            errmsg = msg;
+        }
+    }
+
+    /**
+     * Returns any error messages that have been set.
+     *
+     * @return the error condition
+     */
+    public String getError() {
+        return errmsg;
+    }
+
+
+    /**
+     * <p>Subclasses can override this method to provide checking of their
+     * state. So long as they call validate() from isSelected(), this will
+     * be called automatically (unless they override validate()).</p>
+     * <p>Implementations should check for incorrect settings and call
+     * setError() as necessary.</p>
+     */
+    public void verifySettings() {
+    }
+
+
+    /**
+     * Subclasses can use this to throw the requisite exception
+     * in isSelected() in the case of an error condition.
+     */
+    public void validate() {
+        if (getError() == null) {
+            verifySettings();
+        }
+        if (getError() != null) {
+            throw new BuildException(errmsg);
+        }
+    }
+
+    /**
+     * Method that each selector will implement to create their
+     * selection behaviour. If there is a problem with the setup
+     * of a selector, it can throw a BuildException to indicate
+     * the problem.
+     *
+     * @param basedir A java.io.File object for the base directory
+     * @param filename The name of the file to check
+     * @param file A File object for this filename
+     * @return whether the file should be selected or not
+     */
+    public abstract boolean isSelected(File basedir, String filename,
+                                       File file);
+
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/BaseSelectorContainer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/BaseSelectorContainer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/BaseSelectorContainer.java	(revision 16139)
@@ -0,0 +1,312 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector;
+
+/**
+ * This is the base class for selectors that can contain other selectors.
+ *
+ * @since 1.5
+ */
+public abstract class BaseSelectorContainer extends BaseSelector
+        implements SelectorContainer {
+
+    private Vector selectorsList = new Vector();
+
+    /**
+     * Default constructor.
+     */
+    public BaseSelectorContainer() {
+    }
+
+    /**
+     * Indicates whether there are any selectors here.
+     * @return true if there are selectors
+     */
+    public boolean hasSelectors() {
+        return !(selectorsList.isEmpty());
+    }
+
+    /**
+     * Gives the count of the number of selectors in this container
+     * @return the number of selectors
+     */
+    public int selectorCount() {
+        return selectorsList.size();
+    }
+
+    /**
+     * Returns the set of selectors as an array.
+     * @param p the current project
+     * @return an array of selectors
+     */
+    public FileSelector[] getSelectors(Project p) {
+        FileSelector[] result = new FileSelector[selectorsList.size()];
+        selectorsList.copyInto(result);
+        return result;
+    }
+
+    /**
+     * Returns an enumerator for accessing the set of selectors.
+     * @return an enumerator for the selectors
+     */
+    public Enumeration selectorElements() {
+        return selectorsList.elements();
+    }
+
+    /**
+     * Convert the Selectors within this container to a string. This will
+     * just be a helper class for the subclasses that put their own name
+     * around the contents listed here.
+     *
+     * @return comma separated list of Selectors contained in this one
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        Enumeration e = selectorElements();
+        if (e.hasMoreElements()) {
+            while (e.hasMoreElements()) {
+                buf.append(e.nextElement().toString());
+                if (e.hasMoreElements()) {
+                    buf.append(", ");
+                }
+            }
+        }
+
+        return buf.toString();
+    }
+
+    /**
+     * Add a new selector into this container.
+     *
+     * @param selector the new selector to add
+     */
+    public void appendSelector(FileSelector selector) {
+        selectorsList.addElement(selector);
+    }
+
+    /**
+     * <p>This implementation validates the container by calling
+     * verifySettings() and then validates each contained selector
+     * provided that the selector implements the validate interface.
+     * </p>
+     * <p>Ordinarily, this will validate all the elements of a selector
+     * container even if the isSelected() method of some elements is
+     * never called. This has two effects:</p>
+     * <ul>
+     * <li>Validation will often occur twice.
+     * <li>Since it is not required that selectors derive from
+     * BaseSelector, there could be selectors in the container whose
+     * error conditions are not detected if their isSelected() call
+     * is never made.
+     * </ul>
+     */
+    public void validate() {
+        verifySettings();
+        String errmsg = getError();
+        if (errmsg != null) {
+            throw new BuildException(errmsg);
+        }
+        Enumeration e = selectorElements();
+        while (e.hasMoreElements()) {
+            Object o = e.nextElement();
+            if (o instanceof BaseSelector) {
+                ((BaseSelector) o).validate();
+            }
+        }
+    }
+
+
+    /**
+     * Method that each selector will implement to create their selection
+     * behaviour. This is what makes SelectorContainer abstract.
+     *
+     * @param basedir the base directory the scan is being done from
+     * @param filename the name of the file to check
+     * @param file a java.io.File object for the filename that the selector
+     * can use
+     * @return whether the file should be selected or not
+     */
+    public abstract boolean isSelected(File basedir, String filename,
+                                       File file);
+
+
+    /* Methods below all add specific selectors */
+
+    /**
+     * add a "Select" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addSelector(SelectSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add an "And" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addAnd(AndSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add an "Or" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addOr(OrSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a "Not" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addNot(NotSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a "None" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addNone(NoneSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a majority selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addMajority(MajoritySelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a selector date entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addDate(DateSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a selector size entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addSize(SizeSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a selector filename entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addFilename(FilenameSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add an extended selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addCustom(ExtendSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a contains selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addContains(ContainsSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a present selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addPresent(PresentSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a depth selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addDepth(DepthSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a depends selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addDepend(DependSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * adds a different selector to the selector list
+     * @param selector the selector to add
+     */
+    public void addDifferent(DifferentSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * adds a type selector to the selector list
+     * @param selector the selector to add
+     */
+    public void addType(TypeSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add a regular expression selector entry on the selector list
+     * @param selector the selector to add
+     */
+    public void addContainsRegexp(ContainsRegexpSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add the modified selector
+     * @param selector the selector to add
+     * @since ant 1.6
+     */
+    public void addModified(ModifiedSelector selector) {
+        appendSelector(selector);
+    }
+
+    /**
+     * add an arbitary selector
+     * @param selector the selector to add
+     * @since Ant 1.6
+     */
+    public void add(FileSelector selector) {
+        appendSelector(selector);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/ContainsRegexpSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/ContainsRegexpSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/ContainsRegexpSelector.java	(revision 16139)
@@ -0,0 +1,155 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.RegularExpression;
+import org.apache.tools.ant.util.regexp.Regexp;
+
+/**
+ * Selector that filters files based on a regular expression.
+ *
+ * @since Ant 1.6
+ */
+public class ContainsRegexpSelector extends BaseExtendSelector {
+
+    private String userProvidedExpression = null;
+    private RegularExpression myRegExp = null;
+    private Regexp myExpression = null;
+    /** Key to used for parameterized custom selector */
+    public static final String EXPRESSION_KEY = "expression";
+
+    /**
+     * Creates a new <code>ContainsRegexpSelector</code> instance.
+     */
+    public ContainsRegexpSelector() {
+    }
+
+    /**
+     * @return a string describing this object
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer(
+                "{containsregexpselector expression: ");
+        buf.append(userProvidedExpression);
+        buf.append("}");
+        return buf.toString();
+    }
+
+    /**
+     * The regular expression used to search the file.
+     *
+     * @param theexpression this must match a line in the file to be selected.
+     */
+    public void setExpression(String theexpression) {
+        this.userProvidedExpression = theexpression;
+    }
+
+    /**
+     * When using this as a custom selector, this method will be called.
+     * It translates each parameter into the appropriate setXXX() call.
+     *
+     * @param parameters the complete set of parameters for this selector
+     */
+    public void setParameters(Parameter[] parameters) {
+        super.setParameters(parameters);
+        if (parameters != null) {
+            for (int i = 0; i < parameters.length; i++) {
+                String paramname = parameters[i].getName();
+                if (EXPRESSION_KEY.equalsIgnoreCase(paramname)) {
+                    setExpression(parameters[i].getValue());
+                } else {
+                    setError("Invalid parameter " + paramname);
+                }
+            }
+        }
+    }
+
+    /**
+     * Checks that an expression was specified.
+     *
+     */
+    public void verifySettings() {
+        if (userProvidedExpression == null) {
+            setError("The expression attribute is required");
+        }
+    }
+
+    /**
+     * Tests a regular expression against each line of text in the file.
+     *
+     * @param basedir the base directory the scan is being done from
+     * @param filename is the name of the file to check
+     * @param file is a java.io.File object the selector can use
+     * @return whether the file should be selected or not
+     */
+    public boolean isSelected(File basedir, String filename, File file) {
+        String teststr = null;
+        BufferedReader in = null;
+
+        // throw BuildException on error
+
+        validate();
+
+        if (file.isDirectory()) {
+            return true;
+        }
+
+        if (myRegExp == null) {
+            myRegExp = new RegularExpression();
+            myRegExp.setPattern(userProvidedExpression);
+            myExpression = myRegExp.getRegexp(getProject());
+        }
+
+        try {
+            in = new BufferedReader(new InputStreamReader(
+                    new FileInputStream(file)));
+
+            teststr = in.readLine();
+
+            while (teststr != null) {
+
+                if (myExpression.matches(teststr)) {
+                    return true;
+                }
+                teststr = in.readLine();
+            }
+
+            return false;
+        } catch (IOException ioe) {
+            throw new BuildException("Could not read file " + filename);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (Exception e) {
+                    throw new BuildException("Could not close file "
+                                             + filename);
+                }
+            }
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/ContainsSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/ContainsSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/ContainsSelector.java	(revision 16139)
@@ -0,0 +1,203 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Selector that filters files based on whether they contain a
+ * particular string.
+ *
+ * @since 1.5
+ */
+public class ContainsSelector extends BaseExtendSelector {
+
+    private String contains = null;
+    private boolean casesensitive = true;
+    private boolean ignorewhitespace = false;
+    /** Key to used for parameterized custom selector */
+    public static final String EXPRESSION_KEY = "expression";
+    /** Used for parameterized custom selector */
+    public static final String CONTAINS_KEY = "text";
+    /** Used for parameterized custom selector */
+    public static final String CASE_KEY = "casesensitive";
+    /** Used for parameterized custom selector */
+    public static final String WHITESPACE_KEY = "ignorewhitespace";
+
+
+    /**
+     * Creates a new <code>ContainsSelector</code> instance.
+     *
+     */
+    public ContainsSelector() {
+    }
+
+    /**
+     * @return a string describing this object
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer("{containsselector text: ");
+        buf.append(contains);
+        buf.append(" casesensitive: ");
+        if (casesensitive) {
+            buf.append("true");
+        } else {
+            buf.append("false");
+        }
+        buf.append(" ignorewhitespace: ");
+        if (ignorewhitespace) {
+            buf.append("true");
+        } else {
+            buf.append("false");
+        }
+        buf.append("}");
+        return buf.toString();
+    }
+
+    /**
+     * The string to search for within a file.
+     *
+     * @param contains the string that a file must contain to be selected.
+     */
+    public void setText(String contains) {
+        this.contains = contains;
+    }
+
+    /**
+     * Whether to ignore case in the string being searched.
+     *
+     * @param casesensitive whether to pay attention to case sensitivity
+     */
+    public void setCasesensitive(boolean casesensitive) {
+        this.casesensitive = casesensitive;
+    }
+
+    /**
+     * Whether to ignore whitespace in the string being searched.
+     *
+     * @param ignorewhitespace whether to ignore any whitespace
+     *        (spaces, tabs, etc.) in the searchstring
+     */
+    public void setIgnorewhitespace(boolean ignorewhitespace) {
+        this.ignorewhitespace = ignorewhitespace;
+    }
+
+    /**
+     * When using this as a custom selector, this method will be called.
+     * It translates each parameter into the appropriate setXXX() call.
+     *
+     * @param parameters the complete set of parameters for this selector
+     */
+    public void setParameters(Parameter[] parameters) {
+        super.setParameters(parameters);
+        if (parameters != null) {
+            for (int i = 0; i < parameters.length; i++) {
+                String paramname = parameters[i].getName();
+                if (CONTAINS_KEY.equalsIgnoreCase(paramname)) {
+                    setText(parameters[i].getValue());
+                } else if (CASE_KEY.equalsIgnoreCase(paramname)) {
+                    setCasesensitive(Project.toBoolean(
+                            parameters[i].getValue()));
+                } else if (WHITESPACE_KEY.equalsIgnoreCase(paramname)) {
+                    setIgnorewhitespace(Project.toBoolean(
+                            parameters[i].getValue()));
+                } else {
+                    setError("Invalid parameter " + paramname);
+                }
+            }
+        }
+    }
+
+    /**
+     * Checks to make sure all settings are kosher. In this case, it
+     * means that the pattern attribute has been set.
+     *
+     */
+    public void verifySettings() {
+        if (contains == null) {
+            setError("The text attribute is required");
+        }
+    }
+
+    /**
+     * The heart of the matter. This is where the selector gets to decide
+     * on the inclusion of a file in a particular fileset.
+     *
+     * @param basedir the base directory the scan is being done from
+     * @param filename is the name of the file to check
+     * @param file is a java.io.File object the selector can use
+     * @return whether the file should be selected or not
+     */
+    public boolean isSelected(File basedir, String filename, File file) {
+
+        // throw BuildException on error
+        validate();
+
+        if (file.isDirectory()) {
+            return true;
+        }
+
+        String userstr = contains;
+        if (!casesensitive) {
+            userstr = contains.toLowerCase();
+        }
+        if (ignorewhitespace) {
+            userstr = SelectorUtils.removeWhitespace(userstr);
+        }
+        BufferedReader in = null;
+        try {
+            in = new BufferedReader(new InputStreamReader(
+                    new FileInputStream(file)));
+            String teststr = in.readLine();
+            while (teststr != null) {
+                if (!casesensitive) {
+                    teststr = teststr.toLowerCase();
+                }
+                if (ignorewhitespace) {
+                    teststr = SelectorUtils.removeWhitespace(teststr);
+                }
+                if (teststr.indexOf(userstr) > -1) {
+                    return true;
+                }
+                teststr = in.readLine();
+            }
+            return false;
+        } catch (IOException ioe) {
+            throw new BuildException("Could not read file " + filename);
+        } finally {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (Exception e) {
+                    throw new BuildException("Could not close file "
+                                             + filename);
+                }
+            }
+        }
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/DateSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/DateSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/DateSelector.java	(revision 16139)
@@ -0,0 +1,273 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.text.ParseException;
+import java.util.Locale;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Selector that chooses files based on their last modified date.
+ *
+ * @since 1.5
+ */
+public class DateSelector extends BaseExtendSelector {
+
+    private long millis = -1;
+    private String dateTime = null;
+    private boolean includeDirs = false;
+    private int granularity = 0;
+    private int cmp = 2;
+    private String pattern;
+    /** Key to used for parameterized custom selector */
+    public static final String MILLIS_KEY = "millis";
+    /** Key to used for parameterized custom selector */
+    public static final String DATETIME_KEY = "datetime";
+    /** Key to used for parameterized custom selector */
+    public static final String CHECKDIRS_KEY = "checkdirs";
+    /** Key to used for parameterized custom selector */
+    public static final String GRANULARITY_KEY = "granularity";
+    /** Key to used for parameterized custom selector */
+    public static final String WHEN_KEY = "when";
+    /** Key to used for parameterized custom selector */
+    public static final String PATTERN_KEY = "pattern";
+
+    /**
+     * Creates a new <code>DateSelector</code> instance.
+     *
+     */
+    public DateSelector() {
+        if (Os.isFamily("dos")) {
+            granularity = 2000;
+        }
+    }
+
+    /**
+     * @return a string describing this object
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer("{dateselector date: ");
+        buf.append(dateTime);
+        buf.append(" compare: ");
+        if (cmp == 0) {
+            buf.append("before");
+        } else if (cmp == 1) {
+            buf.append("after");
+        } else {
+            buf.append("equal");
+        }
+        buf.append(" granularity: ");
+        buf.append(granularity);
+        if (pattern != null) {
+            buf.append(" pattern: ").append(pattern);
+        }
+        buf.append("}");
+        return buf.toString();
+    }
+
+    /**
+     * For users that prefer to express time in milliseconds since 1970
+     *
+     * @param millis the time to compare file's last modified date to,
+     *        expressed in milliseconds
+     */
+    public void setMillis(long millis) {
+        this.millis = millis;
+    }
+
+    /**
+     * Returns the millisecond value the selector is set for.
+     * @return the millisecond value
+     */
+    public long getMillis() {
+        if (dateTime != null) {
+            validate();
+        }
+        return millis;
+    }
+
+    /**
+     * Sets the date. The user must supply it in MM/DD/YYYY HH:MM AM_PM
+     * format
+     *
+     * @param dateTime a string in MM/DD/YYYY HH:MM AM_PM format
+     */
+    public void setDatetime(String dateTime) {
+        this.dateTime = dateTime;
+    }
+
+    /**
+     * Should we be checking dates on directories?
+     *
+     * @param includeDirs whether to check the timestamp on directories
+     */
+    public void setCheckdirs(boolean includeDirs) {
+        this.includeDirs = includeDirs;
+    }
+
+    /**
+     * Sets the number of milliseconds leeway we will give before we consider
+     * a file not to have matched a date.
+     * @param granularity the number of milliconds leeway
+     */
+    public void setGranularity(int granularity) {
+        this.granularity = granularity;
+    }
+
+    /**
+     * Sets the type of comparison to be done on the file's last modified
+     * date.
+     *
+     * @param cmp The comparison to perform, an EnumeratedAttribute
+     */
+    public void setWhen(TimeComparisons cmp) {
+        this.cmp = cmp.getIndex();
+    }
+
+    /**
+     * Sets the pattern to be used for the SimpleDateFormat
+     *
+     * @param pattern the pattern that defines the date format
+     */
+    public void setPattern(String pattern) {
+        this.pattern = pattern;
+    }
+
+    /**
+     * When using this as a custom selector, this method will be called.
+     * It translates each parameter into the appropriate setXXX() call.
+     *
+     * @param parameters the complete set of parameters for this selector
+     */
+    public void setParameters(Parameter[] parameters) {
+        super.setParameters(parameters);
+        if (parameters != null) {
+            for (int i = 0; i < parameters.length; i++) {
+                String paramname = parameters[i].getName();
+                if (MILLIS_KEY.equalsIgnoreCase(paramname)) {
+                    try {
+                        setMillis(new Long(parameters[i].getValue()
+                        ).longValue());
+                    } catch (NumberFormatException nfe) {
+                        setError("Invalid millisecond setting "
+                                + parameters[i].getValue());
+                    }
+                } else if (DATETIME_KEY.equalsIgnoreCase(paramname)) {
+                    setDatetime(parameters[i].getValue());
+                } else if (CHECKDIRS_KEY.equalsIgnoreCase(paramname)) {
+                    setCheckdirs(Project.toBoolean(parameters[i].getValue()));
+                } else if (GRANULARITY_KEY.equalsIgnoreCase(paramname)) {
+                    try {
+                        setGranularity(new Integer(parameters[i].getValue()
+                        ).intValue());
+                    } catch (NumberFormatException nfe) {
+                        setError("Invalid granularity setting "
+                            + parameters[i].getValue());
+                    }
+                } else if (WHEN_KEY.equalsIgnoreCase(paramname)) {
+                    TimeComparisons cmp = new TimeComparisons();
+                    cmp.setValue(parameters[i].getValue());
+                    setWhen(cmp);
+                } else if (PATTERN_KEY.equalsIgnoreCase(paramname)) {
+                    setPattern(parameters[i].getValue());
+                } else {
+                    setError("Invalid parameter " + paramname);
+                }
+            }
+        }
+    }
+
+    /**
+     * This is a consistency check to ensure the selector's required
+     * values have been set.
+     */
+    public void verifySettings() {
+        if (dateTime == null && millis < 0) {
+            setError("You must provide a datetime or the number of "
+                    + "milliseconds.");
+        } else if (millis < 0 && dateTime != null) {
+            // check millis and only set it once.
+            DateFormat df = ((pattern == null)
+                ? DateFormat.getDateTimeInstance(
+                    DateFormat.SHORT, DateFormat.SHORT, Locale.US)
+                : new SimpleDateFormat(pattern));
+
+            try {
+                setMillis(df.parse(dateTime).getTime());
+                if (millis < 0) {
+                    setError("Date of " + dateTime
+                        + " results in negative milliseconds value"
+                        + " relative to epoch (January 1, 1970, 00:00:00 GMT).");
+                }
+            } catch (ParseException pe) {
+                setError("Date of " + dateTime
+                        + " Cannot be parsed correctly. It should be in"
+                        + ((pattern == null)
+                        ? " MM/DD/YYYY HH:MM AM_PM" : pattern) + " format.");
+            }
+        }
+    }
+
+    /**
+     * The heart of the matter. This is where the selector gets to decide
+     * on the inclusion of a file in a particular fileset.
+     *
+     * @param basedir the base directory the scan is being done from
+     * @param filename is the name of the file to check
+     * @param file is a java.io.File object the selector can use
+     * @return whether the file should be selected or not
+     */
+    public boolean isSelected(File basedir, String filename, File file) {
+
+        validate();
+
+        if (file.isDirectory() && (!includeDirs)) {
+            return true;
+        }
+        if (cmp == 0) {
+            return ((file.lastModified() - granularity) < millis);
+        } else if (cmp == 1) {
+            return ((file.lastModified() + granularity) > millis);
+        } else {
+            return (Math.abs(file.lastModified() - millis) <= granularity);
+        }
+    }
+
+    /**
+     * Enumerated attribute with the values for time comparison.
+     * <p>
+     */
+    public static class TimeComparisons extends EnumeratedAttribute {
+        /**
+         * @return the values as an array of strings
+         */
+        public String[] getValues() {
+            return new String[]{"before", "after", "equal"};
+        }
+    }
+
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/DependSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/DependSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/DependSelector.java	(revision 16139)
@@ -0,0 +1,77 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+
+/**
+ * Selector that filters files based on whether they are newer than
+ * a matching file in another directory tree. It can contain a mapper
+ * element, so isn't available as an ExtendSelector (since those
+ * parameters can't hold other elements).
+ *
+ * @since 1.5
+ */
+public class DependSelector extends MappingSelector {
+
+    /**
+     * Creates a new <code>DependSelector</code> instance.
+     *
+     */
+    public DependSelector() {
+
+    }
+
+    /**
+     * @return a string describing this object
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer("{dependselector targetdir: ");
+        if (targetdir == null) {
+            buf.append("NOT YET SET");
+        } else {
+            buf.append(targetdir.getName());
+        }
+        buf.append(" granularity: ");
+        buf.append(granularity);
+        if (map != null) {
+            buf.append(" mapper: ");
+            buf.append(map.toString());
+        } else if (mapperElement != null) {
+            buf.append(" mapper: ");
+            buf.append(mapperElement.toString());
+        }
+        buf.append("}");
+        return buf.toString();
+    }
+
+
+    /**
+     * this test is our selection test that compared the file with the destfile
+     * @param srcfile the source file
+     * @param destfile the destination file
+     * @return true if destination is out of date
+     */
+    public boolean selectionTest(File srcfile, File destfile) {
+        boolean selected = SelectorUtils.isOutOfDate(srcfile, destfile,
+                granularity);
+        return selected;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/DepthSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/DepthSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/DepthSelector.java	(revision 16139)
@@ -0,0 +1,177 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Selector that filters files based on the how deep in the directory
+ * tree they are.
+ *
+ * @since 1.5
+ */
+public class DepthSelector extends BaseExtendSelector {
+
+    public int min = -1;
+    public int max = -1;
+    /** Used for parameterized custom selector */
+    public static final String MIN_KEY = "min";
+    /** Used for parameterized custom selector */
+    public static final String MAX_KEY = "max";
+
+    /**
+     * Creates a new <code>DepthSelector</code> instance.
+     *
+     */
+    public DepthSelector() {
+    }
+
+    /**
+     * @return a string describing this object
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer("{depthselector min: ");
+        buf.append(min);
+        buf.append(" max: ");
+        buf.append(max);
+        buf.append("}");
+        return buf.toString();
+    }
+
+    /**
+     * The minimum depth below the basedir before a file is selected.
+     *
+     * @param min minimum directory levels below basedir to go
+     */
+    public void setMin(int min) {
+        this.min = min;
+    }
+
+    /**
+     * The minimum depth below the basedir before a file is selected.
+     *
+     * @param max maximum directory levels below basedir to go
+     */
+    public void setMax(int max) {
+        this.max = max;
+    }
+
+    /**
+     * When using this as a custom selector, this method will be called.
+     * It translates each parameter into the appropriate setXXX() call.
+     *
+     * @param parameters the complete set of parameters for this selector
+     */
+    public void setParameters(Parameter[] parameters) {
+        super.setParameters(parameters);
+        if (parameters != null) {
+            for (int i = 0; i < parameters.length; i++) {
+                String paramname = parameters[i].getName();
+                if (MIN_KEY.equalsIgnoreCase(paramname)) {
+                    try {
+                        setMin(Integer.parseInt(parameters[i].getValue()));
+                    } catch (NumberFormatException nfe1) {
+                        setError("Invalid minimum value "
+                                + parameters[i].getValue());
+                    }
+                } else if (MAX_KEY.equalsIgnoreCase(paramname)) {
+                    try {
+                        setMax(Integer.parseInt(parameters[i].getValue()));
+                    } catch (NumberFormatException nfe1) {
+                        setError("Invalid maximum value "
+                                + parameters[i].getValue());
+                    }
+                } else {
+                    setError("Invalid parameter " + paramname);
+                }
+            }
+        }
+    }
+
+    /**
+     * Checks to make sure all settings are kosher. In this case, it
+     * means that the max depth is not lower than the min depth.
+     */
+    public void verifySettings() {
+        if (min < 0 && max < 0) {
+            setError("You must set at least one of the min or the "
+                    + "max levels.");
+        }
+        if (max < min && max > -1) {
+            setError("The maximum depth is lower than the minimum.");
+        }
+    }
+
+    /**
+     * The heart of the matter. This is where the selector gets to decide
+     * on the inclusion of a file in a particular fileset. Most of the work
+     * for this selector is offloaded into SelectorUtils, a static class
+     * that provides the same services for both FilenameSelector and
+     * DirectoryScanner.
+     *
+     * @param basedir the base directory the scan is being done from
+     * @param filename is the name of the file to check
+     * @param file is a java.io.File object the selector can use
+     * @return whether the file should be selected or not
+     */
+    public boolean isSelected(File basedir, String filename, File file) {
+
+        // throw BuildException on error
+        validate();
+
+        int depth = -1;
+        // If you felt daring, you could cache the basedir absolute path
+        String absBase = basedir.getAbsolutePath();
+        String absFile = file.getAbsolutePath();
+        StringTokenizer tokBase = new StringTokenizer(absBase,
+                File.separator);
+        StringTokenizer tokFile = new StringTokenizer(absFile,
+                File.separator);
+        while (tokFile.hasMoreTokens()) {
+            String filetoken = tokFile.nextToken();
+            if (tokBase.hasMoreTokens()) {
+                String basetoken = tokBase.nextToken();
+                // Sanity check. Ditch it if you want faster performance
+                if (!basetoken.equals(filetoken)) {
+                    throw new BuildException("File " + filename
+                            + " does not appear within " + absBase
+                            + "directory");
+                }
+            } else {
+                depth += 1;
+                if (max > -1 && depth > max) {
+                    return false;
+                }
+            }
+        }
+        if (tokBase.hasMoreTokens()) {
+            throw new BuildException("File " + filename
+                + " is outside of " + absBase + "directory tree");
+        }
+        if (min > -1 && depth < min) {
+            return false;
+        }
+        return true;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/DifferentSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/DifferentSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/DifferentSelector.java	(revision 16139)
@@ -0,0 +1,113 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.BuildException;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * This selector selects files against a mapped set of target files, selecting
+ * all those files which are different.
+ * Files with different lengths are deemed different
+ * automatically
+ * Files with identical timestamps are viewed as matching by
+ * default, unless you specify otherwise.
+ * Contents are compared if the lengths are the same
+ * and the timestamps are ignored or the same,
+ * except if you decide to ignore contents to gain speed.
+ * <p>
+ * This is a useful selector to work with programs and tasks that don't handle
+ * dependency checking properly; Even if a predecessor task always creates its
+ * output files, followup tasks can be driven off copies made with a different
+ * selector, so their dependencies are driven on the absolute state of the
+ * files, not a timestamp.
+ * <p>
+ * Clearly, however, bulk file comparisons is inefficient; anything that can
+ * use timestamps is to be preferred. If this selector must be used, use it
+ * over as few files as possible, perhaps following it with an &lt;uptodate;&gt
+ * to keep the descendent routines conditional.
+ *
+ */
+public class DifferentSelector extends MappingSelector {
+
+    private FileUtils fileUtils = FileUtils.newFileUtils();
+
+    private boolean ignoreFileTimes = true;
+    private boolean ignoreContents = false;
+
+
+    /**
+     * This flag tells the selector to ignore file times in the comparison
+     * @param ignoreFileTimes if true ignore file times
+     */
+    public void setIgnoreFileTimes(boolean ignoreFileTimes) {
+        this.ignoreFileTimes = ignoreFileTimes;
+    }
+    /**
+     * This flag tells the selector to ignore contents
+     * @param ignoreContents if true ignore contents
+     * @since ant 1.6.3
+     */
+    public void setIgnoreContents(boolean ignoreContents) {
+        this.ignoreContents = ignoreContents;
+    }
+    /**
+     * this test is our selection test that compared the file with the destfile
+     * @param srcfile the source file
+     * @param destfile the destination file
+     * @return true if the files are different
+     */
+    protected boolean selectionTest(File srcfile, File destfile) {
+
+        //if either of them is missing, they are different
+        if (srcfile.exists() != destfile.exists()) {
+            return true;
+        }
+
+        if (srcfile.length() != destfile.length()) {
+            // different size =>different files
+            return true;
+        }
+
+        if (!ignoreFileTimes) {
+            //same date if dest timestamp is within granularity of the srcfile
+            boolean sameDate;
+            sameDate = destfile.lastModified() >= srcfile.lastModified() - granularity
+                    && destfile.lastModified() <= srcfile.lastModified() + granularity;
+
+            // different dates => different files
+            if (!sameDate) {
+                return true;
+            }
+        }
+        if (!ignoreContents) {
+            //here do a bulk comparison
+            try {
+                return !fileUtils.contentEquals(srcfile, destfile);
+            } catch (IOException e) {
+                throw new BuildException("while comparing " + srcfile + " and "
+                        + destfile, e);
+            }
+        } else {
+            return false;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/ExtendFileSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/ExtendFileSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/ExtendFileSelector.java	(revision 16139)
@@ -0,0 +1,38 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import org.apache.tools.ant.types.Parameterizable;
+
+/**
+ * This is the interface to be used by all custom selectors, those that are
+ * called through the &lt;custom&gt; tag. It is the amalgamation of two
+ * interfaces, the FileSelector and the Paramterizable interface. Note that
+ * you will almost certainly want the default behaviour for handling
+ * Parameters, so you probably want to use the BaseExtendSelector class
+ * as the base class for your custom selector rather than implementing
+ * this interface from scratch.
+ *
+ * @since 1.5
+ */
+public interface ExtendFileSelector extends FileSelector, Parameterizable {
+
+    // No further methods necessary. This is just an amalgamation of two other
+    // interfaces.
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/ExtendSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/ExtendSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/ExtendSelector.java	(revision 16139)
@@ -0,0 +1,196 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.Vector;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Selector that selects files by forwarding the request on to other classes.
+ *
+ * @since 1.5
+ */
+public class ExtendSelector extends BaseSelector {
+
+    private String classname = null;
+    private FileSelector dynselector = null;
+    private Vector paramVec = new Vector();
+    private Path classpath = null;
+
+    /**
+     * Default constructor.
+     */
+    public ExtendSelector() {
+    }
+
+    /**
+     * Sets the classname of the custom selector.
+     *
+     * @param classname is the class which implements this selector
+     */
+    public void setClassname(String classname) {
+        this.classname = classname;
+    }
+
+    /**
+     * Instantiates the identified custom selector class.
+     */
+    public void selectorCreate() {
+        if (classname != null && classname.length() > 0) {
+            try {
+                Class c = null;
+                if (classpath == null) {
+                    c = Class.forName(classname);
+                } else {
+                    AntClassLoader al
+                            = getProject().createClassLoader(classpath);
+                    c = Class.forName(classname, true, al);
+                }
+                dynselector = (FileSelector) c.newInstance();
+                final Project project = getProject();
+                if (project != null) {
+                    project.setProjectReference(dynselector);
+                }
+            } catch (ClassNotFoundException cnfexcept) {
+                setError("Selector " + classname
+                    + " not initialized, no such class");
+            } catch (InstantiationException iexcept) {
+                setError("Selector " + classname
+                    + " not initialized, could not create class");
+            } catch (IllegalAccessException iaexcept) {
+                setError("Selector " + classname
+                    + " not initialized, class not accessible");
+            }
+        } else {
+            setError("There is no classname specified");
+        }
+    }
+
+    /**
+     * Create new parameters to pass to custom selector.
+     *
+     * @param p The new Parameter object
+     */
+    public void addParam(Parameter p) {
+        paramVec.addElement(p);
+    }
+
+
+    /**
+     * Set the classpath to load the classname specified using an attribute.
+     * @param classpath the classpath to use
+     */
+    public final void setClasspath(Path classpath) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        if (this.classpath == null) {
+            this.classpath = classpath;
+        } else {
+            this.classpath.append(classpath);
+        }
+    }
+
+    /**
+     * Specify the classpath to use to load the Selector (nested element).
+     * @return a classpath to be configured
+     */
+    public final Path createClasspath() {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        if (this.classpath == null) {
+            this.classpath = new Path(getProject());
+        }
+        return this.classpath.createPath();
+    }
+
+    /**
+     * Get the classpath
+     * @return the classpath
+     */
+    public final Path getClasspath() {
+        return classpath;
+    }
+
+    /**
+     * Set the classpath to use for loading a custom selector by using
+     * a reference.
+     * @param r a reference to the classpath
+     */
+    public void setClasspathref(Reference r) {
+        if (isReference()) {
+            throw tooManyAttributes();
+        }
+        createClasspath().setRefid(r);
+    }
+
+    /**
+     * These are errors specific to ExtendSelector only. If there are
+     * errors in the custom selector, it should throw a BuildException
+     * when isSelected() is called.
+     */
+    public void verifySettings() {
+        // Creation is done here rather than in isSelected() because some
+        // containers may do a validation pass before running isSelected(),
+        // but we need to check for the existence of the created class.
+        if (dynselector == null) {
+            selectorCreate();
+        }
+        if (classname == null || classname.length() < 1) {
+            setError("The classname attribute is required");
+        } else if (dynselector == null) {
+            setError("Internal Error: The custom selector was not created");
+        } else if (!(dynselector instanceof ExtendFileSelector)
+                    && (paramVec.size() > 0)) {
+            setError("Cannot set parameters on custom selector that does not "
+                    + "implement ExtendFileSelector");
+        }
+    }
+
+
+    /**
+     * Allows the custom selector to choose whether to select a file. This
+     * is also where the Parameters are passed to the custom selector,
+     * since we know we must have them all by now. And since we must know
+     * both classpath and classname, creating the class is deferred to here
+     * as well.
+     *
+     * @exception BuildException if an error occurs
+     */
+    public boolean isSelected(File basedir, String filename, File file)
+            throws BuildException {
+        validate();
+        if (paramVec.size() > 0 && dynselector instanceof ExtendFileSelector) {
+            Parameter[] paramArray = new Parameter[paramVec.size()];
+            paramVec.copyInto(paramArray);
+            // We know that dynselector must be non-null if no error message
+            ((ExtendFileSelector) dynselector).setParameters(paramArray);
+        }
+        return dynselector.isSelected(basedir, filename, file);
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/FileSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/FileSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/FileSelector.java	(revision 16139)
@@ -0,0 +1,47 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * This is the interface to be used by all selectors.
+ *
+ * @since 1.5
+ */
+public interface FileSelector {
+
+    /**
+     * Method that each selector will implement to create their
+     * selection behaviour. If there is a problem with the setup
+     * of a selector, it can throw a BuildException to indicate
+     * the problem.
+     *
+     * @param basedir A java.io.File object for the base directory
+     * @param filename The name of the file to check
+     * @param file A File object for this filename
+     * @return whether the file should be selected or not
+     * @exception BuildException if the selector was not configured correctly
+     */
+    boolean isSelected(File basedir, String filename, File file)
+            throws BuildException;
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/FilenameSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/FilenameSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/FilenameSelector.java	(revision 16139)
@@ -0,0 +1,165 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Selector that filters files based on the filename.
+ *
+ * @since 1.5
+ */
+public class FilenameSelector extends BaseExtendSelector {
+
+    private String pattern = null;
+    private boolean casesensitive = true;
+
+    private boolean negated = false;
+    /** Used for parameterized custom selector */
+    public static final String NAME_KEY = "name";
+    /** Used for parameterized custom selector */
+    public static final String CASE_KEY = "casesensitive";
+    /** Used for parameterized custom selector */
+    public static final String NEGATE_KEY = "negate";
+
+    /**
+     * Creates a new <code>FilenameSelector</code> instance.
+     *
+     */
+    public FilenameSelector() {
+    }
+
+    /**
+     * @return a string describing this object
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer("{filenameselector name: ");
+        buf.append(pattern);
+        buf.append(" negate: ");
+        if (negated) {
+            buf.append("true");
+        } else {
+            buf.append("false");
+        }
+        buf.append(" casesensitive: ");
+        if (casesensitive) {
+            buf.append("true");
+        } else {
+            buf.append("false");
+        }
+        buf.append("}");
+        return buf.toString();
+    }
+
+    /**
+     * The name of the file, or the pattern for the name, that
+     * should be used for selection.
+     *
+     * @param pattern the file pattern that any filename must match
+     *                against in order to be selected.
+     */
+    public void setName(String pattern) {
+        pattern = pattern.replace('/', File.separatorChar).replace('\\',
+                File.separatorChar);
+        if (pattern.endsWith(File.separator)) {
+            pattern += "**";
+        }
+        this.pattern = pattern;
+    }
+
+    /**
+     * Whether to ignore case when checking filenames.
+     *
+     * @param casesensitive whether to pay attention to case sensitivity
+     */
+    public void setCasesensitive(boolean casesensitive) {
+        this.casesensitive = casesensitive;
+    }
+
+    /**
+     * You can optionally reverse the selection of this selector,
+     * thereby emulating an &lt;exclude&gt; tag, by setting the attribute
+     * negate to true. This is identical to surrounding the selector
+     * with &lt;not&gt;&lt;/not&gt;.
+     *
+     * @param negated whether to negate this selection
+     */
+    public void setNegate(boolean negated) {
+        this.negated = negated;
+    }
+
+    /**
+     * When using this as a custom selector, this method will be called.
+     * It translates each parameter into the appropriate setXXX() call.
+     *
+     * @param parameters the complete set of parameters for this selector
+     */
+    public void setParameters(Parameter[] parameters) {
+        super.setParameters(parameters);
+        if (parameters != null) {
+            for (int i = 0; i < parameters.length; i++) {
+                String paramname = parameters[i].getName();
+                if (NAME_KEY.equalsIgnoreCase(paramname)) {
+                    setName(parameters[i].getValue());
+                } else if (CASE_KEY.equalsIgnoreCase(paramname)) {
+                    setCasesensitive(Project.toBoolean(
+                            parameters[i].getValue()));
+                } else if (NEGATE_KEY.equalsIgnoreCase(paramname)) {
+                    setNegate(Project.toBoolean(parameters[i].getValue()));
+                } else {
+                    setError("Invalid parameter " + paramname);
+                }
+            }
+        }
+    }
+
+    /**
+     * Checks to make sure all settings are kosher. In this case, it
+     * means that the name attribute has been set.
+     *
+     */
+    public void verifySettings() {
+        if (pattern == null) {
+            setError("The name attribute is required");
+        }
+    }
+
+    /**
+     * The heart of the matter. This is where the selector gets to decide
+     * on the inclusion of a file in a particular fileset. Most of the work
+     * for this selector is offloaded into SelectorUtils, a static class
+     * that provides the same services for both FilenameSelector and
+     * DirectoryScanner.
+     *
+     * @param basedir the base directory the scan is being done from
+     * @param filename is the name of the file to check
+     * @param file is a java.io.File object the selector can use
+     * @return whether the file should be selected or not
+     */
+    public boolean isSelected(File basedir, String filename, File file) {
+        validate();
+
+        return (SelectorUtils.matchPath(pattern, filename,
+                casesensitive) == !(negated));
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/MajoritySelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/MajoritySelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/MajoritySelector.java	(revision 16139)
@@ -0,0 +1,104 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.Enumeration;
+
+/**
+ * This selector is here just to shake up your thinking a bit. Don't get
+ * too caught up in boolean, there are other ways you can evaluate a
+ * collection of selectors. This one takes a vote of the selectors it
+ * contains, and majority wins. You could also have an "all-but-one"
+ * selector, a "weighted-average" selector, and so on. These are left
+ * as exercises for the reader (as are the usecases where this would
+ * be necessary).
+ *
+ * @since 1.5
+ */
+public class MajoritySelector extends BaseSelectorContainer {
+
+    private boolean allowtie = true;
+
+    /**
+     * Default constructor.
+     */
+    public MajoritySelector() {
+    }
+
+    /**
+     * @return a string describing this object
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        if (hasSelectors()) {
+            buf.append("{majorityselect: ");
+            buf.append(super.toString());
+            buf.append("}");
+        }
+        return buf.toString();
+    }
+
+    /**
+     * A attribute to specify what will happen if number
+     * of yes votes is the same as the number of no votes
+     * defaults to true
+     *
+     * @param tiebreaker the value to give if there is a tie
+     */
+    public void setAllowtie(boolean tiebreaker) {
+        allowtie = tiebreaker;
+    }
+
+    /**
+     * Returns true (the file is selected) if most of the other selectors
+     * agree. In case of a tie, go by the allowtie setting. That defaults
+     * to true, meaning in case of a tie, the file is selected.
+     *
+     * @param basedir the base directory the scan is being done from
+     * @param filename is the name of the file to check
+     * @param file is a java.io.File object for the filename that the selector
+     * can use
+     * @return whether the file should be selected or not
+     */
+    public boolean isSelected(File basedir, String filename, File file) {
+        validate();
+        int yesvotes = 0;
+        int novotes = 0;
+        Enumeration e = selectorElements();
+        boolean result;
+
+        while (e.hasMoreElements()) {
+            result = ((FileSelector) e.nextElement()).isSelected(basedir,
+                    filename, file);
+            if (result) {
+                yesvotes = yesvotes + 1;
+            } else {
+                novotes = novotes + 1;
+            }
+        }
+        if (yesvotes > novotes) {
+            return true;
+        } else if (novotes > yesvotes) {
+            return false;
+        }
+        // At this point, we know we have a tie.
+        return allowtie;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/MappingSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/MappingSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/MappingSelector.java	(revision 16139)
@@ -0,0 +1,139 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.IdentityMapper;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+
+import java.io.File;
+
+/**
+ * A mapping selector is an abstract class adding mapping support to the base
+ * selector
+ */
+public abstract class MappingSelector extends BaseSelector {
+    protected File targetdir = null;
+    protected Mapper mapperElement = null;
+    protected FileNameMapper map = null;
+    protected int granularity = 0;
+
+    /**
+     * Creates a new <code>MappingSelector</code> instance.
+     *
+     */
+    public MappingSelector() {
+        granularity = (int) FileUtils.newFileUtils().getFileTimestampGranularity();
+    }
+
+
+    /**
+     * The name of the file or directory which is checked for out-of-date
+     * files.
+     *
+     * @param targetdir the directory to scan looking for files.
+     */
+    public void setTargetdir(File targetdir) {
+        this.targetdir = targetdir;
+    }
+
+    /**
+     * Defines the FileNameMapper to use (nested mapper element).
+     * @return a mapper to be configured
+     * @throws BuildException if more that one mapper defined
+     */
+    public Mapper createMapper() throws BuildException {
+        if (mapperElement != null) {
+            throw new BuildException("Cannot define more than one mapper");
+        }
+        mapperElement = new Mapper(getProject());
+        return mapperElement;
+    }
+
+    /**
+     * Checks to make sure all settings are kosher. In this case, it
+     * means that the dest attribute has been set and we have a mapper.
+     */
+    public void verifySettings() {
+        if (targetdir == null) {
+            setError("The targetdir attribute is required.");
+        }
+        if (mapperElement == null) {
+            map = new IdentityMapper();
+        } else {
+            map = mapperElement.getImplementation();
+        }
+        if (map == null) {
+            setError("Could not set <mapper> element.");
+        }
+    }
+
+    /**
+     * The heart of the matter. This is where the selector gets to decide
+     * on the inclusion of a file in a particular fileset.
+     *
+     * @param basedir the base directory the scan is being done from
+     * @param filename is the name of the file to check
+     * @param file is a java.io.File object the selector can use
+     * @return whether the file should be selected or not
+     */
+    public boolean isSelected(File basedir, String filename, File file) {
+
+        // throw BuildException on error
+        validate();
+
+        // Determine file whose out-of-dateness is to be checked
+        String[] destfiles = map.mapFileName(filename);
+        // If filename does not match the To attribute of the mapper
+        // then filter it out of the files we are considering
+        if (destfiles == null) {
+            return false;
+        }
+        // Sanity check
+        if (destfiles.length != 1 || destfiles[0] == null) {
+            throw new BuildException("Invalid destination file results for "
+                    + targetdir.getName() + " with filename " + filename);
+        }
+        String destname = destfiles[0];
+        File destfile = new File(targetdir, destname);
+
+        boolean selected = selectionTest(file, destfile);
+        return selected;
+    }
+
+    /**
+     * this test is our selection test that compared the file with the destfile
+     * @param srcfile file to test; may be null
+     * @param destfile destination file
+     * @return true if source file compares with destination file
+     */
+    protected abstract boolean selectionTest(File srcfile, File destfile);
+
+    /**
+     * Sets the number of milliseconds leeway we will give before we consider
+     * a file out of date. Defaults to 2000 on MS-DOS derivatives as the FAT
+     * file system.
+     * @param granularity the leeway in milliseconds
+     */
+    public void setGranularity(int granularity) {
+        this.granularity = granularity;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/NoneSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/NoneSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/NoneSelector.java	(revision 16139)
@@ -0,0 +1,77 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.Enumeration;
+
+/**
+ * This selector has a collection of other selectors. All of those selectors
+ * must refuse to select a file before the file is considered selected by
+ * this selector.
+ *
+ * @since 1.5
+ */
+public class NoneSelector extends BaseSelectorContainer {
+
+    /**
+     * Default constructor.
+     */
+    public NoneSelector() {
+    }
+
+    /**
+     * @return a string representation of the selector
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        if (hasSelectors()) {
+            buf.append("{noneselect: ");
+            buf.append(super.toString());
+            buf.append("}");
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Returns true (the file is selected) only if all other selectors
+     * agree that the file should not be selected.
+     *
+     * @param basedir the base directory the scan is being done from
+     * @param filename is the name of the file to check
+     * @param file is a java.io.File object for the filename that the selector
+     * can use
+     * @return whether the file should be selected or not
+     */
+    public boolean isSelected(File basedir, String filename, File file) {
+        validate();
+        Enumeration e = selectorElements();
+        boolean result;
+
+        while (e.hasMoreElements()) {
+            result = ((FileSelector) e.nextElement()).isSelected(basedir,
+                    filename, file);
+            if (result) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/NotSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/NotSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/NotSelector.java	(revision 16139)
@@ -0,0 +1,62 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+
+/**
+ * This selector has one other selectors whose meaning it inverts. It
+ * actually relies on NoneSelector for its implementation of the
+ * isSelected() method, but it adds a check to ensure there is only one
+ * other selector contained within.
+ *
+ * @since 1.5
+ */
+public class NotSelector extends NoneSelector {
+
+    /**
+     * Default constructor.
+     */
+    public NotSelector() {
+    }
+
+    /**
+     * @return a string representation of the selector
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        if (hasSelectors()) {
+            buf.append("{notselect: ");
+            buf.append(super.toString());
+            buf.append("}");
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Makes sure that there is only one entry, sets an error message if
+     * not.
+     */
+    public void verifySettings() {
+        if (selectorCount() != 1) {
+            setError("One and only one selector is allowed within the "
+                + "<not> tag");
+        }
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/OrSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/OrSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/OrSelector.java	(revision 16139)
@@ -0,0 +1,77 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.Enumeration;
+
+/**
+ * This selector has a collection of other selectors, any of which have to
+ * select a file in order for this selector to select it.
+ *
+ * @since 1.5
+ */
+public class OrSelector extends BaseSelectorContainer {
+
+    /**
+     * Default constructor.
+     */
+    public OrSelector() {
+    }
+
+    /**
+     * @return a string representation of the selector
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        if (hasSelectors()) {
+            buf.append("{orselect: ");
+            buf.append(super.toString());
+            buf.append("}");
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Returns true (the file is selected) if any of the other selectors
+     * agree that the file should be selected.
+     *
+     * @param basedir the base directory the scan is being done from
+     * @param filename the name of the file to check
+     * @param file a java.io.File object for the filename that the selector
+     * can use
+     * @return whether the file should be selected or not
+     */
+    public boolean isSelected(File basedir, String filename, File file) {
+        validate();
+        Enumeration e = selectorElements();
+        boolean result;
+
+        // First, check that all elements are correctly configured
+        while (e.hasMoreElements()) {
+            result = ((FileSelector) e.nextElement()).isSelected(basedir,
+                    filename, file);
+            if (result) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/PresentSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/PresentSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/PresentSelector.java	(revision 16139)
@@ -0,0 +1,180 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.IdentityMapper;
+
+/**
+ * Selector that filters files based on whether they appear in another
+ * directory tree. It can contain a mapper element, so isn't available
+ * as an ExtendSelector (since those parameters can't hold other
+ * elements).
+ *
+ * @since 1.5
+ */
+public class PresentSelector extends BaseSelector {
+
+    private File targetdir = null;
+    private Mapper mapperElement = null;
+    private FileNameMapper map = null;
+    private boolean destmustexist = true;
+
+    /**
+     * Creates a new <code>PresentSelector</code> instance.
+     *
+     */
+    public PresentSelector() {
+    }
+
+    /**
+     * @return a string describing this object
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer("{presentselector targetdir: ");
+        if (targetdir == null) {
+            buf.append("NOT YET SET");
+        } else {
+            buf.append(targetdir.getName());
+        }
+        buf.append(" present: ");
+        if (destmustexist) {
+            buf.append("both");
+        } else {
+            buf.append("srconly");
+        }
+        if (map != null) {
+            buf.append(map.toString());
+        } else if (mapperElement != null) {
+            buf.append(mapperElement.toString());
+        }
+        buf.append("}");
+        return buf.toString();
+    }
+
+    /**
+     * The name of the file or directory which is checked for matching
+     * files.
+     *
+     * @param targetdir the directory to scan looking for matching files.
+     */
+    public void setTargetdir(File targetdir) {
+        this.targetdir = targetdir;
+    }
+
+    /**
+     * Defines the FileNameMapper to use (nested mapper element).
+     * @return a mapper to be configured
+     * @throws BuildException if more that one mapper defined
+     */
+    public Mapper createMapper() throws BuildException {
+        if (mapperElement != null) {
+            throw new BuildException("Cannot define more than one mapper");
+        }
+        mapperElement = new Mapper(getProject());
+        return mapperElement;
+    }
+
+
+    /**
+     * This sets whether to select a file if its dest file is present.
+     * It could be a <code>negate</code> boolean, but by doing things
+     * this way, we get some documentation on how the system works.
+     * A user looking at the documentation should clearly understand
+     * that the ONLY files whose presence is being tested are those
+     * that already exist in the source directory, hence the lack of
+     * a <code>destonly</code> option.
+     *
+     * @param fp An attribute set to either <code>srconly</code or
+     *           <code>both</code>.
+     */
+    public void setPresent(FilePresence fp) {
+        if (fp.getIndex() == 0) {
+            destmustexist = false;
+        }
+    }
+
+    /**
+     * Checks to make sure all settings are kosher. In this case, it
+     * means that the targetdir attribute has been set and we have a mapper.
+     */
+    public void verifySettings() {
+        if (targetdir == null) {
+            setError("The targetdir attribute is required.");
+        }
+        if (mapperElement == null) {
+            map = new IdentityMapper();
+        } else {
+            map = mapperElement.getImplementation();
+        }
+        if (map == null) {
+            setError("Could not set <mapper> element.");
+        }
+    }
+
+    /**
+     * The heart of the matter. This is where the selector gets to decide
+     * on the inclusion of a file in a particular fileset.
+     *
+     * @param basedir the base directory the scan is being done from
+     * @param filename is the name of the file to check
+     * @param file is a java.io.File object the selector can use
+     * @return whether the file should be selected or not
+     */
+    public boolean isSelected(File basedir, String filename, File file) {
+
+        // throw BuildException on error
+        validate();
+
+        // Determine file whose existence is to be checked
+        String[] destfiles = map.mapFileName(filename);
+        // If filename does not match the To attribute of the mapper
+        // then filter it out of the files we are considering
+        if (destfiles == null) {
+            return false;
+        }
+        // Sanity check
+        if (destfiles.length != 1 || destfiles[0] == null) {
+            throw new BuildException("Invalid destination file results for "
+                    + targetdir + " with filename " + filename);
+        }
+        String destname = destfiles[0];
+        File destfile = new File(targetdir, destname);
+        return destfile.exists() == destmustexist;
+    }
+
+    /**
+     * Enumerated attribute with the values for indicating where a file's
+     * presence is allowed and required.
+     */
+    public static class FilePresence extends EnumeratedAttribute {
+        /**
+         * @return the values as an array of strings
+         */
+        public String[] getValues() {
+            return new String[]{"srconly", "both"};
+        }
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/SelectSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/SelectSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/SelectSelector.java	(revision 16139)
@@ -0,0 +1,210 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.util.Enumeration;
+import java.io.File;
+
+import org.apache.tools.ant.Project;
+
+/**
+ * This selector just holds one other selector and forwards all
+ * requests to it. It exists so that there is a single selector
+ * type that can exist outside of any targets, as an element of
+ * project. It overrides all of the reference stuff so that it
+ * works as expected. Note that this is the only selector you
+ * can reference.
+ *
+ * @since 1.5
+ */
+public class SelectSelector extends BaseSelectorContainer {
+
+    private String ifProperty;
+    private String unlessProperty;
+
+    /**
+     * Default constructor.
+     */
+    public SelectSelector() {
+    }
+
+    /**
+     * @return a string describing this object
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        if (hasSelectors()) {
+            buf.append("{select");
+            if (ifProperty != null) {
+                buf.append(" if: ");
+                buf.append(ifProperty);
+            }
+            if (unlessProperty != null) {
+                buf.append(" unless: ");
+                buf.append(unlessProperty);
+            }
+            buf.append(" ");
+            buf.append(super.toString());
+            buf.append("}");
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Performs the check for circular references and returns the
+     * referenced Selector.
+     */
+    private SelectSelector getRef() {
+        Object o = getCheckedRef(this.getClass(), "SelectSelector");
+        return (SelectSelector) o;
+    }
+
+    /**
+     * Indicates whether there are any selectors here.
+     * @return whether any selectors are in this container
+     */
+    public boolean hasSelectors() {
+        if (isReference()) {
+            return getRef().hasSelectors();
+        }
+        return super.hasSelectors();
+    }
+
+    /**
+     * Gives the count of the number of selectors in this container
+     * @return the number of selectors in this container
+     */
+    public int selectorCount() {
+        if (isReference()) {
+            return getRef().selectorCount();
+        }
+        return super.selectorCount();
+    }
+
+    /**
+     * Returns the set of selectors as an array.
+     * @param p the current project
+     * @return an array of selectors in this container
+     */
+    public FileSelector[] getSelectors(Project p) {
+        if (isReference()) {
+            return getRef().getSelectors(p);
+        }
+        return super.getSelectors(p);
+    }
+
+    /**
+     * Returns an enumerator for accessing the set of selectors.
+     * @return an enumerator that goes through each of the selectors
+     */
+    public Enumeration selectorElements() {
+        if (isReference()) {
+            return getRef().selectorElements();
+        }
+        return super.selectorElements();
+    }
+
+    /**
+     * Add a new selector into this container.
+     *
+     * @param selector the new selector to add
+     */
+    public void appendSelector(FileSelector selector) {
+        if (isReference()) {
+            throw noChildrenAllowed();
+        }
+        super.appendSelector(selector);
+    }
+
+
+    /**
+     * Makes sure that there is only one entry, sets an error message if
+     * not.
+     */
+    public void verifySettings() {
+        int cnt = selectorCount();
+        if (cnt < 0 || cnt > 1) {
+            setError("Only one selector is allowed within the "
+                + "<selector> tag");
+        }
+    }
+
+    /**
+     * Ensures that the selector passes the conditions placed
+     * on it with <code>if</code> and <code>unless</code>.
+     * @return true if conditions are passed
+     */
+    public boolean passesConditions() {
+        if (ifProperty != null
+            && getProject().getProperty(ifProperty) == null) {
+            return false;
+        } else if (unlessProperty != null
+            && getProject().getProperty(unlessProperty) != null) {
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Sets the if attribute to a property which must exist for the
+     * selector to select any files.
+     * @param ifProperty the property to check
+     */
+    public void setIf(String ifProperty) {
+        this.ifProperty = ifProperty;
+    }
+
+    /**
+     * Sets the unless attribute to a property which cannot exist for the
+     * selector to select any files.
+     * @param unlessProperty the property to check
+     */
+    public void setUnless(String unlessProperty) {
+        this.unlessProperty = unlessProperty;
+    }
+
+    /**
+     * Returns true (the file is selected) only if the if property (if any)
+     * exists, the unless property (if any) doesn't exist, and the
+     * contained selector (if any) selects the file. If there is no contained
+     * selector, return true (because we assume that the point was to test
+     * the if and unless conditions).
+     *
+     * @param basedir the base directory the scan is being done from
+     * @param filename the name of the file to check
+     * @param file a java.io.File object for the filename that the selector
+     * can use
+     * @return whether the file should be selected or not
+     */
+    public boolean isSelected(File basedir, String filename, File file) {
+        validate();
+
+        // Deal with if and unless properties first
+        if (!(passesConditions())) {
+            return false;
+        }
+
+        Enumeration e = selectorElements();
+        if (!(e.hasMoreElements())) {
+            return true;
+        }
+        FileSelector f = (FileSelector) e.nextElement();
+        return f.isSelected(basedir, filename, file);
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/SelectorContainer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/SelectorContainer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/SelectorContainer.java	(revision 16139)
@@ -0,0 +1,185 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.util.Enumeration;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector;
+
+/**
+ * This is the interface for selectors that can contain other selectors.
+ *
+ * @since 1.5
+ */
+public interface SelectorContainer {
+
+    /**
+     * Indicates whether there are any selectors here.
+     *
+     * @return whether any selectors are in this container
+     */
+    boolean hasSelectors();
+
+    /**
+     * Gives the count of the number of selectors in this container
+     *
+     * @return the number of selectors in this container
+     */
+    int selectorCount();
+
+    /**
+     * Returns the set of selectors as an array.
+     * @param p the current project
+     * @return an array of selectors in this container
+     */
+    FileSelector[] getSelectors(Project p);
+
+    /**
+     * Returns an enumerator for accessing the set of selectors.
+     *
+     * @return an enumerator that goes through each of the selectors
+     */
+    Enumeration selectorElements();
+
+    /**
+     * Add a new selector into this container.
+     *
+     * @param selector the new selector to add
+     */
+    void appendSelector(FileSelector selector);
+
+    /* Methods below all add specific selectors */
+
+    /**
+     * add a "Select" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    void addSelector(SelectSelector selector);
+
+    /**
+     * add an "And" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    void addAnd(AndSelector selector);
+
+    /**
+     * add an "Or" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    void addOr(OrSelector selector);
+
+    /**
+     * add a "Not" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    void addNot(NotSelector selector);
+
+    /**
+     * add a "None" selector entry on the selector list
+     * @param selector the selector to add
+     */
+    void addNone(NoneSelector selector);
+
+    /**
+     * add a majority selector entry on the selector list
+     * @param selector the selector to add
+     */
+    void addMajority(MajoritySelector selector);
+
+    /**
+     * add a selector date entry on the selector list
+     * @param selector the selector to add
+     */
+    void addDate(DateSelector selector);
+
+    /**
+     * add a selector size entry on the selector list
+     * @param selector the selector to add
+     */
+    void addSize(SizeSelector selector);
+
+    /**
+     * add a selector filename entry on the selector list
+     * @param selector the selector to add
+     */
+    void addFilename(FilenameSelector selector);
+
+    /**
+     * add an extended selector entry on the selector list
+     * @param selector the selector to add
+     */
+    void addCustom(ExtendSelector selector);
+
+    /**
+     * add a contains selector entry on the selector list
+     * @param selector the selector to add
+     */
+    void addContains(ContainsSelector selector);
+
+    /**
+     * add a present selector entry on the selector list
+     * @param selector the selector to add
+     */
+    void addPresent(PresentSelector selector);
+
+    /**
+     * add a depth selector entry on the selector list
+     * @param selector the selector to add
+     */
+    void addDepth(DepthSelector selector);
+
+    /**
+     * add a depends selector entry on the selector list
+     * @param selector the selector to add
+     */
+    void addDepend(DependSelector selector);
+
+    /**
+     * add a regular expression selector entry on the selector list
+     * @param selector the selector to add
+     */
+    void addContainsRegexp(ContainsRegexpSelector selector);
+
+    /**
+     * add the type selector
+     * @param selector the selector to add
+     * @since ant 1.6
+     */
+    void addType(TypeSelector selector);
+
+    /**
+     * add the different selector
+     * @param selector the selector to add
+     * @since ant 1.6
+     */
+    void addDifferent(DifferentSelector selector);
+
+    /**
+     * add the modified selector
+     * @param selector the selector to add
+     * @since ant 1.6
+     */
+    void addModified(ModifiedSelector selector);
+
+    /**
+     * add an arbitary selector
+     * @param selector the selector to add
+     * @since Ant 1.6
+     */
+    void add(FileSelector selector);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/SelectorScanner.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/SelectorScanner.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/SelectorScanner.java	(revision 16139)
@@ -0,0 +1,48 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types.selectors;
+
+/**
+ * An interface used to describe the actions required by any type of
+ * directory scanner that supports Selecters.
+ *
+ * @since 1.5
+ */
+public interface SelectorScanner {
+    /**
+     * Sets the selectors the scanner should use.
+     *
+     * @param selectors the list of selectors
+     */
+    void setSelectors(FileSelector[] selectors);
+
+    /**
+     * Directories which were selected out of a scan.
+     *
+     * @return list of directories not selected
+     */
+    String[] getDeselectedDirectories();
+
+    /**
+     * Files which were selected out of a scan.
+     *
+     * @return list of files not selected
+     */
+    String[] getDeselectedFiles();
+
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/SelectorUtils.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/SelectorUtils.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/SelectorUtils.java	(revision 16139)
@@ -0,0 +1,654 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * <p>This is a utility class used by selectors and DirectoryScanner. The
+ * functionality more properly belongs just to selectors, but unfortunately
+ * DirectoryScanner exposed these as protected methods. Thus we have to
+ * support any subclasses of DirectoryScanner that may access these methods.
+ * </p>
+ * <p>This is a Singleton.</p>
+ *
+ * @since 1.5
+ */
+public final class SelectorUtils {
+
+    private static SelectorUtils instance = new SelectorUtils();
+
+    /**
+     * Private Constructor
+     */
+    private SelectorUtils() {
+    }
+
+    /**
+     * Retrieves the instance of the Singleton.
+     * @return singleton instance
+     */
+    public static SelectorUtils getInstance() {
+        return instance;
+    }
+
+    /**
+     * Tests whether or not a given path matches the start of a given
+     * pattern up to the first "**".
+     * <p>
+     * This is not a general purpose test and should only be used if you
+     * can live with false positives. For example, <code>pattern=**\a</code>
+     * and <code>str=b</code> will yield <code>true</code>.
+     *
+     * @param pattern The pattern to match against. Must not be
+     *                <code>null</code>.
+     * @param str     The path to match, as a String. Must not be
+     *                <code>null</code>.
+     *
+     * @return whether or not a given path matches the start of a given
+     * pattern up to the first "**".
+     */
+    public static boolean matchPatternStart(String pattern, String str) {
+        return matchPatternStart(pattern, str, true);
+    }
+
+    /**
+     * Tests whether or not a given path matches the start of a given
+     * pattern up to the first "**".
+     * <p>
+     * This is not a general purpose test and should only be used if you
+     * can live with false positives. For example, <code>pattern=**\a</code>
+     * and <code>str=b</code> will yield <code>true</code>.
+     *
+     * @param pattern The pattern to match against. Must not be
+     *                <code>null</code>.
+     * @param str     The path to match, as a String. Must not be
+     *                <code>null</code>.
+     * @param isCaseSensitive Whether or not matching should be performed
+     *                        case sensitively.
+     *
+     * @return whether or not a given path matches the start of a given
+     * pattern up to the first "**".
+     */
+    public static boolean matchPatternStart(String pattern, String str,
+                                            boolean isCaseSensitive) {
+        // When str starts with a File.separator, pattern has to start with a
+        // File.separator.
+        // When pattern starts with a File.separator, str has to start with a
+        // File.separator.
+        if (str.startsWith(File.separator)
+                != pattern.startsWith(File.separator)) {
+            return false;
+        }
+
+        String[] patDirs = tokenizePathAsArray(pattern);
+        String[] strDirs = tokenizePathAsArray(str);
+
+        int patIdxStart = 0;
+        int patIdxEnd = patDirs.length - 1;
+        int strIdxStart = 0;
+        int strIdxEnd = strDirs.length - 1;
+
+        // up to first '**'
+        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+            String patDir = patDirs[patIdxStart];
+            if (patDir.equals("**")) {
+                break;
+            }
+            if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) {
+                return false;
+            }
+            patIdxStart++;
+            strIdxStart++;
+        }
+
+        if (strIdxStart > strIdxEnd) {
+            // String is exhausted
+            return true;
+        } else if (patIdxStart > patIdxEnd) {
+            // String not exhausted, but pattern is. Failure.
+            return false;
+        } else {
+            // pattern now holds ** while string is not exhausted
+            // this will generate false positives but we can live with that.
+            return true;
+        }
+    }
+
+    /**
+     * Tests whether or not a given path matches a given pattern.
+     *
+     * @param pattern The pattern to match against. Must not be
+     *                <code>null</code>.
+     * @param str     The path to match, as a String. Must not be
+     *                <code>null</code>.
+     *
+     * @return <code>true</code> if the pattern matches against the string,
+     *         or <code>false</code> otherwise.
+     */
+    public static boolean matchPath(String pattern, String str) {
+        return matchPath(pattern, str, true);
+    }
+
+    /**
+     * Tests whether or not a given path matches a given pattern.
+     *
+     * @param pattern The pattern to match against. Must not be
+     *                <code>null</code>.
+     * @param str     The path to match, as a String. Must not be
+     *                <code>null</code>.
+     * @param isCaseSensitive Whether or not matching should be performed
+     *                        case sensitively.
+     *
+     * @return <code>true</code> if the pattern matches against the string,
+     *         or <code>false</code> otherwise.
+     */
+    public static boolean matchPath(String pattern, String str,
+                                    boolean isCaseSensitive) {
+        // When str starts with a File.separator, pattern has to start with a
+        // File.separator.
+        // When pattern starts with a File.separator, str has to start with a
+        // File.separator.
+        if (str.startsWith(File.separator)
+                != pattern.startsWith(File.separator)) {
+            return false;
+        }
+
+        String[] patDirs = tokenizePathAsArray(pattern);
+        String[] strDirs = tokenizePathAsArray(str);
+
+        int patIdxStart = 0;
+        int patIdxEnd = patDirs.length - 1;
+        int strIdxStart = 0;
+        int strIdxEnd = strDirs.length - 1;
+
+        // up to first '**'
+        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+            String patDir = patDirs[patIdxStart];
+            if (patDir.equals("**")) {
+                break;
+            }
+            if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) {
+                patDirs = null;
+                strDirs = null;
+                return false;
+            }
+            patIdxStart++;
+            strIdxStart++;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // String is exhausted
+            for (int i = patIdxStart; i <= patIdxEnd; i++) {
+                if (!patDirs[i].equals("**")) {
+                    patDirs = null;
+                    strDirs = null;
+                    return false;
+                }
+            }
+            return true;
+        } else {
+            if (patIdxStart > patIdxEnd) {
+                // String not exhausted, but pattern is. Failure.
+                patDirs = null;
+                strDirs = null;
+                return false;
+            }
+        }
+
+        // up to last '**'
+        while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+            String patDir = patDirs[patIdxEnd];
+            if (patDir.equals("**")) {
+                break;
+            }
+            if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) {
+                patDirs = null;
+                strDirs = null;
+                return false;
+            }
+            patIdxEnd--;
+            strIdxEnd--;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // String is exhausted
+            for (int i = patIdxStart; i <= patIdxEnd; i++) {
+                if (!patDirs[i].equals("**")) {
+                    patDirs = null;
+                    strDirs = null;
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
+            int patIdxTmp = -1;
+            for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
+                if (patDirs[i].equals("**")) {
+                    patIdxTmp = i;
+                    break;
+                }
+            }
+            if (patIdxTmp == patIdxStart + 1) {
+                // '**/**' situation, so skip one
+                patIdxStart++;
+                continue;
+            }
+            // Find the pattern between padIdxStart & padIdxTmp in str between
+            // strIdxStart & strIdxEnd
+            int patLength = (patIdxTmp - patIdxStart - 1);
+            int strLength = (strIdxEnd - strIdxStart + 1);
+            int foundIdx = -1;
+            strLoop:
+                        for (int i = 0; i <= strLength - patLength; i++) {
+                            for (int j = 0; j < patLength; j++) {
+                                String subPat = patDirs[patIdxStart + j + 1];
+                                String subStr = strDirs[strIdxStart + i + j];
+                                if (!match(subPat, subStr, isCaseSensitive)) {
+                                    continue strLoop;
+                                }
+                            }
+
+                            foundIdx = strIdxStart + i;
+                            break;
+                        }
+
+            if (foundIdx == -1) {
+                patDirs = null;
+                strDirs = null;
+                return false;
+            }
+
+            patIdxStart = patIdxTmp;
+            strIdxStart = foundIdx + patLength;
+        }
+
+        for (int i = patIdxStart; i <= patIdxEnd; i++) {
+            if (!patDirs[i].equals("**")) {
+                patDirs = null;
+                strDirs = null;
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Tests whether or not a string matches against a pattern.
+     * The pattern may contain two special characters:<br>
+     * '*' means zero or more characters<br>
+     * '?' means one and only one character
+     *
+     * @param pattern The pattern to match against.
+     *                Must not be <code>null</code>.
+     * @param str     The string which must be matched against the pattern.
+     *                Must not be <code>null</code>.
+     *
+     * @return <code>true</code> if the string matches against the pattern,
+     *         or <code>false</code> otherwise.
+     */
+    public static boolean match(String pattern, String str) {
+        return match(pattern, str, true);
+    }
+
+    /**
+     * Tests whether or not a string matches against a pattern.
+     * The pattern may contain two special characters:<br>
+     * '*' means zero or more characters<br>
+     * '?' means one and only one character
+     *
+     * @param pattern The pattern to match against.
+     *                Must not be <code>null</code>.
+     * @param str     The string which must be matched against the pattern.
+     *                Must not be <code>null</code>.
+     * @param isCaseSensitive Whether or not matching should be performed
+     *                        case sensitively.
+     *
+     *
+     * @return <code>true</code> if the string matches against the pattern,
+     *         or <code>false</code> otherwise.
+     */
+    public static boolean match(String pattern, String str,
+                                boolean isCaseSensitive) {
+        char[] patArr = pattern.toCharArray();
+        char[] strArr = str.toCharArray();
+        int patIdxStart = 0;
+        int patIdxEnd = patArr.length - 1;
+        int strIdxStart = 0;
+        int strIdxEnd = strArr.length - 1;
+        char ch;
+
+        boolean containsStar = false;
+        for (int i = 0; i < patArr.length; i++) {
+            if (patArr[i] == '*') {
+                containsStar = true;
+                break;
+            }
+        }
+
+        if (!containsStar) {
+            // No '*'s, so we make a shortcut
+            if (patIdxEnd != strIdxEnd) {
+                return false; // Pattern and string do not have the same size
+            }
+            for (int i = 0; i <= patIdxEnd; i++) {
+                ch = patArr[i];
+                if (ch != '?') {
+                    if (isCaseSensitive && ch != strArr[i]) {
+                        return false; // Character mismatch
+                    }
+                    if (!isCaseSensitive && Character.toUpperCase(ch)
+                            != Character.toUpperCase(strArr[i])) {
+                        return false;  // Character mismatch
+                    }
+                }
+            }
+            return true; // String matches against pattern
+        }
+
+        if (patIdxEnd == 0) {
+            return true; // Pattern contains only '*', which matches anything
+        }
+
+        // Process characters before first star
+        while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
+            if (ch != '?') {
+                if (isCaseSensitive && ch != strArr[strIdxStart]) {
+                    return false; // Character mismatch
+                }
+                if (!isCaseSensitive && Character.toUpperCase(ch)
+                        != Character.toUpperCase(strArr[strIdxStart])) {
+                    return false; // Character mismatch
+                }
+            }
+            patIdxStart++;
+            strIdxStart++;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // All characters in the string are used. Check if only '*'s are
+            // left in the pattern. If so, we succeeded. Otherwise failure.
+            for (int i = patIdxStart; i <= patIdxEnd; i++) {
+                if (patArr[i] != '*') {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        // Process characters after last star
+        while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
+            if (ch != '?') {
+                if (isCaseSensitive && ch != strArr[strIdxEnd]) {
+                    return false; // Character mismatch
+                }
+                if (!isCaseSensitive && Character.toUpperCase(ch)
+                        != Character.toUpperCase(strArr[strIdxEnd])) {
+                    return false; // Character mismatch
+                }
+            }
+            patIdxEnd--;
+            strIdxEnd--;
+        }
+        if (strIdxStart > strIdxEnd) {
+            // All characters in the string are used. Check if only '*'s are
+            // left in the pattern. If so, we succeeded. Otherwise failure.
+            for (int i = patIdxStart; i <= patIdxEnd; i++) {
+                if (patArr[i] != '*') {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        // process pattern between stars. padIdxStart and patIdxEnd point
+        // always to a '*'.
+        while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
+            int patIdxTmp = -1;
+            for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
+                if (patArr[i] == '*') {
+                    patIdxTmp = i;
+                    break;
+                }
+            }
+            if (patIdxTmp == patIdxStart + 1) {
+                // Two stars next to each other, skip the first one.
+                patIdxStart++;
+                continue;
+            }
+            // Find the pattern between padIdxStart & padIdxTmp in str between
+            // strIdxStart & strIdxEnd
+            int patLength = (patIdxTmp - patIdxStart - 1);
+            int strLength = (strIdxEnd - strIdxStart + 1);
+            int foundIdx = -1;
+            strLoop:
+            for (int i = 0; i <= strLength - patLength; i++) {
+                for (int j = 0; j < patLength; j++) {
+                    ch = patArr[patIdxStart + j + 1];
+                    if (ch != '?') {
+                        if (isCaseSensitive && ch != strArr[strIdxStart + i
+                                + j]) {
+                            continue strLoop;
+                        }
+                        if (!isCaseSensitive
+                            && Character.toUpperCase(ch)
+                                != Character.toUpperCase(strArr[strIdxStart + i + j])) {
+                            continue strLoop;
+                        }
+                    }
+                }
+
+                foundIdx = strIdxStart + i;
+                break;
+            }
+
+            if (foundIdx == -1) {
+                return false;
+            }
+
+            patIdxStart = patIdxTmp;
+            strIdxStart = foundIdx + patLength;
+        }
+
+        // All characters in the string are used. Check if only '*'s are left
+        // in the pattern. If so, we succeeded. Otherwise failure.
+        for (int i = patIdxStart; i <= patIdxEnd; i++) {
+            if (patArr[i] != '*') {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Breaks a path up into a Vector of path elements, tokenizing on
+     * <code>File.separator</code>.
+     *
+     * @param path Path to tokenize. Must not be <code>null</code>.
+     *
+     * @return a Vector of path elements from the tokenized path
+     */
+    public static Vector tokenizePath (String path) {
+        return tokenizePath(path, File.separator);
+    }
+
+    /**
+     * Breaks a path up into a Vector of path elements, tokenizing on
+     *
+     * @param path Path to tokenize. Must not be <code>null</code>.
+     * @param separator the separator against which to tokenize.
+     *
+     * @return a Vector of path elements from the tokenized path
+     * @since Ant 1.6
+     */
+    public static Vector tokenizePath (String path, String separator) {
+        Vector ret = new Vector();
+        StringTokenizer st = new StringTokenizer(path, separator);
+        while (st.hasMoreTokens()) {
+            ret.addElement(st.nextToken());
+        }
+        return ret;
+    }
+
+    /**
+     * Same as {@link #tokenizePath tokenizePath} but hopefully faster.
+     */
+    private static String[] tokenizePathAsArray(String path) {
+        char sep = File.separatorChar;
+        int start = 0;
+        int len = path.length();
+        int count = 0;
+        for (int pos = 0; pos < len; pos++) {
+            if (path.charAt(pos) == sep) {
+                if (pos != start) {
+                    count++;
+                }
+                start = pos + 1;
+            }
+        }
+        if (len != start) {
+            count++;
+        }
+        String[] l = new String[count];
+        count = 0;
+        start = 0;
+        for (int pos = 0; pos < len; pos++) {
+            if (path.charAt(pos) == sep) {
+                if (pos != start) {
+                    String tok = path.substring(start, pos);
+                    l[count++] = tok;
+                }
+                start = pos + 1;
+            }
+        }
+        if (len != start) {
+            String tok = path.substring(start);
+            l[count/*++*/] = tok;
+        }
+        return l;
+    }
+
+
+    /**
+     * Returns dependency information on these two files. If src has been
+     * modified later than target, it returns true. If target doesn't exist,
+     * it likewise returns true. Otherwise, target is newer than src and
+     * is not out of date, thus the method returns false. It also returns
+     * false if the src file doesn't even exist, since how could the
+     * target then be out of date.
+     *
+     * @param src the original file
+     * @param target the file being compared against
+     * @param granularity the amount in seconds of slack we will give in
+     *        determining out of dateness
+     * @return whether the target is out of date
+     */
+    public static boolean isOutOfDate(File src, File target, int granularity) {
+        if (!src.exists()) {
+            return false;
+        }
+        if (!target.exists()) {
+            return true;
+        }
+        if ((src.lastModified() - granularity) > target.lastModified()) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns dependency information on these two resources. If src has been
+     * modified later than target, it returns true. If target doesn't exist,
+     * it likewise returns true. Otherwise, target is newer than src and
+     * is not out of date, thus the method returns false. It also returns
+     * false if the src file doesn't even exist, since how could the
+     * target then be out of date.
+     *
+     * @param src the original resource
+     * @param target the resource being compared against
+     * @param granularity the amount in seconds of slack we will give in
+     *        determining out of dateness
+     * @return whether the target is out of date
+     */
+    public static boolean isOutOfDate(Resource src, Resource target,
+                                      int granularity) {
+        if (!src.isExists()) {
+            return false;
+        }
+        if (!target.isExists()) {
+            return true;
+        }
+        if ((src.getLastModified() - granularity) > target.getLastModified()) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * "Flattens" a string by removing all whitespace (space, tab, linefeed,
+     * carriage return, and formfeed). This uses StringTokenizer and the
+     * default set of tokens as documented in the single arguement constructor.
+     *
+     * @param input a String to remove all whitespace.
+     * @return a String that has had all whitespace removed.
+     */
+    public static String removeWhitespace(String input) {
+        StringBuffer result = new StringBuffer();
+        if (input != null) {
+            StringTokenizer st = new StringTokenizer(input);
+            while (st.hasMoreTokens()) {
+                result.append(st.nextToken());
+            }
+        }
+        return result.toString();
+    }
+
+    /**
+     * Tests if a string contains stars or question marks
+     * @param input a String which one wants to test for containing wildcard
+     * @return true if the string contains at least a star or a question mark
+     */
+    public static boolean hasWildcards(String input) {
+        return (input.indexOf('*') != -1 || input.indexOf('?') != -1);
+    }
+
+    /**
+     * removes from a pattern all tokens to the right containing wildcards
+     * @param input the input string
+     * @return the leftmost part of the pattern without wildcards
+     */
+    public static String rtrimWildcardTokens(String input) {
+        Vector v = tokenizePath(input, File.separator);
+        StringBuffer sb = new StringBuffer();
+        for (int counter = 0; counter < v.size(); counter++) {
+            if (hasWildcards((String) v.elementAt(counter))) {
+                break;
+            }
+            if (counter > 0) {
+                sb.append(File.separator);
+            }
+            sb.append((String) v.elementAt(counter));
+        }
+        return sb.toString();
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/SizeSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/SizeSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/SizeSelector.java	(revision 16139)
@@ -0,0 +1,277 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Selector that filters files based on their size.
+ *
+ * @since 1.5
+ */
+public class SizeSelector extends BaseExtendSelector {
+
+    private long size = -1;
+    private long multiplier = 1;
+    private long sizelimit = -1;
+    private int cmp = 2;
+    /** Used for parameterized custom selector */
+    public static final String SIZE_KEY = "value";
+    /** Used for parameterized custom selector */
+    public static final String UNITS_KEY = "units";
+    /** Used for parameterized custom selector */
+    public static final String WHEN_KEY = "when";
+
+    /**
+     * Creates a new <code>SizeSelector</code> instance.
+     *
+     */
+    public SizeSelector() {
+    }
+
+    /**
+     * Returns a <code>String</code> object representing the specified
+     * SizeSelector. This is "{sizeselector value: " + <"compare",
+     * "less", "more", "equal"> + "}".
+     * @return a string describing this object
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer("{sizeselector value: ");
+        buf.append(sizelimit);
+        buf.append("compare: ");
+        if (cmp == 0) {
+            buf.append("less");
+        } else if (cmp == 1) {
+            buf.append("more");
+        } else {
+            buf.append("equal");
+        }
+        buf.append("}");
+        return buf.toString();
+    }
+
+    /**
+     * A size selector needs to know what size to base its selecting on.
+     * This will be further modified by the multiplier to get an
+     * actual size limit.
+     *
+     * @param size the size to select against expressed in units.
+     */
+    public void setValue(long size) {
+        this.size = size;
+        if ((multiplier != 0) && (size > -1)) {
+            sizelimit = size * multiplier;
+        }
+    }
+
+    /**
+     * Sets the units to use for the comparison. This is a little
+     * complicated because common usage has created standards that
+     * play havoc with capitalization rules. Thus, some people will
+     * use "K" for indicating 1000's, when the SI standard calls for
+     * "k". Others have tried to introduce "K" as a multiple of 1024,
+     * but that falls down when you reach "M", since "m" is already
+     * defined as 0.001.
+     * <p>
+     * To get around this complexity, a number of standards bodies
+     * have proposed the 2^10 standard, and at least one has adopted
+     * it. But we are still left with a populace that isn't clear on
+     * how capitalization should work.
+     * <p>
+     * We therefore ignore capitalization as much as possible.
+     * Completely mixed case is not possible, but all upper and lower
+     * forms are accepted for all long and short forms. Since we have
+     * no need to work with the 0.001 case, this practice works here.
+     * <p>
+     * This function translates all the long and short forms that a
+     * unit prefix can occur in and translates them into a single
+     * multiplier.
+     *
+     * @param units The units to compare the size to, using an
+     *        EnumeratedAttribute.
+     */
+    public void setUnits(ByteUnits units) {
+        int i = units.getIndex();
+        multiplier = 0;
+        if ((i > -1) && (i < 4)) {
+            multiplier = 1000;
+        } else if ((i > 3) && (i < 9)) {
+            multiplier = 1024;
+        } else if ((i > 8) && (i < 13)) {
+            multiplier = 1000000;
+        } else if ((i > 12) && (i < 18)) {
+            multiplier = 1048576;
+        } else if ((i > 17) && (i < 22)) {
+            multiplier = 1000000000L;
+        } else if ((i > 21) && (i < 27)) {
+            multiplier = 1073741824L;
+        } else if ((i > 26) && (i < 31)) {
+            multiplier = 1000000000000L;
+        } else if ((i > 30) && (i < 36)) {
+            multiplier = 1099511627776L;
+        }
+        if ((multiplier > 0) && (size > -1)) {
+            sizelimit = size * multiplier;
+        }
+    }
+
+    /**
+     * This specifies when the file should be selected, whether it be
+     * when the file matches a particular size, when it is smaller,
+     * or whether it is larger.
+     *
+     * @param scmp The comparison to perform, an EnumeratedAttribute.
+     */
+    public void setWhen(SizeComparisons scmp) {
+        this.cmp = scmp.getIndex();
+    }
+
+    /**
+     * When using this as a custom selector, this method will be called.
+     * It translates each parameter into the appropriate setXXX() call.
+     *
+     * @param parameters the complete set of parameters for this selector.
+     */
+    public void setParameters(Parameter[] parameters) {
+        super.setParameters(parameters);
+        if (parameters != null) {
+            for (int i = 0; i < parameters.length; i++) {
+                String paramname = parameters[i].getName();
+                if (SIZE_KEY.equalsIgnoreCase(paramname)) {
+                    try {
+                        setValue(new Long(parameters[i].getValue()
+                        ).longValue());
+                    } catch (NumberFormatException nfe) {
+                        setError("Invalid size setting "
+                                + parameters[i].getValue());
+                    }
+                } else if (UNITS_KEY.equalsIgnoreCase(paramname)) {
+                    ByteUnits units = new ByteUnits();
+                    units.setValue(parameters[i].getValue());
+                    setUnits(units);
+                } else if (WHEN_KEY.equalsIgnoreCase(paramname)) {
+                    SizeComparisons scmp = new SizeComparisons();
+                    scmp.setValue(parameters[i].getValue());
+                    setWhen(scmp);
+                } else {
+                    setError("Invalid parameter " + paramname);
+                }
+            }
+        }
+    }
+
+    /**
+     * <p>Checks to make sure all settings are kosher. In this case, it
+     * means that the size attribute has been set (to a positive value),
+     * that the multiplier has a valid setting, and that the size limit
+     * is valid. Since the latter is a calculated value, this can only
+     * fail due to a programming error.
+     * </p>
+     * <p>If a problem is detected, the setError() method is called.
+     * </p>
+     */
+    public void verifySettings() {
+        if (size < 0) {
+            setError("The value attribute is required, and must be positive");
+        } else if (multiplier < 1) {
+            setError("Invalid Units supplied, must be K,Ki,M,Mi,G,Gi,T,or Ti");
+        } else if (sizelimit < 0) {
+            setError("Internal error: Code is not setting sizelimit correctly");
+        }
+    }
+
+    /**
+     * The heart of the matter. This is where the selector gets to decide
+     * on the inclusion of a file in a particular fileset.
+     *
+     * @param basedir A java.io.File object for the base directory.
+     * @param filename The name of the file to check.
+     * @param file A File object for this filename.
+     * @return whether the file should be selected or not.
+     */
+    public boolean isSelected(File basedir, String filename, File file) {
+
+        // throw BuildException on error
+        validate();
+
+        // Directory size never selected for
+        if (file.isDirectory()) {
+            return true;
+        }
+        if (cmp == 0) {
+            return (file.length() < sizelimit);
+        } else if (cmp == 1) {
+            return (file.length() > sizelimit);
+        } else {
+            return (file.length() == sizelimit);
+        }
+    }
+
+
+    /**
+     * Enumerated attribute with the values for units.
+     * <p>
+     * This treats the standard SI units as representing powers of ten,
+     * as they should. If you want the powers of 2 that approximate
+     * the SI units, use the first two characters followed by a
+     * <code>bi</code>. So 1024 (2^10) becomes <code>kibi</code>,
+     * 1048576 (2^20) becomes <code>mebi</code>, 1073741824 (2^30)
+     * becomes <code>gibi</code>, and so on. The symbols are also
+     * accepted, and these are the first letter capitalized followed
+     * by an <code>i</code>. <code>Ki</code>, <code>Mi</code>,
+     * <code>Gi</code>, and so on. Capitalization variations on these
+     * are also accepted.
+     * <p>
+     * This binary prefix system is approved by the IEC and appears on
+     * its way for approval by other agencies, but it is not an SI
+     * standard. It disambiguates things for us, though.
+     */
+    public static class ByteUnits extends EnumeratedAttribute {
+        /**
+         * @return the values as an array of strings
+         */
+        public String[] getValues() {
+            return new String[]{"K", "k", "kilo", "KILO",
+                                "Ki", "KI", "ki", "kibi", "KIBI",
+                                "M", "m", "mega", "MEGA",
+                                "Mi", "MI", "mi", "mebi", "MEBI",
+                                "G", "g", "giga", "GIGA",
+                                "Gi", "GI", "gi", "gibi", "GIBI",
+                                "T", "t", "tera", "TERA",
+           /* You wish! */      "Ti", "TI", "ti", "tebi", "TEBI"
+            };
+        }
+    }
+
+    /**
+     * Enumerated attribute with the values for size comparison.
+     */
+    public static class SizeComparisons extends EnumeratedAttribute {
+        /**
+         * @return the values as an array of strings
+         */
+        public String[] getValues() {
+            return new String[]{"less", "more", "equal"};
+        }
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/TypeSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/TypeSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/TypeSelector.java	(revision 16139)
@@ -0,0 +1,134 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import java.io.File;
+
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Parameter;
+
+/**
+ * Selector that selects a certain kind of file: directory or regular.
+ *
+ * @since 1.6
+ */
+public class TypeSelector extends BaseExtendSelector {
+
+    private String type = null;
+
+    /** Key to used for parameterized custom selector */
+    public static final String TYPE_KEY = "type";
+
+    /**
+     * Creates a new <code>TypeSelector</code> instance.
+     *
+     */
+    public TypeSelector() {
+    }
+
+    /**
+     * @return a string describing this object
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer("{typeselector type: ");
+        buf.append(type);
+        buf.append("}");
+        return buf.toString();
+    }
+
+    /**
+     * Set the type of file to require.
+     * @param fileTypes the type of file - file or dir
+     */
+    public void setType(FileType fileTypes) {
+        this.type = fileTypes.getValue();
+    }
+
+    /**
+     * When using this as a custom selector, this method will be called.
+     * It translates each parameter into the appropriate setXXX() call.
+     *
+     * @param parameters the complete set of parameters for this selector
+     */
+    public void setParameters(Parameter[] parameters) {
+        super.setParameters(parameters);
+        if (parameters != null) {
+            for (int i = 0; i < parameters.length; i++) {
+                String paramname = parameters[i].getName();
+                if (TYPE_KEY.equalsIgnoreCase(paramname)) {
+                    FileType type = new FileType();
+                    type.setValue(parameters[i].getValue());
+                    setType(type);
+                } else {
+                    setError("Invalid parameter " + paramname);
+                }
+            }
+        }
+    }
+
+    /**
+     * Checks to make sure all settings are kosher. In this case, it
+     * means that the pattern attribute has been set.
+     *
+     */
+    public void verifySettings() {
+        if (type == null) {
+            setError("The type attribute is required");
+        }
+    }
+
+    /**
+     * The heart of the matter. This is where the selector gets to decide
+     * on the inclusion of a file in a particular fileset.
+     *
+     * @param basedir the base directory the scan is being done from
+     * @param filename is the name of the file to check
+     * @param file is a java.io.File object the selector can use
+     * @return whether the file should be selected or not
+     */
+    public boolean isSelected(File basedir, String filename, File file) {
+
+        // throw BuildException on error
+        validate();
+
+        if (file.isDirectory()) {
+            return type.equals(FileType.DIR);
+        } else {
+            return type.equals(FileType.FILE);
+        }
+    }
+
+    /**
+     * Enumerated attribute with the values for types of file
+     */
+    public static class FileType extends EnumeratedAttribute {
+        /** the string value for file */
+        public static final String FILE = "file";
+        /** the string value for dir */
+        public static final String DIR = "dir";
+
+        /**
+         * @return the values as an array of strings
+         */
+        public String[] getValues() {
+            return new String[]{FILE, DIR};
+        }
+    }
+
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/Algorithm.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/Algorithm.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/Algorithm.java	(revision 16139)
@@ -0,0 +1,47 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors.modifiedselector;
+
+
+import java.io.File;
+
+
+/**
+ * The <i>Algorithm</i> defines how a value for a file is computed.
+ * It must be sure that multiple calls for the same file results in the
+ * same value.
+ * The implementing class should implement a useful toString() method.
+ *
+ * @version 2003-09-13
+ * @since  Ant 1.6
+ */
+public interface Algorithm {
+
+    /**
+     * Checks its prerequisites.
+     * @return <i>true</i> if all is ok, otherwise <i>false</i>.
+     */
+    boolean isValid();
+
+    /**
+     * Get the value for a file.
+     * @param file    File object for which the value should be evaluated.
+     * @return        The value for that file
+     */
+    String getValue(File file);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/Cache.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/Cache.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/Cache.java	(revision 16139)
@@ -0,0 +1,71 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors.modifiedselector;
+
+
+import java.util.Iterator;
+
+
+/**
+ * A Cache let the user store key-value-pairs in a permanent manner and access
+ * them.
+ * It is possible that a client uses get() before load() therefore the
+ * implementation must ensure that no error occurred because of the wrong
+ * <i>order</i>.
+ * The implementing class should implement a useful toString() method.
+ *
+ * @version 2003-09-13
+ * @since  Ant 1.6
+ */
+public interface Cache {
+
+    /**
+     * Checks its prerequisites.
+     * @return <i>true</i> if all is ok, otherwise <i>false</i>.
+     */
+    boolean isValid();
+
+    /** Deletes the cache. If file based the file has to be deleted also. */
+    void delete();
+
+    /** Loads the cache, must handle not existing cache. */
+    void load();
+
+    /** Saves modification of the cache. */
+    void save();
+
+    /**
+     * Returns a value for a given key from the cache.
+     * @param key the key
+     * @return the stored value
+     */
+    Object get(Object key);
+
+    /**
+     * Saves a key-value-pair in the cache.
+     * @param key the key
+     * @param value the value
+     */
+    void put(Object key, Object value);
+
+    /**
+     * Returns an iterator over the keys in the cache.
+     * @return An iterator over the keys.
+     */
+    Iterator iterator();
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/DigestAlgorithm.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/DigestAlgorithm.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/DigestAlgorithm.java	(revision 16139)
@@ -0,0 +1,203 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors.modifiedselector;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.security.DigestInputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import org.apache.tools.ant.BuildException;
+
+
+/**
+ * Computes a 'hashvalue' for the content of file using
+ * java.security.MessageDigest.
+ * Use of this algorithm doesn't require any additional nested <param>s.
+ * Supported <param>s are:
+ * <table>
+ * <tr>
+ *   <th>name</th><th>values</th><th>description</th><th>required</th>
+ * </tr>
+ * <tr>
+ *   <td> algorithm.algorithm </td>
+ *   <td> MD5 | SHA (default provider) </td>
+ *   <td> name of the algorithm the provider should use </td>
+ *   <td> no, defaults to MD5 </td>
+ * </tr>
+ * <tr>
+ *   <td> algorithm.provider </td>
+ *   <td> </td>
+ *   <td> name of the provider to use </td>
+ *   <td> no, defaults to <i>null</i> </td>
+ * </tr>
+ * </table>
+ *
+ * @version 2003-09-13
+ * @since  Ant 1.6
+ */
+public class DigestAlgorithm implements Algorithm {
+
+
+    // -----  member variables  -----
+
+
+    /**
+     * MessageDigest algorithm to be used.
+     */
+    private String algorithm = "MD5";
+
+    /**
+     * MessageDigest Algorithm provider
+     */
+    private String provider = null;
+
+    /**
+     * Message Digest instance
+     */
+    private MessageDigest messageDigest = null;
+
+    /**
+     * Size of the read buffer to use.
+     */
+    private int readBufferSize = 8 * 1024;
+
+
+    // -----  Algorithm-Configuration  -----
+
+
+    /**
+     * Specifies the algorithm to be used to compute the checksum.
+     * Defaults to "MD5". Other popular algorithms like "SHA" may be used as well.
+     * @param algorithm the digest algorithm to use
+     */
+    public void setAlgorithm(String algorithm) {
+        this.algorithm = algorithm;
+    }
+
+
+    /**
+     * Sets the MessageDigest algorithm provider to be used
+     * to calculate the checksum.
+     * @param provider provider to use
+     */
+    public void setProvider(String provider) {
+        this.provider = provider;
+    }
+
+
+    /** Initialize the security message digest. */
+    public void initMessageDigest() {
+        if (messageDigest != null) {
+            return;
+        }
+
+        if ((provider != null) && !"".equals(provider) && !"null".equals(provider)) {
+            try {
+                messageDigest = MessageDigest.getInstance(algorithm, provider);
+            } catch (NoSuchAlgorithmException noalgo) {
+                throw new BuildException(noalgo);
+            } catch (NoSuchProviderException noprovider) {
+                throw new BuildException(noprovider);
+            }
+        } else {
+            try {
+                messageDigest = MessageDigest.getInstance(algorithm);
+            } catch (NoSuchAlgorithmException noalgo) {
+                throw new BuildException(noalgo);
+            }
+        }
+    }
+
+
+    // -----  Logic  -----
+
+
+    /**
+     * This algorithm doesn't need any configuration.
+     * Therefore it's always valid.
+     * @return <i>true</i> if all is ok, otherwise <i>false</i>.
+     */
+    public boolean isValid() {
+        return true;
+    }
+
+
+    /**
+     * Computes a value for a file content with the specified digest algorithm.
+     * @param file    File object for which the value should be evaluated.
+     * @return        The value for that file
+     */
+    // implementation adapted from ...taskdefs.Checksum, thanks to Magesh for hint
+    public String getValue(File file) {
+        initMessageDigest();
+        String checksum = null;
+        try {
+            if (!file.canRead()) {
+                return null;
+            }
+            FileInputStream fis = null;
+            FileOutputStream fos = null;
+            byte[] buf = new byte[readBufferSize];
+            try {
+                messageDigest.reset();
+                fis = new FileInputStream(file);
+                DigestInputStream dis = new DigestInputStream(fis,
+                                                              messageDigest);
+                while (dis.read(buf, 0, readBufferSize) != -1) {
+                    // do nothing
+                }
+                dis.close();
+                fis.close();
+                fis = null;
+                byte[] fileDigest = messageDigest.digest();
+                StringBuffer checksumSb = new StringBuffer();
+                for (int i = 0; i < fileDigest.length; i++) {
+                    String hexStr = Integer.toHexString(0x00ff & fileDigest[i]);
+                    if (hexStr.length() < 2) {
+                        checksumSb.append("0");
+                    }
+                    checksumSb.append(hexStr);
+                }
+                checksum = checksumSb.toString();
+            } catch (Exception e) {
+                return null;
+            }
+        } catch (Exception e) {
+            return null;
+        }
+        return checksum;
+    }
+
+
+    /**
+     * Override Object.toString().
+     * @return some information about this algorithm.
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        buf.append("<DigestAlgorithm:");
+        buf.append("algorithm=").append(algorithm);
+        buf.append(";provider=").append(provider);
+        buf.append(">");
+        return buf.toString();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/EqualComparator.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/EqualComparator.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/EqualComparator.java	(revision 16139)
@@ -0,0 +1,59 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors.modifiedselector;
+
+
+import java.util.Comparator;
+
+
+/**
+ * Simple implementation of Comparator for use in CacheSelector.
+ * compare() returns '0' (should not be selected) if both parameter
+ * are equal otherwise '1' (should be selected).
+ *
+ * @version 2003-09-13
+ * @since  Ant 1.6
+ */
+public class EqualComparator implements Comparator {
+
+    /**
+     * Implements Comparator.compare().
+     * @param o1 the first object
+     * @param o2 the second object
+     * @return 0, if both are equal, otherwise 1
+     */
+    public int compare(Object o1, Object o2) {
+        if (o1 == null) {
+            if (o2 == null) {
+                return 1;
+            } else {
+                return 0;
+            }
+        } else {
+            return (o1.equals(o2)) ? 0 : 1;
+        }
+    }
+
+    /**
+     * Override Object.toString().
+     * @return information about this comparator
+     */
+    public String toString() {
+        return "EqualComparator";
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/HashvalueAlgorithm.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/HashvalueAlgorithm.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/HashvalueAlgorithm.java	(revision 16139)
@@ -0,0 +1,78 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors.modifiedselector;
+
+
+import java.io.File;
+
+
+/**
+ * Computes a 'hashvalue' for the content of file using String.hashValue().
+ * Use of this algorithm doesn't require any additional nested <param>s and
+ * doesn't support any.
+ *
+ * @version 2003-09-13
+ * @since  Ant 1.6
+ */
+public class HashvalueAlgorithm implements Algorithm {
+
+    /**
+     * This algorithm doesn't need any configuration.
+     * Therefore it's always valid.
+     * @return always true
+     */
+    public boolean isValid() {
+        return true;
+    }
+
+    /**
+     * Computes a 'hashvalue' for a file content.
+     * It reads the content of a file, convert that to String and use the
+     * String.hashCode() method.
+     * @param file  The file for which the value should be computed
+     * @return the hashvalue or <i>null</i> if the file couldn't be read
+     */
+     // Because the content is only read the file will not be damaged. I tested
+     // with JPG, ZIP and PDF as binary files.
+    public String getValue(File file) {
+        try {
+            if (!file.canRead()) {
+                return null;
+            }
+            java.io.FileInputStream fis = new java.io.FileInputStream(file);
+            byte[] content = new byte[fis.available()];
+            fis.read(content);
+            fis.close();
+            String s = new String(content);
+            int hash = s.hashCode();
+            return Integer.toString(hash);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+
+
+    /**
+     * Override Object.toString().
+     * @return information about this comparator
+     */
+    public String toString() {
+        return "HashvalueAlgorithm";
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ModifiedSelector.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ModifiedSelector.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/ModifiedSelector.java	(revision 16139)
@@ -0,0 +1,615 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors.modifiedselector;
+
+
+// Java
+import java.util.Comparator;
+import java.util.Vector;
+import java.util.Iterator;
+import java.io.File;
+
+// Ant
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.IntrospectionHelper;
+import org.apache.tools.ant.types.EnumeratedAttribute;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.types.selectors.BaseExtendSelector;
+
+
+/**
+ * <p>Selector class that uses <i>Algorithm</i>, <i>Cache</i> and <i>Comparator</i>
+ * for its work.
+ * The <i>Algorithm</i> is used for computing a hashvalue for a file.
+ * The <i>Comparator</i> decides whether to select or not.
+ * The <i>Cache</i> stores the other value for comparison by the <i>Comparator</i>
+ * in a persistent manner.</p>
+ *
+ * <p>The ModifiedSelector is implemented as a <b>CoreSelector</b> and uses default
+ * values for all its attributes therefore the simpliest example is <pre>
+ *   <copy todir="dest">
+ *       <filelist dir="src">
+ *           <modified/>
+ *       </filelist>
+ *   </copy>
+ * </pre></p>
+ *
+ * <p>The same example rewritten as CoreSelector with setting the all values
+ * (same as defaults are) would be <pre>
+ *   <copy todir="dest">
+ *       <filelist dir="src">
+ *           <modified update="true"
+ *                     cache="propertyfile"
+ *                     algorithm="digest"
+ *                     comparator="equal">
+ *               <param name="cache.cachefile"     value="cache.properties"/>
+ *               <param name="algorithm.algorithm" value="MD5"/>
+ *           </modified>
+ *       </filelist>
+ *   </copy>
+ * </pre></p>
+ *
+ * <p>And the same rewritten as CustomSelector would be<pre>
+ *   <copy todir="dest">
+ *       <filelist dir="src">
+ *           <custom class="org.apache.tools.ant.type.selectors.ModifiedSelector">
+ *               <param name="update"     value="true"/>
+ *               <param name="cache"      value="propertyfile"/>
+ *               <param name="algorithm"  value="digest"/>
+ *               <param name="comparator" value="equal"/>
+ *               <param name="cache.cachefile"     value="cache.properties"/>
+ *               <param name="algorithm.algorithm" value="MD5"/>
+ *           </custom>
+ *       </filelist>
+ *   </copy>
+ * </pre></p>
+ *
+ * <p>All these three examples copy the files from <i>src</i> to <i>dest</i>
+ * using the ModifiedSelector. The ModifiedSelector uses the <i>PropertyfileCache
+ * </i>, the <i>DigestAlgorithm</i> and the <i>EqualComparator</i> for its
+ * work. The PropertyfileCache stores key-value-pairs in a simple java
+ * properties file. The filename is <i>cache.properties</i>. The <i>update</i>
+ * flag lets the selector update the values in the cache (and on first call
+ * creates the cache). The <i>DigestAlgorithm</i> computes a hashvalue using the
+ * java.security.MessageDigest class with its MD5-Algorithm and its standard
+ * provider. The new computed hashvalue and the stored one are compared by
+ * the <i>EqualComparator</i> which returns 'true' (more correct a value not
+ * equals zero (1)) if the values are not the same using simple String
+ * comparison.</p>
+ *
+ * <p>A useful scenario for this selector is inside a build environment
+ * for homepage generation (e.g. with <a href="http://xml.apache.org/forrest/">
+ * Apache Forrest</a>). <pre>
+ * <target name="generate-and-upload-site">
+ *     <echo> generate the site using forrest </echo>
+ *     <antcall target="site"/>
+ *
+ *     <echo> upload the changed files </echo>
+ *     <ftp server="${ftp.server}" userid="${ftp.user}" password="${ftp.pwd}">
+ *         <fileset dir="htdocs/manual">
+ *             <modified/>
+ *         </fileset>
+ *     </ftp>
+ * </target>
+ * </pre> Here all <b>changed</b> files are uploaded to the server. The
+ * ModifiedSelector saves therefore much upload time.</p>
+ *
+ * <p>This selector supports the following nested param's:
+ * <table>
+ * <tr><th>name</th><th>values</th><th>description</th><th>required</th></tr>
+ * <tr>
+ *     <td> cache </td>
+ *     <td> propertyfile </td>
+ *     <td> which cache implementation should be used <ul>
+ *          <li><b>propertyfile</b> - using java.util.Properties </li>
+ *     </td>
+ *     <td> no, defaults to 'propertyfile' </td>
+ * </tr>
+ * <tr>
+ *     <td> algorithm </td>
+ *     <td> hashvalue | digest </td>
+ *     <td> which algorithm implementation should be used
+ *          <li><b>hashvalue</b> - loads the file content into a String and
+ *                                 uses its hashValue() method </li>
+ *          <li><b>digest</b> - uses java.security.MessageDigest class </i>
+ *     </td>
+ *     <td> no, defaults to digest </td>
+ * </tr>
+ * <tr>
+ *     <td> comparator </td>
+ *     <td> equal | role </td>
+ *     <td> which comparator implementation should be used
+ *          <li><b>equal</b> - simple comparison using String.equals() </li>
+ *          <li><b>role</b> - uses java.text.RuleBasedCollator class </i>
+ *     </td>
+ *     <td> no, defaults to equal </td>
+ * </tr>
+ * <tr>
+ *     <td> update </td>
+ *     <td> true | false </td>
+ *     <td> If set to <i>true</i>, the cache will be stored, otherwise the values
+ *          will be lost. </td>
+ *     <td> no, defaults to true </td>
+ * </tr>
+ * <tr>
+ *     <td> seldirs </td>
+ *     <td> true | false </td>
+ *     <td> If set to <i>true</i>, directories will be selected otherwise not </td>
+ *     <td> no, defaults to true </td>
+ * </tr>
+ * <tr>
+ *     <td> cache.* </td>
+ *     <td> depends on used cache </td>
+ *     <td> value is stored and given to the Cache-Object for initialisation </td>
+ *     <td> depends on used cache </td>
+ * </tr>
+ * <tr>
+ *     <td> algorithm.* </td>
+ *     <td> depends on used algorithm </td>
+ *     <td> value is stored and given to the Algorithm-Object for initialisation </td>
+ *     <td> depends on used algorithm </td>
+ * </tr>
+ * <tr>
+ *     <td> comparator.* </td>
+ *     <td> depends on used comparator </td>
+ *     <td> value is stored and given to the Comparator-Object for initialisation </td>
+ *     <td> depends on used comparator </td>
+ * </tr>
+ * </table>
+ * If another name is used a BuildException "Invalid parameter" is thrown. </p>
+ *
+ * <p>This selector uses reflection for setting the values of its three interfaces
+ * (using org.apache.tools.ant.IntrospectionHelper) therefore no special
+ * 'configuration interfaces' has to be implemented by new caches, algorithms or
+ * comparators. All present <i>set</i>XX methods can be used. E.g. the DigestAlgorithm
+ * can use a specified provider for computing its value. For selecting this
+ * there is a <i>setProvider(String providername)</i> method. So you can use
+ * a nested <i><param name="algorithm.provider" value="MyProvider"/></i>.
+ *
+ *
+ * @version 2003-09-13
+ * @since  Ant 1.6
+*/
+public class ModifiedSelector extends BaseExtendSelector {
+
+
+    // -----  member variables - configuration
+
+
+    /** The Cache containing the old values. */
+    private Cache cache = null;
+
+    /** Algorithm for computing new values and updating the cache. */
+    private Algorithm algorithm = null;
+
+    /** How should the cached value and the new one compared? */
+    private Comparator comparator = null;
+
+    /** Should the cache be updated? */
+    private boolean update = true;
+
+    /** Are directories selected? */
+    private boolean selectDirectories = true;
+
+
+    // -----  member variables - internal use
+
+
+    /** Flag whether this object is configured. Configuration is only done once. */
+    private boolean isConfigured = false;
+
+    /** Algorithm name for later instantiation. */
+    private AlgorithmName algoName = null;
+
+    /** Cache name for later instantiation. */
+    private CacheName cacheName = null;
+
+    /** Comparator name for later instantiation. */
+    private ComparatorName compName = null;
+
+
+    /**
+     * Parameter vector with parameters for later initialization.
+     * @see #configure
+     */
+    private Vector configParameter = new Vector();
+
+    /**
+     * Parameter vector with special parameters for later initialization.
+     * The names have the pattern '*.*', e.g. 'cache.cachefile'.
+     * These parameters are used <b>after</b> the parameters with the pattern '*'.
+     * @see #configure
+     */
+    private Vector specialParameter = new Vector();
+
+
+    // -----  constructors  -----
+
+
+    /** Bean-Constructor. */
+    public ModifiedSelector() {
+    }
+
+
+    // ----- configuration  -----
+
+
+    /** Overrides BaseSelector.verifySettings(). */
+    public void verifySettings() {
+        configure();
+        if (cache == null) {
+            setError("Cache must be set.");
+        } else if (algorithm == null) {
+            setError("Algorithm must be set.");
+        } else if (!cache.isValid()) {
+            setError("Cache must be proper configured.");
+        } else if (!algorithm.isValid()) {
+            setError("Algorithm must be proper configured.");
+        }
+    }
+
+
+    /**
+     * Configures this Selector.
+     * Does this work only once per Selector object.
+     * <p>Because some problems while configuring from <custom>Selector
+     * the configuration is done in the following order:<ol>
+     * <li> collect the configuration data </li>
+     * <li> wait for the first isSelected() call </li>
+     * <li> set the default values </li>
+     * <li> set values for name pattern '*': update, cache, algorithm, comparator </li>
+     * <li> set values for name pattern '*.*: cache.cachefile, ... </li>
+     * </ol></p>
+     * <p>This configuration algorithm is needed because you don't know
+     * the order of arriving config-data. E.g. if you first set the
+     * <i>cache.cachefilename</i> and after that the <i>cache</i> itself,
+     * the default value for cachefilename is used, because setting the
+     * cache implies creating a new Cache instance - with its defaults.</p>
+     */
+    public void configure() {
+        //
+        // -----  The "Singleton"  -----
+        //
+        if (isConfigured) {
+            return;
+        }
+        isConfigured = true;
+
+        //
+        // -----  Set default values  -----
+        //
+        org.apache.tools.ant.Project project = getProject();
+        String filename = "cache.properties";
+        File cachefile = null;
+        if (project != null) {
+            // normal use inside Ant
+            cachefile = new File(project.getBaseDir(), filename);
+        } else {
+            // no reference to project - e.g. during JUnit tests
+            cachefile = new File(filename);
+        }
+        cache = new PropertiesfileCache(cachefile);
+        algorithm = new DigestAlgorithm();
+        comparator = new EqualComparator();
+        update = true;
+        selectDirectories = true;
+
+
+        //
+        // -----  Set the main attributes, pattern '*'  -----
+        //
+        for (Iterator itConfig = configParameter.iterator(); itConfig.hasNext();) {
+            Parameter par = (Parameter) itConfig.next();
+            if (par.getName().indexOf(".") > 0) {
+                // this is a *.* parameter for later use
+                specialParameter.add(par);
+            } else {
+                useParameter(par);
+            }
+        }
+        configParameter = new Vector();
+
+        //
+        // -----  Instantiate the interfaces  -----
+        //
+        String className = null;
+        String pkg = "org.apache.tools.ant.types.selectors.cacheselector";
+
+        // the algorithm
+        if (algorithm == null) {
+            if ("hashvalue".equals(algoName.getValue())) {
+                className = pkg + ".HashvalueAlgorithm";
+            } else if ("digest".equals(algoName.getValue())) {
+                className = pkg + ".DigestAlgorithm";
+            }
+            if (className != null) {
+                try {
+                    // load the specified Algorithm, save the reference and configure it
+                    algorithm = (Algorithm) Class.forName(className).newInstance();
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        // the cache
+        if (cache == null) {
+            if ("propertyfile".equals(cacheName.getValue())) {
+                className = pkg + ".PropertiesfileCache";
+            }
+            if (className != null) {
+                try {
+                    // load the specified Cache, save the reference and configure it
+                    cache = (Cache) Class.forName(className).newInstance();
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        // the comparator
+        if (comparator == null) {
+            if ("equal".equals(compName.getValue())) {
+                className = pkg + ".EqualComparator";
+            } else if ("role".equals(compName.getValue())) {
+                className = "java.text.RuleBasedCollator";
+            }
+            if (className != null) {
+                try {
+                    // load the specified Cache, save the reference and configure it
+                    comparator = (Comparator) Class.forName(className).newInstance();
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        //
+        // -----  Set the special attributes, pattern '*.*'  -----
+        //
+        for (Iterator itSpecial = specialParameter.iterator(); itSpecial.hasNext();) {
+            Parameter par = (Parameter) itSpecial.next();
+            useParameter(par);
+        }
+        specialParameter = new Vector();
+    }
+
+
+    // -----  the selection work  -----
+
+
+    /**
+     * Implementation of BaseExtendSelector.isSelected().
+     * @param basedir as described in BaseExtendSelector
+     * @param filename as described in BaseExtendSelector
+     * @param file as described in BaseExtendSelector
+     * @return as described in BaseExtendSelector
+     */
+    public boolean isSelected(File basedir, String filename, File file) {
+        validate();
+        File f = new File(basedir, filename);
+
+        // You can not compute a value for a directory
+        if (f.isDirectory()) {
+            return selectDirectories;
+        }
+
+        // Get the values and do the comparison
+        String cachedValue = String.valueOf(cache.get(f.getAbsolutePath()));
+        String newValue    = algorithm.getValue(f);
+        boolean rv = (comparator.compare(cachedValue, newValue) != 0);
+
+        // Maybe update the cache
+        if (update && !cachedValue.equals(newValue)) {
+            cache.put(f.getAbsolutePath(), newValue);
+            cache.save();
+        }
+
+        return rv;
+    }
+
+
+    // -----  attribute and nested element support  -----
+
+
+    /**
+     * Support for <i>update</i> attribute.
+     * @param update new value
+     */
+    public void setUpdate(boolean update) {
+        this.update = update;
+    }
+
+
+    /**
+     * Support for <i>seldirs</i> attribute.
+     * @param seldirs new value
+     */
+    public void setSeldirs(boolean seldirs) {
+        selectDirectories = seldirs;
+    }
+
+
+    /**
+     * Support for nested &lt;param&gt; tags.
+     * @param key the key of the parameter
+     * @param value the value of the parameter
+     */
+    public void addParam(String key, Object value) {
+        Parameter par = new Parameter();
+        par.setName(key);
+        par.setValue(String.valueOf(value));
+        configParameter.add(par);
+    }
+
+
+    /**
+     * Support for nested &lt;param&gt; tags.
+     * @param parameter the parameter object
+     */
+    public void addParam(Parameter parameter) {
+        configParameter.add(parameter);
+    }
+
+
+    /**
+     * Defined in org.apache.tools.ant.types.Parameterizable.
+     * Overwrite implementation in superclass because only special
+     * parameters are valid.
+     * @see #addParam(String,Object).
+     */
+    public void setParameters(Parameter[] parameters) {
+        if (parameters != null) {
+            for (int i = 0; i < parameters.length; i++) {
+                configParameter.add(parameters[i]);
+            }
+        }
+    }
+
+
+    /**
+     * Support for nested <param name="" value=""/> tags.
+     * Parameter named <i>cache</i>, <i>algorithm</i>,
+     * <i>comparator</i> or <i>update</i> are mapped to
+     * the respective set-Method.
+     * Parameter which names starts with <i>cache.</i> or
+     * <i>algorithm.</i> or <i>comparator.</i> are tried
+     * to set on the appropriate object via its set-methods.
+     * Other parameters are invalid and an BuildException will
+     * be thrown.
+     *
+     * @param parameter  Key and value as parameter object
+     */
+    public void useParameter(Parameter parameter) {
+        String key = parameter.getName();
+        String value = parameter.getValue();
+        if ("cache".equals(key)) {
+            CacheName cn = new CacheName();
+            cn.setValue(value);
+            setCache(cn);
+        } else if ("algorithm".equals(key)) {
+            AlgorithmName an = new AlgorithmName();
+            an.setValue(value);
+            setAlgorithm(an);
+        } else if ("comparator".equals(key)) {
+            ComparatorName cn = new ComparatorName();
+            cn.setValue(value);
+            setComparator(cn);
+        } else if ("update".equals(key)) {
+            boolean updateValue =
+                ("true".equalsIgnoreCase(value))
+                ? true
+                : false;
+            setUpdate(updateValue);
+        } else if ("seldirs".equals(key)) {
+            boolean sdValue =
+                ("true".equalsIgnoreCase(value))
+                ? true
+                : false;
+            setSeldirs(sdValue);
+        } else if (key.startsWith("cache.")) {
+            String name = key.substring(6);
+            tryToSetAParameter(cache, name, value);
+        } else if (key.startsWith("algorithm.")) {
+            String name = key.substring(10);
+            tryToSetAParameter(algorithm, name, value);
+        } else if (key.startsWith("comparator.")) {
+            String name = key.substring(11);
+            tryToSetAParameter(comparator, name, value);
+        } else {
+            setError("Invalid parameter " + key);
+        }
+    }
+
+
+    /**
+     * Try to set a value on an object using reflection.
+     * Helper method for easier access to IntrospectionHelper.setAttribute().
+     * @param obj the object on which the attribute should be set
+     * @param name the attributename
+     * @param value the new value
+     */
+    protected void tryToSetAParameter(Object obj, String name, String value) {
+        Project prj = (getProject() != null) ? getProject() : new Project();
+        IntrospectionHelper iHelper
+            = IntrospectionHelper.getHelper(prj, obj.getClass());
+
+        try {
+            iHelper.setAttribute(prj, obj, name, value);
+        } catch (org.apache.tools.ant.BuildException e) {
+            // no-op
+        }
+    }
+
+
+    // ----- 'beautiful' output -----
+
+
+    /**
+     * Override Object.toString().
+     * @return information about this selector
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer("{modifiedselector");
+        buf.append(" update=").append(update);
+        buf.append(" seldirs=").append(selectDirectories);
+        buf.append(" cache=").append(cache);
+        buf.append(" algorithm=").append(algorithm);
+        buf.append(" comparator=").append(comparator);
+        buf.append("}");
+        return buf.toString();
+    }
+
+
+    // The EnumeratedAttributes for the three interface implementations.
+    // Name-Classname mapping is done in the configure() method.
+
+
+    public Cache getCache() { return cache; }
+    public void setCache(CacheName name) {
+        cacheName = name;
+    }
+    public static class CacheName extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[] {"propertyfile" };
+        }
+    }
+
+
+    public Algorithm getAlgorithm() { return algorithm; }
+    public void setAlgorithm(AlgorithmName name) {
+        algoName = name;
+    }
+    public static class AlgorithmName extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[] {"hashvalue", "digest" };
+        }
+    }
+
+
+    public Comparator getComparator() { return comparator; }
+    public void setComparator(ComparatorName name) {
+        compName = name;
+    }
+    public static class ComparatorName extends EnumeratedAttribute {
+        public String[] getValues() {
+            return new String[] {"equal", "rule" };
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/PropertiesfileCache.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/PropertiesfileCache.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/types/selectors/modifiedselector/PropertiesfileCache.java	(revision 16139)
@@ -0,0 +1,217 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors.modifiedselector;
+
+
+import java.util.Iterator;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.io.File;
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+
+
+/**
+ * Use java.util.Properties for storing the values.
+ * The use of this Cache-implementation requires the use of the parameter
+ * <param name="cache.cachefile" .../> for defining, where to store the
+ * properties file.
+ *
+ * The ModifiedSelector sets the <i>cachefile</i> to the default value
+ * <i>cache.properties</i>.
+ *
+ * Supported <param>s are:
+ * <table>
+ * <tr>
+ *   <th>name</th><th>values</th><th>description</th><th>required</th>
+ * </tr>
+ * <tr>
+ *   <td> cache.cachefile </td>
+ *   <td> <i>path to file</i> </td>
+ *   <td> the name of the properties file </td>
+ *   <td> yes </td>
+ * </tr>
+ * </table>
+ *
+ * @version 2003-09-13
+ * @since  Ant 1.6
+ */
+public class PropertiesfileCache implements Cache {
+
+
+    // -----  member variables - configuration  -----
+
+
+    /** Where to store the properties? */
+    private File cachefile = null;
+
+    /** Object for storing the key-value-pairs. */
+    private Properties cache = new Properties();
+
+
+    // -----  member variables - internal use  -----
+
+
+    /** Is the cache already loaded? Prevents from multiple load operations. */
+    private boolean cacheLoaded = false;
+
+    /** Must the cache be saved? Prevents from multiple save operations. */
+    private boolean cacheDirty  = true;
+
+
+    // -----  Constructors  -----
+
+
+    /** Bean-Constructor. */
+    public PropertiesfileCache() {
+    }
+
+    /**
+     * Constructor.
+     * @param cachefile set the cachefile
+     */
+    public PropertiesfileCache(File cachefile) {
+        this.cachefile = cachefile;
+    }
+
+
+    // -----  Cache-Configuration  -----
+
+
+    public void setCachefile(File file) {
+        cachefile = file;
+    }
+
+    public File getCachefile() { return cachefile; }
+
+    public boolean isValid() {
+        return (cachefile != null);
+    }
+
+
+    // -----  Data Access
+
+
+    public void load() {
+        if ((cachefile != null) && cachefile.isFile() && cachefile.canRead()) {
+            try {
+                BufferedInputStream bis = new BufferedInputStream(
+                    new FileInputStream(cachefile));
+                cache.load(bis);
+                bis.close();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        // after loading the cache is up to date with the file
+        cacheLoaded = true;
+        cacheDirty  = false;
+    }
+
+    /**
+     * Saves modification of the cache.
+     * Cache is only saved if there is one ore more entries.
+     * Because entries can not be deleted by this API, this Cache
+     * implementation checks the existence of entries before creating the file
+     * for performance optimisation.
+     */
+    public void save() {
+        if (!cacheDirty) {
+            return;
+        }
+        if ((cachefile != null) && cache.propertyNames().hasMoreElements()) {
+            try {
+                BufferedOutputStream bos = new BufferedOutputStream(
+                      new FileOutputStream(cachefile));
+                cache.store(bos, null);
+                bos.flush();
+                bos.close();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+        cacheDirty = false;
+    }
+
+    /** Deletes the cache and its underlying file. */
+    public void delete() {
+        cache = new Properties();
+        cachefile.delete();
+        cacheLoaded = true;
+        cacheDirty = false;
+    }
+
+    /**
+     * Returns a value for a given key from the cache.
+     * @param key the key
+     * @return the stored value
+     */
+    public Object get(Object key) {
+        if (!cacheLoaded) {
+            load();
+        }
+        try {
+            return cache.getProperty(String.valueOf(key));
+        } catch (ClassCastException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Saves a key-value-pair in the cache.
+     * @param key the key
+     * @param value the value
+     */
+    public void put(Object key, Object value) {
+        cache.put(String.valueOf(key), String.valueOf(value));
+        cacheDirty = true;
+    }
+
+    /**
+     * Returns an iterator over the keys in the cache.
+     * @return An iterator over the keys.
+     */
+    public Iterator iterator() {
+        Vector v = new java.util.Vector();
+        Enumeration en = cache.propertyNames();
+        while (en.hasMoreElements()) {
+            v.add(en.nextElement());
+        }
+        return v.iterator();
+    }
+
+
+    // -----  additional  -----
+
+
+    /**
+     * Override Object.toString().
+     * @return information about this cache
+     */
+    public String toString() {
+        StringBuffer buf = new StringBuffer();
+        buf.append("<PropertiesfileCache:");
+        buf.append("cachefile=").append(cachefile);
+        buf.append(";noOfEntries=").append(cache.size());
+        buf.append(">");
+        return buf.toString();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ChainedMapper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ChainedMapper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ChainedMapper.java	(revision 16139)
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import java.util.List;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.ArrayList;
+import org.apache.tools.ant.types.Mapper;
+
+/**
+ * A <CODE>ContainerMapper</CODE> that chains the results of the first
+ * nested <CODE>FileNameMapper</CODE>s into sourcefiles for the second,
+ * the second to the third, and so on, returning the resulting mapped
+ * filenames from the last nested <CODE>FileNameMapper</CODE>.
+ */
+public class ChainedMapper extends ContainerMapper {
+
+    //inherit doc
+    public String[] mapFileName(String sourceFileName) {
+        List inputs = new ArrayList();
+        List results = new ArrayList();
+        results.add(sourceFileName);
+        FileNameMapper mapper = null;
+
+        for (Iterator mIter = getMappers().iterator(); mIter.hasNext();) {
+            mapper = (FileNameMapper)(mIter.next());
+            if (mapper != null) {
+                inputs.clear();
+                inputs.addAll(results);
+                results.clear();
+
+                for (Iterator it = inputs.iterator(); it.hasNext();) {
+                    String[] mapped = mapper.mapFileName((String)(it.next()));
+                    if (mapped != null) {
+                        results.addAll(Arrays.asList(mapped));
+                    }
+                }
+            }
+        }
+        return (results.size() == 0) ? null
+            : (String[]) results.toArray(new String[results.size()]);
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ClasspathUtils.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ClasspathUtils.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ClasspathUtils.java	(revision 16139)
@@ -0,0 +1,468 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import org.apache.tools.ant.AntClassLoader;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Offers some helper methods on the Path structure in ant.
+ *
+ * <p>Basic idea behind this utility class is to use it from inside the
+ * different ant objects (and user defined objects) that need classLoading
+ * for their operation.
+ * Normally those would have a setClasspathRef() {for the @classpathref}
+ * and/or a createClasspath() {for the nested &lt;classpath&gt;}
+ * Typically one would have in your Ant Task or DataType</p>
+ *
+ * <pre><code>
+ * ClasspathUtils.Delegate cpDelegate;
+ *
+ * public void init() {
+ *     this.cpDelegate = ClasspathUtils.getDelegate(this);
+ *     super.init();
+ * }
+ *
+ * public void setClasspathRef(Reference r) {
+ *     this.cpDelegate.setClasspathRef(r);
+ * }
+ *
+ * public Path createClasspath() {
+ *     return this.cpDelegate.createClasspath();
+ * }
+ *
+ * public void setClassname(String fqcn) {
+ *     this.cpDelegate.setClassname(fqcn);
+ * }
+ * </code></pre>
+ *
+ * <p>At execution time, when you actually need the classloading
+ * you can just:</p>
+ *
+ * <pre><code>
+ *     Object o = this.cpDelegate.newInstance();
+ * </code></pre>
+ *
+ * @since Ant 1.6
+ */
+public class ClasspathUtils {
+    private static final String LOADER_ID_PREFIX = "ant.loader.";
+    /**
+     * Name of the magic property that controls classloader reuse in Ant 1.4.
+     */
+    public static final String REUSE_LOADER_REF = "ant.reuse.loader";
+
+    /**
+     * Convenience overloaded version of {@link
+     * #getClassLoaderForPath(Project, Reference, boolean)}.
+     *
+     * <p>Assumes the logical 'false' for the reverseLoader.</p>
+     *
+     * @param p the project
+     * @param ref the reference
+     * @return The class loader
+     */
+    public static ClassLoader getClassLoaderForPath(
+        Project p, Reference ref) {
+
+        return getClassLoaderForPath(p, ref, false);
+    }
+
+    /**
+     * Convenience overloaded version of {@link #getClassLoaderForPath(Project, Path,
+     * String, boolean)}.
+     *
+     * <p>Delegates to the other one after extracting the referenced
+     * Path from the Project This checks also that the passed
+     * Reference is pointing to a Path all right.</p>
+     * @param p current ant project
+     * @param ref Reference to Path structure
+     * @param reverseLoader if set to true this new loader will take
+     * precedence over it's parent (which is contra the regular
+     * classloader behaviour)
+     * @return The class loader
+     */
+    public static ClassLoader getClassLoaderForPath(
+        Project p, Reference ref, boolean reverseLoader) {
+
+        String pathId = ref.getRefId();
+        Object path = p.getReference(pathId);
+        if (!(path instanceof Path)) {
+            throw new BuildException(
+                "The specified classpathref "
+                    + pathId
+                    + " does not reference a Path.");
+        }
+        String loaderId = LOADER_ID_PREFIX + pathId;
+        return getClassLoaderForPath(p, (Path) path, loaderId, reverseLoader);
+    }
+
+    /**
+     * Convenience overloaded version of {@link
+     * #getClassLoaderForPath(Project, Path, String, boolean)}.
+     *
+     * <p>Assumes the logical 'false' for the reverseLoader.</p>
+     *
+     * @param p current ant project
+     * @param path the path
+     * @param loaderId the loader id string
+     * @return The class loader
+     */
+    public static ClassLoader getClassLoaderForPath(
+        Project p, Path path, String loaderId) {
+
+        return getClassLoaderForPath(p, path, loaderId, false);
+    }
+
+    /**
+     * Convenience overloaded version of {@link
+     * #getClassLoaderForPath(Project, Path, String, boolean, boolean)}.
+     *
+     * <p>Sets value for 'reuseLoader' to true if the magic property
+     * has been set.</p>
+     *
+     * @param p the project
+     * @param path the path
+     * @param loaderId the loader id string
+     * @param reverseLoader if set to true this new loader will take
+     * precedence over it's parent (which is contra the regular
+     * classloader behaviour)
+     * @return The class loader
+     */
+    public static ClassLoader getClassLoaderForPath(
+        Project p, Path path, String loaderId, boolean reverseLoader) {
+        return getClassLoaderForPath(p, path, loaderId, reverseLoader,
+                                     isMagicPropertySet(p));
+    }
+
+    /**
+     * Gets a classloader that loads classes from the classpath
+     * defined in the path argument.
+     *
+     * <p>Based on the setting of the magic property
+     * 'ant.reuse.loader' this will try to reuse the perviously
+     * created loader with that id, and of course store it there upon
+     * creation.</p>
+     * @param p             Ant Project where the handled components are living in.
+     * @param path          Path object to be used as classpath for this classloader
+     * @param loaderId      identification for this Loader,
+     * @param reverseLoader if set to true this new loader will take
+     *                      precedence over it's parent (which is contra the regular
+     *                      classloader behaviour)
+     * @param reuseLoader   if true reuse the loader if it is found
+     * @return              ClassLoader that uses the Path as its classpath.
+     */
+    public static ClassLoader getClassLoaderForPath(
+        Project p, Path path, String loaderId, boolean reverseLoader,
+        boolean reuseLoader) {
+
+        ClassLoader cl = null;
+
+        // magic property
+        if (loaderId != null && reuseLoader) {
+            Object reusedLoader = p.getReference(loaderId);
+            if (reusedLoader != null
+                && !(reusedLoader instanceof ClassLoader)) {
+                throw new BuildException("The specified loader id " + loaderId
+                    + " does not reference a class loader");
+            }
+
+            cl = (ClassLoader) reusedLoader;
+        }
+        if (cl == null) {
+            cl = getUniqueClassLoaderForPath(p, path, reverseLoader);
+            if (loaderId != null && reuseLoader) {
+                p.addReference(loaderId, cl);
+            }
+        }
+
+        return cl;
+    }
+
+    /**
+     * Gets a fresh, different, not used before classloader that uses the
+     * passed path as it's classpath.
+     *
+     * <p>This method completely ignores the ant.reuse.loader magic
+     * property and should be used with caution.</p>
+     * @param p             Ant Project where the handled components are living in.
+     * @param path          the classpath for this loader
+     * @param reverseLoader if set to true this new loader will take
+     *                      precedence over it's parent (which is contra the regular
+     *                      classloader behaviour)
+     * @return The fresh, different, not used before class loader.
+     */
+    public static ClassLoader getUniqueClassLoaderForPath(
+        Project p,
+        Path path,
+        boolean reverseLoader) {
+        AntClassLoader acl = p.createClassLoader(path);
+        if (reverseLoader) {
+            acl.setParentFirst(false);
+            acl.addJavaLibraries();
+        }
+
+        return acl;
+    }
+
+    /**
+     * Creates a fresh object instance of the specified classname.
+     *
+     * <p> This uses the userDefinedLoader to load the specified class,
+     * and then makes an instance using the default no-argument constructor
+     * </p>
+     *
+     * @param className the full qualified class name to load.
+     * @param userDefinedLoader the classloader to use.
+     * @return The fresh object instance
+     * @throws BuildException when loading or instantiation failed.
+     */
+    public static Object newInstance(
+        String className,
+        ClassLoader userDefinedLoader) {
+        try {
+            Class clazz = userDefinedLoader.loadClass(className);
+            Object o = clazz.newInstance();
+            return o;
+        } catch (ClassNotFoundException e) {
+            throw new BuildException(
+                "Class "
+                    + className
+                    + " not found by the specific classLoader.",
+                e);
+        } catch (InstantiationException e) {
+            throw new BuildException(
+                "Could not instantiate "
+                    + className
+                    + ". Specified class should have a no "
+                    + "argument constructor.",
+                e);
+        } catch (IllegalAccessException e) {
+            throw new BuildException(
+                "Could not instantiate "
+                    + className
+                    + ". Specified class should have a "
+                    + "public constructor.",
+                e);
+        }
+    }
+
+    /**
+     * Obtains a delegate that helps out with classic classpath configuration.
+     *
+     * @param component your projectComponent that needs the assistence
+     * @return the helper, delegate.
+     * @see ClasspathUtils.Delegate
+     */
+    public static Delegate getDelegate(ProjectComponent component) {
+        return new Delegate(component);
+    }
+
+    /**
+     * Checks for the magic property that enables class loader reuse
+     * for <taskdef> and <typedef> in Ant 1.5 and earlier.
+     */
+    private static boolean isMagicPropertySet(Project p) {
+        return p.getProperty(REUSE_LOADER_REF) != null;
+    }
+
+    /**
+     * Delegate that helps out any specific ProjectComponent that needs
+     * dynamic classloading.
+     *
+     * <p>Ant ProjectComponents that need a to be able to dynamically load
+     * Classes and instantiate them often expose the following ant syntax
+     * sugar: </p>
+     *
+     * <ul><li> nested &lt;classpath&gt; </li>
+     * <li> attribute @classpathref </li>
+     * <li> attribute @classname </li></ul>
+     *
+     * <p> This class functions as a delegate handling the configuration
+     * issues for this recuring pattern.  Its usage pattern, as the name
+     * suggests is delegation, not inheritance. </p>
+     *
+     * @since Ant 1.6
+     */
+    public static class Delegate {
+        private final ProjectComponent component;
+        private Path classpath;
+        private String classpathId;
+        private String className;
+        private String loaderId;
+        private boolean reverseLoader = false;
+
+        /**
+         * Constructs Delegate
+         * @param component the ProjectComponent this delegate is for.
+         */
+        Delegate(ProjectComponent component) {
+            this.component = component;
+        }
+
+        /**
+         * This method is a Delegate method handling the @classpath attribute.
+         *
+         * <p>This attribute can set a path to add to the classpath.</p>
+         *
+         * @param classpath the path to use for the classpath.
+         */
+        public void setClasspath(Path classpath) {
+            if (this.classpath == null) {
+                this.classpath = classpath;
+            } else {
+                this.classpath.append(classpath);
+            }
+        }
+
+        /**
+         * Delegate method handling the &lt;classpath&gt; tag.
+         *
+         * <p>This nested path-like structure can set a path to add to the
+         * classpath.</p>
+         *
+         * @return the created path.
+         */
+        public Path createClasspath() {
+            if (this.classpath == null) {
+                this.classpath = new Path(component.getProject());
+            }
+            return this.classpath.createPath();
+        }
+
+        /**
+         * Delegate method handling the @classname attribute.
+         *
+         * <p>This attribute sets the full qualified class name of the class
+         * to load and instantiate.</p>
+         *
+         * @param fcqn the name of the class to load.
+         */
+        public void setClassname(String fcqn) {
+            this.className = fcqn;
+        }
+
+        /**
+         * Delegate method handling the @classpathref attribute.
+         *
+         * <p>This attribute can add a referenced path-like structure to the
+         * classpath.</p>
+         *
+         * @param r the reference to the classpath.
+         */
+        public void setClasspathref(Reference r) {
+            this.classpathId = r.getRefId();
+            createClasspath().setRefid(r);
+        }
+
+        /**
+         * Delegate method handling the @reverseLoader attribute.
+         *
+         * <p>This attribute can set a boolean indicating that the used
+         * classloader should NOT follow the classical parent-first scheme.
+         * </p>
+         *
+         * <p>By default this is supposed to be false.</p>
+         *
+         * <p>Caution: this behaviour is contradictory to the normal way
+         * classloaders work.  Do not let your ProjectComponent use it if
+         * you are not really sure.</p>
+         *
+         * @param reverseLoader if true reverse the order of looking up a class.
+         */
+        public void setReverseLoader(boolean reverseLoader) {
+            this.reverseLoader = reverseLoader;
+        }
+
+        /**
+         * Sets the loaderRef.
+         * @param r the reference to the loader.
+         */
+        public void setLoaderRef(Reference r) {
+            this.loaderId = r.getRefId();
+        }
+
+
+        /**
+         * Finds or creates the classloader for this object.
+         * @return The class loader.
+         */
+        public ClassLoader getClassLoader() {
+            ClassLoader cl;
+            cl = ClasspathUtils.getClassLoaderForPath(
+                    getContextProject(),
+                    this.classpath,
+                    getClassLoadId(),
+                    this.reverseLoader,
+                    loaderId != null || isMagicPropertySet(getContextProject()));
+            return cl;
+        }
+
+        /**
+         * The project of the ProjectComponent we are working for.
+         */
+        private Project getContextProject() {
+            return this.component.getProject();
+        }
+
+        /**
+         * Computes the loaderId based on the configuration of the component.
+         * @return a loader identifier.
+         */
+        public String getClassLoadId() {
+            if (this.loaderId == null && this.classpathId != null) {
+                return ClasspathUtils.LOADER_ID_PREFIX + this.classpathId;
+            } else {
+                return this.loaderId;
+            }
+        }
+
+        /**
+         * Helper method obtaining a fresh instance of the class specified
+         * in the @classname and using the specified classpath.
+         *
+         * @return the fresh instantiated object.
+         */
+        public Object newInstance() {
+            ClassLoader cl = getClassLoader();
+            return ClasspathUtils.newInstance(this.className, cl);
+        }
+
+        /**
+         * The classpath.
+         * @return the classpath.
+         */
+        public Path getClasspath() {
+            return classpath;
+        }
+
+        /**
+         * Get the reverseLoader setting.
+         * @return true if looking up in reverse order.
+         */
+        public boolean isReverseLoader() {
+            return reverseLoader;
+        }
+
+        //TODO no methods yet for getClassname
+        //TODO no method for newInstance using a reverse-classloader
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/CollectionUtils.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/CollectionUtils.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/CollectionUtils.java	(revision 16139)
@@ -0,0 +1,152 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.util.Dictionary;
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+import java.util.Vector;
+
+/**
+ * A set of helper methods related to collection manipulation.
+ *
+ *
+ * @since Ant 1.5
+ */
+public class CollectionUtils {
+
+    /**
+     * Please use Vector.equals() or List.equals()
+     *
+     * @since Ant 1.5
+     * @deprecated
+     */
+    public static boolean equals(Vector v1, Vector v2) {
+        if (v1 == v2) {
+            return true;
+        }
+
+        if (v1 == null || v2 == null) {
+            return false;
+        }
+
+        return v1.equals(v2);
+    }
+
+    /**
+     * Dictionary does not have an equals.
+     * Please use  Map.equals()
+     *
+     * <p>Follows the equals contract of Java 2's Map.</p>
+     *
+     * @since Ant 1.5
+     * @deprecated
+     */
+    public static boolean equals(Dictionary d1, Dictionary d2) {
+        if (d1 == d2) {
+            return true;
+        }
+
+        if (d1 == null || d2 == null) {
+            return false;
+        }
+
+        if (d1.size() != d2.size()) {
+            return false;
+        }
+
+        Enumeration e1 = d1.keys();
+        while (e1.hasMoreElements()) {
+            Object key = e1.nextElement();
+            Object value1 = d1.get(key);
+            Object value2 = d2.get(key);
+            if (value2 == null || !value1.equals(value2)) {
+                return false;
+            }
+        }
+
+        // don't need the opposite check as the Dictionaries have the
+        // same size, so we've also covered all keys of d2 already.
+
+        return true;
+    }
+
+    /**
+     * Dictionary does not know the putAll method. Please use Map.putAll().
+     *
+     * @since Ant 1.6
+     * @deprecated
+     */
+    public static void putAll(Dictionary m1, Dictionary m2) {
+        for (Enumeration it = m2.keys(); it.hasMoreElements();) {
+            Object key = it.nextElement();
+            m1.put(key, m2.get(key));
+        }
+    }
+
+    /**
+     * @since Ant 1.6
+     */
+    public static final class EmptyEnumeration implements Enumeration {
+        public EmptyEnumeration() {
+        }
+
+        public boolean hasMoreElements() {
+            return false;
+        }
+
+        public Object nextElement() throws NoSuchElementException {
+            throw new NoSuchElementException();
+        }
+    }
+
+    /**
+     * Append one enumeration to another.
+     * Elements are evaluated lazily.
+     * @param e1 the first enumeration
+     * @param e2 the subsequent enumeration
+     * @return an enumeration representing e1 followed by e2
+     * @since Ant 1.6.3
+     */
+    public static Enumeration append(Enumeration e1, Enumeration e2) {
+        return new CompoundEnumeration(e1, e2);
+    }
+
+    private static final class CompoundEnumeration implements Enumeration {
+
+        private final Enumeration e1, e2;
+
+        public CompoundEnumeration(Enumeration e1, Enumeration e2) {
+            this.e1 = e1;
+            this.e2 = e2;
+        }
+
+        public boolean hasMoreElements() {
+            return e1.hasMoreElements() || e2.hasMoreElements();
+        }
+
+        public Object nextElement() throws NoSuchElementException {
+            if (e1.hasMoreElements()) {
+                return e1.nextElement();
+            } else {
+                return e2.nextElement();
+            }
+        }
+
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/CompositeMapper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/CompositeMapper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/CompositeMapper.java	(revision 16139)
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Iterator;
+
+/**
+ * A <CODE>ContainerMapper</CODE> that unites the results of its constituent
+ * <CODE>FileNameMapper</CODE>s into a single set of result filenames.
+ */
+public class CompositeMapper extends ContainerMapper {
+
+    //inherit doc
+    public String[] mapFileName(String sourceFileName) {
+        HashSet results = new HashSet();
+
+        FileNameMapper mapper = null;
+        for (Iterator mIter = getMappers().iterator(); mIter.hasNext();) {
+            mapper = (FileNameMapper)(mIter.next());
+            if (mapper != null) {
+                String[] mapped = mapper.mapFileName(sourceFileName);
+                if (mapped != null) {
+                    results.addAll(Arrays.asList(mapped));
+                }
+            }
+        }
+        return (results.size() == 0) ? null
+            : (String[]) results.toArray(new String[results.size()]);
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ConcatFileInputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ConcatFileInputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ConcatFileInputStream.java	(revision 16139)
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.FileInputStream;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+
+/**
+ * Special <code>InputStream</code> that will
+ * concatenate the contents of an array of files.
+ */
+public class ConcatFileInputStream extends InputStream {
+
+    private static final int EOF = -1;
+    private int currentIndex = -1;
+    private boolean eof = false;
+    private File[] file;
+    private InputStream currentStream;
+    private ProjectComponent managingPc;
+
+  /**
+   * Construct a new <code>ConcatFileInputStream</code>
+   * with the specified <code>File[]</code>.
+   * @param file   <code>File[]</code>.
+   * @throws IOException if I/O errors occur.
+   */
+    public ConcatFileInputStream(File[] file) throws IOException {
+        this.file = file;
+    }
+
+    // inherit doc
+    public void close() throws IOException {
+        closeCurrent();
+        eof = true;
+    }
+
+    // inherit doc
+    public int read() throws IOException {
+        int result = readCurrent();
+        if (result == EOF && !eof) {
+            openFile(++currentIndex);
+            result = readCurrent();
+        }
+        return result;
+    }
+
+    /**
+     * Set a managing <code>Task</code> for
+     * this <code>ConcatFileInputStream</code>.
+     * @param task   the managing <code>Task</code>.
+     */
+    public void setManagingTask(Task task) {
+        setManagingComponent(task);
+    }
+
+    /**
+     * Set a managing <code>Task</code> for
+     * this <code>ConcatFileInputStream</code>.
+     * @param task   the managing <code>Task</code>.
+     */
+    public void setManagingComponent(ProjectComponent pc) {
+        this.managingPc = pc;
+    }
+
+    /**
+     * Log a message with the specified logging level.
+     * @param message    the <code>String</code> message.
+     * @param loglevel   the <code>int</code> logging level.
+     */
+    public void log(String message, int loglevel) {
+        if (managingPc != null) {
+            managingPc.log(message, loglevel);
+        } else {
+            if (loglevel > Project.MSG_WARN) {
+                System.out.println(message);
+            } else {
+                System.err.println(message);
+            }
+        }
+    }
+
+    private int readCurrent() throws IOException {
+        return (eof || currentStream == null) ? EOF : currentStream.read();
+    }
+
+    private void openFile(int index) throws IOException {
+        closeCurrent();
+        if (file != null && index < file.length) {
+            log("Opening " + file[index], Project.MSG_VERBOSE);
+            try {
+                currentStream = new BufferedInputStream(
+                    new FileInputStream(file[index]));
+            } catch (IOException eyeOhEx) {
+                log("Failed to open " + file[index], Project.MSG_ERR);
+                throw eyeOhEx;
+            }
+        } else {
+            eof = true;
+        }
+    }
+
+    private void closeCurrent() {
+        FileUtils.close(currentStream);
+        currentStream = null;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ContainerMapper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ContainerMapper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ContainerMapper.java	(revision 16139)
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import java.util.List;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.Collections;
+import org.apache.tools.ant.types.Mapper;
+
+/**
+ * A <code>FileNameMapper</code> that contains
+ * other <CODE>FileNameMapper</CODE>s.
+ * @see FileNameMapper
+ */
+public abstract class ContainerMapper implements FileNameMapper {
+
+    private List mappers = new ArrayList();
+
+    /**
+     * Add a <code>Mapper</code>.
+     * @param mapper the <code>Mapper</code> to add.
+     */
+    public void addConfiguredMapper(Mapper mapper) {
+        add(mapper.getImplementation());
+    }
+
+    /**
+     * Add a <code>FileNameMapper</code>.
+     * @param fileNameMapper a <CODE>FileNameMapper</CODE>.
+     * @throws <CODE>IllegalArgumentException</CODE> if attempting to add this
+     *         <CODE>ContainerMapper</CODE> to itself, or if the specified
+     *         <CODE>FileNameMapper</CODE> is itself a <CODE>ContainerMapper</CODE>
+     *         that contains this <CODE>ContainerMapper</CODE>.
+     */
+    public synchronized void add(FileNameMapper fileNameMapper) {
+        if (this == fileNameMapper
+            || (fileNameMapper instanceof ContainerMapper
+            && ((ContainerMapper)fileNameMapper).contains(this))) {
+            throw new IllegalArgumentException(
+                "Circular mapper containment condition detected");
+        } else {
+            mappers.add(fileNameMapper);
+        }
+    }
+
+    /**
+     * Return <CODE>true</CODE> if this <CODE>ContainerMapper</CODE> or any of
+     * its sub-elements contains the specified <CODE>FileNameMapper</CODE>.
+     * @param fileNameMapper   the <CODE>FileNameMapper</CODE> to search for.
+     * @return <CODE>boolean</CODE>.
+     */
+    protected synchronized boolean contains(FileNameMapper fileNameMapper) {
+        boolean foundit = false;
+        for (Iterator iter = mappers.iterator(); iter.hasNext() && !foundit;) {
+            FileNameMapper next = (FileNameMapper)(iter.next());
+            foundit|= (next == fileNameMapper
+                || (next instanceof ContainerMapper
+                && ((ContainerMapper)next).contains(fileNameMapper)));
+        }
+        return foundit;
+    }
+
+    /**
+     * Get the <CODE>List</CODE> of <CODE>FileNameMapper</CODE>s.
+     * @return <CODE>List</CODE>.
+     */
+    public synchronized List getMappers() {
+        return Collections.unmodifiableList(mappers);
+    }
+
+    /**
+     * Empty implementation.
+     */
+    public void setFrom(String ignore) {
+    }
+
+    /**
+     * Empty implementation.
+     */
+    public void setTo(String ignore) {
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/DOMElementWriter.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/DOMElementWriter.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/DOMElementWriter.java	(revision 16139)
@@ -0,0 +1,336 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.w3c.dom.Text;
+
+/**
+ * Writes a DOM tree to a given Writer.
+ *
+ * <p>Utility class used by {@link org.apache.tools.ant.XmlLogger
+ * XmlLogger} and
+ * org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter
+ * XMLJUnitResultFormatter}.</p>
+ *
+ */
+public class DOMElementWriter {
+
+    private static String lSep = System.getProperty("line.separator");
+
+    /**
+     * Don't try to be too smart but at least recognize the predefined
+     * entities.
+     */
+    protected String[] knownEntities = {"gt", "amp", "lt", "apos", "quot"};
+
+
+    /**
+     * Writes a DOM tree to a stream in UTF8 encoding. Note that
+     * it prepends the &lt;?xml version='1.0' encoding='UTF-8'?&gt;.
+     * The indent number is set to 0 and a 2-space indent.
+     * @param root the root element of the DOM tree.
+     * @param out the outputstream to write to.
+     * @throws IOException if an error happens while writing to the stream.
+     */
+    public void write(Element root, OutputStream out) throws IOException {
+        Writer wri = new OutputStreamWriter(out, "UTF8");
+        wri.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
+        write(root, wri, 0, "  ");
+        wri.flush();
+    }
+
+    /**
+     * Writes a DOM tree to a stream.
+     *
+     * @param element the Root DOM element of the tree
+     * @param out where to send the output
+     * @param indent number of
+     * @param indentWith string that should be used to indent the corresponding tag.
+     * @throws IOException if an error happens while writing to the stream.
+     */
+    public void write(Element element, Writer out, int indent,
+                      String indentWith)
+        throws IOException {
+
+        openElement(element, out, indent, indentWith);
+
+        // Write child elements and text
+        boolean hasChildren = false;
+        NodeList children = element.getChildNodes();
+        for (int i = 0; i < children.getLength(); i++) {
+            Node child = children.item(i);
+
+            switch (child.getNodeType()) {
+
+            case Node.ELEMENT_NODE:
+                if (!hasChildren) {
+                    out.write(lSep);
+                    hasChildren = true;
+                }
+                write((Element) child, out, indent + 1, indentWith);
+                break;
+
+            case Node.TEXT_NODE:
+                out.write(encode(child.getNodeValue()));
+                break;
+
+            case Node.COMMENT_NODE:
+                out.write("<!--");
+                out.write(encode(child.getNodeValue()));
+                out.write("-->");
+                break;
+
+            case Node.CDATA_SECTION_NODE:
+                out.write("<![CDATA[");
+                out.write(encodedata(((Text) child).getData()));
+                out.write("]]>");
+                break;
+
+            case Node.ENTITY_REFERENCE_NODE:
+                out.write('&');
+                out.write(child.getNodeName());
+                out.write(';');
+                break;
+
+            case Node.PROCESSING_INSTRUCTION_NODE:
+                out.write("<?");
+                out.write(child.getNodeName());
+                String data = child.getNodeValue();
+                if (data != null && data.length() > 0) {
+                    out.write(' ');
+                    out.write(data);
+                }
+                out.write("?>");
+                break;
+            }
+        }
+
+        closeElement(element, out, indent, indentWith, hasChildren);
+    }
+
+    /**
+     * Writes the opening tag - including all attributes -
+     * correspondong to a DOM element.
+     *
+     * @param element the DOM element to write
+     * @param out where to send the output
+     * @param indent number of
+     * @param indentWith string that should be used to indent the
+     * corresponding tag.
+     * @throws IOException if an error happens while writing to the stream.
+     */
+    public void openElement(Element element, Writer out, int indent,
+                            String indentWith)
+        throws IOException {
+        // Write indent characters
+        for (int i = 0; i < indent; i++) {
+            out.write(indentWith);
+        }
+
+        // Write element
+        out.write("<");
+        out.write(element.getTagName());
+
+        // Write attributes
+        NamedNodeMap attrs = element.getAttributes();
+        for (int i = 0; i < attrs.getLength(); i++) {
+            Attr attr = (Attr) attrs.item(i);
+            out.write(" ");
+            out.write(attr.getName());
+            out.write("=\"");
+            out.write(encode(attr.getValue()));
+            out.write("\"");
+        }
+        out.write(">");
+    }
+
+    /**
+     * Writes a DOM tree to a stream.
+     *
+     * @param element the Root DOM element of the tree
+     * @param out where to send the output
+     * @param indent number of
+     * @param indentWith string that should be used to indent the
+     * corresponding tag.
+     * @throws IOException if an error happens while writing to the stream.
+     */
+    public void closeElement(Element element, Writer out, int indent,
+                             String indentWith, boolean hasChildren)
+        throws IOException {
+        // If we had child elements, we need to indent before we close
+        // the element, otherwise we're on the same line and don't need
+        // to indent
+        if (hasChildren) {
+            for (int i = 0; i < indent; i++) {
+                out.write(indentWith);
+            }
+        }
+
+        // Write element close
+        out.write("</");
+        out.write(element.getTagName());
+        out.write(">");
+        out.write(lSep);
+        out.flush();
+    }
+
+    /**
+     * Escape &lt;, &gt; &amp; &apos;, &quot; as their entities and
+     * drop characters that are illegal in XML documents.
+     */
+    public String encode(String value) {
+        StringBuffer sb = new StringBuffer();
+        int len = value.length();
+        for (int i = 0; i < len; i++) {
+            char c = value.charAt(i);
+            switch (c) {
+            case '<':
+                sb.append("&lt;");
+                break;
+            case '>':
+                sb.append("&gt;");
+                break;
+            case '\'':
+                sb.append("&apos;");
+                break;
+            case '\"':
+                sb.append("&quot;");
+                break;
+            case '&':
+                int nextSemi = value.indexOf(";", i);
+                if (nextSemi < 0
+                    || !isReference(value.substring(i, nextSemi + 1))) {
+                    sb.append("&amp;");
+                } else {
+                    sb.append('&');
+                }
+                break;
+            default:
+                if (isLegalCharacter(c)) {
+                    sb.append(c);
+                }
+                break;
+            }
+        }
+        return sb.substring(0);
+    }
+
+    /**
+     * Drop characters that are illegal in XML documents.
+     *
+     * <p>Also ensure that we are not including an <code>]]&gt;</code>
+     * marker by replacing that sequence with
+     * <code>&amp;#x5d;&amp;#x5d;&amp;gt;</code>.</p>
+     *
+     * <p>See XML 1.0 2.2 <a
+     * href="http://www.w3.org/TR/1998/REC-xml-19980210#charsets">http://www.w3.org/TR/1998/REC-xml-19980210#charsets</a> and
+     * 2.7 <a
+     * href="http://www.w3.org/TR/1998/REC-xml-19980210#sec-cdata-sect">http://www.w3.org/TR/1998/REC-xml-19980210#sec-cdata-sect</a>.</p>
+
+     */
+    public String encodedata(final String value) {
+        StringBuffer sb = new StringBuffer();
+        int len = value.length();
+        for (int i = 0; i < len; ++i) {
+            char c = value.charAt(i);
+            if (isLegalCharacter(c)) {
+                sb.append(c);
+            }
+        }
+
+        String result = sb.substring(0);
+        int cdEnd = result.indexOf("]]>");
+        while (cdEnd != -1) {
+            sb.setLength(cdEnd);
+            sb.append("&#x5d;&#x5d;&gt;")
+                .append(result.substring(cdEnd + 3));
+            result = sb.substring(0);
+            cdEnd = result.indexOf("]]>");
+        }
+
+        return result;
+    }
+
+    /**
+     * Is the given argument a character or entity reference?
+     */
+    public boolean isReference(String ent) {
+        if (!(ent.charAt(0) == '&') || !ent.endsWith(";")) {
+            return false;
+        }
+
+        if (ent.charAt(1) == '#') {
+            if (ent.charAt(2) == 'x') {
+                try {
+                    Integer.parseInt(ent.substring(3, ent.length() - 1), 16);
+                    return true;
+                } catch (NumberFormatException nfe) {
+                    return false;
+                }
+            } else {
+                try {
+                    Integer.parseInt(ent.substring(2, ent.length() - 1));
+                    return true;
+                } catch (NumberFormatException nfe) {
+                    return false;
+                }
+            }
+        }
+
+        String name = ent.substring(1, ent.length() - 1);
+        for (int i = 0; i < knownEntities.length; i++) {
+            if (name.equals(knownEntities[i])) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Is the given character allowed inside an XML document?
+     *
+     * <p>See XML 1.0 2.2 <a
+     * href="http://www.w3.org/TR/1998/REC-xml-19980210#charsets">
+     * http://www.w3.org/TR/1998/REC-xml-19980210#charsets</a>.</p>
+     *
+     * @since 1.10, Ant 1.5
+     */
+    public boolean isLegalCharacter(char c) {
+        if (c == 0x9 || c == 0xA || c == 0xD) {
+            return true;
+        } else if (c < 0x20) {
+            return false;
+        } else if (c <= 0xD7FF) {
+            return true;
+        } else if (c < 0xE000) {
+            return false;
+        } else if (c <= 0xFFFD) {
+            return true;
+        }
+        return false;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/DOMUtils.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/DOMUtils.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/DOMUtils.java	(revision 16139)
@@ -0,0 +1,165 @@
+/*
+ * Copyright  2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import org.w3c.dom.CDATASection;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Text;
+
+/**
+ * Some utility methods for common tasks when building DOM trees in memory.
+ *
+ * <p>In this documentation <code>&lt;a&gt;</code> means an {@link
+ * org.w3c.do.Element Element} instance with name <code>a</code>.</p>
+ *
+ * @since Ant 1.6.3
+ */
+public class DOMUtils {
+
+    /**
+     * Get a new Document instance,
+     *
+     * @since Ant 1.6.3
+     */
+    public static Document newDocument() {
+        return JAXPUtils.getDocumentBuilder().newDocument();
+    }
+
+    /**
+     * Creates a named Element and appends it to the given element,
+     * returns it.
+     *
+     * <p>This means
+     * <pre>createChildElement(&lt;a&gt;, "b")</pre>
+     * creates
+     * <pre>
+     * &lt;a&gt;
+     *   &lt;b/&gt;
+     * &lt;/a&gt;
+     * </pre>
+     * and returns <code>&lt;b&gt;</code>.</p>
+     *
+     * @param parent element that will receive the new element as child.
+     * @param name name of the new element.
+     * @return the new element.
+     *
+     * @since Ant 1.6.3
+     */
+    public static Element createChildElement(Element parent, String name) {
+        Document doc = parent.getOwnerDocument();
+        Element e = doc.createElement(name);
+        parent.appendChild(e);
+        return e;
+    }
+
+    /**
+     * Adds nested text.
+     *
+     * <p>This means
+     * <pre>appendText(&lt;a&gt;, "b")</pre>
+     * creates
+     * <pre>
+     * &lt;a&gt;b&lt;/a&gt;
+     * </pre>
+     * </p>
+     *
+     * @param parent element that will receive the new element as child.
+     * @param content text content.
+     *
+     * @since Ant 1.6.3
+     */
+    public static void appendText(Element parent, String content) {
+        Document doc = parent.getOwnerDocument();
+        Text t = doc.createTextNode(content);
+        parent.appendChild(t);
+    }
+
+    /**
+     * Adds a nested CDATA section.
+     *
+     * <p>This means
+     * <pre>appendCDATA(&lt;a&gt;, "b")</pre>
+     * creates
+     * <pre>
+     * &lt;a&gt;&lt;[!CDATA[b]]&gt;&lt;/a&gt;
+     * </pre>
+     * </p>
+     *
+     * @param parent element that will receive the new element as child.
+     * @param content text content.
+     *
+     * @since Ant 1.6.3
+     */
+    public static void appendCDATA(Element parent, String content) {
+        Document doc = parent.getOwnerDocument();
+        CDATASection c = doc.createCDATASection(content);
+        parent.appendChild(c);
+    }
+
+    /**
+     * Adds nested text in a new child element.
+     *
+     * <p>This means
+     * <pre>appendTextElement(&lt;a&gt;, "b", "c")</pre>
+     * creates
+     * <pre>
+     * &lt;a&gt;
+     *   &lt;b&gt;c&lt;/b&gt;
+     * &lt;/a&gt;
+     * </pre>
+     * </p>
+     *
+     * @param parent element that will receive the new element as child.
+     * @param name of the child element.
+     * @param content text content.
+     *
+     * @since Ant 1.6.3
+     */
+    public static void appendTextElement(Element parent, String name,
+                                         String content) {
+        Element e = createChildElement(parent, name);
+        appendText(e, content);
+    }
+
+    /**
+     * Adds a nested CDATA section in a new child element.
+     *
+     * <p>This means
+     * <pre>appendCDATAElement(&lt;a&gt;, "b", "c")</pre>
+     * creates
+     * <pre>
+     * &lt;a&gt;
+     *   &lt;b&gt;&lt;![CDATA[c]]>&lt;/b&gt;
+     * &lt;/a&gt;
+     * </pre>
+     * </pre>
+     * </p>
+     *
+     * @param parent element that will receive the new element as child.
+     * @param name of the child element.
+     * @param content text content.
+     *
+     * @since Ant 1.6.3
+     */
+    public static void appendCDATAElement(Element parent, String name,
+                                          String content) {
+        Element e = createChildElement(parent, name);
+        appendCDATA(e, content);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/DateUtils.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/DateUtils.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/DateUtils.java	(revision 16139)
@@ -0,0 +1,269 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.text.ChoiceFormat;
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * Helper methods to deal with date/time formatting with a specific
+ * defined format (<a href="http://www.w3.org/TR/NOTE-datetime">ISO8601</a>)
+ * or a plurialization correct elapsed time in minutes and seconds.
+ *
+ *
+ * @since Ant 1.5
+ *
+ */
+public final class DateUtils {
+
+    /**
+     * ISO8601-like pattern for date-time. It does not support timezone.
+     *  <tt>yyyy-MM-ddTHH:mm:ss</tt>
+     */
+    public static final String ISO8601_DATETIME_PATTERN
+            = "yyyy-MM-dd'T'HH:mm:ss";
+
+    /**
+     * ISO8601-like pattern for date. <tt>yyyy-MM-dd</tt>
+     */
+    public static final String ISO8601_DATE_PATTERN
+            = "yyyy-MM-dd";
+
+    /**
+     * ISO8601-like pattern for time.  <tt>HH:mm:ss</tt>
+     */
+    public static final String ISO8601_TIME_PATTERN
+            = "HH:mm:ss";
+
+    /**
+     * Format used for SMTP (and probably other) Date headers.
+     */
+    public static final DateFormat DATE_HEADER_FORMAT
+        = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss ", Locale.US);
+
+
+// code from Magesh moved from DefaultLogger and slightly modified
+    private static final MessageFormat MINUTE_SECONDS
+            = new MessageFormat("{0}{1}");
+
+    private static final double[] LIMITS = {0, 1, 2};
+
+    private static final String[] MINUTES_PART = {"", "1 minute ", "{0,number} minutes "};
+
+    private static final String[] SECONDS_PART = {"0 seconds", "1 second", "{1,number} seconds"};
+
+    private static final ChoiceFormat MINUTES_FORMAT =
+            new ChoiceFormat(LIMITS, MINUTES_PART);
+
+    private static final ChoiceFormat SECONDS_FORMAT =
+            new ChoiceFormat(LIMITS, SECONDS_PART);
+
+    static {
+        MINUTE_SECONDS.setFormat(0, MINUTES_FORMAT);
+        MINUTE_SECONDS.setFormat(1, SECONDS_FORMAT);
+    }
+
+    /** private constructor */
+    private DateUtils() {
+    }
+
+
+    /**
+     * Format a date/time into a specific pattern.
+     * @param date the date to format expressed in milliseconds.
+     * @param pattern the pattern to use to format the date.
+     * @return the formatted date.
+     */
+    public static String format(long date, String pattern) {
+        return format(new Date(date), pattern);
+    }
+
+
+    /**
+     * Format a date/time into a specific pattern.
+     * @param date the date to format expressed in milliseconds.
+     * @param pattern the pattern to use to format the date.
+     * @return the formatted date.
+     */
+    public static String format(Date date, String pattern) {
+        DateFormat df = createDateFormat(pattern);
+        return df.format(date);
+    }
+
+
+    /**
+     * Format an elapsed time into a plurialization correct string.
+     * It is limited only to report elapsed time in minutes and
+     * seconds and has the following behavior.
+     * <ul>
+     * <li>minutes are not displayed when 0. (ie: "45 seconds")</li>
+     * <li>seconds are always displayed in plural form (ie "0 seconds" or
+     * "10 seconds") except for 1 (ie "1 second")</li>
+     * </ul>
+     * @param millis the elapsed time to report in milliseconds.
+     * @return the formatted text in minutes/seconds.
+     */
+    public static String formatElapsedTime(long millis) {
+        long seconds = millis / 1000;
+        long minutes = seconds / 60;
+        Object[] args = {new Long(minutes), new Long(seconds % 60)};
+        return MINUTE_SECONDS.format(args);
+    }
+
+    /**
+     * return a lenient date format set to GMT time zone.
+     * @param pattern the pattern used for date/time formatting.
+     * @return the configured format for this pattern.
+     */
+    private static DateFormat createDateFormat(String pattern) {
+        SimpleDateFormat sdf = new SimpleDateFormat(pattern);
+        TimeZone gmt = TimeZone.getTimeZone("GMT");
+        sdf.setTimeZone(gmt);
+        sdf.setLenient(true);
+        return sdf;
+    }
+
+    /**
+     * Calculate the phase of the moon for a given date.
+     *
+     * <p>Code heavily influenced by hacklib.c in <a
+     * href="http://www.nethack.org/">Nethack</a></p>
+     *
+     * <p>The Algorithm:
+     *
+     * <pre>
+     * moon period = 29.53058 days ~= 30, year = 365.2422 days
+     *
+     * days moon phase advances on first day of year compared to preceding year
+     *  = 365.2422 - 12*29.53058 ~= 11
+     *
+     * years in Metonic cycle (time until same phases fall on the same days of
+     *  the month) = 18.6 ~= 19
+     *
+     * moon phase on first day of year (epact) ~= (11*(year%19) + 18) % 30
+     *  (18 as initial condition for 1900)
+     *
+     * current phase in days = first day phase + days elapsed in year
+     *
+     * 6 moons ~= 177 days
+     * 177 ~= 8 reported phases * 22
+     * + 11/22 for rounding
+     * </pre>
+     *
+     * @return The phase of the moon as a number between 0 and 7 with
+     *         0 meaning new moon and 4 meaning full moon.
+     *
+     * @since 1.2, Ant 1.5
+     */
+    public static int getPhaseOfMoon(Calendar cal) {
+        int dayOfTheYear = cal.get(Calendar.DAY_OF_YEAR);
+        int yearInMetonicCycle = ((cal.get(Calendar.YEAR) - 1900) % 19) + 1;
+        int epact = (11 * yearInMetonicCycle + 18) % 30;
+        if ((epact == 25 && yearInMetonicCycle > 11) || epact == 24) {
+            epact++;
+        }
+        return (((((dayOfTheYear + epact) * 6) + 11) % 177) / 22) & 7;
+    }
+
+    /**
+     * Returns the current Date in a format suitable for a SMTP date
+     * header.
+     *
+     * @since Ant 1.5.2
+     */
+    public static String getDateForHeader() {
+        Calendar cal = Calendar.getInstance();
+        TimeZone tz = cal.getTimeZone();
+        int offset = tz.getOffset(cal.get(Calendar.ERA),
+                                  cal.get(Calendar.YEAR),
+                                  cal.get(Calendar.MONTH),
+                                  cal.get(Calendar.DAY_OF_MONTH),
+                                  cal.get(Calendar.DAY_OF_WEEK),
+                                  cal.get(Calendar.MILLISECOND));
+        StringBuffer tzMarker = new StringBuffer(offset < 0 ? "-" : "+");
+        offset = Math.abs(offset);
+        int hours = offset / (60 * 60 * 1000);
+        int minutes = offset / (60 * 1000) - 60 * hours;
+        if (hours < 10) {
+            tzMarker.append("0");
+        }
+        tzMarker.append(hours);
+        if (minutes < 10) {
+            tzMarker.append("0");
+        }
+        tzMarker.append(minutes);
+        return DATE_HEADER_FORMAT.format(cal.getTime()) + tzMarker.toString();
+    }
+
+    /**
+     * Parse a string as a datetime using the ISO8601_DATETIME format which is
+     * <code>yyyy-MM-dd'T'HH:mm:ss</code>
+     *
+     * @param datestr string to be parsed
+     *
+     * @return a java.util.Date object as parsed by the format.
+     * @exception ParseException if the supplied string cannot be parsed by
+     * this pattern.
+     * @since Ant 1.6
+     */
+    public static Date parseIso8601DateTime(String datestr)
+        throws ParseException {
+        return new SimpleDateFormat(ISO8601_DATETIME_PATTERN).parse(datestr);
+    }
+
+    /**
+     * Parse a string as a date using the ISO8601_DATE format which is
+     * <code>yyyy-MM-dd</code>
+     *
+     * @param datestr string to be parsed
+     *
+     * @return a java.util.Date object as parsed by the format.
+     * @exception ParseException if the supplied string cannot be parsed by
+     * this pattern.
+     * @since Ant 1.6
+     */
+    public static Date parseIso8601Date(String datestr) throws ParseException {
+        return new SimpleDateFormat(ISO8601_DATE_PATTERN).parse(datestr);
+    }
+
+    /**
+     * Parse a string as a date using the either the ISO8601_DATETIME
+     * or ISO8601_DATE formats.
+     *
+     * @param datestr string to be parsed
+     *
+     * @return a java.util.Date object as parsed by the formats.
+     * @exception ParseException if the supplied string cannot be parsed by
+     * either of these patterns.
+     * @since Ant 1.6
+     */
+    public static Date parseIso8601DateTimeOrDate(String datestr)
+        throws ParseException {
+        try {
+            return parseIso8601DateTime(datestr);
+        } catch (ParseException px) {
+            return parseIso8601Date(datestr);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/FileNameMapper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/FileNameMapper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/FileNameMapper.java	(revision 16139)
@@ -0,0 +1,55 @@
+/*
+ * Copyright  2000,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+/**
+ * Interface to be used by SourceFileScanner.
+ *
+ * <p>Used to find the name of the target file(s) corresponding to a
+ * source file.</p>
+ *
+ * <p>The rule by which the file names are transformed is specified
+ * via the setFrom and setTo methods. The exact meaning of these is
+ * implementation dependent.</p>
+ *
+ */
+public interface FileNameMapper {
+
+    /**
+     * Sets the from part of the transformation rule.
+     */
+    void setFrom(String from);
+
+    /**
+     * Sets the to part of the transformation rule.
+     */
+    void setTo(String to);
+
+    /**
+     * Returns an array containing the target filename(s) for the
+     * given source file.
+     *
+     * <p>if the given rule doesn't apply to the source file,
+     * implementation must return null. SourceFileScanner will then
+     * omit the source file in question.</p>
+     *
+     * @param sourceFileName the name of the source file relative to
+     *                       some given basedirectory.
+     */
+    String[] mapFileName(String sourceFileName);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/FileUtils.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/FileUtils.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/FileUtils.java	(revision 16139)
@@ -0,0 +1,1476 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.Reader;
+import java.io.Writer;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.CharacterIterator;
+import java.text.DecimalFormat;
+import java.text.StringCharacterIterator;
+import java.util.Random;
+import java.util.Stack;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.filters.util.ChainReaderHelper;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.FilterSetCollection;
+import org.apache.tools.ant.launch.Locator;
+
+/**
+ * This class also encapsulates methods which allow Files to be
+ * referred to using abstract path names which are translated to native
+ * system file paths at runtime as well as copying files or setting
+ * their last modification time.
+ *
+ */
+
+public class FileUtils {
+
+    private static final FileUtils PRIMARY_INSTANCE = new FileUtils();
+
+    //get some non-crypto-grade randomness from various places.
+    private static Random rand = new Random(System.currentTimeMillis()
+            + Runtime.getRuntime().freeMemory());
+
+    private static boolean onNetWare = Os.isFamily("netware");
+    private static boolean onDos = Os.isFamily("dos");
+
+    private static final int BUF_SIZE = 8192;
+
+    // for toURI
+    private static boolean[] isSpecial = new boolean[256];
+    private static char[] escapedChar1 = new char[256];
+    private static char[] escapedChar2 = new char[256];
+
+    /**
+     * The granularity of timestamps under FAT.
+     */
+    public static final long FAT_FILE_TIMESTAMP_GRANULARITY = 2000;
+
+    /**
+     * The granularity of timestamps under Unix.
+     */
+    public static final long UNIX_FILE_TIMESTAMP_GRANULARITY = 1000;
+
+
+    // stolen from FilePathToURI of the Xerces-J team
+    static {
+        for (int i = 0; i <= 0x20; i++) {
+            isSpecial[i] = true;
+            escapedChar1[i] = Character.forDigit(i >> 4, 16);
+            escapedChar2[i] = Character.forDigit(i & 0xf, 16);
+        }
+        isSpecial[0x7f] = true;
+        escapedChar1[0x7f] = '7';
+        escapedChar2[0x7f] = 'F';
+        char[] escChs = {'<', '>', '#', '%', '"', '{', '}',
+                         '|', '\\', '^', '~', '[', ']', '`'};
+        int len = escChs.length;
+        char ch;
+        for (int i = 0; i < len; i++) {
+            ch = escChs[i];
+            isSpecial[ch] = true;
+            escapedChar1[ch] = Character.forDigit(ch >> 4, 16);
+            escapedChar2[ch] = Character.forDigit(ch & 0xf, 16);
+        }
+    }
+
+    /**
+     * Factory method.
+     *
+     * @return a new instance of FileUtils.
+     */
+    public static FileUtils newFileUtils() {
+        return new FileUtils();
+    }
+
+    /**
+     * Method to retrieve The FileUtils, which is shared by all users of this
+     * method.
+     * @return an instance of FileUtils.
+     * @since Ant 1.6.3
+     */
+    public static FileUtils getFileUtils() {
+        return PRIMARY_INSTANCE;
+    }
+
+    /**
+     * Empty constructor.
+     */
+    protected FileUtils() {
+    }
+
+    /**
+     * Get the URL for a file taking into account # characters.
+     *
+     * @param file the file whose URL representation is required.
+     * @return The FileURL value.
+     * @throws MalformedURLException if the URL representation cannot be
+     *      formed.
+     */
+    public URL getFileURL(File file) throws MalformedURLException {
+        return new URL(toURI(file.getAbsolutePath()));
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a destination.
+     * No filtering is performed.
+     *
+     * @param sourceFile Name of file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile Name of file to copy to.
+     *                 Must not be <code>null</code>.
+     *
+     * @throws IOException if the copying fails.
+     */
+    public void copyFile(String sourceFile, String destFile)
+        throws IOException {
+        copyFile(new File(sourceFile), new File(destFile), null, false, false);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a destination
+     * specifying if token filtering must be used.
+     *
+     * @param sourceFile Name of file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile Name of file to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filters the collection of filters to apply to this copy.
+     *
+     * @throws IOException if the copying fails.
+     */
+    public void copyFile(String sourceFile, String destFile,
+                         FilterSetCollection filters)
+        throws IOException {
+        copyFile(new File(sourceFile), new File(destFile), filters,
+                 false, false);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a
+     * destination specifying if token filtering must be used and if
+     * source files may overwrite newer destination files.
+     *
+     * @param sourceFile Name of file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile Name of file to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filters the collection of filters to apply to this copy.
+     * @param overwrite Whether or not the destination file should be
+     *                  overwritten if it already exists.
+     *
+     * @throws IOException if the copying fails.
+     */
+    public void copyFile(String sourceFile, String destFile, FilterSetCollection filters,
+                         boolean overwrite) throws IOException {
+        copyFile(new File(sourceFile), new File(destFile), filters,
+                 overwrite, false);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a
+     * destination specifying if token filtering must be used, if
+     * source files may overwrite newer destination files and the
+     * last modified time of <code>destFile</code> file should be made equal
+     * to the last modified time of <code>sourceFile</code>.
+     *
+     * @param sourceFile Name of file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile Name of file to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filters the collection of filters to apply to this copy.
+     * @param overwrite Whether or not the destination file should be
+     *                  overwritten if it already exists.
+     * @param preserveLastModified Whether or not the last modified time of
+     *                             the resulting file should be set to that
+     *                             of the source file.
+     *
+     * @throws IOException if the copying fails.
+     */
+    public void copyFile(String sourceFile, String destFile, FilterSetCollection filters,
+                         boolean overwrite, boolean preserveLastModified)
+        throws IOException {
+        copyFile(new File(sourceFile), new File(destFile), filters,
+                 overwrite, preserveLastModified);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a
+     * destination specifying if token filtering must be used, if
+     * source files may overwrite newer destination files and the
+     * last modified time of <code>destFile</code> file should be made equal
+     * to the last modified time of <code>sourceFile</code>.
+     *
+     * @param sourceFile Name of file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile Name of file to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filters the collection of filters to apply to this copy.
+     * @param overwrite Whether or not the destination file should be
+     *                  overwritten if it already exists.
+     * @param preserveLastModified Whether or not the last modified time of
+     *                             the resulting file should be set to that
+     *                             of the source file.
+     * @param encoding the encoding used to read and write the files.
+     *
+     * @throws IOException if the copying fails.
+     *
+     * @since Ant 1.5
+     */
+    public void copyFile(String sourceFile, String destFile,
+                         FilterSetCollection filters, boolean overwrite,
+                         boolean preserveLastModified, String encoding)
+        throws IOException {
+        copyFile(new File(sourceFile), new File(destFile), filters,
+                 overwrite, preserveLastModified, encoding);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a
+     * destination specifying if token filtering must be used, if
+     * filter chains must be used, if source files may overwrite
+     * newer destination files and the last modified time of
+     * <code>destFile</code> file should be made equal
+     * to the last modified time of <code>sourceFile</code>.
+     *
+     * @param sourceFile Name of file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile Name of file to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filters the collection of filters to apply to this copy.
+     * @param filterChains filterChains to apply during the copy.
+     * @param overwrite Whether or not the destination file should be
+     *                  overwritten if it already exists.
+     * @param preserveLastModified Whether or not the last modified time of
+     *                             the resulting file should be set to that
+     *                             of the source file.
+     * @param encoding the encoding used to read and write the files.
+     * @param project the project instance.
+     *
+     * @throws IOException if the copying fails.
+     *
+     * @since Ant 1.5
+     */
+    public void copyFile(String sourceFile, String destFile,
+                         FilterSetCollection filters, Vector filterChains,
+                         boolean overwrite, boolean preserveLastModified,
+                         String encoding, Project project)
+        throws IOException {
+        copyFile(new File(sourceFile), new File(destFile), filters,
+                 filterChains, overwrite, preserveLastModified,
+                 encoding, project);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a
+     * destination specifying if token filtering must be used, if
+     * filter chains must be used, if source files may overwrite
+     * newer destination files and the last modified time of
+     * <code>destFile</code> file should be made equal
+     * to the last modified time of <code>sourceFile</code>.
+     *
+     * @param sourceFile Name of file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile Name of file to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filters the collection of filters to apply to this copy.
+     * @param filterChains filterChains to apply during the copy.
+     * @param overwrite Whether or not the destination file should be
+     *                  overwritten if it already exists.
+     * @param preserveLastModified Whether or not the last modified time of
+     *                             the resulting file should be set to that
+     *                             of the source file.
+     * @param inputEncoding the encoding used to read the files.
+     * @param outputEncoding the encoding used to write the files.
+     * @param project the project instance.
+     *
+     * @throws IOException if the copying fails.
+     *
+     * @since Ant 1.6
+     */
+    public void copyFile(String sourceFile, String destFile,
+                         FilterSetCollection filters, Vector filterChains,
+                         boolean overwrite, boolean preserveLastModified,
+                         String inputEncoding, String outputEncoding,
+                         Project project)
+        throws IOException {
+        copyFile(new File(sourceFile), new File(destFile), filters,
+                 filterChains, overwrite, preserveLastModified,
+                 inputEncoding, outputEncoding, project);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a destination.
+     * No filtering is performed.
+     *
+     * @param sourceFile the file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile the file to copy to.
+     *                 Must not be <code>null</code>.
+     *
+     * @throws IOException if the copying fails.
+     */
+    public void copyFile(File sourceFile, File destFile) throws IOException {
+        copyFile(sourceFile, destFile, null, false, false);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a destination
+     * specifying if token filtering must be used.
+     *
+     * @param sourceFile the file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile the file to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filters the collection of filters to apply to this copy.
+     *
+     * @throws IOException if the copying fails.
+     */
+    public void copyFile(File sourceFile, File destFile, FilterSetCollection filters)
+        throws IOException {
+        copyFile(sourceFile, destFile, filters, false, false);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a
+     * destination specifying if token filtering must be used and if
+     * source files may overwrite newer destination files.
+     *
+     * @param sourceFile the file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile the file to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filters the collection of filters to apply to this copy.
+     * @param overwrite Whether or not the destination file should be
+     *                  overwritten if it already exists.
+     *
+     * @throws IOException if the copying fails.
+     */
+    public void copyFile(File sourceFile, File destFile, FilterSetCollection filters,
+                         boolean overwrite) throws IOException {
+        copyFile(sourceFile, destFile, filters, overwrite, false);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a
+     * destination specifying if token filtering must be used, if
+     * source files may overwrite newer destination files and the
+     * last modified time of <code>destFile</code> file should be made equal
+     * to the last modified time of <code>sourceFile</code>.
+     *
+     * @param sourceFile the file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile the file to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filters the collection of filters to apply to this copy.
+     * @param overwrite Whether or not the destination file should be
+     *                  overwritten if it already exists.
+     * @param preserveLastModified Whether or not the last modified time of
+     *                             the resulting file should be set to that
+     *                             of the source file.
+     *
+     * @throws IOException if the copying fails.
+     */
+    public void copyFile(File sourceFile, File destFile, FilterSetCollection filters,
+                         boolean overwrite, boolean preserveLastModified)
+        throws IOException {
+        copyFile(sourceFile, destFile, filters, overwrite,
+                 preserveLastModified, null);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a
+     * destination specifying if token filtering must be used, if
+     * source files may overwrite newer destination files, the last
+     * modified time of <code>destFile</code> file should be made
+     * equal to the last modified time of <code>sourceFile</code> and
+     * which character encoding to assume.
+     *
+     * @param sourceFile the file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile the file to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filters the collection of filters to apply to this copy.
+     * @param overwrite Whether or not the destination file should be
+     *                  overwritten if it already exists.
+     * @param preserveLastModified Whether or not the last modified time of
+     *                             the resulting file should be set to that
+     *                             of the source file.
+     * @param encoding the encoding used to read and write the files.
+     *
+     * @throws IOException if the copying fails.
+     *
+     * @since Ant 1.5
+     */
+    public void copyFile(File sourceFile, File destFile,
+                         FilterSetCollection filters, boolean overwrite,
+                         boolean preserveLastModified, String encoding)
+        throws IOException {
+        copyFile(sourceFile, destFile, filters, null, overwrite,
+                 preserveLastModified, encoding, null);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a
+     * destination specifying if token filtering must be used, if
+     * filter chains must be used, if source files may overwrite
+     * newer destination files and the last modified time of
+     * <code>destFile</code> file should be made equal
+     * to the last modified time of <code>sourceFile</code>.
+     *
+     * @param sourceFile the file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile the file to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filters the collection of filters to apply to this copy.
+     * @param filterChains filterChains to apply during the copy.
+     * @param overwrite Whether or not the destination file should be
+     *                  overwritten if it already exists.
+     * @param preserveLastModified Whether or not the last modified time of
+     *                             the resulting file should be set to that
+     *                             of the source file.
+     * @param encoding the encoding used to read and write the files.
+     * @param project the project instance.
+     *
+     * @throws IOException if the copying fails.
+     *
+     * @since Ant 1.5
+     */
+    public void copyFile(File sourceFile, File destFile,
+                         FilterSetCollection filters, Vector filterChains,
+                         boolean overwrite, boolean preserveLastModified,
+                         String encoding, Project project)
+        throws IOException {
+        copyFile(sourceFile, destFile, filters, filterChains,
+                 overwrite, preserveLastModified, encoding, encoding, project);
+    }
+
+    /**
+     * Convenience method to copy a file from a source to a
+     * destination specifying if token filtering must be used, if
+     * filter chains must be used, if source files may overwrite
+     * newer destination files and the last modified time of
+     * <code>destFile</code> file should be made equal
+     * to the last modified time of <code>sourceFile</code>.
+     *
+     * @param sourceFile the file to copy from.
+     *                   Must not be <code>null</code>.
+     * @param destFile the file to copy to.
+     *                 Must not be <code>null</code>.
+     * @param filters the collection of filters to apply to this copy.
+     * @param filterChains filterChains to apply during the copy.
+     * @param overwrite Whether or not the destination file should be
+     *                  overwritten if it already exists.
+     * @param preserveLastModified Whether or not the last modified time of
+     *                             the resulting file should be set to that
+     *                             of the source file.
+     * @param inputEncoding the encoding used to read the files.
+     * @param outputEncoding the encoding used to write the files.
+     * @param project the project instance.
+     *
+     *
+     * @throws IOException if the copying fails.
+     *
+     * @since Ant 1.6
+     */
+    public void copyFile(File sourceFile, File destFile,
+                         FilterSetCollection filters, Vector filterChains,
+                         boolean overwrite, boolean preserveLastModified,
+                         String inputEncoding, String outputEncoding,
+                         Project project)
+        throws IOException {
+
+        if (overwrite || !destFile.exists()
+            || destFile.lastModified() < sourceFile.lastModified()) {
+
+            if (destFile.exists() && destFile.isFile()) {
+                destFile.delete();
+            }
+            // ensure that parent dir of dest file exists!
+            // not using getParentFile method to stay 1.1 compat
+            File parent = destFile.getParentFile();
+            if (parent != null && !parent.exists()) {
+                parent.mkdirs();
+            }
+            final boolean filterSetsAvailable = (filters != null
+                                                 && filters.hasFilters());
+            final boolean filterChainsAvailable = (filterChains != null
+                                                   && filterChains.size() > 0);
+            if (filterSetsAvailable) {
+                BufferedReader in = null;
+                BufferedWriter out = null;
+                try {
+                    if (inputEncoding == null) {
+                        in = new BufferedReader(new FileReader(sourceFile));
+                    } else {
+                        InputStreamReader isr
+                            = new InputStreamReader(new FileInputStream(sourceFile),
+                                                    inputEncoding);
+                        in = new BufferedReader(isr);
+                    }
+                    if (outputEncoding == null) {
+                        out = new BufferedWriter(new FileWriter(destFile));
+                    } else {
+                        OutputStreamWriter osw
+                            = new OutputStreamWriter(new FileOutputStream(destFile),
+                                                     outputEncoding);
+                        out = new BufferedWriter(osw);
+                    }
+                    if (filterChainsAvailable) {
+                        ChainReaderHelper crh = new ChainReaderHelper();
+                        crh.setBufferSize(BUF_SIZE);
+                        crh.setPrimaryReader(in);
+                        crh.setFilterChains(filterChains);
+                        crh.setProject(project);
+                        Reader rdr = crh.getAssembledReader();
+                        in = new BufferedReader(rdr);
+                    }
+                    LineTokenizer lineTokenizer = new LineTokenizer();
+                    lineTokenizer.setIncludeDelims(true);
+                    String newline = null;
+                    String line = lineTokenizer.getToken(in);
+                    while (line != null) {
+                        if (line.length() == 0) {
+                            // this should not happen, because the lines are
+                            // returned with the end of line delimiter
+                            out.newLine();
+                        } else {
+                            newline = filters.replaceTokens(line);
+                            out.write(newline);
+                        }
+                        line = lineTokenizer.getToken(in);
+                    }
+                } finally {
+                    close(out);
+                    close(in);
+                }
+            } else if (filterChainsAvailable
+                       || (inputEncoding != null
+                           && !inputEncoding.equals(outputEncoding))
+                       || (inputEncoding == null && outputEncoding != null)) {
+                BufferedReader in = null;
+                BufferedWriter out = null;
+                 try {
+                     if (inputEncoding == null) {
+                         in = new BufferedReader(new FileReader(sourceFile));
+                     } else {
+                         in =
+                             new BufferedReader(
+                                 new InputStreamReader(
+                                     new FileInputStream(sourceFile),
+                                     inputEncoding));
+                     }
+                     if (outputEncoding == null) {
+                         out = new BufferedWriter(new FileWriter(destFile));
+                     } else {
+                         out =
+                             new BufferedWriter(
+                                 new OutputStreamWriter(
+                                     new FileOutputStream(destFile),
+                                     outputEncoding));
+                     }
+                     if (filterChainsAvailable) {
+                         ChainReaderHelper crh = new ChainReaderHelper();
+                         crh.setBufferSize(BUF_SIZE);
+                         crh.setPrimaryReader(in);
+                         crh.setFilterChains(filterChains);
+                         crh.setProject(project);
+                         Reader rdr = crh.getAssembledReader();
+                         in = new BufferedReader(rdr);
+                     }
+                     char[] buffer = new char[BUF_SIZE];
+                     while (true) {
+                         int nRead = in.read(buffer, 0, buffer.length);
+                         if (nRead == -1) {
+                             break;
+                         }
+                         out.write(buffer, 0, nRead);
+                      }
+                 } finally {
+                     close(out);
+                     close(in);
+                 }
+            } else {
+                FileInputStream in = null;
+                FileOutputStream out = null;
+                try {
+                    in = new FileInputStream(sourceFile);
+                    out = new FileOutputStream(destFile);
+
+                    byte[] buffer = new byte[BUF_SIZE];
+                    int count = 0;
+                    do {
+                        out.write(buffer, 0, count);
+                        count = in.read(buffer, 0, buffer.length);
+                    } while (count != -1);
+                } finally {
+                    close(out);
+                    close(in);
+                }
+            }
+            if (preserveLastModified) {
+                setFileLastModified(destFile, sourceFile.lastModified());
+            }
+        }
+    }
+
+    /**
+     * Calls File.setLastModified(long time). Originally written to
+     * to dynamically bind to that call on Java1.2+.
+     *
+     * @param file the file whose modified time is to be set
+     * @param time the time to which the last modified time is to be set.
+     *             if this is -1, the current time is used.
+     */
+    public void setFileLastModified(File file, long time) {
+        file.setLastModified((time < 0) ? System.currentTimeMillis() : time);
+    }
+
+    /**
+     * Interpret the filename as a file relative to the given file
+     * unless the filename already represents an absolute filename.
+     *
+     * @param file the "reference" file for relative paths. This
+     * instance must be an absolute file and must not contain
+     * &quot;./&quot; or &quot;../&quot; sequences (same for \ instead
+     * of /).  If it is null, this call is equivalent to
+     * <code>new java.io.File(filename)</code>.
+     *
+     * @param filename a file name.
+     *
+     * @return an absolute file that doesn't contain &quot;./&quot; or
+     * &quot;../&quot; sequences and uses the correct separator for
+     * the current platform.
+     */
+    public File resolveFile(File file, String filename) {
+        filename = filename.replace('/', File.separatorChar)
+            .replace('\\', File.separatorChar);
+
+        // deal with absolute files
+        if (isAbsolutePath(filename)) {
+            return normalize(filename);
+        }
+        if (file == null) {
+            return new File(filename);
+        }
+        File helpFile = new File(file.getAbsolutePath());
+        StringTokenizer tok = new StringTokenizer(filename, File.separator);
+        while (tok.hasMoreTokens()) {
+            String part = tok.nextToken();
+            if (part.equals("..")) {
+                helpFile = helpFile.getParentFile();
+                if (helpFile == null) {
+                    String msg = "The file or path you specified ("
+                        + filename + ") is invalid relative to "
+                        + file.getPath();
+                    throw new BuildException(msg);
+                }
+            } else if (part.equals(".")) {
+                // Do nothing here
+            } else {
+                helpFile = new File(helpFile, part);
+            }
+        }
+        return new File(helpFile.getAbsolutePath());
+    }
+
+    /**
+     * Verifies that the specified filename represents an absolute path.
+     * @param filename the filename to be checked.
+     * @return true if the filename represents an absolute path.
+     */
+    public static boolean isAbsolutePath(String filename) {
+        if (filename.startsWith(File.separator)) {
+            // common for all os
+            return true;
+        }
+        if (onDos && filename.length() >= 2
+            && Character.isLetter(filename.charAt(0))
+            && filename.charAt(1) == ':') {
+            // Actually on windows the : must be followed by a \ for
+            // the path to be absolute, else the path is relative
+            // to the current working directory on that drive.
+            // (Every drive may have another current working directory)
+            return true;
+        }
+        return (onNetWare && filename.indexOf(":") > -1);
+    }
+
+    /**
+     * &quot;Normalize&quot; the given absolute path.
+     *
+     * <p>This includes:
+     * <ul>
+     *   <li>Uppercase the drive letter if there is one.</li>
+     *   <li>Remove redundant slashes after the drive spec.</li>
+     *   <li>Resolve all ./, .\, ../ and ..\ sequences.</li>
+     *   <li>DOS style paths that start with a drive letter will have
+     *     \ as the separator.</li>
+     * </ul>
+     * Unlike <code>File#getCanonicalPath()</code> this method
+     * specifically does not resolve symbolic links.
+     *
+     * @param path the path to be normalized.
+     * @return the normalized version of the path.
+     *
+     * @throws java.lang.NullPointerException if the file path is
+     * equal to null.
+     */
+    public File normalize(String path) {
+        String orig = path;
+
+        path = path.replace('/', File.separatorChar)
+            .replace('\\', File.separatorChar);
+
+        // make sure we are dealing with an absolute path
+        int colon = path.indexOf(":");
+
+        if (!isAbsolutePath(path)) {
+            String msg = path + " is not an absolute path";
+            throw new BuildException(msg);
+        }
+        boolean dosWithDrive = false;
+        String root = null;
+        // Eliminate consecutive slashes after the drive spec
+        if ((onDos && path.length() >= 2
+                && Character.isLetter(path.charAt(0))
+                && path.charAt(1) == ':')
+            || (onNetWare && colon > -1)) {
+
+            dosWithDrive = true;
+
+            char[] ca = path.replace('/', '\\').toCharArray();
+            StringBuffer sbRoot = new StringBuffer();
+            for (int i = 0; i < colon; i++) {
+                sbRoot.append(Character.toUpperCase(ca[i]));
+            }
+            sbRoot.append(':');
+            if (colon + 1 < path.length()) {
+                sbRoot.append(File.separatorChar);
+            }
+            root = sbRoot.toString();
+
+            // Eliminate consecutive slashes after the drive spec
+            StringBuffer sbPath = new StringBuffer();
+            for (int i = colon + 1; i < ca.length; i++) {
+                if ((ca[i] != '\\')
+                    || (ca[i] == '\\' && ca[i - 1] != '\\')) {
+                    sbPath.append(ca[i]);
+                }
+            }
+            path = sbPath.toString().replace('\\', File.separatorChar);
+        } else {
+            if (path.length() == 1) {
+                root = File.separator;
+                path = "";
+            } else if (path.charAt(1) == File.separatorChar) {
+                // UNC drive
+                root = File.separator + File.separator;
+                path = path.substring(2);
+            } else {
+                root = File.separator;
+                path = path.substring(1);
+            }
+        }
+        Stack s = new Stack();
+        s.push(root);
+        StringTokenizer tok = new StringTokenizer(path, File.separator);
+        while (tok.hasMoreTokens()) {
+            String thisToken = tok.nextToken();
+            if (".".equals(thisToken)) {
+                continue;
+            } else if ("..".equals(thisToken)) {
+                if (s.size() < 2) {
+                    throw new BuildException("Cannot resolve path " + orig);
+                } else {
+                    s.pop();
+                }
+            } else { // plain component
+                s.push(thisToken);
+            }
+        }
+        StringBuffer sb = new StringBuffer();
+        for (int i = 0; i < s.size(); i++) {
+            if (i > 1) {
+                // not before the filesystem root and not after it, since root
+                // already contains one
+                sb.append(File.separatorChar);
+            }
+            sb.append(s.elementAt(i));
+        }
+        path = sb.toString();
+        if (dosWithDrive) {
+            path = path.replace('/', '\\');
+        }
+        return new File(path);
+    }
+
+    /**
+     * Returns a VMS String representation of a <code>File</code> object.
+     * This is useful since the JVM by default internally converts VMS paths
+     * to Unix style.
+     * The returned String is always an absolute path.
+     *
+     * @param f The <code>File</code> to get the VMS path for.
+     * @return The absolute VMS path to <code>f</code>.
+     */
+    public String toVMSPath(File f) {
+        // format: "DEVICE:[DIR.SUBDIR]FILE"
+        String osPath;
+        String path = normalize(f.getAbsolutePath()).getPath();
+        String name = f.getName();
+        boolean isAbsolute = path.charAt(0) == File.separatorChar;
+        // treat directories specified using .DIR syntax as files
+        boolean isDirectory = f.isDirectory()
+            && !name.regionMatches(true, name.length() - 4, ".DIR", 0, 4);
+
+        String device = null;
+        StringBuffer directory = null;
+        String file = null;
+
+        int index = 0;
+
+        if (isAbsolute) {
+            index = path.indexOf(File.separatorChar, 1);
+            if (index == -1) {
+                return path.substring(1) + ":[000000]";
+            } else {
+                device = path.substring(1, index++);
+            }
+        }
+        if (isDirectory) {
+            directory = new StringBuffer(path.substring(index).
+                                         replace(File.separatorChar, '.'));
+        } else {
+            int dirEnd =
+                path.lastIndexOf(File.separatorChar, path.length());
+            if (dirEnd == -1 || dirEnd < index) {
+                file = path.substring(index);
+            } else {
+                directory = new StringBuffer(path.substring(index, dirEnd).
+                                             replace(File.separatorChar, '.'));
+                index = dirEnd + 1;
+                if (path.length() > index) {
+                    file = path.substring(index);
+                }
+            }
+        }
+        if (!isAbsolute && directory != null) {
+            directory.insert(0, '.');
+        }
+        osPath = ((device != null) ? device + ":" : "")
+            + ((directory != null) ? "[" + directory + "]" : "")
+            + ((file != null) ? file : "");
+        return osPath;
+    }
+
+    /**
+     * Create a temporary file in a given directory.
+     *
+     * <p>The file denoted by the returned abstract pathname did not
+     * exist before this method was invoked, any subsequent invocation
+     * of this method will yield a different file name.</p>
+     * <p>
+     * The filename is prefixNNNNNsuffix where NNNN is a random number.
+     * </p>
+     * <p>This method is different from File.createTempFile() of JDK 1.2
+     * as it doesn't create the file itself.  It uses the location pointed
+     * to by java.io.tmpdir when the parentDir attribute is null.</p>
+     *
+     * @param prefix prefix before the random number.
+     * @param suffix file extension; include the '.'.
+     * @param parentDir Directory to create the temporary file in;
+     * java.io.tmpdir used if not specified.
+     *
+     * @return a File reference to the new temporary file.
+     * @since Ant 1.5
+     */
+    public File createTempFile(String prefix, String suffix, File parentDir) {
+        File result = null;
+        String parent = (parentDir == null)
+            ? System.getProperty("java.io.tmpdir")
+            : parentDir.getPath();
+
+        DecimalFormat fmt = new DecimalFormat("#####");
+        synchronized (rand) {
+            do {
+                result = new File(parent,
+                                  prefix + fmt.format(Math.abs(rand.nextInt()))
+                                  + suffix);
+            } while (result.exists());
+        }
+        return result;
+    }
+
+    /**
+     * Compares the contents of two files.
+     *
+     * @param f1 the file whose content is to be compared.
+     * @param f2 the other file whose content is to be compared.
+     *
+     * @return true if the content of the files is the same.
+     *
+     * @throws IOException if the files cannot be read.
+     */
+    public boolean contentEquals(File f1, File f2) throws IOException {
+        return contentEquals(f1, f2, false);
+    }
+
+    /**
+     * Compares the contents of two files.
+     *
+     * @param f1 the file whose content is to be compared.
+     * @param f2 the other file whose content is to be compared.
+     * @param textfile true if the file is to be treated as a text file and
+     *        differences in kind of line break are to be ignored.
+     *
+     * @return true if the content of the files is the same.
+     *
+     * @throws IOException if the files cannot be read.
+     * @since Ant 1.6.3
+     */
+    public boolean contentEquals(File f1, File f2, boolean textfile) throws IOException {
+        if (f1.exists() != f2.exists()) {
+            return false;
+        }
+        if (!f1.exists()) {
+            // two not existing files are equal
+            return true;
+        }
+        // should the following two be switched?  If f1 and f2 refer to the same file,
+        // isn't their content equal regardless of whether that file is a directory?
+        if (f1.isDirectory() || f2.isDirectory()) {
+            // don't want to compare directory contents for now
+            return false;
+        }
+        if (fileNameEquals(f1, f2)) {
+            // same filename => true
+            return true;
+        }
+        return textfile ? textEquals(f1, f2) : binaryEquals(f1, f2);
+    }
+
+    /**
+     * Binary compares the contents of two files.
+     * <p>
+     * simple but sub-optimal comparision algorithm. written for working
+     * rather than fast. Better would be a block read into buffers followed
+     * by long comparisions apart from the final 1-7 bytes.
+     * </p>
+     *
+     * @param f1 the file whose content is to be compared.
+     * @param f2 the other file whose content is to be compared.
+     * @return true if the content of the files is the same.
+     * @throws IOException if the files cannot be read.
+     */
+    private boolean binaryEquals(File f1, File f2) throws IOException {
+        if (f1.length() != f2.length()) {
+            // different size =>false
+            return false;
+        }
+
+        InputStream in1 = null;
+        InputStream in2 = null;
+        try {
+            in1 = new BufferedInputStream(new FileInputStream(f1));
+            in2 = new BufferedInputStream(new FileInputStream(f2));
+
+            int expectedByte = in1.read();
+            while (expectedByte != -1) {
+                if (expectedByte != in2.read()) {
+                    return false;
+                }
+                expectedByte = in1.read();
+            }
+            if (in2.read() != -1) {
+                return false;
+            }
+            return true;
+        } finally {
+            close(in1);
+            close(in2);
+        }
+    }
+
+    /**
+     * Text compares the contents of two files.
+     *
+     * Ignores different kinds of line endings.
+     *
+     * @param f1 the file whose content is to be compared.
+     * @param f2 the other file whose content is to be compared.
+     * @return true if the content of the files is the same.
+     * @throws IOException if the files cannot be read.
+     */
+    private boolean textEquals(File f1, File f2) throws IOException {
+        BufferedReader in1 = null;
+        BufferedReader in2 = null;
+        try {
+            in1 = new BufferedReader(new FileReader(f1));
+            in2 = new BufferedReader(new FileReader(f2));
+
+            String expected = in1.readLine();
+            while (expected != null) {
+                if (!expected.equals(in2.readLine())) {
+                    return false;
+                }
+                expected = in1.readLine();
+            }
+            if (in2.readLine() != null) {
+                return false;
+            }
+            return true;
+        } finally {
+            close(in1);
+            close(in2);
+        }
+    }
+
+    /**
+     * This was originally an emulation of {@link File#getParentFile} for JDK 1.1,
+     * but it is now implemented using that method (Ant 1.6.3 onwards).
+     * @param f the file whose parent is required.
+     * @return the given file's parent, or null if the file does not have a
+     *         parent.
+     * @since 1.10
+     */
+    public File getParentFile(File f) {
+        return (f == null) ? null : f.getParentFile();
+    }
+
+    /**
+     * Read from reader till EOF.
+     * @param rdr the reader from which to read.
+     * @return the contents read out of the given reader.
+     *
+     * @throws IOException if the contents could not be read out from the
+     *         reader.
+     */
+    public static final String readFully(Reader rdr) throws IOException {
+        return readFully(rdr, BUF_SIZE);
+    }
+
+    /**
+     * Read from reader till EOF.
+     *
+     * @param rdr the reader from which to read.
+     * @param bufferSize the buffer size to use when reading.
+     *
+     * @return the contents read out of the given reader.
+     *
+     * @throws IOException if the contents could not be read out from the
+     *         reader.
+     */
+    public static final String readFully(Reader rdr, int bufferSize)
+        throws IOException {
+        if (bufferSize <= 0) {
+            throw new IllegalArgumentException("Buffer size must be greater "
+                                               + "than 0");
+        }
+        final char[] buffer = new char[bufferSize];
+        int bufferLength = 0;
+        StringBuffer textBuffer = null;
+        while (bufferLength != -1) {
+            bufferLength = rdr.read(buffer);
+            if (bufferLength > 0) {
+                textBuffer = (textBuffer == null) ? new StringBuffer() : textBuffer;
+                textBuffer.append(new String(buffer, 0, bufferLength));
+            }
+        }
+        return (textBuffer == null) ? null : textBuffer.toString();
+    }
+
+    /**
+     * This was originally an emulation of File.createNewFile for JDK 1.1,
+     * but it is now implemented using that method (Ant 1.6.3 onwards).
+     *
+     * <p>This method has historically <strong>not</strong> guaranteed that the
+     * operation was atomic. In its current implementation it is.
+     *
+     * @param f the file to be created.
+     * @return true if the file did not exist already.
+     * @throws IOException on error.
+     * @since Ant 1.5
+     */
+    public boolean createNewFile(File f) throws IOException {
+        return f.createNewFile();
+    }
+
+    /**
+     * Create a new file, optionally creating parent directories.
+     *
+     * @param f the file to be created.
+     * @param mkdirs <code>boolean</code> whether to create parent directories.
+     * @return true if the file did not exist already.
+     * @throws IOException on error.
+     * @since Ant 1.6.3
+     */
+    public boolean createNewFile(File f, boolean mkdirs) throws IOException {
+        File parent = f.getParentFile();
+        if (mkdirs && !(parent.exists())) {
+            parent.mkdirs();
+        }
+        return f.createNewFile();
+    }
+
+    /**
+     * Checks whether a given file is a symbolic link.
+     *
+     * <p>It doesn't really test for symbolic links but whether the
+     * canonical and absolute paths of the file are identical--this
+     * may lead to false positives on some platforms.</p>
+     *
+     * @param parent the parent directory of the file to test
+     * @param name the name of the file to test.
+     *
+     * @return true if the file is a symbolic link.
+     * @throws IOException on error.
+     * @since Ant 1.5
+     */
+    public boolean isSymbolicLink(File parent, String name)
+        throws IOException {
+        if (parent == null) {
+            File f = new File(name);
+            parent = f.getParentFile();
+            name = f.getName();
+        }
+        File toTest = new File(parent.getCanonicalPath(), name);
+        return !toTest.getAbsolutePath().equals(toTest.getCanonicalPath());
+    }
+
+    /**
+     * Removes a leading path from a second path.
+     *
+     * @param leading The leading path, must not be null, must be absolute.
+     * @param path The path to remove from, must not be null, must be absolute.
+     *
+     * @return path's normalized absolute if it doesn't start with
+     * leading; path's path with leading's path removed otherwise.
+     *
+     * @since Ant 1.5
+     */
+    public String removeLeadingPath(File leading, File path) {
+        String l = normalize(leading.getAbsolutePath()).getAbsolutePath();
+        String p = normalize(path.getAbsolutePath()).getAbsolutePath();
+        if (l.equals(p)) {
+            return "";
+        }
+
+        // ensure that l ends with a /
+        // so we never think /foo was a parent directory of /foobar
+        if (!l.endsWith(File.separator)) {
+            l += File.separator;
+        }
+        return (p.startsWith(l)) ? p.substring(l.length()) : p;
+    }
+
+    /**
+     * Constructs a <code>file:</code> URI that represents the
+     * external form of the given pathname.
+     *
+     * <p>Will be an absolute URI if the given path is absolute.</p>
+     *
+     * <p>This code doesn't handle non-ASCII characters properly.</p>
+     *
+     * @param path the path in the local file system.
+     * @return the URI version of the local path.
+     * @since Ant 1.6
+     */
+    public String toURI(String path) {
+        boolean isDir = (new File(path)).isDirectory();
+
+        StringBuffer sb = new StringBuffer("file:");
+
+        // catch exception if normalize thinks this is not an absolute path
+        try {
+            path = normalize(path).getAbsolutePath();
+            sb.append("//");
+            // add an extra slash for filesystems with drive-specifiers
+            if (!path.startsWith(File.separator)) {
+                sb.append("/");
+            }
+        } catch (BuildException e) {
+            // relative path
+        }
+
+        path = path.replace('\\', '/');
+
+        CharacterIterator iter = new StringCharacterIterator(path);
+        for (char c = iter.first(); c != CharacterIterator.DONE;
+             c = iter.next()) {
+            if (c < 256 && isSpecial[c]) {
+                sb.append('%');
+                sb.append(escapedChar1[c]);
+                sb.append(escapedChar2[c]);
+            } else {
+                sb.append(c);
+            }
+        }
+        if (isDir && !path.endsWith("/")) {
+            sb.append('/');
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Constructs a file path from a <code>file:</code> URI.
+     *
+     * <p>Will be an absolute path if the given URI is absolute.</p>
+     *
+     * <p>Swallows '%' that are not followed by two characters,
+     * doesn't deal with non-ASCII characters.</p>
+     *
+     * @param uri the URI designating a file in the local filesystem.
+     * @return the local file system path for the file.
+     * @since Ant 1.6
+     */
+    public String fromURI(String uri) {
+        String path = Locator.fromURI(uri);
+
+        // catch exception if normalize thinks this is not an absolute path
+        try {
+            path = normalize(path).getAbsolutePath();
+        } catch (BuildException e) {
+            // relative path
+        }
+        return path;
+    }
+
+    /**
+     * Compares two filenames.
+     *
+     * <p>Unlike java.io.File#equals this method will try to compare
+     * the absolute paths and &quot;normalize&quot; the filenames
+     * before comparing them.</p>
+     *
+     * @param f1 the file whose name is to be compared.
+     * @param f2 the other file whose name is to be compared.
+     *
+     * @return true if the file are for the same file.
+     *
+     * @since Ant 1.5.3
+     */
+    public boolean fileNameEquals(File f1, File f2) {
+        return normalize(f1.getAbsolutePath())
+            .equals(normalize(f2.getAbsolutePath()));
+    }
+
+    /**
+     * Renames a file, even if that involves crossing file system boundaries.
+     *
+     * <p>This will remove <code>to</code> (if it exists), ensure that
+     * <code>to</code>'s parent directory exists and move
+     * <code>from</code>, which involves deleting <code>from</code> as
+     * well.</p>
+     *
+     * @param from the file to move.
+     * @param to the new file name.
+     *
+     * @throws IOException if anything bad happens during this
+     * process.  Note that <code>to</code> may have been deleted
+     * already when this happens.
+     *
+     * @since Ant 1.6
+     */
+    public void rename(File from, File to) throws IOException {
+        if (to.exists() && !to.delete()) {
+            throw new IOException("Failed to delete " + to
+                                  + " while trying to rename " + from);
+        }
+        File parent = to.getParentFile();
+        if (parent != null && !parent.exists() && !parent.mkdirs()) {
+            throw new IOException("Failed to create directory " + parent
+                                  + " while trying to rename " + from);
+        }
+        if (!from.renameTo(to)) {
+            copyFile(from, to);
+            if (!from.delete()) {
+                throw new IOException("Failed to delete " + from
+                                      + " while trying to rename it.");
+            }
+        }
+    }
+
+    /**
+     * Get the granularity of file timestamps.
+     * The choice is made based on OS, which is incorrect--it should really be
+     * by filesystem. We do not have an easy way to probe for file systems,
+     * however.
+     * @return the difference, in milliseconds, which two file timestamps must have
+     * in order for the two files to be given a creation order.
+     */
+    public long getFileTimestampGranularity() {
+        return onDos
+            ? FAT_FILE_TIMESTAMP_GRANULARITY : UNIX_FILE_TIMESTAMP_GRANULARITY;
+    }
+
+    /**
+     * Returns true if the source is older than the dest.
+     * If the dest file does not exist, then the test returns false; it is
+     * implicitly not up do date.
+     * @param source source file (should be the older).
+     * @param dest dest file (should be the newer).
+     * @param granularity an offset added to the source time.
+     * @return true if the source is older than the dest after accounting
+     *              for granularity.
+     * @since Ant 1.6.3
+     */
+    public boolean isUpToDate(File source, File dest, long granularity) {
+        //do a check for the destination file existing
+        if (!dest.exists()) {
+            //if it does not, then the file is not up to date.
+            return false;
+        }
+        long sourceTime = source.lastModified();
+        long destTime = dest.lastModified();
+        return isUpToDate(sourceTime, destTime, granularity);
+    }
+
+
+    /**
+     * Returns true if the source is older than the dest.
+     * @param source source file (should be the older).
+     * @param dest dest file (should be the newer).
+     * @return true if the source is older than the dest, taking the granularity into account.
+     * @since Ant 1.6.3
+     */
+    public boolean isUpToDate(File source, File dest) {
+        return isUpToDate(source, dest, getFileTimestampGranularity());
+    }
+
+    /**
+     * Compare two timestamps for being up to date using
+     * the specified granularity.
+     *
+     * @param sourceTime timestamp of source file.
+     * @param destTime timestamp of dest file.
+     * @param granularity os/filesys granularity.
+     * @return true if the dest file is considered up to date.
+     */
+    public boolean isUpToDate(long sourceTime, long destTime, long granularity) {
+        if (destTime == -1) {
+            return false;
+        }
+        return destTime >= sourceTime + granularity;
+    }
+
+    /**
+     * Compare two timestamps for being up to date using the
+     * current granularity.
+     *
+     * @param sourceTime  timestamp of source file.
+     * @param destTime    timestamp of dest file.
+     * @return true if the dest file is considered up to date.
+     */
+    public boolean isUpToDate(long sourceTime, long destTime) {
+        return isUpToDate(sourceTime, destTime, getFileTimestampGranularity());
+    }
+
+    /**
+     * Close a Writer without throwing any exception if something went wrong.
+     * Do not attempt to close it if the argument is null.
+     * @param device output writer, can be null.
+     */
+    public static void close(Writer device) {
+        if (device != null) {
+            try {
+                device.close();
+            } catch (IOException ioex) {
+                //ignore
+            }
+        }
+    }
+
+    /**
+     * Close a stream without throwing any exception if something went wrong.
+     * Do not attempt to close it if the argument is null.
+     *
+     * @param device Reader, can be null.
+     */
+    public static void close(Reader device) {
+        if (device != null) {
+            try {
+                device.close();
+            } catch (IOException ioex) {
+                //ignore
+            }
+        }
+    }
+
+    /**
+     * Close a stream without throwing any exception if something went wrong.
+     * Do not attempt to close it if the argument is null.
+     *
+     * @param device stream, can be null.
+     */
+    public static void close(OutputStream device) {
+        if (device != null) {
+            try {
+                device.close();
+            } catch (IOException ioex) {
+                //ignore
+            }
+        }
+    }
+
+    /**
+     * Close a stream without throwing any exception if something went wrong.
+     * Do not attempt to close it if the argument is null.
+     *
+     * @param device stream, can be null.
+     */
+    public static void close(InputStream device) {
+        if (device != null) {
+            try {
+                device.close();
+            } catch (IOException ioex) {
+                //ignore
+            }
+        }
+    }
+
+    /**
+     * Delete the file with {@link File#delete()} if the argument is not null.
+     * Do nothing on a null argument.
+     * @param file file to delete.
+     */
+    public static void delete(File file) {
+        if (file != null) {
+            file.delete();
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/FlatFileNameMapper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/FlatFileNameMapper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/FlatFileNameMapper.java	(revision 16139)
@@ -0,0 +1,49 @@
+/*
+ * Copyright  2000,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+/**
+ * Implementation of FileNameMapper that always returns the source
+ * file name without any leading directory information.
+ *
+ * <p>This is the default FileNameMapper for the copy and move
+ * tasks if the flatten attribute has been set.</p>
+ *
+ */
+public class FlatFileNameMapper implements FileNameMapper {
+
+    /**
+     * Ignored.
+     */
+    public void setFrom(String from) {
+    }
+
+    /**
+     * Ignored.
+     */
+    public void setTo(String to) {
+    }
+
+    /**
+     * Returns an one-element array containing the source file name
+     * without any leading directory information.
+     */
+    public String[] mapFileName(String sourceFileName) {
+        return new String[] {new java.io.File(sourceFileName).getName()};
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/GlobPatternMapper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/GlobPatternMapper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/GlobPatternMapper.java	(revision 16139)
@@ -0,0 +1,166 @@
+/*
+ * Copyright  2000,2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+/**
+ * Implementation of FileNameMapper that does simple wildcard pattern
+ * replacements.
+ *
+ * <p>This does simple translations like *.foo -> *.bar where the
+ * prefix to .foo will be left unchanged. It only handles a single *
+ * character, use regular expressions for more complicated
+ * situations.</p>
+ *
+ * <p>This is one of the more useful Mappers, it is used by javac for
+ * example.</p>
+ *
+ */
+public class GlobPatternMapper implements FileNameMapper {
+
+    /**
+     * Part of &quot;from&quot; pattern before the *.
+     */
+    protected String fromPrefix = null;
+
+    /**
+     * Part of &quot;from&quot; pattern after the *.
+     */
+    protected String fromPostfix = null;
+
+    /**
+     * Length of the prefix (&quot;from&quot; pattern).
+     */
+    protected int prefixLength;
+
+    /**
+     * Length of the postfix (&quot;from&quot; pattern).
+     */
+    protected int postfixLength;
+
+    /**
+     * Part of &quot;to&quot; pattern before the *.
+     */
+    protected String toPrefix = null;
+
+    /**
+     * Part of &quot;to&quot; pattern after the *.
+     */
+    protected String toPostfix = null;
+
+    private boolean handleDirSep = false;
+    private boolean caseSensitive = true;
+
+    /**
+     * Attribute specifing whether to ignore the difference
+     * between / and \ (the two common directory characters).
+     * @param handleDirSep a boolean, default is false.
+     * @since Ant 1.6.3
+     */
+    public void setHandleDirSep(boolean handleDirSep) {
+        this.handleDirSep = handleDirSep;
+    }
+
+    /**
+     * Attribute specifing whether to ignore the case difference
+     * in the names.
+     *
+     * @param caseSensitive a boolean, default is false.
+     * @since Ant 1.6.3
+     */
+    public void setCaseSensitive(boolean caseSensitive) {
+        this.caseSensitive = caseSensitive;
+    }
+
+    /**
+     * Sets the &quot;from&quot; pattern. Required.
+     * @param from a string
+     */
+    public void setFrom(String from) {
+        int index = from.lastIndexOf("*");
+        if (index == -1) {
+            fromPrefix = from;
+            fromPostfix = "";
+        } else {
+            fromPrefix = from.substring(0, index);
+            fromPostfix = from.substring(index + 1);
+        }
+        prefixLength = fromPrefix.length();
+        postfixLength = fromPostfix.length();
+    }
+
+    /**
+     * Sets the &quot;to&quot; pattern. Required.
+     * @param to a string
+     */
+    public void setTo(String to) {
+        int index = to.lastIndexOf("*");
+        if (index == -1) {
+            toPrefix = to;
+            toPostfix = "";
+        } else {
+            toPrefix = to.substring(0, index);
+            toPostfix = to.substring(index + 1);
+        }
+    }
+
+    /**
+     * Returns null if the source file name doesn't match the
+     * &quot;from&quot; pattern, an one-element array containing the
+     * translated file otherwise.
+     * @param sourceFileName the filename to map
+     * @return a list of converted filenames
+     */
+    public String[] mapFileName(String sourceFileName) {
+        if (fromPrefix == null
+            || !modifyName(sourceFileName).startsWith(modifyName(fromPrefix))
+            || !modifyName(sourceFileName).endsWith(modifyName(fromPostfix))) {
+            return null;
+        }
+        return new String[] {toPrefix
+                                 + extractVariablePart(sourceFileName)
+                                 + toPostfix};
+    }
+
+    /**
+     * Returns the part of the given string that matches the * in the
+     * &quot;from&quot; pattern.
+     * @param name the source file name
+     * @return the variable part of the name
+     */
+    protected String extractVariablePart(String name) {
+        return name.substring(prefixLength,
+                              name.length() - postfixLength);
+    }
+
+    /**
+     * modify string based on dir char mapping and case sensitivity
+     * @param name the name to convert
+     * @return the converted name
+     */
+    private String modifyName(String name) {
+        if (!caseSensitive) {
+            name = name.toLowerCase();
+        }
+        if (handleDirSep) {
+            if (name.indexOf('\\') != -1) {
+                name = name.replace('\\', '/');
+            }
+        }
+        return name;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/IdentityMapper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/IdentityMapper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/IdentityMapper.java	(revision 16139)
@@ -0,0 +1,47 @@
+/*
+ * Copyright  2000,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+/**
+ * Implementation of FileNameMapper that always returns the source file name.
+ *
+ * <p>This is the default FileNameMapper for the copy and move
+ * tasks.</p>
+ *
+ */
+public class IdentityMapper implements FileNameMapper {
+
+    /**
+     * Ignored.
+     */
+    public void setFrom(String from) {
+    }
+
+    /**
+     * Ignored.
+     */
+    public void setTo(String to) {
+    }
+
+    /**
+     * Returns an one-element array containing the source file name.
+     */
+    public String[] mapFileName(String sourceFileName) {
+        return new String[] {sourceFileName};
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/JAXPUtils.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/JAXPUtils.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/JAXPUtils.java	(revision 16139)
@@ -0,0 +1,248 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.File;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+import org.apache.tools.ant.BuildException;
+import org.xml.sax.Parser;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+/**
+ * Collection of helper methods that retrieve a ParserFactory or
+ * Parsers and Readers.
+ *
+ * <p>This class will create only a single factory instance.</p>
+ *
+ *
+ * @since Ant 1.5
+ */
+public class JAXPUtils {
+
+    /**
+     * Helper for systemId.
+     *
+     * @since Ant 1.6
+     */
+    private static final FileUtils fu = FileUtils.newFileUtils();
+
+    /**
+     * Parser factory to use to create parsers.
+     * @see #getParserFactory
+     *
+     * @since Ant 1.5
+     */
+    private static SAXParserFactory parserFactory = null;
+
+    /**
+     * Parser Factory to create Namespace aware parsers.
+     *
+     * @since Ant 1.6
+     */
+    private static SAXParserFactory nsParserFactory = null;
+
+    /**
+     * Parser factory to use to create document builders.
+     *
+     * @since Ant 1.6
+     */
+    private static DocumentBuilderFactory builderFactory = null;
+
+    /**
+     * Returns the parser factory to use. Only one parser factory is
+     * ever created by this method and is then cached for future use.
+     *
+     * @return a SAXParserFactory to use
+     *
+     * @since Ant 1.5
+     */
+    public static synchronized SAXParserFactory getParserFactory()
+        throws BuildException {
+
+        if (parserFactory == null) {
+            parserFactory = newParserFactory();
+        }
+        return parserFactory;
+    }
+
+    /**
+     * Returns the parser factory to use to create namespace aware parsers.
+     *
+     * @return a SAXParserFactory to use which supports manufacture of
+     * namespace aware parsers
+     *
+     * @since Ant 1.6
+     */
+    public static synchronized SAXParserFactory getNSParserFactory()
+        throws BuildException {
+
+        if (nsParserFactory == null) {
+            nsParserFactory = newParserFactory();
+            nsParserFactory.setNamespaceAware(true);
+        }
+        return nsParserFactory;
+    }
+
+    /**
+     * Returns a new  parser factory instance.
+     *
+     * @since Ant 1.5
+     */
+    public static SAXParserFactory newParserFactory() throws BuildException {
+
+        try {
+            return SAXParserFactory.newInstance();
+        } catch (FactoryConfigurationError e) {
+            throw new BuildException("XML parser factory has not been "
+                                     + "configured correctly: "
+                                     + e.getMessage(), e);
+        }
+    }
+
+    /**
+     * Returns a newly created SAX 1 Parser, using the default parser
+     * factory.
+     *
+     * @return a SAX 1 Parser.
+     * @see #getParserFactory
+     * @since Ant 1.5
+     */
+    public static Parser getParser() throws BuildException {
+        try {
+            return newSAXParser(getParserFactory()).getParser();
+        } catch (SAXException e) {
+            throw convertToBuildException(e);
+        }
+    }
+
+    /**
+     * Returns a newly created SAX 2 XMLReader, using the default parser
+     * factory.
+     *
+     * @return a SAX 2 XMLReader.
+     * @see #getParserFactory
+     * @since Ant 1.5
+     */
+    public static XMLReader getXMLReader() throws BuildException {
+        try {
+            return newSAXParser(getParserFactory()).getXMLReader();
+        } catch (SAXException e) {
+            throw convertToBuildException(e);
+        }
+    }
+
+    /**
+     * Returns a newly created SAX 2 XMLReader, which is namespace aware
+     *
+     * @return a SAX 2 XMLReader.
+     * @see #getParserFactory
+     * @since Ant 1.6
+     */
+    public static XMLReader getNamespaceXMLReader() throws BuildException {
+        try {
+            return newSAXParser(getNSParserFactory()).getXMLReader();
+        } catch (SAXException e) {
+            throw convertToBuildException(e);
+        }
+    }
+
+    /**
+     * This is a best attempt to provide a URL.toExternalForm() from
+     * a file URL. Some parsers like Crimson choke on uri that are made of
+     * backslashed paths (ie windows) as it is does not conform
+     * URI specifications.
+     * @param file the file to create the system id from.
+     * @return the systemid corresponding to the given file.
+     * @since Ant 1.5.2
+     */
+    public static String getSystemId(File file) {
+        return fu.toURI(file.getAbsolutePath());
+    }
+
+    /**
+     * Returns a newly created DocumentBuilder.
+     *
+     * @return a DocumentBuilder
+     * @since Ant 1.6
+     */
+    public static DocumentBuilder getDocumentBuilder() throws BuildException {
+        try {
+            return getDocumentBuilderFactory().newDocumentBuilder();
+        } catch (ParserConfigurationException e) {
+            throw new BuildException(e);
+        }
+    }
+
+    /**
+     * @return a new SAXParser instance as helper for getParser and
+     * getXMLReader.
+     *
+     * @since Ant 1.5
+     */
+    private static SAXParser newSAXParser(SAXParserFactory factory)
+         throws BuildException {
+        try {
+            return factory.newSAXParser();
+        } catch (ParserConfigurationException e) {
+            throw new BuildException("Cannot create parser for the given "
+                                     + "configuration: " + e.getMessage(), e);
+        } catch (SAXException e) {
+            throw convertToBuildException(e);
+        }
+    }
+
+    /**
+     * Translate a SAXException into a BuildException
+     *
+     * @since Ant 1.5
+     */
+    private static BuildException convertToBuildException(SAXException e) {
+        Exception nested = e.getException();
+        if (nested != null) {
+            return new BuildException(nested);
+        } else {
+            return new BuildException(e);
+        }
+    }
+
+    /**
+     * Obtains the default builder factory if not already.
+     *
+     * @since Ant 1.6
+     */
+    private static synchronized
+        DocumentBuilderFactory getDocumentBuilderFactory()
+        throws BuildException {
+        if (builderFactory == null) {
+            try {
+                builderFactory = DocumentBuilderFactory.newInstance();
+            } catch (FactoryConfigurationError e) {
+                throw new BuildException("Document builder factory has not "
+                                         + "been configured correctly: "
+                                         + e.getMessage(), e);
+            }
+        }
+        return builderFactory;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/JavaEnvUtils.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/JavaEnvUtils.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/JavaEnvUtils.java	(revision 16139)
@@ -0,0 +1,361 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.File;
+import java.util.Vector;
+import org.apache.tools.ant.taskdefs.condition.Os;
+
+/**
+ * A set of helper methods related to locating executables or checking
+ * conditons of a given Java installation.
+ *
+ * @since Ant 1.5
+ */
+public final class JavaEnvUtils {
+
+    private JavaEnvUtils() {
+    }
+
+    /** Are we on a DOS-based system */
+    private static final boolean isDos = Os.isFamily("dos");
+    /** Are we on Novell NetWare */
+    private static final boolean isNetware = Os.isName("netware");
+    /** Are we on AIX */
+    private static final boolean isAix = Os.isName("aix");
+
+    /** shortcut for System.getProperty("java.home") */
+    private static final String javaHome = System.getProperty("java.home");
+
+    /** FileUtils instance for path normalization */
+    private static final FileUtils fileUtils = FileUtils.newFileUtils();
+
+    /** Version of currently running VM. */
+    private static String javaVersion;
+
+    /** floating version of the JVM */
+    private static int javaVersionNumber;
+
+    /** Version constant for Java 1.0 */
+    public static final String JAVA_1_0 = "1.0";
+    /** Version constant for Java 1.1 */
+    public static final String JAVA_1_1 = "1.1";
+    /** Version constant for Java 1.2 */
+    public static final String JAVA_1_2 = "1.2";
+    /** Version constant for Java 1.3 */
+    public static final String JAVA_1_3 = "1.3";
+    /** Version constant for Java 1.4 */
+    public static final String JAVA_1_4 = "1.4";
+    /** Version constant for Java 1.5 */
+    public static final String JAVA_1_5 = "1.5";
+
+    /** Whether this is the Kaffe VM */
+    private static boolean kaffeDetected;
+
+    /** array of packages in the runtime */
+    private static Vector jrePackages;
+
+
+    static {
+
+        // Determine the Java version by looking at available classes
+        // java.lang.Readable was introduced in JDK 1.5
+        // java.lang.CharSequence was introduced in JDK 1.4
+        // java.lang.StrictMath was introduced in JDK 1.3
+        // java.lang.ThreadLocal was introduced in JDK 1.2
+        // java.lang.Void was introduced in JDK 1.1
+        // Count up version until a NoClassDefFoundError ends the try
+
+        try {
+            javaVersion = JAVA_1_0;
+            javaVersionNumber = 10;
+            Class.forName("java.lang.Void");
+            javaVersion = JAVA_1_1;
+            javaVersionNumber++;
+            Class.forName("java.lang.ThreadLocal");
+            javaVersion = JAVA_1_2;
+            javaVersionNumber++;
+            Class.forName("java.lang.StrictMath");
+            javaVersion = JAVA_1_3;
+            javaVersionNumber++;
+            Class.forName("java.lang.CharSequence");
+            javaVersion = JAVA_1_4;
+            javaVersionNumber++;
+            Class.forName("java.lang.Readable");
+            javaVersion = JAVA_1_5;
+            javaVersionNumber++;
+        } catch (Throwable t) {
+            // swallow as we've hit the max class version that
+            // we have
+        }
+        kaffeDetected = false;
+        try {
+            Class.forName("kaffe.util.NotImplemented");
+            kaffeDetected = true;
+        } catch (Throwable t) {
+            // swallow as this simply doesn't seem to be Kaffe
+        }
+    }
+
+    /**
+     * Returns the version of Java this class is running under.
+     * @return the version of Java as a String, e.g. "1.1"
+     */
+    public static String getJavaVersion() {
+        return javaVersion;
+    }
+
+    /**
+     * Compares the current Java version to the passed in String -
+     * assumes the argument is one of the constants defined in this
+     * class.
+     * @param version the version to check against the current version.
+     * @return true if the version of Java is the same as the given version.
+     * @since Ant 1.5
+     */
+    public static boolean isJavaVersion(String version) {
+        return javaVersion.equals(version);
+    }
+
+    /**
+     * Checks whether the current Java VM is Kaffe.
+     * @return true if the current Java VM is Kaffe.
+     * @since Ant 1.6.3
+     * @see http://www.kaffe.org/
+     */
+    public static boolean isKaffe() {
+        return kaffeDetected;
+    }
+
+    /**
+     * Finds an executable that is part of a JRE installation based on
+     * the java.home system property.
+     *
+     * <p><code>java</code>, <code>keytool</code>,
+     * <code>policytool</code>, <code>orbd</code>, <code>rmid</code>,
+     * <code>rmiregistry</code>, <code>servertool</code> and
+     * <code>tnameserv</code> are JRE executables on Sun based
+     * JRE's.</p>
+     *
+     * <p>You typically find them in <code>JAVA_HOME/jre/bin</code> if
+     * <code>JAVA_HOME</code> points to your JDK installation.  JDK
+     * &lt; 1.2 has them in the same directory as the JDK
+     * executables.</p>
+     * @param command the java executable to find.
+     * @return the path to the command.
+     * @since Ant 1.5
+     */
+    public static String getJreExecutable(String command) {
+        if (isNetware) {
+            // Extrapolating from:
+            // "NetWare may have a "java" in that directory, but 99% of
+            // the time, you don't want to execute it" -- Jeff Tulley
+            // <JTULLEY@novell.com>
+            return command;
+        }
+
+        File jExecutable = null;
+
+        if (isAix) {
+            // On IBM's JDK 1.2 the directory layout is different, 1.3 follows
+            // Sun's layout.
+            jExecutable = findInDir(javaHome + "/sh", command);
+        }
+
+        if (jExecutable == null) {
+            jExecutable = findInDir(javaHome + "/bin", command);
+        }
+
+        if (jExecutable != null) {
+            return jExecutable.getAbsolutePath();
+        } else {
+            // Unfortunately on Windows java.home doesn't always refer
+            // to the correct location, so we need to fall back to
+            // assuming java is somewhere on the PATH.
+            return addExtension(command);
+        }
+    }
+
+    /**
+     * Finds an executable that is part of a JDK installation based on
+     * the java.home system property.
+     *
+     * <p>You typically find them in <code>JAVA_HOME/bin</code> if
+     * <code>JAVA_HOME</code> points to your JDK installation.</p>
+     * @param command the java executable to find.
+     * @return the path to the command.
+     * @since Ant 1.5
+     */
+    public static String getJdkExecutable(String command) {
+        if (isNetware) {
+            // Extrapolating from:
+            // "NetWare may have a "java" in that directory, but 99% of
+            // the time, you don't want to execute it" -- Jeff Tulley
+            // <JTULLEY@novell.com>
+            return command;
+        }
+
+        File jExecutable = null;
+
+        if (isAix) {
+            // On IBM's JDK 1.2 the directory layout is different, 1.3 follows
+            // Sun's layout.
+            jExecutable = findInDir(javaHome + "/../sh", command);
+        }
+
+        if (jExecutable == null) {
+            jExecutable = findInDir(javaHome + "/../bin", command);
+        }
+
+        if (jExecutable != null) {
+            return jExecutable.getAbsolutePath();
+        } else {
+            // fall back to JRE bin directory, also catches JDK 1.0 and 1.1
+            // where java.home points to the root of the JDK and Mac OS X where
+            // the whole directory layout is different from Sun's
+            return getJreExecutable(command);
+        }
+    }
+
+    /**
+     * Adds a system specific extension to the name of an executable.
+     *
+     * @since Ant 1.5
+     */
+    private static String addExtension(String command) {
+        // This is the most common extension case - exe for windows and OS/2,
+        // nothing for *nix.
+        return command + (isDos ? ".exe" : "");
+    }
+
+    /**
+     * Look for an executable in a given directory.
+     *
+     * @return null if the executable cannot be found.
+     */
+    private static File findInDir(String dirName, String commandName) {
+        File dir = fileUtils.normalize(dirName);
+        File executable = null;
+        if (dir.exists()) {
+            executable = new File(dir, addExtension(commandName));
+            if (!executable.exists()) {
+                executable = null;
+            }
+        }
+        return executable;
+    }
+
+    /**
+     * demand creation of the package list.
+     * When you add a new package, add a new test below.
+     */
+
+    private static void buildJrePackages() {
+        jrePackages = new Vector();
+        switch(javaVersionNumber) {
+            case 15:
+            case 14:
+                jrePackages.addElement("org.apache.crimson");
+                jrePackages.addElement("org.apache.xalan");
+                jrePackages.addElement("org.apache.xml");
+                jrePackages.addElement("org.apache.xpath");
+                jrePackages.addElement("org.ietf.jgss");
+                jrePackages.addElement("org.w3c.dom");
+                jrePackages.addElement("org.xml.sax");
+                // fall through
+            case 13:
+                jrePackages.addElement("org.omg");
+                jrePackages.addElement("com.sun.corba");
+                jrePackages.addElement("com.sun.jndi");
+                jrePackages.addElement("com.sun.media");
+                jrePackages.addElement("com.sun.naming");
+                jrePackages.addElement("com.sun.org.omg");
+                jrePackages.addElement("com.sun.rmi");
+                jrePackages.addElement("sunw.io");
+                jrePackages.addElement("sunw.util");
+                // fall through
+            case 12:
+                jrePackages.addElement("com.sun.java");
+                jrePackages.addElement("com.sun.image");
+                // are there any here that we forgot?
+                // fall through
+            case 11:
+            default:
+                //things like sun.reflection, sun.misc, sun.net
+                jrePackages.addElement("sun");
+                jrePackages.addElement("java");
+                jrePackages.addElement("javax");
+                break;
+        }
+    }
+
+    /**
+     * Testing helper method; kept here for unification of changes.
+     * @return a list of test classes depending on the java version.
+     */
+    public static Vector getJrePackageTestCases() {
+        Vector tests = new Vector();
+        tests.addElement("java.lang.Object");
+        switch(javaVersionNumber) {
+            case 15:
+            case 14:
+                tests.addElement("sun.audio.AudioPlayer");
+                tests.addElement("org.apache.crimson.parser.ContentModel");
+                tests.addElement("org.apache.xalan.processor.ProcessorImport");
+                tests.addElement("org.apache.xml.utils.URI");
+                tests.addElement("org.apache.xpath.XPathFactory");
+                tests.addElement("org.ietf.jgss.Oid");
+                tests.addElement("org.w3c.dom.Attr");
+                tests.addElement("org.xml.sax.XMLReader");
+                // fall through
+            case 13:
+                tests.addElement("org.omg.CORBA.Any");
+                tests.addElement("com.sun.corba.se.internal.corba.AnyImpl");
+                tests.addElement("com.sun.jndi.ldap.LdapURL");
+                tests.addElement("com.sun.media.sound.Printer");
+                tests.addElement("com.sun.naming.internal.VersionHelper");
+                tests.addElement("com.sun.org.omg.CORBA.Initializer");
+                tests.addElement("sunw.io.Serializable");
+                tests.addElement("sunw.util.EventListener");
+                // fall through
+            case 12:
+                tests.addElement("javax.accessibility.Accessible");
+                tests.addElement("sun.misc.BASE64Encoder");
+                tests.addElement("com.sun.image.codec.jpeg.JPEGCodec");
+                // fall through
+            case 11:
+            default:
+                //things like sun.reflection, sun.misc, sun.net
+                tests.addElement("sun.reflect.SerializationConstructorAccessorImpl");
+                tests.addElement("sun.net.www.http.HttpClient");
+                tests.addElement("sun.audio.AudioPlayer");
+                break;
+        }
+        return tests;
+    }
+    /**
+     * get a vector of strings of packages built into
+     * that platforms runtime jar(s)
+     * @return list of packages.
+     */
+    public static Vector getJrePackages() {
+        if (jrePackages == null) {
+            buildJrePackages();
+        }
+        return jrePackages;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/KeepAliveInputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/KeepAliveInputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/KeepAliveInputStream.java	(revision 16139)
@@ -0,0 +1,49 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Class that can be used to wrap <tt>System.in</tt>
+ * without getting anxious about any client closing the stream.
+ *
+ * <p>
+ * In code-language it means that it is not necessary to do:
+ * <pre>
+ * if (out != System.in) {
+ *   in.close();
+ * }
+ * </pre>
+ * </p>
+ *
+ * @since Ant 1.6
+ */
+public class KeepAliveInputStream extends FilterInputStream {
+
+    public KeepAliveInputStream(InputStream in) {
+        super(in);
+    }
+
+    /** this method does nothing */
+    public void close() throws IOException {
+        //
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/KeepAliveOutputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/KeepAliveOutputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/KeepAliveOutputStream.java	(revision 16139)
@@ -0,0 +1,48 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Class that can be used to wrap <tt>System.out</tt> and <tt>System.err</tt>
+ * without getting anxious about any client closing the stream.
+ *
+ * <p>
+ * In code-language it means that it is not necessary to do:
+ * <pre>
+ * if (out != System.out && out!= System.err) {
+ *   out.close();
+ * }
+ * </pre>
+ * </p>
+ *
+ */
+public class KeepAliveOutputStream extends FilterOutputStream {
+
+    public KeepAliveOutputStream(OutputStream out) {
+        super(out);
+    }
+
+    /** this method does nothing */
+    public void close() throws IOException {
+        //
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/LazyFileOutputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/LazyFileOutputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/LazyFileOutputStream.java	(revision 16139)
@@ -0,0 +1,135 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Class that delays opening the output file until the first bytes
+ * shall be written or the method {@link #open open} has been invoked
+ * explicitly.
+ *
+ * @since Ant 1.6
+ */
+public class LazyFileOutputStream extends OutputStream {
+
+    private FileOutputStream fos;
+    private File file;
+    private boolean append;
+    private boolean alwaysCreate;
+    private boolean opened = false;
+    private boolean closed = false;
+
+    /**
+     * Creates a stream that will eventually write to the file with
+     * the given name and replace it.
+     */
+    public LazyFileOutputStream(String name) {
+        this(name, false);
+    }
+
+    /**
+     * Creates a stream that will eventually write to the file with
+     * the given name and optionally append to instead of replacing
+     * it.
+     */
+    public LazyFileOutputStream(String name, boolean append) {
+        this(new File(name), append);
+    }
+
+    /**
+     * Creates a stream that will eventually write to the file with
+     * the given name and replace it.
+     */
+    public LazyFileOutputStream(File f) {
+        this(f, false);
+    }
+
+    /**
+     * Creates a stream that will eventually write to the file with
+     * the given name and optionally append to instead of replacing
+     * it.
+     */
+    public LazyFileOutputStream(File file, boolean append) {
+        this(file, append, false);
+    }
+
+    /**
+     * Creates a stream that will eventually write to the file with
+     * the given name, optionally append to instead of replacing
+     * it, and optionally always create a file (even if zero length).
+     */
+    public LazyFileOutputStream(File file, boolean append,
+                                boolean alwaysCreate) {
+        this.file = file;
+        this.append = append;
+        this.alwaysCreate = alwaysCreate;
+    }
+
+    /**
+     * Explicitly open the file for writing.
+     *
+     * <p>Returns silently if the file has already been opened.</p>
+     */
+    public void open() throws IOException {
+        ensureOpened();
+    }
+
+    public synchronized void close() throws IOException {
+        if (alwaysCreate && !closed) {
+            ensureOpened();
+        }
+        if (opened) {
+            fos.close();
+        }
+        closed = true;
+    }
+
+    /**
+     * Delegates to the three-arg version.
+     */
+    public void write(byte[] b) throws IOException {
+        write(b, 0, b.length);
+    }
+
+    //inherit doc
+    public synchronized void write(byte[] b, int offset, int len)
+        throws IOException {
+        ensureOpened();
+        fos.write(b, offset, len);
+    }
+
+    //inherit doc
+    public synchronized void write(int b) throws IOException {
+        ensureOpened();
+        fos.write(b);
+    }
+
+    private synchronized void ensureOpened() throws IOException {
+        if (closed) {
+            throw new IOException(file + " has already been closed.");
+        }
+
+        if (!opened) {
+            fos = new FileOutputStream(file.getAbsolutePath(), append);
+            opened = true;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/LazyHashtable.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/LazyHashtable.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/LazyHashtable.java	(revision 16139)
@@ -0,0 +1,88 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.util.Hashtable;
+import java.util.Enumeration;
+
+/** Hashtable implementation that allows delayed construction
+ * of expensive objects
+ *
+ * All operations that need access to the full list of objects
+ * will call initAll() first. Get and put are cheap.
+ *
+ * @since Ant 1.6
+ */
+public class LazyHashtable extends Hashtable {
+    protected boolean initAllDone = false;
+
+    public LazyHashtable() {
+        super();
+    }
+
+    /** Used to be part of init. It must be done once - but
+     * we delay it until we do need _all_ tasks. Otherwise we
+     * just get the tasks that we need, and avoid costly init.
+     */
+    protected void initAll() {
+        if (initAllDone) {
+            return;
+        }
+        initAllDone = true;
+    }
+
+
+    public Enumeration elements() {
+        initAll();
+        return super.elements();
+    }
+
+    public boolean isEmpty() {
+        initAll();
+        return super.isEmpty();
+    }
+
+    public int size() {
+        initAll();
+        return super.size();
+    }
+
+    public boolean contains(Object value) {
+        initAll();
+        return super.contains(value);
+    }
+
+    public boolean containsKey(Object value) {
+        initAll();
+        return super.containsKey(value);
+    }
+
+    /**
+     * Delegates to {@link #contains contains}.
+     */
+    public boolean containsValue(Object value) {
+        return contains(value);
+    }
+
+    public Enumeration keys() {
+        initAll();
+        return super.keys();
+    }
+
+    // XXX Unfortunately JDK1.2 adds entrySet(), keySet(), values() -
+    // implementing this requires a small hack, we can add it later.
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/LeadPipeInputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/LeadPipeInputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/LeadPipeInputStream.java	(revision 16139)
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.Project;
+
+/**
+ * Special <CODE>PipedInputStream</CODE> that will not die
+ * when the writing <CODE>Thread</CODE> is no longer alive.
+ * @since Ant 1.6.2
+ */
+public class LeadPipeInputStream extends PipedInputStream {
+    private ProjectComponent managingPc;
+
+    /**
+     * Construct a new <CODE>LeadPipeInputStream</CODE>.
+     */
+    public LeadPipeInputStream() {
+        super();
+    }
+
+    /**
+     * Construct a new <CODE>LeadPipeInputStream</CODE>
+     * with the specified buffer size.
+     * @param size   the size of the circular buffer.
+     */
+    public LeadPipeInputStream(int size) {
+        super();
+        setBufferSize(size);
+    }
+
+    /**
+     * Construct a new <CODE>LeadPipeInputStream</CODE> to pull
+     * from the specified <CODE>PipedOutputStream</CODE>.
+     * @param src   the <CODE>PipedOutputStream</CODE> source.
+     * @throws IOException if unable to construct the stream.
+     */
+    public LeadPipeInputStream(PipedOutputStream src) throws IOException {
+        super(src);
+    }
+
+    /**
+     * Construct a new <CODE>LeadPipeInputStream</CODE> to pull
+     * from the specified <CODE>PipedOutputStream</CODE>, using a
+     * circular buffer of the specified size.
+     * @param src    the <CODE>PipedOutputStream</CODE> source.
+     * @param size   the size of the circular buffer.
+     */
+    public LeadPipeInputStream(PipedOutputStream src, int size) throws IOException {
+        super(src);
+        setBufferSize(size);
+    }
+
+    //inherit doc
+    public synchronized int read() throws IOException {
+        int result = -1;
+        try {
+            result = super.read();
+        } catch (IOException eyeOhEx) {
+            if ("write end dead".equalsIgnoreCase(eyeOhEx.getMessage())) {
+                if (super.in > 0 && super.out < super.buffer.length
+                    && super.out > super.in) {
+                    result = super.buffer[super.out++] & 0xFF;
+                }
+            } else {
+                log("error at LeadPipeInputStream.read():  "
+                    + eyeOhEx.getMessage(), Project.MSG_INFO);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Set the size of the buffer.
+     * @param size   the new buffer size.  Ignored if <= current size.
+     */
+    public synchronized void setBufferSize(int size) {
+        if (size > buffer.length) {
+            byte[] newBuffer = new byte[size];
+            if (in >= 0) {
+                if (in > out) {
+                    System.arraycopy(buffer, out, newBuffer, out, in - out);
+                } else {
+                    int outlen = buffer.length - out;
+                    System.arraycopy(buffer, out, newBuffer, 0, outlen);
+                    System.arraycopy(buffer, 0, newBuffer, outlen, in);
+                    in+= outlen;
+                    out = 0;
+                }
+            }
+            buffer = newBuffer;
+        }
+    }
+
+    /**
+     * Set a managing <CODE>Task</CODE> for
+     * this <CODE>LeadPipeInputStream</CODE>.
+     * @param task   the managing <CODE>Task</CODE>.
+     */
+    public void setManagingTask(Task task) {
+        setManagingComponent(task);
+    }
+
+    /**
+     * Set a managing <CODE>ProjectComponent</CODE> for
+     * this <CODE>LeadPipeInputStream</CODE>.
+     * @param pc   the managing <CODE>ProjectComponent</CODE>.
+     */
+    public void setManagingComponent(ProjectComponent pc) {
+        this.managingPc = pc;
+    }
+
+    /**
+     * Log a message with the specified logging level.
+     * @param message    the <CODE>String</CODE> message.
+     * @param loglevel   the <CODE>int</CODE> logging level.
+     */
+    public void log(String message, int loglevel) {
+        if (managingPc != null) {
+            managingPc.log(message, loglevel);
+        } else {
+            if (loglevel > Project.MSG_WARN) {
+                System.out.println(message);
+            } else {
+                System.err.println(message);
+            }
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/LineTokenizer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/LineTokenizer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/LineTokenizer.java	(revision 16139)
@@ -0,0 +1,113 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.Reader;
+import java.io.IOException;
+
+import org.apache.tools.ant.ProjectComponent;
+
+/**
+ * class to tokenize the input as lines seperated
+ * by \r (mac style), \r\n (dos/windows style) or \n (unix style)
+ * @since Ant 1.6
+ */
+public class LineTokenizer extends ProjectComponent
+    implements Tokenizer {
+    private String  lineEnd = "";
+    private int     pushed = -2;
+    private boolean includeDelims = false;
+
+    /**
+     * attribute includedelims - whether to include
+     * the line ending with the line, or to return
+     * it in the posttoken
+     * default false
+     * @param includeDelims if true include /r and /n in the line
+     */
+
+    public void setIncludeDelims(boolean includeDelims) {
+        this.includeDelims = includeDelims;
+    }
+
+    /**
+     * get the next line from the input
+     *
+     * @param in the input reader
+     * @return the line excluding /r or /n, unless includedelims is set
+     * @exception IOException if an error occurs reading
+     */
+    public String getToken(Reader in) throws IOException {
+        int ch = -1;
+        if (pushed != -2) {
+            ch = pushed;
+            pushed = -2;
+        } else {
+            ch = in.read();
+        }
+        if (ch == -1) {
+            return null;
+        }
+
+        lineEnd = "";
+        StringBuffer line = new StringBuffer();
+
+        int state = 0;
+        while (ch != -1) {
+            if (state == 0) {
+                if (ch == '\r') {
+                    state = 1;
+                } else if (ch == '\n') {
+                    lineEnd = "\n";
+                    break;
+                } else {
+                    line.append((char) ch);
+                }
+            } else {
+                state = 0;
+                if (ch == '\n') {
+                    lineEnd = "\r\n";
+                } else {
+                    pushed = ch;
+                    lineEnd = "\r";
+                }
+                break;
+            }
+            ch = in.read();
+        }
+        if (ch == -1 && state == 1) {
+            lineEnd = "\r";
+        }
+
+        if (includeDelims) {
+            line.append(lineEnd);
+        }
+        return line.toString();
+    }
+
+    /**
+     * @return the line ending character(s) for the current line
+     */
+    public String getPostToken() {
+        if (includeDelims) {
+            return "";
+        }
+        return lineEnd;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/LoaderUtils.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/LoaderUtils.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/LoaderUtils.java	(revision 16139)
@@ -0,0 +1,112 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.File;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.launch.Locator;
+
+/**
+ * ClassLoader utility methods
+ *
+ */
+public class LoaderUtils {
+    /**
+     * Set the context classloader
+     *
+     * @param loader the ClassLoader to be used as the context class loader
+     *      on the current thread.
+     */
+    public static void setContextClassLoader(ClassLoader loader) {
+        Thread currentThread = Thread.currentThread();
+        currentThread.setContextClassLoader(loader);
+    }
+
+
+    /**
+     * JDK1.1 compatible access to set the context class loader.
+     *
+     * @return the ClassLoader instance being used as the context
+     *      classloader on the current thread. Returns null on JDK 1.1
+     */
+    public static ClassLoader getContextClassLoader() {
+        Thread currentThread = Thread.currentThread();
+        return currentThread.getContextClassLoader();
+    }
+
+    /**
+     * Indicates if the context class loader methods are available
+     *
+     * @return true if the get and set methods dealing with the context
+     *      classloader are available.
+     */
+    public static boolean isContextLoaderAvailable() {
+        return true;
+    }
+
+    /**
+     * Normalize a source location
+     *
+     * @param source the source location to be normalized.
+     *
+     * @return the normalized source location.
+     */
+    private static File normalizeSource(File source) {
+        if (source != null) {
+            FileUtils fileUtils = FileUtils.newFileUtils();
+            try {
+                source = fileUtils.normalize(source.getAbsolutePath());
+            } catch (BuildException e) {
+                // relative path
+            }
+        }
+
+        return source;
+    }
+
+    /**
+     * Find the directory or jar file the class has been loaded from.
+     *
+     * @param c the class whose location is required.
+     * @return the file or jar with the class or null if we cannot
+     *         determine the location.
+     *
+     * @since Ant 1.6
+     */
+    public static File getClassSource(Class c) {
+        return normalizeSource(Locator.getClassSource(c));
+    }
+
+    /**
+     * Find the directory or a give resource has been loaded from.
+     *
+     * @param c the classloader to be consulted for the source
+     * @param resource the resource whose location is required.
+     *
+     * @return the file with the resource source or null if
+     *         we cannot determine the location.
+     *
+     * @since Ant 1.6
+     */
+    public static File getResourceSource(ClassLoader c, String resource) {
+        if (c == null) {
+            c = LoaderUtils.class.getClassLoader();
+        }
+        return normalizeSource(Locator.getResourceSource(c, resource));
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/MergingMapper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/MergingMapper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/MergingMapper.java	(revision 16139)
@@ -0,0 +1,51 @@
+/*
+ * Copyright  2000,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+/**
+ * Implementation of FileNameMapper that always returns the same
+ * target file name.
+ *
+ * <p>This is the default FileNameMapper for the archiving tasks and
+ * uptodate.</p>
+ *
+ */
+public class MergingMapper implements FileNameMapper {
+    protected String[] mergedFile = null;
+
+    /**
+     * Ignored.
+     */
+    public void setFrom(String from) {
+    }
+
+    /**
+     * Sets the name of the merged file.
+     */
+    public void setTo(String to) {
+        mergedFile = new String[] {to};
+    }
+
+    /**
+     * Returns an one-element array containing the file name set via setTo.
+     */
+    public String[] mapFileName(String sourceFileName) {
+        return mergedFile;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/OutputStreamFunneler.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/OutputStreamFunneler.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/OutputStreamFunneler.java	(revision 16139)
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Manages a set of <CODE>OutputStream</CODE>s to
+ * write to a single underlying stream, which is
+ * closed only when the last &quot;funnel&quot;
+ * has been closed.
+ */
+public class OutputStreamFunneler {
+
+    /**
+     * Default timeout.
+     * @see #setTimeout(long)
+     */
+    public static final long DEFAULT_TIMEOUT_MILLIS = 1000;
+
+    private final class Funnel extends OutputStream {
+        private boolean closed = false;
+
+        private Funnel() {
+            synchronized (OutputStreamFunneler.this) {
+                ++count;
+            }
+        }
+
+        public void flush() throws IOException {
+            synchronized (OutputStreamFunneler.this) {
+                dieIfClosed();
+                out.flush();
+            }
+        }
+
+        public void write(int b) throws IOException {
+            synchronized (OutputStreamFunneler.this) {
+                dieIfClosed();
+                out.write(b);
+            }
+        }
+
+        public void write(byte[] b) throws IOException {
+            synchronized (OutputStreamFunneler.this) {
+                dieIfClosed();
+                out.write(b);
+            }
+        }
+
+        public void write(byte[] b, int off, int len) throws IOException {
+            synchronized (OutputStreamFunneler.this) {
+                dieIfClosed();
+                out.write(b, off, len);
+            }
+        }
+
+        public void close() throws IOException {
+            release(this);
+        }
+    }
+
+    private OutputStream out;
+    private int count = 0;
+    private boolean closed;
+    private long timeoutMillis;
+
+    /**
+     * Create a new <CODE>OutputStreamFunneler</CODE> for
+     * the specified <CODE>OutputStream</CODE>.
+     * @param out   <CODE>OutputStream</CODE>.
+     */
+    public OutputStreamFunneler(OutputStream out) {
+        this(out, DEFAULT_TIMEOUT_MILLIS);
+    }
+
+    /**
+     * Create a new <CODE>OutputStreamFunneler</CODE> for
+     * the specified <CODE>OutputStream</CODE>, with the
+     * specified timeout value.
+     * @param out             <CODE>OutputStream</CODE>.
+     * @param timeoutMillis   <CODE>long</CODE>.
+     * @see #setTimeout(long)
+     */
+    public OutputStreamFunneler(OutputStream out, long timeoutMillis) {
+        if (out == null) {
+            throw new IllegalArgumentException(
+                "OutputStreamFunneler.<init>:  out == null");
+        }
+        this.out = out;
+        this.closed = false; //as far as we know
+        setTimeout(timeoutMillis);
+    }
+
+    /**
+     * Set the timeout for this <CODE>OutputStreamFunneler</CODE>.
+     * This is the maximum time that may elapse between the closure
+     * of the last &quot;funnel&quot; and the next call to
+     * <CODE>getOutputStream()</CODE> without closing the
+     * underlying stream.
+     * @param timeoutMillis   <CODE>long</CODE> timeout value.
+     */
+    public synchronized void setTimeout(long timeoutMillis) {
+        this.timeoutMillis = timeoutMillis;
+    }
+
+    /**
+     * Get a &quot;funnel&quot; <CODE>OutputStream</CODE> instance to
+     * write to this <CODE>OutputStreamFunneler</CODE>'s underlying
+     * <CODE>OutputStream</CODE>.
+     * @return <code>OutputStream</code>.
+     * @throws IOException if unable to create the funnel.
+     */
+    public synchronized OutputStream getFunnelInstance()
+        throws IOException {
+        dieIfClosed();
+        try {
+            return new Funnel();
+        } finally {
+            notifyAll();
+        }
+    }
+
+    private synchronized void release(Funnel funnel) throws IOException {
+        //ignore release of an already-closed funnel
+        if (!funnel.closed) {
+            try {
+                if (timeoutMillis > 0) {
+                    try {
+                        wait(timeoutMillis);
+                    } catch (InterruptedException eyeEx) {
+                        //ignore
+                    }
+                }
+                if (--count == 0) {
+                    close();
+                }
+            } finally {
+                funnel.closed = true;
+            }
+        }
+   }
+
+    private synchronized void close() throws IOException {
+        try {
+            dieIfClosed();
+            out.close();
+        } finally {
+            closed = true;
+        }
+    }
+
+    private synchronized void dieIfClosed() throws IOException {
+        if (closed) {
+            throw new IOException("The funneled OutputStream has been closed.");
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/PackageNameMapper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/PackageNameMapper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/PackageNameMapper.java	(revision 16139)
@@ -0,0 +1,45 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.File;
+
+/**
+ * Maps directory name matches into a dotted package name. This is
+ * useful for matching JUnit test cases againt their XML formatter
+ * results.
+ * <pre>
+ * &lt;mapper classname="org.apache.tools.ant.util.PackageNameMapper"
+ *         from="*Test.java" to="${test.data.dir}/TEST-*Test.xml"/&gt;
+ * </pre>
+ *
+ */
+public class PackageNameMapper extends GlobPatternMapper {
+    /**
+     *  Returns the part of the given string that matches the * in the
+     *  &quot;from&quot; pattern replacing file separators with dots
+     *
+     *@param  name  Source filename
+     *@return       Replaced variable part
+     */
+    protected String extractVariablePart(String name) {
+        String var = name.substring(prefixLength,
+                name.length() - postfixLength);
+        return var.replace(File.separatorChar, '.');
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ReaderInputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ReaderInputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ReaderInputStream.java	(revision 16139)
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2004 The Apache Software Foundation.
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * Adapts a <code>Reader</code> as an <code>InputStream</code>.
+ * Adapted from <CODE>StringInputStream</CODE>.
+ *
+ */
+public class ReaderInputStream extends InputStream {
+
+    /** Source Reader */
+    private Reader in;
+
+    private String encoding = System.getProperty("file.encoding");
+
+    private byte[] slack;
+
+    private int begin;
+
+    /**
+     * Construct a <CODE>ReaderInputStream</CODE>
+     * for the specified <CODE>Reader</CODE>.
+     *
+     * @param reader   <CODE>Reader</CODE>.  Must not be <code>null</code>.
+     */
+    public ReaderInputStream(Reader reader) {
+        in = reader;
+    }
+
+    /**
+     * Construct a <CODE>ReaderInputStream</CODE>
+     * for the specified <CODE>Reader</CODE>,
+     * with the specified encoding.
+     *
+     * @param reader     non-null <CODE>Reader</CODE>.
+     * @param encoding   non-null <CODE>String</CODE> encoding.
+     */
+    public ReaderInputStream(Reader reader, String encoding) {
+        this(reader);
+        if (encoding == null) {
+            throw new IllegalArgumentException("encoding must not be null");
+        } else {
+            this.encoding = encoding;
+        }
+    }
+
+    /**
+     * Reads from the <CODE>Reader</CODE>, returning the same value.
+     *
+     * @return the value of the next character in the <CODE>Reader</CODE>.
+     *
+     * @exception IOException if the original <code>Reader</code> fails to be read
+     */
+    public synchronized int read() throws IOException {
+        if (in == null) {
+            throw new IOException("Stream Closed");
+        }
+
+        byte result;
+        if (slack != null && begin < slack.length) {
+            result = slack[begin];
+            if (++begin == slack.length) {
+                slack = null;
+            }
+        } else {
+            byte[] buf = new byte[1];
+            if (read(buf, 0, 1) <= 0) {
+                result = -1;
+            }
+            result = buf[0];
+        }
+
+        if (result < -1) {
+            result+= 256;
+        }
+
+        return result;
+    }
+
+    /**
+     * Reads from the <code>Reader</code> into a byte array
+     *
+     * @param b  the byte array to read into
+     * @param off the offset in the byte array
+     * @param len the length in the byte array to fill
+     * @return the actual number read into the byte array, -1 at
+     *         the end of the stream
+     * @exception IOException if an error occurs
+     */
+    public synchronized int read(byte[] b, int off, int len)
+        throws IOException {
+        if (in == null) {
+            throw new IOException("Stream Closed");
+        }
+
+        while (slack == null) {
+            char[] buf = new char[len]; // might read too much
+            int n = in.read(buf);
+            if (n == -1) {
+                return -1;
+            }
+            if (n > 0) {
+                slack = new String(buf, 0, n).getBytes(encoding);
+                begin = 0;
+            }
+        }
+
+        if (len > slack.length - begin) {
+            len = slack.length - begin;
+        }
+
+        System.arraycopy(slack, begin, b, off, len);
+
+        if ((begin += len) >= slack.length) {
+            slack = null;
+        }
+
+        return len;
+    }
+
+    /**
+     * Marks the read limit of the StringReader.
+     *
+     * @param limit the maximum limit of bytes that can be read before the
+     *              mark position becomes invalid
+     */
+    public synchronized void mark(final int limit) {
+        try {
+            in.mark(limit);
+        } catch (IOException ioe) {
+            throw new RuntimeException(ioe.getMessage());
+        }
+    }
+
+
+    /**
+     * @return   the current number of bytes ready for reading
+     * @exception IOException if an error occurs
+     */
+    public synchronized int available() throws IOException {
+        if (in == null) {
+            throw new IOException("Stream Closed");
+        }
+        if (slack != null) {
+            return slack.length - begin;
+        }
+        if (in.ready()) {
+            return 1;
+        } else {
+            return 0;
+        }
+    }
+
+    /**
+     * @return false - mark is not supported
+     */
+    public boolean markSupported () {
+        return false;   // would be imprecise
+    }
+
+    /**
+     * Resets the StringReader.
+     *
+     * @exception IOException if the StringReader fails to be reset
+     */
+    public synchronized void reset() throws IOException {
+        if (in == null) {
+            throw new IOException("Stream Closed");
+        }
+        slack = null;
+        in.reset();
+    }
+
+    /**
+     * Closes the Stringreader.
+     *
+     * @exception IOException if the original StringReader fails to be closed
+     */
+    public synchronized void close() throws IOException {
+        in.close();
+        slack = null;
+        in = null;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/RegexpPatternMapper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/RegexpPatternMapper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/RegexpPatternMapper.java	(revision 16139)
@@ -0,0 +1,147 @@
+/*
+ * Copyright  2000,2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.regexp.RegexpMatcher;
+import org.apache.tools.ant.util.regexp.RegexpMatcherFactory;
+
+/**
+ * Implementation of FileNameMapper that does regular expression
+ * replacements.
+ *
+ */
+public class RegexpPatternMapper implements FileNameMapper {
+    protected RegexpMatcher reg = null;
+    protected char[] to = null;
+    protected StringBuffer result = new StringBuffer();
+
+    /**
+     * Constructor for RegexpPatternMapper.
+     * @throws BuildException on error.
+     */
+    public RegexpPatternMapper() throws BuildException {
+        reg = (new RegexpMatcherFactory()).newRegexpMatcher();
+    }
+
+    private boolean handleDirSep = false;
+    private int     regexpOptions = 0;
+
+    /**
+     * Attribute specifing whether to ignore the difference
+     * between / and \ (the two common directory characters).
+     * @param handleDirSep a boolean, default is false.
+     * @since Ant 1.6.3
+     */
+    public void setHandleDirSep(boolean handleDirSep) {
+        this.handleDirSep = handleDirSep;
+    }
+
+    /**
+     * Attribute specifing whether to ignore the case difference
+     * in the names.
+     *
+     * @param caseSensitive a boolean, default is false.
+     * @since Ant 1.6.3
+     */
+    public void setCaseSensitive(boolean caseSensitive) {
+        if (!caseSensitive) {
+            regexpOptions = RegexpMatcher.MATCH_CASE_INSENSITIVE;
+        } else {
+            regexpOptions = 0;
+        }
+    }
+
+    /**
+     * Sets the &quot;from&quot; pattern. Required.
+     * @param from the from pattern.
+     * @throws BuildException on error.
+     */
+    public void setFrom(String from) throws BuildException {
+        try {
+            reg.setPattern(from);
+        } catch (NoClassDefFoundError e) {
+            // depending on the implementation the actual RE won't
+            // get instantiated in the constructor.
+            throw new BuildException("Cannot load regular expression matcher",
+                                     e);
+        }
+    }
+
+    /**
+     * Sets the &quot;to&quot; pattern. Required.
+     * @param to the to pattern.
+     * @throws BuildException on error.
+     */
+    public void setTo(String to) {
+        this.to = to.toCharArray();
+    }
+
+    /**
+     * Returns null if the source file name doesn't match the
+     * &quot;from&quot; pattern, an one-element array containing the
+     * translated file otherwise.
+     * @param sourceFileName the source file name
+     * @return a one-element array containing the translated file or
+     *         null if the to pattern did not match
+     */
+    public String[] mapFileName(String sourceFileName) {
+        if (handleDirSep) {
+            if (sourceFileName.indexOf("\\") != -1) {
+                sourceFileName = sourceFileName.replace('\\', '/');
+            }
+        }
+        if (reg == null  || to == null
+            || !reg.matches(sourceFileName, regexpOptions)) {
+            return null;
+        }
+        return new String[] {replaceReferences(sourceFileName)};
+    }
+
+    /**
+     * Replace all backreferences in the to pattern with the matched
+     * groups of the source.
+     * @param source the source file name.
+     * @return the translated file name.
+     */
+    protected String replaceReferences(String source) {
+        Vector v = reg.getGroups(source, regexpOptions);
+
+        result.setLength(0);
+        for (int i = 0; i < to.length; i++) {
+            if (to[i] == '\\') {
+                if (++i < to.length) {
+                    int value = Character.digit(to[i], 10);
+                    if (value > -1) {
+                        result.append((String) v.elementAt(value));
+                    } else {
+                        result.append(to[i]);
+                    }
+                } else {
+                    // XXX - should throw an exception instead?
+                    result.append('\\');
+                }
+            } else {
+                result.append(to[i]);
+            }
+        }
+        return result.substring(0);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ResourceUtils.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ResourceUtils.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ResourceUtils.java	(revision 16139)
@@ -0,0 +1,143 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceFactory;
+import org.apache.tools.ant.types.selectors.SelectorUtils;
+
+import java.io.File;
+import java.util.Vector;
+
+/**
+ * this class provides utility methods to process resources
+ *
+ * @since Ant 1.5.2
+ */
+public class ResourceUtils {
+
+    /**
+     * tells which source files should be reprocessed based on the
+     * last modification date of target files
+     * @param logTo where to send (more or less) interesting output
+     * @param source array of resources bearing relative path and last
+     * modification date
+     * @param mapper filename mapper indicating how to find the target
+     * files
+     * @param targets object able to map as a resource a relative path
+     * at <b>destination</b>
+     * @return array containing the source files which need to be
+     * copied or processed, because the targets are out of date or do
+     * not exist
+     */
+    public static Resource[] selectOutOfDateSources(ProjectComponent logTo,
+                                                    Resource[] source,
+                                                    FileNameMapper mapper,
+                                                    ResourceFactory targets) {
+        return selectOutOfDateSources(logTo, source, mapper, targets,
+                                      FileUtils.newFileUtils()
+                                      .getFileTimestampGranularity());
+    }
+
+    /**
+     * tells which source files should be reprocessed based on the
+     * last modification date of target files
+     * @param logTo where to send (more or less) interesting output
+     * @param source array of resources bearing relative path and last
+     * modification date
+     * @param mapper filename mapper indicating how to find the target
+     * files
+     * @param targets object able to map as a resource a relative path
+     * at <b>destination</b>
+     * @param granularity The number of milliseconds leeway to give
+     * before deciding a target is out of date.
+     * @return array containing the source files which need to be
+     * copied or processed, because the targets are out of date or do
+     * not exist
+     * @since Ant 1.6.2
+     */
+    public static Resource[] selectOutOfDateSources(ProjectComponent logTo,
+                                                    Resource[] source,
+                                                    FileNameMapper mapper,
+                                                    ResourceFactory targets,
+                                                    long granularity) {
+        long now = (new java.util.Date()).getTime() + granularity;
+
+        Vector vresult = new Vector();
+        for (int counter = 0; counter < source.length; counter++) {
+            if (source[counter].getLastModified() > now) {
+                logTo.log("Warning: " + source[counter].getName()
+                         + " modified in the future.",
+                         Project.MSG_WARN);
+            }
+
+            String[] targetnames =
+                mapper.mapFileName(source[counter].getName()
+                                   .replace('/', File.separatorChar));
+            if (targetnames != null) {
+                boolean added = false;
+                StringBuffer targetList = new StringBuffer();
+                for (int ctarget = 0; !added && ctarget < targetnames.length;
+                     ctarget++) {
+                    Resource atarget =
+                        targets.getResource(targetnames[ctarget]
+                                            .replace(File.separatorChar, '/'));
+                    // if the target does not exist, or exists and
+                    // is older than the source, then we want to
+                    // add the resource to what needs to be copied
+                    if (!atarget.isExists()) {
+                        logTo.log(source[counter].getName() + " added as "
+                                  + atarget.getName()
+                                  + " doesn\'t exist.", Project.MSG_VERBOSE);
+                        vresult.addElement(source[counter]);
+                        added = true;
+                    } else if (!atarget.isDirectory()
+                               && SelectorUtils.isOutOfDate(source[counter],
+                                                         atarget,
+                                                         (int) granularity)) {
+                        logTo.log(source[counter].getName() + " added as "
+                                  + atarget.getName()
+                                  + " is outdated.", Project.MSG_VERBOSE);
+                        vresult.addElement(source[counter]);
+                        added = true;
+                    } else {
+                        if (targetList.length() > 0) {
+                            targetList.append(", ");
+                        }
+                        targetList.append(atarget.getName());
+                    }
+                }
+
+                if (!added) {
+                    logTo.log(source[counter].getName()
+                              + " omitted as " + targetList.toString()
+                              + (targetnames.length == 1 ? " is" : " are ")
+                              + " up to date.", Project.MSG_VERBOSE);
+                }
+            } else {
+                logTo.log(source[counter].getName()
+                          + " skipped - don\'t know how to handle it",
+                          Project.MSG_VERBOSE);
+            }
+        }
+        Resource[] result = new Resource[vresult.size()];
+        vresult.copyInto(result);
+        return result;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ScriptRunner.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ScriptRunner.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/ScriptRunner.java	(revision 16139)
@@ -0,0 +1,193 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import org.apache.bsf.BSFException;
+import org.apache.bsf.BSFManager;
+import org.apache.tools.ant.BuildException;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.Iterator;
+
+/**
+ * This class is used to run BSF scripts
+ *
+ */
+public class ScriptRunner {
+
+    // Register Groovy ourselves, since BSF does not
+    // natively support it (yet).
+    // This "hack" can be removed once BSF has been
+    // modified to support Groovy or more dynamic
+    // registration.
+    static {
+        BSFManager.registerScriptingEngine(
+            "groovy",
+            "org.codehaus.groovy.bsf.GroovyEngine",
+            new String[] {"groovy", "gy"});
+    }
+
+    /** Script language */
+    private String language;
+
+    /** Script content */
+    private String script = "";
+
+    /** Beans to be provided to the script */
+    private Map beans = new HashMap();
+
+
+    /**
+     * Add a list of named objects to the list to be exported to the script
+     *
+     * @param dictionary a map of objects to be placed into the script context
+     *        indexed by String names.
+     */
+    public void addBeans(Map dictionary) {
+        for (Iterator i = dictionary.keySet().iterator(); i.hasNext();) {
+            String key = (String) i.next();
+            try {
+                Object val = dictionary.get(key);
+                addBean(key, val);
+            } catch (BuildException ex) {
+                // The key is in the dictionary but cannot be retrieved
+                // This is usually due references that refer to tasks
+                // that have not been taskdefed in the current run.
+                // Ignore
+            }
+        }
+    }
+
+    /**
+     * Add a single object into the script context.
+     *
+     * @param key the name in the context this object is to stored under.
+     * @param bean the object to be stored in the script context.
+     */
+    public void addBean(String key, Object bean) {
+        boolean isValid = key.length() > 0
+            && Character.isJavaIdentifierStart(key.charAt(0));
+
+        for (int i = 1; isValid && i < key.length(); i++) {
+            isValid = Character.isJavaIdentifierPart(key.charAt(i));
+        }
+
+        if (isValid) {
+            beans.put(key, bean);
+        }
+    }
+
+    /**
+     * Do the work.
+     *
+     * @param execName the name that will be passed to BSF for this script
+     *        execution.
+     *
+     * @exception BuildException if someting goes wrong exectuing the script.
+     */
+    public void executeScript(String execName) throws BuildException {
+        if (language == null) {
+            throw new BuildException("script language must be specified");
+        }
+
+        try {
+            BSFManager manager = new BSFManager ();
+
+            for (Iterator i = beans.keySet().iterator(); i.hasNext();) {
+                String key = (String) i.next();
+                Object value = beans.get(key);
+                if (value != null) {
+                    manager.declareBean(key, value, value.getClass());
+                } else {
+                    // BSF uses a hashtable to store values
+                    // so cannot declareBean with a null value
+                    // So need to remove any bean of this name as
+                    // that bean should not be visible
+                    manager.undeclareBean(key);
+                }
+            }
+
+            // execute the script
+            manager.exec(language, execName, 0, 0, script);
+        } catch (BSFException be) {
+            Throwable t = be;
+            Throwable te = be.getTargetException();
+            if (te != null) {
+                if  (te instanceof BuildException) {
+                    throw (BuildException) te;
+                } else {
+                    t = te;
+                }
+            }
+            throw new BuildException(t);
+        }
+    }
+
+    /**
+     * Defines the language (required).
+     *
+     * @param language the scripting language name for the script.
+     */
+    public void setLanguage(String language) {
+        this.language = language;
+    }
+
+    /**
+     * Get the script language
+     *
+     * @return the script language
+     */
+    public String getLanguage() {
+        return language;
+    }
+
+    /**
+     * Load the script from an external file ; optional.
+     *
+     * @param file the file containing the script source.
+     */
+    public void setSrc(File file) {
+        if (!file.exists()) {
+            throw new BuildException("file " + file.getPath() + " not found.");
+        }
+
+        int count = (int) file.length();
+        byte[] data = new byte[count];
+
+        try {
+            FileInputStream inStream = new FileInputStream(file);
+            inStream.read(data);
+            inStream.close();
+        } catch (IOException e) {
+            throw new BuildException(e);
+        }
+
+        script += new String(data);
+    }
+
+    /**
+     * Set the script text.
+     *
+     * @param text a component of the script text to be added.
+     */
+    public void addText(String text) {
+        this.script += text;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/SourceFileScanner.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/SourceFileScanner.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/SourceFileScanner.java	(revision 16139)
@@ -0,0 +1,148 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import java.io.File;
+import java.util.Vector;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.ResourceFactory;
+import org.apache.tools.ant.types.Resource;
+
+/**
+ * Utility class that collects the functionality of the various
+ * scanDir methods that have been scattered in several tasks before.
+ *
+ * <p>The only method returns an array of source files. The array is a
+ * subset of the files given as a parameter and holds only those that
+ * are newer than their corresponding target files.</p>
+ *
+ */
+public class SourceFileScanner implements ResourceFactory {
+
+    protected Task task;
+
+    private FileUtils fileUtils;
+    private File destDir;     // base directory of the fileset
+
+    /**
+     * @param task The task we should log messages through
+     */
+    public SourceFileScanner(Task task) {
+        this.task = task;
+        fileUtils = FileUtils.newFileUtils();
+    }
+
+    /**
+     * Restrict the given set of files to those that are newer than
+     * their corresponding target files.
+     *
+     * @param files   the original set of files
+     * @param srcDir  all files are relative to this directory
+     * @param destDir target files live here. if null file names
+     *                returned by the mapper are assumed to be absolute.
+     * @param mapper  knows how to construct a target file names from
+     *                source file names.
+     */
+    public String[] restrict(String[] files, File srcDir, File destDir,
+                             FileNameMapper mapper) {
+        return restrict(files, srcDir, destDir, mapper,
+                        fileUtils.getFileTimestampGranularity());
+    }
+
+    /**
+     * Restrict the given set of files to those that are newer than
+     * their corresponding target files.
+     *
+     * @param files   the original set of files
+     * @param srcDir  all files are relative to this directory
+     * @param destDir target files live here. if null file names
+     *                returned by the mapper are assumed to be absolute.
+     * @param mapper  knows how to construct a target file names from
+     *                source file names.
+     * @param granularity The number of milliseconds leeway to give
+     *                    before deciding a target is out of date.
+     *
+     * @since Ant 1.6.2
+     */
+    public String[] restrict(String[] files, File srcDir, File destDir,
+                             FileNameMapper mapper, long granularity) {
+        // record destdir for later use in getResource
+        this.destDir = destDir;
+        Vector v = new Vector();
+        for (int i = 0; i < files.length; i++) {
+            File src = fileUtils.resolveFile(srcDir, files[i]);
+            v.addElement(new Resource(files[i], src.exists(),
+                                      src.lastModified(), src.isDirectory()));
+        }
+        Resource[] sourceresources = new Resource[v.size()];
+        v.copyInto(sourceresources);
+
+        // build the list of sources which are out of date with
+        // respect to the target
+        Resource[] outofdate =
+            ResourceUtils.selectOutOfDateSources(task, sourceresources,
+                                                 mapper, this, granularity);
+        String[] result = new String[outofdate.length];
+        for (int counter = 0; counter < outofdate.length; counter++) {
+            result[counter] = outofdate[counter].getName();
+        }
+        return result;
+    }
+
+    /**
+     * Convinience layer on top of restrict that returns the source
+     * files as File objects (containing absolute paths if srcDir is
+     * absolute).
+     */
+    public File[] restrictAsFiles(String[] files, File srcDir, File destDir,
+                                  FileNameMapper mapper) {
+        return restrictAsFiles(files, srcDir, destDir, mapper,
+                               fileUtils.getFileTimestampGranularity());
+    }
+
+    /**
+     * Convinience layer on top of restrict that returns the source
+     * files as File objects (containing absolute paths if srcDir is
+     * absolute).
+     *
+     * @since Ant 1.6.2
+     */
+    public File[] restrictAsFiles(String[] files, File srcDir, File destDir,
+                                  FileNameMapper mapper, long granularity) {
+        String[] res = restrict(files, srcDir, destDir, mapper, granularity);
+        File[] result = new File[res.length];
+        for (int i = 0; i < res.length; i++) {
+            result[i] = new File(srcDir, res[i]);
+        }
+        return result;
+    }
+
+    /**
+     * returns resource information for a file at destination
+     * @param name relative path of file at destination
+     * @return data concerning a file whose relative path to destDir is name
+     *
+     * @since Ant 1.5.2
+     */
+    public Resource getResource(String name) {
+        File src = fileUtils.resolveFile(destDir, name);
+        return new Resource(name, src.exists(), src.lastModified(),
+                            src.isDirectory());
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/StringUtils.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/StringUtils.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/StringUtils.java	(revision 16139)
@@ -0,0 +1,95 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Vector;
+
+/**
+ * A set of helper methods related to string manipulation.
+ *
+ */
+public final class StringUtils {
+
+    /** the line separator for this OS */
+    public static final String LINE_SEP = System.getProperty("line.separator");
+
+    /**
+     * Splits up a string into a list of lines. It is equivalent
+     * to <tt>split(data, '\n')</tt>.
+     * @param data the string to split up into lines.
+     * @return the list of lines available in the string.
+     */
+    public static Vector lineSplit(String data) {
+        return split(data, '\n');
+    }
+
+    /**
+     * Splits up a string where elements are separated by a specific
+     * character and return all elements.
+     * @param data the string to split up.
+     * @param ch the separator character.
+     * @return the list of elements.
+     */
+    public static Vector split(String data, int ch) {
+        Vector elems = new Vector();
+        int pos = -1;
+        int i = 0;
+        while ((pos = data.indexOf(ch, i)) != -1) {
+            String elem = data.substring(i, pos);
+            elems.addElement(elem);
+            i = pos + 1;
+        }
+        elems.addElement(data.substring(i));
+        return elems;
+    }
+
+    /**
+     * Replace occurrences into a string.
+     * @param data the string to replace occurrences into
+     * @param from the occurrence to replace.
+     * @param to the occurrence to be used as a replacement.
+     * @return the new string with replaced occurrences.
+     */
+    public static String replace(String data, String from, String to) {
+        StringBuffer buf = new StringBuffer(data.length());
+        int pos = -1;
+        int i = 0;
+        while ((pos = data.indexOf(from, i)) != -1) {
+            buf.append(data.substring(i, pos)).append(to);
+            i = pos + from.length();
+        }
+        buf.append(data.substring(i));
+        return buf.toString();
+    }
+
+    /**
+     * Convenient method to retrieve the full stacktrace from a given exception.
+     * @param t the exception to get the stacktrace from.
+     * @return the stacktrace from the given exception.
+     */
+    public static String getStackTrace(Throwable t) {
+        StringWriter sw = new StringWriter();
+        PrintWriter pw = new PrintWriter(sw, true);
+        t.printStackTrace(pw);
+        pw.flush();
+        pw.close();
+        return sw.toString();
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/TaskLogger.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/TaskLogger.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/TaskLogger.java	(revision 16139)
@@ -0,0 +1,55 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * A facade that makes logging nicers to use.
+ *
+ */
+public final class TaskLogger {
+    /**
+     * Task to use to do logging.
+     */
+    private Task m_task;
+
+    public TaskLogger(final Task task) {
+        this.m_task = task;
+    }
+
+    public void info(final String message) {
+        m_task.log(message, Project.MSG_INFO);
+    }
+
+    public void error(final String message) {
+        m_task.log(message, Project.MSG_ERR);
+    }
+
+    public void warning(final String message) {
+        m_task.log(message, Project.MSG_WARN);
+    }
+
+    public void verbose(final String message) {
+        m_task.log(message, Project.MSG_VERBOSE);
+    }
+
+    public void debug(final String message) {
+        m_task.log(message, Project.MSG_DEBUG);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/TeeOutputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/TeeOutputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/TeeOutputStream.java	(revision 16139)
@@ -0,0 +1,94 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * A simple T-piece to replicate an output stream into two separate streams
+ *
+ */
+public class TeeOutputStream extends OutputStream {
+    private OutputStream left;
+    private OutputStream right;
+
+    /**
+     * Constructor for TeeOutputStream.
+     * @param left one of the output streams.
+     * @param right the other output stream.
+     */
+    public TeeOutputStream(OutputStream left, OutputStream right) {
+        this.left = left;
+        this.right = right;
+    }
+
+    /**
+     * Close both output streams.
+     * @throws IOException on error.
+     */
+    public void close() throws IOException {
+        try {
+            left.close();
+        } finally {
+            right.close();
+        }
+    }
+
+    /**
+     * Flush both output streams.
+     * @throws IOException on error
+     */
+    public void flush() throws IOException {
+        left.flush();
+        right.flush();
+    }
+
+    /**
+     * Write a byte array to both output streams.
+     * @param b an array of bytes.
+     * @throws IOException on error.
+     */
+    public void write(byte[] b) throws IOException {
+        left.write(b);
+        right.write(b);
+    }
+
+    /**
+     * Write a byte array to both output streams.
+     * @param b     the data.
+     * @param off   the start offset in the data.
+     * @param len   the number of bytes to write.
+     * @throws IOException on error.
+     */
+    public void write(byte[] b, int off, int len) throws IOException {
+        left.write(b, off, len);
+        right.write(b, off, len);
+    }
+
+    /**
+     * Write a byte to both output streams.
+     * @param b the byte to write.
+     * @throws IOException on error.
+     */
+    public void write(int b) throws IOException {
+        left.write(b);
+        right.write(b);
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/TimeoutObserver.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/TimeoutObserver.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/TimeoutObserver.java	(revision 16139)
@@ -0,0 +1,36 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+/**
+ * Interface for classes that want to be notified by Watchdog.
+ *
+ * @since Ant 1.5
+ *
+ * @see org.apache.tools.ant.util.Watchdog
+ *
+ */
+public interface TimeoutObserver {
+
+    /**
+     * Called when the watchdow times out.
+     *
+     * @param w the watchdog that timed out.
+     */
+    void timeoutOccured(Watchdog w);
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/Tokenizer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/Tokenizer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/Tokenizer.java	(revision 16139)
@@ -0,0 +1,44 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.Reader;
+import java.io.IOException;
+
+/**
+ * input stream tokenizers implement this interface
+ *
+ * @version Ant 1.6
+ */
+public interface Tokenizer {
+    /**
+     * get the next token from the input stream
+     * @param in the input stream
+     * @return the next token, or null for the end
+     *         of the stream
+     * @throws IOException if an error occurs
+     */
+    String getToken(Reader in)
+        throws IOException;
+
+    /**
+     * return the string between tokens, after the
+     * previous token.
+     * @return the intra-token string
+     */
+    String getPostToken();
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/UnPackageNameMapper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/UnPackageNameMapper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/UnPackageNameMapper.java	(revision 16139)
@@ -0,0 +1,47 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.File;
+
+/**
+ * Maps dotted package name matches to a directory name.
+ * This is the inverse of the package mapper.
+ * This is useful for matching XML formatter results against their JUnit test
+ * cases.
+ * <pre>
+ * &lt;mapper classname="org.apache.tools.ant.util.UnPackageNameMapper"
+ *         from="${test.data.dir}/TEST-*Test.xml" to="*Test.java"&gt;
+ * </pre>
+ *
+ *
+ */
+public class UnPackageNameMapper extends GlobPatternMapper {
+    /**
+     *  Returns the part of the given string that matches the * in the
+     *  &quot;from&quot; pattern replacing dots with file separators
+     *
+     *@param  name  Source filename
+     *@return       Replaced variable part
+     */
+    protected String extractVariablePart(String name) {
+        String var = name.substring(prefixLength,
+                name.length() - postfixLength);
+        return var.replace('.', File.separatorChar);
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/Watchdog.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/Watchdog.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/Watchdog.java	(revision 16139)
@@ -0,0 +1,113 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * Generalization of <code>ExecuteWatchdog</code>
+ *
+ * @since Ant 1.5
+ *
+ * @see org.apache.tools.ant.taskdefs.ExecuteWatchdog
+ *
+ */
+public class Watchdog implements Runnable {
+
+    private Vector observers = new Vector(1);
+    private long timeout = -1;
+    private boolean stopped = false;
+
+    /**
+     * Constructor for Watchdog.
+     * @param timeout the timeout to use in milliseconds (must be >= 1).
+     */
+    public Watchdog(long timeout) {
+        if (timeout < 1) {
+            throw new IllegalArgumentException("timeout less than 1.");
+        }
+        this.timeout = timeout;
+    }
+
+    /**
+     * Add a timeout observer.
+     * @param to the timeout observer to add.
+     */
+    public void addTimeoutObserver(TimeoutObserver to) {
+        observers.addElement(to);
+    }
+
+    /**
+     * Remove a timeout observer.
+     * @param to the timeout observer to remove.
+     */
+    public void removeTimeoutObserver(TimeoutObserver to) {
+        observers.removeElement(to);
+    }
+
+    /**
+     * Inform the observers that a timeout has occured.
+     */
+    protected final void fireTimeoutOccured() {
+        Enumeration e = observers.elements();
+        while (e.hasMoreElements()) {
+            ((TimeoutObserver) e.nextElement()).timeoutOccured(this);
+        }
+    }
+
+    /**
+     * Start the watch dog.
+     */
+    public synchronized void start() {
+        stopped = false;
+        Thread t = new Thread(this, "WATCHDOG");
+        t.setDaemon(true);
+        t.start();
+    }
+
+    /**
+     * Stop the watch dog.
+     */
+    public synchronized void stop() {
+        stopped = true;
+        notifyAll();
+    }
+
+    /**
+     * The run method of the watch dog thread.
+     * This simply does a wait for the timeout time, and
+     * if the stop flag has not been set when the wait has returned or
+     * has been interrupted, the watch dog listeners are informed.
+     */
+    public synchronized void run() {
+        final long until = System.currentTimeMillis() + timeout;
+        long now;
+        while (!stopped && until > (now = System.currentTimeMillis())) {
+            try {
+                wait(until - now);
+            } catch (InterruptedException e) {
+                // Ignore exception
+            }
+        }
+        if (!stopped) {
+            fireTimeoutOccured();
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/WeakishReference.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/WeakishReference.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/WeakishReference.java	(revision 16139)
@@ -0,0 +1,110 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import org.apache.tools.ant.BuildException;
+
+import java.lang.reflect.Constructor;
+
+
+/**
+ * this is a weak reference on java1.2 and up, a hard
+ * reference on java1.1
+ * @since ant1.6
+ */
+public abstract class WeakishReference  {
+
+    private static Constructor referenceConstructor;
+
+    private final static String WEAK_REFERENCE_NAME
+        = "org.apache.tools.ant.util.optional.WeakishReference12";
+
+    /**
+     * create the appropriate type of reference for the java version
+     * @param object
+     * @return reference to the Object.
+     */
+    public static WeakishReference createReference(Object object) {
+        if (referenceConstructor == null) {
+            createReferenceConstructor();
+        }
+        try {
+            return (WeakishReference) referenceConstructor
+                        .newInstance(new Object[]{object});
+        } catch (Exception e) {
+            throw new BuildException("while creating a weakish reference", e);
+        }
+    }
+
+    /**
+     * create the appropriate constructor method for the
+     */
+    private static void createReferenceConstructor() {
+        Class[] ctor = new Class[]{Object.class};
+        try {
+            referenceConstructor = HardReference.class.getConstructor(ctor);
+        } catch (NoSuchMethodException e) {
+            //deep trouble here
+            throw new BuildException("when creating a Hard Reference constructor", e);
+        }
+        if (!JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
+            //create a weak ref constructor. If this fails we have that hard one anyway
+            try {
+                Class clazz = Class.forName(WEAK_REFERENCE_NAME);
+                referenceConstructor = clazz.getConstructor(ctor);
+            } catch (ClassNotFoundException e) {
+                // ignore
+            } catch (NoSuchMethodException e) {
+                // ignore
+            }
+        }
+    }
+
+
+    /**
+     * Returns this reference object's referent.  If this reference object has
+     * been cleared, then this method returns <code>null</code>.
+     *
+     * @return	 The object to which this reference refers, or
+     *		 <code>null</code> if this reference object has been cleared
+     */
+    public abstract Object get();
+
+    /**
+     * A hard reference for Java 1.1
+     */
+    public static class HardReference extends WeakishReference {
+        private Object object;
+
+        /**
+         * construct
+         * @param object
+         */
+        public HardReference(Object object) {
+            this.object = object;
+        }
+
+        /**
+         * Returns this reference object's referent.
+         */
+        public Object get() {
+            return object;
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/depend/AbstractAnalyzer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/depend/AbstractAnalyzer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/depend/AbstractAnalyzer.java	(revision 16139)
@@ -0,0 +1,283 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util.depend;
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.zip.ZipFile;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * An abstract implementation of the analyzer interface providing support
+ * for the bulk of interface methods.
+ *
+ */
+public abstract class AbstractAnalyzer implements DependencyAnalyzer {
+    /** Maximum number of loops for looking for indirect dependencies. */
+    public static final int MAX_LOOPS = 1000;
+
+    /** The source path for the source files */
+    private Path sourcePath = new Path(null);
+
+    /** The classpath containg dirs and jars of class files */
+    private Path classPath = new Path(null);
+
+    /** The list of root classes */
+    private Vector rootClasses = new Vector();
+
+    /** true if dependencies have been determined */
+    private boolean determined = false;
+
+    /** the list of File objects that the root classes depend upon */
+    private Vector fileDependencies;
+    /** the list of java classes the root classes depend upon */
+    private Vector classDependencies;
+
+    /** true if indirect dependencies should be gathered */
+    private boolean closure = true;
+
+    /** Setup the analyzer */
+    protected AbstractAnalyzer() {
+        reset();
+    }
+
+    /**
+     * Set the closure flag. If this flag is true the analyzer will traverse
+     * all class relationships until it has collected the entire set of
+     * direct and indirect dependencies
+     *
+     * @param closure true if dependencies should be traversed to determine
+     *      indirect dependencies.
+     */
+    public void setClosure(boolean closure) {
+        this.closure = closure;
+    }
+
+    /**
+     * Get the list of files in the file system upon which the root classes
+     * depend. The files will be either the classfiles or jar files upon
+     * which the root classes depend.
+     *
+     * @return an enumeration of File instances.
+     */
+    public Enumeration getFileDependencies() {
+        if (!supportsFileDependencies()) {
+            throw new RuntimeException("File dependencies are not supported "
+                + "by this analyzer");
+        }
+        if (!determined) {
+            determineDependencies(fileDependencies, classDependencies);
+        }
+        return fileDependencies.elements();
+    }
+
+    /**
+     * Get the list of classes upon which root classes depend. This is a
+     * list of Java classnames in dot notation.
+     *
+     * @return an enumeration of Strings, each being the name of a Java
+     *      class in dot notation.
+     */
+    public Enumeration getClassDependencies() {
+        if (!determined) {
+            determineDependencies(fileDependencies, classDependencies);
+        }
+        return classDependencies.elements();
+    }
+
+    /**
+     * Get the file that contains the class definition
+     *
+     * @param classname the name of the required class
+     * @return the file instance, zip or class, containing the
+     *         class or null if the class could not be found.
+     * @exception IOException if the files in the classpath cannot be read.
+     */
+    public File getClassContainer(String classname) throws IOException {
+        String classLocation = classname.replace('.', '/') + ".class";
+        // we look through the classpath elements. If the element is a dir
+        // we look for the file. IF it is a zip, we look for the zip entry
+        return getResourceContainer(classLocation, classPath.list());
+    }
+
+    /**
+     * Get the file that contains the class source.
+     *
+     * @param classname the name of the required class
+     * @return the file instance, zip or java, containing the
+     *         source or null if the source for the class could not be found.
+     * @exception IOException if the files in the sourcepath cannot be read.
+     */
+    public File getSourceContainer(String classname) throws IOException {
+        String sourceLocation = classname.replace('.', '/') + ".java";
+
+        // we look through the source path elements. If the element is a dir
+        // we look for the file. If it is a zip, we look for the zip entry.
+        // This isn't normal for source paths but we get it for free
+        return getResourceContainer(sourceLocation, sourcePath.list());
+    }
+
+    /**
+     * Add a source path to the source path used by this analyzer. The
+     * elements in the given path contain the source files for the classes
+     * being analyzed. Not all analyzers will use this information.
+     *
+     * @param sourcePath The Path instance specifying the source path
+     *      elements.
+     */
+    public void addSourcePath(Path sourcePath) {
+        if (sourcePath == null) {
+            return;
+        }
+        this.sourcePath.append(sourcePath);
+        this.sourcePath.setProject(sourcePath.getProject());
+    }
+
+    /**
+     * Add a classpath to the classpath being used by the analyzer. The
+     * classpath contains the binary classfiles for the classes being
+     * analyzed The elements may either be the directories or jar files.Not
+     * all analyzers will use this information.
+     *
+     * @param classPath the Path instance specifying the classpath elements
+     */
+    public void addClassPath(Path classPath) {
+        if (classPath == null) {
+            return;
+        }
+
+        this.classPath.append(classPath);
+        this.classPath.setProject(classPath.getProject());
+    }
+
+    /**
+     * Add a root class. The root classes are used to drive the
+     * determination of dependency information. The analyzer will start at
+     * the root classes and add dependencies from there.
+     *
+     * @param className the name of the class in Java dot notation.
+     */
+    public void addRootClass(String className) {
+        if (className == null) {
+            return;
+        }
+        if (!rootClasses.contains(className)) {
+            rootClasses.addElement(className);
+        }
+    }
+
+    /**
+     * Configure an aspect of the analyzer. The set of aspects that are
+     * supported is specific to each analyzer instance.
+     *
+     * @param name the name of the aspect being configured
+     * @param info the configuration info.
+     */
+    public void config(String name, Object info) {
+        // do nothing by default
+    }
+
+    /**
+     * Reset the dependency list. This will reset the determined
+     * dependencies and the also list of root classes.
+     */
+    public void reset() {
+        rootClasses.removeAllElements();
+        determined = false;
+        fileDependencies = new Vector();
+        classDependencies = new Vector();
+    }
+
+    /**
+     * Get an enumeration of the root classes
+     *
+     * @return an enumeration of Strings, each of which is a class name
+     *         for a root class.
+     */
+    protected Enumeration getRootClasses() {
+        return rootClasses.elements();
+    }
+
+    /**
+     * Indicate if the analyzer is required to follow
+     * indirect class relationships.
+     *
+     * @return true if indirect relationships should be followed.
+     */
+    protected boolean isClosureRequired() {
+        return closure;
+    }
+
+    /**
+     * Determine the dependencies of the current set of root classes
+     *
+     * @param files a vector into which Files upon which the root classes
+     *      depend should be placed.
+     * @param classes a vector of Strings into which the names of classes
+     *      upon which the root classes depend should be placed.
+     */
+    protected abstract void determineDependencies(Vector files, Vector classes);
+
+    /**
+     * Indicate if the particular subclass supports file dependency
+     * information.
+     *
+     * @return true if file dependencies are supported.
+     */
+    protected abstract boolean supportsFileDependencies();
+
+    /**
+     * Get the file that contains the resource
+     *
+     * @param resourceLocation the name of the required resource.
+     * @param paths the paths which will be searched for the resource.
+     * @return the file instance, zip or class, containing the
+     *         class or null if the class could not be found.
+     * @exception IOException if the files in the given paths cannot be read.
+     */
+    private File getResourceContainer(String resourceLocation, String[] paths)
+         throws IOException {
+        for (int i = 0; i < paths.length; ++i) {
+            File element = new File(paths[i]);
+            if (!element.exists()) {
+                continue;
+            }
+            if (element.isDirectory()) {
+                File resource = new File(element, resourceLocation);
+                if (resource.exists()) {
+                    return resource;
+                }
+            } else {
+                // must be a zip of some sort
+                ZipFile zipFile = null;
+                try {
+                    zipFile = new ZipFile(element);
+                    if (zipFile.getEntry(resourceLocation) != null) {
+                        return element;
+                    }
+                } finally {
+                    if (zipFile != null) {
+                        zipFile.close();
+                    }
+                }
+            }
+        }
+        return null;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/depend/DependencyAnalyzer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/depend/DependencyAnalyzer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/depend/DependencyAnalyzer.java	(revision 16139)
@@ -0,0 +1,128 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util.depend;
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import org.apache.tools.ant.types.Path;
+
+/**
+ * A dependency analyzer analyzes dependencies between Java classes to
+ * determine the minimal set of classes which are required by a set of
+ * &quot;root&quot; classes. Different implementations of this interface can
+ * use different strategies and libraries to determine the required set. For
+ * example, some analyzers will use class files while others might use
+ * source files. Analyzer specific configuration is catered for through a
+ * generic configure method
+ *
+ */
+public interface DependencyAnalyzer {
+    /**
+     * Add a source path to the source path used by this analyzer. The
+     * elements in the given path contain the source files for the classes
+     * being analyzed. Not all analyzers will use this information.
+     *
+     * @param sourcePath The Path instance specifying the source path
+     *      elements.
+     */
+    void addSourcePath(Path sourcePath);
+
+    /**
+     * Add a classpath to the classpath being used by the analyzer. The
+     * classpath contains the binary classfiles for the classes being
+     * analyzed The elements may either be the directories or jar files.Not
+     * all analyzers will use this information.
+     *
+     * @param classpath the Path instance specifying the classpath elements
+     */
+    void addClassPath(Path classpath);
+
+    /**
+     * Add a root class. The root classes are used to drive the
+     * determination of dependency information. The analyzer will start at
+     * the root classes and add dependencies from there.
+     *
+     * @param classname the name of the class in Java dot notation.
+     */
+    void addRootClass(String classname);
+
+    /**
+     * Get the list of files in the file system upon which the root classes
+     * depend. The files will be either the classfiles or jar files upon
+     * which the root classes depend.
+     *
+     * @return an enumeration of File instances.
+     */
+    Enumeration getFileDependencies();
+
+    /**
+     * Get the list of classes upon which root classes depend. This is a
+     * list of Java classnames in dot notation.
+     *
+     * @return an enumeration of Strings, each being the name of a Java
+     *      class in dot notation.
+     */
+    Enumeration getClassDependencies();
+
+
+    /**
+     * Reset the dependency list. This will reset the determined
+     * dependencies and the also list of root classes.
+     */
+    void reset();
+
+    /**
+     * Configure an aspect of the analyzer. The set of aspects that are
+     * supported is specific to each analyzer instance.
+     *
+     * @param name the name of the aspect being configured
+     * @param info the configuration information.
+     */
+    void config(String name, Object info);
+
+    /**
+     * Set the closure flag. If this flag is true the analyzer will traverse
+     * all class relationships until it has collected the entire set of
+     * direct and indirect dependencies
+     *
+     * @param closure true if dependencies should be traversed to determine
+     *      indirect dependencies.
+     */
+    void setClosure(boolean closure);
+
+
+    /**
+     * Get the file that contains the class definition
+     *
+     * @param classname the name of the required class
+     * @return the file instance, zip or class, containing the
+     *         class or null if the class could not be found.
+     * @exception IOException if the files in the classpath cannot be read.
+     */
+    File getClassContainer(String classname) throws IOException;
+
+    /**
+     * Get the file that contains the class source.
+     *
+     * @param classname the name of the required class
+     * @return the file instance, zip or java, containing the
+     *         source or null if the source for the class could not be found.
+     * @exception IOException if the files in the sourcepath cannot be read.
+     */
+    File getSourceContainer(String classname) throws IOException;
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/depend/bcel/AncestorAnalyzer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/depend/bcel/AncestorAnalyzer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/depend/bcel/AncestorAnalyzer.java	(revision 16139)
@@ -0,0 +1,137 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util.depend.bcel;
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import org.apache.bcel.classfile.ClassParser;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.tools.ant.util.depend.AbstractAnalyzer;
+
+/**
+ * A dependency analyzer which returns superclass and superinterface
+ * dependencies.
+ *
+ */
+public class AncestorAnalyzer extends AbstractAnalyzer {
+
+    /**
+     * Default constructor
+     *
+     * Causes the BCEL classes to load to ensure BCEL dependencies can
+     * be satisfied
+     */
+    public AncestorAnalyzer() {
+        // force BCEL classes to load now
+        try {
+            new ClassParser("force");
+        } catch (IOException e) {
+            // ignore
+        }
+    }
+
+    /**
+     * Determine the dependencies of the configured root classes.
+     *
+     * @param files a vector to be populated with the files which contain
+     *      the dependency classes
+     * @param classes a vector to be populated with the names of the
+     *      depencency classes.
+     */
+    protected void determineDependencies(Vector files, Vector classes) {
+        // we get the root classes and build up a set of
+        // classes upon which they depend
+        Hashtable dependencies = new Hashtable();
+        Hashtable containers = new Hashtable();
+        Hashtable toAnalyze = new Hashtable();
+        Hashtable nextAnalyze = new Hashtable();
+        for (Enumeration e = getRootClasses(); e.hasMoreElements();) {
+            String classname = (String) e.nextElement();
+            toAnalyze.put(classname, classname);
+        }
+
+        int count = 0;
+        int maxCount = isClosureRequired() ? MAX_LOOPS : 2;
+        while (toAnalyze.size() != 0 && count++ < maxCount) {
+            nextAnalyze.clear();
+            for (Enumeration e = toAnalyze.keys(); e.hasMoreElements();) {
+                String classname = (String) e.nextElement();
+                dependencies.put(classname, classname);
+                try {
+                    File container = getClassContainer(classname);
+                    if (container == null) {
+                        continue;
+                    }
+                    containers.put(container, container);
+
+                    ClassParser parser = null;
+                    if (container.getName().endsWith(".class")) {
+                        parser = new ClassParser(container.getPath());
+                    } else {
+                        parser = new ClassParser(container.getPath(),
+                            classname.replace('.', '/') + ".class");
+                    }
+
+                    JavaClass javaClass = parser.parse();
+                    String[] interfaces = javaClass.getInterfaceNames();
+                    for (int i = 0; i < interfaces.length; ++i) {
+                        String interfaceName = interfaces[i];
+                        if (!dependencies.containsKey(interfaceName)) {
+                            nextAnalyze.put(interfaceName, interfaceName);
+                        }
+                    }
+
+                    if (javaClass.isClass()) {
+                        String superClass = javaClass.getSuperclassName();
+                        if (!dependencies.containsKey(superClass)) {
+                            nextAnalyze.put(superClass, superClass);
+                        }
+                    }
+                } catch (IOException ioe) {
+                    // ignore
+                }
+            }
+
+            Hashtable temp = toAnalyze;
+            toAnalyze = nextAnalyze;
+            nextAnalyze = temp;
+        }
+
+        files.removeAllElements();
+        for (Enumeration e = containers.keys(); e.hasMoreElements();) {
+            files.addElement((File) e.nextElement());
+        }
+
+        classes.removeAllElements();
+        for (Enumeration e = dependencies.keys(); e.hasMoreElements();) {
+            classes.addElement((String) e.nextElement());
+        }
+    }
+
+    /**
+     * Indicate if this analyzer can determine dependent files.
+     *
+     * @return true if the analyzer provides dependency file information.
+     */
+    protected boolean supportsFileDependencies() {
+        return true;
+    }
+
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/depend/bcel/DependencyVisitor.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/depend/bcel/DependencyVisitor.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/depend/bcel/DependencyVisitor.java	(revision 16139)
@@ -0,0 +1,187 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util.depend.bcel;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+import org.apache.bcel.classfile.ConstantClass;
+import org.apache.bcel.classfile.ConstantPool;
+import org.apache.bcel.classfile.EmptyVisitor;
+import org.apache.bcel.classfile.Field;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.bcel.classfile.Method;
+import org.apache.bcel.classfile.ConstantNameAndType;
+
+/**
+ * A BCEL visitor implementation to collect class dependency information
+ *
+ */
+public class DependencyVisitor extends EmptyVisitor {
+    /** The collectd dependencies */
+    private Hashtable dependencies = new Hashtable();
+    /**
+     * The current class's constant pool - used to determine class names
+     * from class references.
+     */
+    private ConstantPool constantPool;
+
+    /**
+     * Get the dependencies collected by this visitor
+     *
+     * @return a Enumeration of classnames, being the classes upon which the
+     *      visited classes depend.
+     */
+    public Enumeration getDependencies() {
+        return dependencies.keys();
+    }
+
+    /** Clear the curretn set of collected dependencies. */
+    public void clearDependencies() {
+        dependencies.clear();
+    }
+
+    /**
+     * Visit the constant pool of a class
+     *
+     * @param constantPool the constant pool of the class being visited.
+     */
+    public void visitConstantPool(ConstantPool constantPool) {
+        this.constantPool = constantPool;
+    }
+
+    /**
+     * Visit a class reference
+     *
+     * @param constantClass the constantClass entry for the class reference
+     */
+    public void visitConstantClass(ConstantClass constantClass) {
+        String classname
+             = constantClass.getConstantValue(constantPool).toString();
+        addSlashClass(classname);
+    }
+
+    /**
+     * Visit a name and type ref
+     *
+     * Look for class references in this
+     *
+     * @param obj the name and type reference being visited.
+     */
+    public void visitConstantNameAndType(ConstantNameAndType obj) {
+        String name = obj.getName(constantPool);
+        if (obj.getSignature(constantPool).equals("Ljava/lang/Class;")
+                && name.startsWith("class$")) {
+            String classname = name.substring(6).replace('$', '.');
+            // does the class have a package structure
+            int index = classname.lastIndexOf(".");
+            if (index > 0) {
+                char start;
+                // check if the package structure is more than 1 level deep
+                int index2 = classname.lastIndexOf(".", index - 1);
+                if (index2 != -1) {
+                    // class name has more than 1 package level 'com.company.Class'
+                    start = classname.charAt(index2 + 1);
+                } else {
+                    // class name has only 1 package level 'package.Class'
+                    start = classname.charAt(0);
+                }
+                // Check to see if it's an inner class 'com.company.Class$Inner'
+                if ((start > 0x40) && (start < 0x5B)) {
+                    // first letter of the previous segment of the class name 'Class'
+                    // is upper case ascii. so according to the spec it's an inner class
+                    classname = classname.substring(0, index) + "$"
+                        + classname.substring(index + 1);
+                    addClass(classname);
+                } else {
+                    // Add the class in dotted notation 'com.company.Class'
+                    addClass(classname);
+                }
+            } else {
+                // Add a class with no package 'Class'
+                addClass(classname);
+            }
+        }
+    }
+
+    /**
+     * Visit a field of the class.
+     *
+     * @param field the field being visited
+     */
+    public void visitField(Field field) {
+        addClasses(field.getSignature());
+    }
+
+    /**
+     * Visit a Java class
+     *
+     * @param javaClass the class being visited.
+     */
+    public void visitJavaClass(JavaClass javaClass) {
+        addClass(javaClass.getClassName());
+    }
+
+    /**
+     * Visit a method of the current class
+     *
+     * @param method the method being visited.
+     */
+    public void visitMethod(Method method) {
+        String signature = method.getSignature();
+        int pos = signature.indexOf(")");
+        addClasses(signature.substring(1, pos));
+        addClasses(signature.substring(pos + 1));
+    }
+
+    /**
+     * Add a classname to the list of dependency classes
+     *
+     * @param classname the class to be added to the list of dependencies.
+     */
+    void addClass(String classname) {
+        dependencies.put(classname, classname);
+    }
+
+    /**
+     * Add all the classes from a descriptor string.
+     *
+     * @param string the descriptor string, being descriptors separated by
+     *      ';' characters.
+     */
+    private void addClasses(String string) {
+        StringTokenizer tokens = new StringTokenizer(string, ";");
+        while (tokens.hasMoreTokens()) {
+            String descriptor = tokens.nextToken();
+            int pos = descriptor.indexOf('L');
+            if (pos != -1) {
+                addSlashClass(descriptor.substring(pos + 1));
+            }
+        }
+    }
+
+    /**
+     * Adds a class name in slash format
+     * (for example org/apache/tools/ant/Main).
+     *
+     * @param classname the class name in slash format
+     */
+    private void addSlashClass(String classname) {
+        addClass(classname.replace('/', '.'));
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/depend/bcel/FullAnalyzer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/depend/bcel/FullAnalyzer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/depend/bcel/FullAnalyzer.java	(revision 16139)
@@ -0,0 +1,130 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util.depend.bcel;
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+import org.apache.bcel.classfile.ClassParser;
+import org.apache.bcel.classfile.DescendingVisitor;
+import org.apache.bcel.classfile.JavaClass;
+import org.apache.tools.ant.util.depend.AbstractAnalyzer;
+
+/**
+ * An analyzer capable fo traversing all class - class relationships.
+ *
+ */
+public class FullAnalyzer extends AbstractAnalyzer {
+    /**
+     * Default constructor
+     *
+     * Causes the BCEL classes to load to ensure BCEL dependencies can
+     * be satisfied
+     */
+    public FullAnalyzer() {
+        // force BCEL classes to load now
+        try {
+            new ClassParser("force");
+        } catch (IOException e) {
+            // ignore
+        }
+    }
+
+    /**
+     * Determine the dependencies of the configured root classes.
+     *
+     * @param files a vector to be populated with the files which contain
+     *      the dependency classes
+     * @param classes a vector to be populated with the names of the
+     *      depencency classes.
+     */
+    protected void determineDependencies(Vector files, Vector classes) {
+        // we get the root classes and build up a set of
+        // classes upon which they depend
+        Hashtable dependencies = new Hashtable();
+        Hashtable containers = new Hashtable();
+        Hashtable toAnalyze = new Hashtable();
+        for (Enumeration e = getRootClasses(); e.hasMoreElements();) {
+            String classname = (String) e.nextElement();
+            toAnalyze.put(classname, classname);
+        }
+
+        int count = 0;
+        int maxCount = isClosureRequired() ? MAX_LOOPS : 2;
+        while (toAnalyze.size() != 0 && count++ < maxCount) {
+            DependencyVisitor dependencyVisitor = new DependencyVisitor();
+            for (Enumeration e = toAnalyze.keys(); e.hasMoreElements();) {
+                String classname = (String) e.nextElement();
+                dependencies.put(classname, classname);
+                try {
+                    File container = getClassContainer(classname);
+                    if (container == null) {
+                        continue;
+                    }
+                    containers.put(container, container);
+
+                    ClassParser parser = null;
+                    if (container.getName().endsWith(".class")) {
+                        parser = new ClassParser(container.getPath());
+                    } else {
+                        parser = new ClassParser(container.getPath(),
+                            classname.replace('.', '/') + ".class");
+                    }
+
+                    JavaClass javaClass = parser.parse();
+                    DescendingVisitor traverser
+                         = new DescendingVisitor(javaClass, dependencyVisitor);
+                    traverser.visit();
+                } catch (IOException ioe) {
+                    // ignore
+                }
+            }
+
+            toAnalyze.clear();
+
+            // now recover all the dependencies collected and add to the list.
+            Enumeration depsEnum = dependencyVisitor.getDependencies();
+            while (depsEnum.hasMoreElements()) {
+                String className = (String) depsEnum.nextElement();
+                if (!dependencies.containsKey(className)) {
+                    toAnalyze.put(className, className);
+                }
+            }
+        }
+
+        files.removeAllElements();
+        for (Enumeration e = containers.keys(); e.hasMoreElements();) {
+            files.addElement((File) e.nextElement());
+        }
+
+        classes.removeAllElements();
+        for (Enumeration e = dependencies.keys(); e.hasMoreElements();) {
+            classes.addElement((String) e.nextElement());
+        }
+    }
+
+    /**
+     * Indicate if this analyzer can determine dependent files.
+     *
+     * @return true if the analyzer provides dependency file information.
+     */
+    protected boolean supportsFileDependencies() {
+        return true;
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/facade/FacadeTaskHelper.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/facade/FacadeTaskHelper.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/facade/FacadeTaskHelper.java	(revision 16139)
@@ -0,0 +1,143 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.facade;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * Helper class for facade implementations - encapsulates treatment of
+ * explicit implementation choices, magic properties and
+ * implementation specific command line arguments.
+ *
+ *
+ * @since Ant 1.5
+ */
+public class FacadeTaskHelper {
+
+    /**
+     * Command line arguments.
+     */
+    private Vector args = new Vector();
+
+    /**
+     * The explicitly chosen implementation.
+     */
+    private String userChoice;
+
+    /**
+     * The magic property to consult.
+     */
+    private String magicValue;
+
+    /**
+     * The default value.
+     */
+    private String defaultValue;
+
+    /**
+     * @param defaultValue The default value for the implementation.
+     * Must not be null.
+     */
+    public FacadeTaskHelper(String defaultValue) {
+        this(defaultValue, null);
+    }
+
+    /**
+     * @param defaultValue The default value for the implementation.
+     * Must not be null.
+     * @param magicValue the value of a magic property that may hold a user.
+     * choice.  May be null.
+     */
+    public FacadeTaskHelper(String defaultValue, String magicValue) {
+        this.defaultValue = defaultValue;
+        this.magicValue = magicValue;
+    }
+
+    /**
+     * Used to set the value of the magic property.
+     * @param magicValue the value of a magic property that may hold a user.
+     */
+    public void setMagicValue(String magicValue) {
+        this.magicValue = magicValue;
+    }
+
+    /**
+     * Used for explicit user choices.
+     * @param userChoice the explicitly chosen implementation.
+     */
+    public void setImplementation(String userChoice) {
+        this.userChoice = userChoice;
+    }
+
+    /**
+     * Retrieves the implementation.
+     * @return the implementation.
+     */
+    public String getImplementation() {
+        return userChoice != null ? userChoice
+                                  : (magicValue != null ? magicValue
+                                                        : defaultValue);
+    }
+
+    /**
+     * Retrieves the explicit user choice.
+     * @return the explicit user choice.
+     */
+    public String getExplicitChoice() {
+        return userChoice;
+    }
+
+    /**
+     * Command line argument.
+     * @param arg an argument to add.
+     */
+    public void addImplementationArgument(ImplementationSpecificArgument arg) {
+        args.addElement(arg);
+    }
+
+    /**
+     * Retrieves the command line arguments enabled for the current
+     * facade implementation.
+     * @return an array of command line arguements.
+     */
+    public String[] getArgs() {
+        Vector tmp = new Vector(args.size());
+        for (Enumeration e = args.elements(); e.hasMoreElements();) {
+            ImplementationSpecificArgument arg =
+                ((ImplementationSpecificArgument) e.nextElement());
+            String[] curr = arg.getParts(getImplementation());
+            for (int i = 0; i < curr.length; i++) {
+                tmp.addElement(curr[i]);
+            }
+        }
+        String[] res = new String[tmp.size()];
+        tmp.copyInto(res);
+        return res;
+    }
+
+    /**
+     * Tests whether the implementation has been chosen by the user
+     * (either via a magic property or explicitly.
+     * @return true if magic or user choice has be set.
+     * @since Ant 1.5.2
+     */
+    public boolean hasBeenSet() {
+        return userChoice != null || magicValue != null;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/facade/ImplementationSpecificArgument.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/facade/ImplementationSpecificArgument.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/facade/ImplementationSpecificArgument.java	(revision 16139)
@@ -0,0 +1,60 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.facade;
+
+import org.apache.tools.ant.types.Commandline;
+
+/**
+ * Extension of Commandline.Argument with a new attribute that choses
+ * a specific implementation of the facade.
+ *
+ *
+ * @since Ant 1.5
+ */
+public class ImplementationSpecificArgument extends Commandline.Argument {
+    private String impl;
+
+    /** Constructor for ImplementationSpecificArgument. */
+    public ImplementationSpecificArgument() {
+        super();
+    }
+
+    /**
+     * Set the implementation this argument is for.
+     * @param impl the implementation this command line argument is for.
+     */
+    public void setImplementation(String impl) {
+        this.impl = impl;
+    }
+
+    /**
+     * Return the parts this Argument consists of, if the
+     * implementation matches the chosen implementation.
+     * @see Commandline.Argument#getParts()
+     * @param chosenImpl the implementation to check against.
+     * @return the parts if the implemention matches or an zero length
+     *         array if not.
+     */
+    public final String[] getParts(String chosenImpl) {
+        if (impl == null || impl.equals(chosenImpl)) {
+            return super.getParts();
+        } else {
+            return new String[0];
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/optional/NoExitSecurityManager.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/optional/NoExitSecurityManager.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/optional/NoExitSecurityManager.java	(revision 16139)
@@ -0,0 +1,49 @@
+/*
+ * Copyright  2001-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util.optional;
+
+import java.security.Permission;
+import org.apache.tools.ant.ExitException;
+
+/**
+ * This is intended as a replacement for the default system manager.
+ * The goal is to intercept System.exit calls and make it throw an
+ * exception instead so that a System.exit in a task does not
+ * fully terminate Ant.
+ *
+ * @see ExitException
+ */
+public class NoExitSecurityManager extends SecurityManager {
+
+    /**
+     * Override SecurityManager#checkExit.
+     * This throws an ExitException(status) exception.
+     * @param status the exit status
+     */
+    public void checkExit(int status) {
+        throw new ExitException(status);
+    }
+
+    /**
+     * Override SecurityManager#checkPermission.
+     * This does nothing.
+     * @param perm the requested permission.
+     */
+    public void checkPermission(Permission perm) {
+        // no permission here
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/optional/WeakishReference12.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/optional/WeakishReference12.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/optional/WeakishReference12.java	(revision 16139)
@@ -0,0 +1,52 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.optional;
+
+import org.apache.tools.ant.util.WeakishReference;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * This is a reference that really is is Weak, as it uses the
+ * appropriate java.lang.ref class.
+ *
+ */
+public class WeakishReference12 extends WeakishReference  {
+
+    private WeakReference weakref;
+
+    /**
+     * create a new soft reference, which is bound to a
+     * Weak reference inside
+     * @param reference
+     * @see java.lang.ref.WeakReference
+     */
+    public WeakishReference12(Object reference) {
+        this.weakref = new WeakReference(reference);
+    }
+
+    /**
+     * Returns this reference object's referent.
+     *
+     * @return referent.
+     */
+    public Object get() {
+        return weakref.get();
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/JakartaOroMatcher.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/JakartaOroMatcher.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/JakartaOroMatcher.java	(revision 16139)
@@ -0,0 +1,165 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.regexp;
+
+import java.util.Vector;
+import org.apache.oro.text.regex.MatchResult;
+import org.apache.oro.text.regex.Pattern;
+import org.apache.oro.text.regex.Perl5Compiler;
+import org.apache.oro.text.regex.Perl5Matcher;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Implementation of RegexpMatcher for Jakarta-ORO.
+ *
+ */
+public class JakartaOroMatcher implements RegexpMatcher {
+
+    private String pattern;
+    protected final Perl5Compiler compiler = new Perl5Compiler();
+    protected final Perl5Matcher matcher = new Perl5Matcher();
+
+    /**
+     * Constructor for JakartaOroMatcher.
+     */
+    public JakartaOroMatcher() {
+    }
+
+    /**
+     * Set the regexp pattern from the String description.
+     * @param pattern the pattern to match
+     */
+    public void setPattern(String pattern) {
+        this.pattern = pattern;
+    }
+
+    /**
+     * Get a String representation of the regexp pattern
+     * @return the pattern
+     */
+    public String getPattern() {
+        return this.pattern;
+    }
+
+    /**
+     * Get a compiled representation of the regexp pattern
+     * @param options the options
+     * @return the compiled pattern
+     * @throws BuildException on error
+     */
+    protected Pattern getCompiledPattern(int options)
+        throws BuildException {
+        try {
+            // compute the compiler options based on the input options first
+            Pattern p = compiler.compile(pattern, getCompilerOptions(options));
+            return p;
+        } catch (Exception e) {
+            throw new BuildException(e);
+        }
+    }
+
+    /**
+     * Does the given argument match the pattern using default options?
+     * @param argument the string to match against
+     * @return true if the pattern matches
+     * @throws BuildException on error
+     */
+    public boolean matches(String argument) throws BuildException {
+        return matches(argument, MATCH_DEFAULT);
+    }
+
+    /**
+     * Does the given argument match the pattern?
+     * @param input the string to match against
+     * @param options the regex options to use
+     * @return true if the pattern matches
+     * @throws BuildException on error
+     */
+    public boolean matches(String input, int options)
+        throws BuildException {
+        Pattern p = getCompiledPattern(options);
+        return matcher.contains(input, p);
+    }
+
+    /**
+     * Returns a Vector of matched groups found in the argument
+     * using default options.
+     *
+     * <p>Group 0 will be the full match, the rest are the
+     * parenthesized subexpressions</p>.
+     *
+     * @param argument the string to match against
+     * @return the vector of groups
+     * @throws BuildException on error
+     */
+    public Vector getGroups(String argument) throws BuildException {
+        return getGroups(argument, MATCH_DEFAULT);
+    }
+
+    /**
+     * Returns a Vector of matched groups found in the argument.
+     *
+     * <p>Group 0 will be the full match, the rest are the
+     * parenthesized subexpressions</p>.
+     *
+     * @param input the string to match against
+     * @param options the regex options to use
+     * @return the vector of groups
+     * @throws BuildException on error
+     */
+    public Vector getGroups(String input, int options)
+        throws BuildException {
+        if (!matches(input, options)) {
+            return null;
+        }
+        Vector v = new Vector();
+        MatchResult mr = matcher.getMatch();
+        int cnt = mr.groups();
+        for (int i = 0; i < cnt; i++) {
+            String match = mr.group(i);
+            // treat non-matching groups as empty matches
+            if (match == null) {
+                match = "";
+            }
+            v.addElement(match);
+        }
+        return v;
+    }
+
+    /**
+     * Convert the generic options to the regex compiler specific options.
+     * @param options the generic options
+     * @return the specific options
+     */
+    protected int getCompilerOptions(int options) {
+        int cOptions = Perl5Compiler.DEFAULT_MASK;
+
+        if (RegexpUtil.hasFlag(options, MATCH_CASE_INSENSITIVE)) {
+            cOptions |= Perl5Compiler.CASE_INSENSITIVE_MASK;
+        }
+        if (RegexpUtil.hasFlag(options, MATCH_MULTILINE)) {
+            cOptions |= Perl5Compiler.MULTILINE_MASK;
+        }
+        if (RegexpUtil.hasFlag(options, MATCH_SINGLELINE)) {
+            cOptions |= Perl5Compiler.SINGLELINE_MASK;
+        }
+
+        return cOptions;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/JakartaOroRegexp.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/JakartaOroRegexp.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/JakartaOroRegexp.java	(revision 16139)
@@ -0,0 +1,98 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util.regexp;
+
+import org.apache.oro.text.regex.Perl5Substitution;
+import org.apache.oro.text.regex.Substitution;
+import org.apache.oro.text.regex.Util;
+import org.apache.tools.ant.BuildException;
+
+
+
+/***
+ * Regular expression implementation using the Jakarta Oro package
+ */
+public class JakartaOroRegexp extends JakartaOroMatcher implements Regexp {
+
+    /** Constructor for JakartaOroRegexp */
+    public JakartaOroRegexp() {
+        super();
+    }
+
+    /**
+     * Perform a substitution on the regular expression.
+     * @param input The string to substitute on
+     * @param argument The string which defines the substitution
+     * @param options The list of options for the match and replace.
+     * @return the result of the operation
+     * @throws BuildException on error
+     */
+    public String substitute(String input, String argument, int options)
+        throws BuildException {
+        // translate \1 to $1 so that the Perl5Substitution will work
+        StringBuffer subst = new StringBuffer();
+        for (int i = 0; i < argument.length(); i++) {
+            char c = argument.charAt(i);
+            if (c == '$') {
+                subst.append('\\');
+                subst.append('$');
+            } else if (c == '\\') {
+                if (++i < argument.length()) {
+                    c = argument.charAt(i);
+                    int value = Character.digit(c, 10);
+                    if (value > -1) {
+                        subst.append("$").append(value);
+                    } else {
+                        subst.append(c);
+                    }
+                } else {
+                    // XXX - should throw an exception instead?
+                    subst.append('\\');
+                }
+            } else {
+                subst.append(c);
+            }
+        }
+
+
+        // Do the substitution
+        Substitution s =
+            new Perl5Substitution(subst.toString(),
+                                  Perl5Substitution.INTERPOLATE_ALL);
+        return Util.substitute(matcher,
+                               getCompiledPattern(options),
+                               s,
+                               input,
+                               getSubsOptions(options));
+    }
+
+    /**
+     * Convert ant regexp substitution option to oro options.
+     *
+     * @param options the ant regexp options
+     * @return the oro substition options
+     */
+    protected int getSubsOptions(int options) {
+        boolean replaceAll = RegexpUtil.hasFlag(options, REPLACE_ALL);
+        int subsOptions = 1;
+        if (replaceAll) {
+            subsOptions = Util.SUBSTITUTE_ALL;
+        }
+        return subsOptions;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/JakartaRegexpMatcher.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/JakartaRegexpMatcher.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/JakartaRegexpMatcher.java	(revision 16139)
@@ -0,0 +1,160 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.regexp;
+
+import java.util.Vector;
+import org.apache.regexp.RE;
+import org.apache.regexp.RESyntaxException;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Implementation of RegexpMatcher for Jakarta-Regexp.
+ *
+ */
+public class JakartaRegexpMatcher implements RegexpMatcher {
+
+    private String pattern;
+
+    /**
+     * Set the regexp pattern from the String description.
+     * @param pattern the pattern to match
+     */
+    public void setPattern(String pattern) {
+        this.pattern = pattern;
+    }
+
+    /**
+     * Get a String representation of the regexp pattern
+     * @return the pattern
+     */
+    public String getPattern() {
+        return pattern;
+    }
+
+    /**
+     * Compile the pattern.
+     *
+     * @param options the ant regexp options
+     * @return a compiled pattern
+     * @exception BuildException if an error occurs
+     */
+    protected RE getCompiledPattern(int options)
+        throws BuildException {
+        int cOptions = getCompilerOptions(options);
+        try {
+            RE reg = new RE(pattern);
+            reg.setMatchFlags(cOptions);
+            return reg;
+        } catch (RESyntaxException e) {
+            throw new BuildException(e);
+        }
+    }
+
+    /**
+     * Does the given argument match the pattern?
+     * @param argument the string to match against
+     * @return true if the pattern matches
+     * @throws BuildException on error
+     */
+    public boolean matches(String argument) throws BuildException {
+        return matches(argument, MATCH_DEFAULT);
+    }
+
+    /**
+     * Does the given argument match the pattern?
+     * @param input the string to match against
+     * @param options the regex options to use
+     * @return true if the pattern matches
+     * @throws BuildException on error
+     */
+    public boolean matches(String input, int options)
+        throws BuildException {
+        return matches(input, getCompiledPattern(options));
+    }
+
+    private boolean matches(String input, RE reg) {
+        return reg.match(input);
+    }
+
+    /**
+     * Returns a Vector of matched groups found in the argument
+     * using default options.
+     *
+     * <p>Group 0 will be the full match, the rest are the
+     * parenthesized subexpressions</p>.
+     *
+     * @param argument the string to match against
+     * @return the vector of groups
+     * @throws BuildException on error
+     */
+    public Vector getGroups(String argument) throws BuildException {
+        return getGroups(argument, MATCH_DEFAULT);
+    }
+
+    /**
+     * Returns a Vector of matched groups found in the argument.
+     *
+     * <p>Group 0 will be the full match, the rest are the
+     * parenthesized subexpressions</p>.
+     *
+     * @param input the string to match against
+     * @param options the regex options to use
+     * @return the vector of groups
+     * @throws BuildException on error
+     */
+    public Vector getGroups(String input, int options)
+        throws BuildException {
+        RE reg = getCompiledPattern(options);
+        if (!matches(input, reg)) {
+            return null;
+        }
+        Vector v = new Vector();
+        int cnt = reg.getParenCount();
+        for (int i = 0; i < cnt; i++) {
+            String match = reg.getParen(i);
+            // treat non-matching groups as empty matches
+            if (match == null) {
+                match = "";
+            }
+            v.addElement(match);
+        }
+        return v;
+    }
+
+    /**
+     * Convert the generic options to the regex compiler specific options.
+     * @param options the generic options
+     * @return the specific options
+     */
+    protected int getCompilerOptions(int options) {
+        int cOptions = RE.MATCH_NORMAL;
+
+        if (RegexpUtil.hasFlag(options, MATCH_CASE_INSENSITIVE)) {
+            cOptions |= RE.MATCH_CASEINDEPENDENT;
+        }
+        if (RegexpUtil.hasFlag(options, MATCH_MULTILINE)) {
+            cOptions |= RE.MATCH_MULTILINE;
+        }
+        if (RegexpUtil.hasFlag(options, MATCH_SINGLELINE)) {
+            cOptions |= RE.MATCH_SINGLELINE;
+        }
+
+        return cOptions;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/JakartaRegexpRegexp.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/JakartaRegexpRegexp.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/JakartaRegexpRegexp.java	(revision 16139)
@@ -0,0 +1,88 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util.regexp;
+
+
+import java.util.Vector;
+import org.apache.regexp.RE;
+import org.apache.tools.ant.BuildException;
+
+/***
+ * Regular expression implementation using the Jakarta Regexp package
+ */
+public class JakartaRegexpRegexp extends JakartaRegexpMatcher
+    implements Regexp {
+
+    /** Constructor for JakartaRegexpRegexp */
+    public JakartaRegexpRegexp() {
+        super();
+    }
+
+    /**
+     * Convert ant regexp substitution option to apache regex options.
+     *
+     * @param options the ant regexp options
+     * @return the apache regex substition options
+     */
+    protected int getSubsOptions(int options) {
+        int subsOptions = RE.REPLACE_FIRSTONLY;
+        if (RegexpUtil.hasFlag(options, REPLACE_ALL)) {
+            subsOptions = RE.REPLACE_ALL;
+        }
+        return subsOptions;
+    }
+
+    /**
+     * Perform a substitution on the regular expression.
+     * @param input The string to substitute on
+     * @param argument The string which defines the substitution
+     * @param options The list of options for the match and replace.
+     * @return the result of the operation
+     * @throws BuildException on error
+     */
+    public String substitute(String input, String argument, int options)
+        throws BuildException {
+        Vector v = getGroups(input, options);
+
+        // replace \1 with the corresponding group
+        StringBuffer result = new StringBuffer();
+        for (int i = 0; i < argument.length(); i++) {
+            char c = argument.charAt(i);
+            if (c == '\\') {
+                if (++i < argument.length()) {
+                    c = argument.charAt(i);
+                    int value = Character.digit(c, 10);
+                    if (value > -1) {
+                        result.append((String) v.elementAt(value));
+                    } else {
+                        result.append(c);
+                    }
+                } else {
+                    // XXX - should throw an exception instead?
+                    result.append('\\');
+                }
+            } else {
+                result.append(c);
+            }
+        }
+        argument = result.toString();
+
+        RE reg = getCompiledPattern(options);
+        int sOptions = getSubsOptions(options);
+        return reg.subst(input, argument, sOptions);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/Jdk14RegexpMatcher.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/Jdk14RegexpMatcher.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/Jdk14RegexpMatcher.java	(revision 16139)
@@ -0,0 +1,168 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.regexp;
+
+import java.util.Vector;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Implementation of RegexpMatcher for the built-in regexp matcher of
+ * JDK 1.4. UNIX_LINES option is enabled as a default.
+ *
+ */
+public class Jdk14RegexpMatcher implements RegexpMatcher {
+
+    private String pattern;
+
+    /** Constructor for JakartaOroRegexp */
+    public Jdk14RegexpMatcher() {
+    }
+
+    /**
+     * Set the regexp pattern from the String description.
+     * @param pattern the pattern to match
+     */
+    public void setPattern(String pattern) {
+        this.pattern = pattern;
+    }
+
+    /**
+     * Get a String representation of the regexp pattern
+     * @return the pattern
+     * @throws BuildException on error
+     */
+    public String getPattern() {
+        return pattern;
+    }
+
+    /**
+     * Get a compiled representation of the regexp pattern
+     * @param options the options
+     * @return the compiled pattern
+     * @throws BuildException on error
+     */
+    protected Pattern getCompiledPattern(int options)
+        throws BuildException {
+        int cOptions = getCompilerOptions(options);
+        try {
+            Pattern p = Pattern.compile(this.pattern, cOptions);
+            return p;
+        } catch (PatternSyntaxException e) {
+            throw new BuildException(e);
+        }
+    }
+
+    /**
+     * Does the given argument match the pattern using default options?
+     * @param argument the string to match against
+     * @return true if the pattern matches
+     * @throws BuildException on error
+     */
+    public boolean matches(String argument) throws BuildException {
+        return matches(argument, MATCH_DEFAULT);
+    }
+
+    /**
+     * Does the given argument match the pattern?
+     * @param input the string to match against
+     * @param options the regex options to use
+     * @return true if the pattern matches
+     * @throws BuildException on error
+     */
+    public boolean matches(String input, int options)
+        throws BuildException {
+        try {
+            Pattern p = getCompiledPattern(options);
+            return p.matcher(input).find();
+        } catch (Exception e) {
+            throw new BuildException(e);
+        }
+    }
+
+    /**
+     * Returns a Vector of matched groups found in the argument
+     * using default options.
+     *
+     * <p>Group 0 will be the full match, the rest are the
+     * parenthesized subexpressions</p>.
+     *
+     * @param argument the string to match against
+     * @return the vector of groups
+     * @throws BuildException on error
+     */
+    public Vector getGroups(String argument) throws BuildException {
+        return getGroups(argument, MATCH_DEFAULT);
+    }
+
+    /**
+     * Returns a Vector of matched groups found in the argument.
+     *
+     * <p>Group 0 will be the full match, the rest are the
+     * parenthesized subexpressions</p>.
+     *
+     * @param input the string to match against
+     * @param options the regex options to use
+     * @return the vector of groups
+     * @throws BuildException on error
+     */
+    public Vector getGroups(String input, int options)
+        throws BuildException {
+        Pattern p = getCompiledPattern(options);
+        Matcher matcher = p.matcher(input);
+        if (!matcher.find()) {
+            return null;
+        }
+        Vector v = new Vector();
+        int cnt = matcher.groupCount();
+        for (int i = 0; i <= cnt; i++) {
+            String match = matcher.group(i);
+            // treat non-matching groups as empty matches
+            if (match == null) {
+                match = "";
+            }
+            v.addElement(match);
+        }
+        return v;
+    }
+
+    /**
+     * Convert the generic options to the regex compiler specific options.
+     * @param options the generic options
+     * @return the specific options
+     */
+    protected int getCompilerOptions(int options) {
+        // be strict about line separator
+        int cOptions = Pattern.UNIX_LINES;
+
+        if (RegexpUtil.hasFlag(options, MATCH_CASE_INSENSITIVE)) {
+            cOptions |= Pattern.CASE_INSENSITIVE;
+        }
+        if (RegexpUtil.hasFlag(options, MATCH_MULTILINE)) {
+            cOptions |= Pattern.MULTILINE;
+        }
+        if (RegexpUtil.hasFlag(options, MATCH_SINGLELINE)) {
+            cOptions |= Pattern.DOTALL;
+        }
+
+        return cOptions;
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/Jdk14RegexpRegexp.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/Jdk14RegexpRegexp.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/Jdk14RegexpRegexp.java	(revision 16139)
@@ -0,0 +1,104 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util.regexp;
+
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import org.apache.tools.ant.BuildException;
+
+
+/***
+ * Regular expression implementation using the JDK 1.4 regular expression package
+ */
+public class Jdk14RegexpRegexp extends Jdk14RegexpMatcher implements Regexp {
+
+    /** Constructor for Jdk14RegexpRegexp */
+    public Jdk14RegexpRegexp() {
+        super();
+    }
+
+    /**
+     * Convert ant regexp substitution option to jdk1.4 options.
+     *
+     * @param options the ant regexp options
+     * @return the jdk14 substition options
+     */
+    protected int getSubsOptions(int options) {
+        int subsOptions = REPLACE_FIRST;
+        if (RegexpUtil.hasFlag(options, REPLACE_ALL)) {
+            subsOptions = REPLACE_ALL;
+        }
+        return subsOptions;
+    }
+
+    /**
+     * Perform a substitution on the regular expression.
+     * @param input The string to substitute on
+     * @param argument The string which defines the substitution
+     * @param options The list of options for the match and replace.
+     * @return the result of the operation
+     * @throws BuildException on error
+     */
+    public String substitute(String input, String argument, int options)
+        throws BuildException {
+        // translate \1 to $(1) so that the Matcher will work
+        StringBuffer subst = new StringBuffer();
+        for (int i = 0; i < argument.length(); i++) {
+            char c = argument.charAt(i);
+            if (c == '$') {
+                subst.append('\\');
+                subst.append('$');
+            } else if (c == '\\') {
+                if (++i < argument.length()) {
+                    c = argument.charAt(i);
+                    int value = Character.digit(c, 10);
+                    if (value > -1) {
+                        subst.append("$").append(value);
+                    } else {
+                        subst.append(c);
+                    }
+                } else {
+                    // XXX - should throw an exception instead?
+                    subst.append('\\');
+                }
+            } else {
+                subst.append(c);
+            }
+        }
+        argument = subst.toString();
+
+        int sOptions = getSubsOptions(options);
+        Pattern p = getCompiledPattern(options);
+        StringBuffer sb = new StringBuffer();
+
+        Matcher m = p.matcher(input);
+        if (RegexpUtil.hasFlag(sOptions, REPLACE_ALL)) {
+            sb.append(m.replaceAll(argument));
+        } else {
+            boolean res = m.find();
+            if (res) {
+                m.appendReplacement(sb, argument);
+                m.appendTail(sb);
+            } else {
+                sb.append(input);
+            }
+        }
+
+        return sb.toString();
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/Regexp.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/Regexp.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/Regexp.java	(revision 16139)
@@ -0,0 +1,49 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util.regexp;
+
+import org.apache.tools.ant.BuildException;
+
+/***
+ * Interface which represents a regular expression, and the operations
+ * that can be performed on it.
+ *
+ */
+public interface Regexp extends RegexpMatcher {
+
+    /**
+     * Replace only the first occurance of the regular expression
+     */
+    int REPLACE_FIRST          = 0x00000001;
+
+    /**
+     * Replace all occurances of the regular expression
+     */
+    int REPLACE_ALL            = 0x00000010;
+
+    /**
+     * Perform a substitution on the regular expression.
+     * @param input The string to substitute on
+     * @param argument The string which defines the substitution
+     * @param options The list of options for the match and replace. See the
+     *                MATCH_ and REPLACE_ constants above.
+     * @return the result of the operation
+     * @throws BuildException on error
+     */
+    String substitute(String input, String argument, int options)
+        throws BuildException;
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/RegexpFactory.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/RegexpFactory.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/RegexpFactory.java	(revision 16139)
@@ -0,0 +1,107 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util.regexp;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/***
+ * Regular expression factory, which will create Regexp objects.  The
+ * actual implementation class depends on the System or Ant Property:
+ * <code>ant.regexp.regexpimpl</code>.
+ *
+ */
+public class RegexpFactory extends RegexpMatcherFactory {
+
+    /** Constructor for RegexpFactory */
+    public RegexpFactory() {
+    }
+
+    /***
+     * Create a new regular expression matcher instance.
+     * @return the matcher instance
+     * @throws BuildException on error
+     */
+    public Regexp newRegexp() throws BuildException {
+        return (Regexp) newRegexp(null);
+    }
+
+    /***
+     * Create a new regular expression matcher instance.
+     *
+     * @param p Project whose ant.regexp.regexpimpl property will be used.
+     * @return the matcher instance
+     * @throws BuildException on error
+     */
+    public Regexp newRegexp(Project p) throws BuildException {
+        String systemDefault = null;
+        if (p == null) {
+            systemDefault = System.getProperty("ant.regexp.regexpimpl");
+        } else {
+            systemDefault = p.getProperty("ant.regexp.regexpimpl");
+        }
+
+        if (systemDefault != null) {
+            return createRegexpInstance(systemDefault);
+            // XXX     should we silently catch possible exceptions and try to
+            //         load a different implementation?
+        }
+
+        try {
+            testAvailability("java.util.regex.Matcher");
+            return createRegexpInstance("org.apache.tools.ant.util.regexp.Jdk14RegexpRegexp");
+        } catch (BuildException be) {
+            // ignore
+        }
+
+        try {
+            testAvailability("org.apache.oro.text.regex.Pattern");
+            return createRegexpInstance("org.apache.tools.ant.util.regexp.JakartaOroRegexp");
+        } catch (BuildException be) {
+            // ignore
+        }
+
+        try {
+            testAvailability("org.apache.regexp.RE");
+            return createRegexpInstance("org.apache.tools.ant.util.regexp.JakartaRegexpRegexp");
+        } catch (BuildException be) {
+            // ignore
+        }
+
+        throw new BuildException("No supported regular expression matcher found");
+    }
+
+    /**
+     * Wrapper over RegexpMatcherFactory.createInstance that ensures that
+     * we are dealing with a Regexp implementation.
+     *
+     * @since 1.3
+     *
+     * @see RegexpMatcherFactory#createInstance(String)
+     */
+    protected Regexp createRegexpInstance(String classname)
+        throws BuildException {
+
+        RegexpMatcher m = createInstance(classname);
+        if (m instanceof Regexp) {
+            return (Regexp) m;
+        } else {
+            throw new BuildException(classname + " doesn't implement the Regexp interface");
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/RegexpMatcher.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/RegexpMatcher.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/RegexpMatcher.java	(revision 16139)
@@ -0,0 +1,108 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.regexp;
+
+import java.util.Vector;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Interface describing a regular expression matcher.
+ *
+ */
+public interface RegexpMatcher {
+
+    /***
+     * Default Mask (case insensitive, neither multiline nor
+     * singleline specified).
+     */
+    int MATCH_DEFAULT          = 0x00000000;
+
+    /***
+     * Perform a case insenstive match
+     */
+    int MATCH_CASE_INSENSITIVE = 0x00000100;
+
+    /***
+     * Treat the input as a multiline input
+     */
+    int MATCH_MULTILINE        = 0x00001000;
+
+    /***
+     * Treat the input as singleline input ('.' matches newline)
+     */
+    int MATCH_SINGLELINE       = 0x00010000;
+
+
+    /**
+     * Set the regexp pattern from the String description.
+     * @param pattern the pattern to match
+     * @throws BuildException on error
+     */
+    void setPattern(String pattern) throws BuildException;
+
+    /**
+     * Get a String representation of the regexp pattern
+     * @return the pattern
+     * @throws BuildException on error
+     */
+    String getPattern() throws BuildException;
+
+    /**
+     * Does the given argument match the pattern?
+     * @param argument the string to match against
+     * @return true if the pattern matches
+     * @throws BuildException on error
+     */
+    boolean matches(String argument) throws BuildException;
+
+    /**
+     * Returns a Vector of matched groups found in the argument
+     * using default options.
+     *
+     * <p>Group 0 will be the full match, the rest are the
+     * parenthesized subexpressions</p>.
+     *
+     * @param argument the string to match against
+     * @return the vector of groups
+     * @throws BuildException on error
+     */
+    Vector getGroups(String argument) throws BuildException;
+
+    /***
+     * Does this regular expression match the input, given
+     * certain options
+     * @param input The string to check for a match
+     * @param options The list of options for the match. See the
+     *                MATCH_ constants above.
+     * @return true if the pattern matches
+     * @throws BuildException on error
+     */
+    boolean matches(String input, int options) throws BuildException;
+
+    /***
+     * Get the match groups from this regular expression.  The return
+     * type of the elements is always String.
+     * @param input The string to check for a match
+     * @param options The list of options for the match. See the
+     *                MATCH_ constants above.
+     * @return the vector of groups
+     * @throws BuildException on error
+     */
+    Vector getGroups(String input, int options) throws BuildException;
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/RegexpMatcherFactory.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/RegexpMatcherFactory.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/RegexpMatcherFactory.java	(revision 16139)
@@ -0,0 +1,124 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.regexp;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+/**
+ * Simple Factory Class that produces an implementation of
+ * RegexpMatcher based on the system property
+ * <code>ant.regexp.matcherimpl</code> and the classes
+ * available.
+ *
+ * <p>In a more general framework this class would be abstract and
+ * have a static newInstance method.</p>
+ *
+ */
+public class RegexpMatcherFactory {
+
+    /** Constructor for RegexpMatcherFactory. */
+    public RegexpMatcherFactory() {
+    }
+
+    /***
+     * Create a new regular expression instance.
+     * @return the matcher
+     * @throws BuildException on error
+     */
+    public RegexpMatcher newRegexpMatcher() throws BuildException {
+        return newRegexpMatcher(null);
+    }
+
+    /***
+     * Create a new regular expression instance.
+     *
+     * @param p Project whose ant.regexp.regexpimpl property will be used.
+     * @return the matcher
+     * @throws BuildException on error
+     */
+    public RegexpMatcher newRegexpMatcher(Project p)
+        throws BuildException {
+        String systemDefault = null;
+        if (p == null) {
+            systemDefault = System.getProperty("ant.regexp.regexpimpl");
+        } else {
+            systemDefault = p.getProperty("ant.regexp.regexpimpl");
+        }
+
+        if (systemDefault != null) {
+            return createInstance(systemDefault);
+            // XXX     should we silently catch possible exceptions and try to
+            //         load a different implementation?
+        }
+
+        try {
+            testAvailability("java.util.regex.Matcher");
+            return createInstance("org.apache.tools.ant.util.regexp.Jdk14RegexpMatcher");
+        } catch (BuildException be) {
+            // ignore
+        }
+
+        try {
+            testAvailability("org.apache.oro.text.regex.Pattern");
+            return createInstance("org.apache.tools.ant.util.regexp.JakartaOroMatcher");
+        } catch (BuildException be) {
+            // ignore
+        }
+
+        try {
+            testAvailability("org.apache.regexp.RE");
+            return createInstance("org.apache.tools.ant.util.regexp.JakartaRegexpMatcher");
+        } catch (BuildException be) {
+            // ignore
+        }
+
+        throw new BuildException("No supported regular expression matcher found");
+   }
+
+    /**
+     * Create an instance of a matcher from a classname.
+     *
+     * @param className a <code>String</code> value
+     * @return a <code>RegexpMatcher</code> value
+     * @exception BuildException if an error occurs
+     */
+    protected RegexpMatcher createInstance(String className)
+        throws BuildException {
+        try {
+            Class implClass = Class.forName(className);
+            return (RegexpMatcher) implClass.newInstance();
+        } catch (Throwable t) {
+            throw new BuildException(t);
+        }
+    }
+
+    /**
+     * Test if a particular class is available to be used.
+     *
+     * @param className a <code>String</code> value
+     * @exception BuildException if an error occurs
+     */
+    protected void testAvailability(String className) throws BuildException {
+        try {
+            Class.forName(className);
+        } catch (Throwable t) {
+            throw new BuildException(t);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/RegexpUtil.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/RegexpUtil.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/util/regexp/RegexpUtil.java	(revision 16139)
@@ -0,0 +1,46 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util.regexp;
+
+/***
+ * Regular expression utilities class which handles flag operations.
+ *
+ */
+public class RegexpUtil {
+
+    /**
+     * Check the options has a particular flag set.
+     *
+     * @param options an <code>int</code> value
+     * @param flag an <code>int</code> value
+     * @return true if the flag is set
+     */
+    public static boolean hasFlag(int options, int flag) {
+        return ((options & flag) > 0);
+    }
+
+    /**
+     * Remove a particular flag from an int value contains the option flags.
+     *
+     * @param options an <code>int</code> value
+     * @param flag an <code>int</code> value
+     * @return the options with the flag unset
+     */
+    public static int removeFlag(int options, int flag) {
+        return (options & (0xFFFFFFFF - flag));
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/ant/version.txt
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/ant/version.txt	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/ant/version.txt	(revision 16139)
@@ -0,0 +1,2 @@
+VERSION=@VERSION@
+DATE=@DATE@
Index: /release-kits/shared/ant/src/main/org/apache/tools/bzip2/BZip2Constants.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/bzip2/BZip2Constants.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/bzip2/BZip2Constants.java	(revision 16139)
@@ -0,0 +1,98 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Keiron Liddle, Aftex Software
+ * <keiron@aftexsw.com> to whom the Ant project is very grateful for his
+ * great code.
+ */
+
+package org.apache.tools.bzip2;
+
+/**
+ * Base class for both the compress and decompress classes.
+ * Holds common arrays, and static data.
+ *
+ */
+public interface BZip2Constants {
+
+    int baseBlockSize = 100000;
+    int MAX_ALPHA_SIZE = 258;
+    int MAX_CODE_LEN = 23;
+    int RUNA = 0;
+    int RUNB = 1;
+    int N_GROUPS = 6;
+    int G_SIZE = 50;
+    int N_ITERS = 4;
+    int MAX_SELECTORS = (2 + (900000 / G_SIZE));
+    int NUM_OVERSHOOT_BYTES = 20;
+
+    int[] rNums = {
+        619, 720, 127, 481, 931, 816, 813, 233, 566, 247,
+        985, 724, 205, 454, 863, 491, 741, 242, 949, 214,
+        733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
+        419, 436, 278, 496, 867, 210, 399, 680, 480, 51,
+        878, 465, 811, 169, 869, 675, 611, 697, 867, 561,
+        862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
+        150, 238, 59, 379, 684, 877, 625, 169, 643, 105,
+        170, 607, 520, 932, 727, 476, 693, 425, 174, 647,
+        73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
+        909, 545, 703, 919, 874, 474, 882, 500, 594, 612,
+        641, 801, 220, 162, 819, 984, 589, 513, 495, 799,
+        161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
+        382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
+        98, 553, 163, 354, 666, 933, 424, 341, 533, 870,
+        227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
+        469, 68, 770, 919, 190, 373, 294, 822, 808, 206,
+        184, 943, 795, 384, 383, 461, 404, 758, 839, 887,
+        715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
+        951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
+        652, 934, 970, 447, 318, 353, 859, 672, 112, 785,
+        645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
+        609, 772, 154, 274, 580, 184, 79, 626, 630, 742,
+        653, 282, 762, 623, 680, 81, 927, 626, 789, 125,
+        411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
+        170, 774, 972, 275, 999, 639, 495, 78, 352, 126,
+        857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
+        669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
+        944, 375, 748, 52, 600, 747, 642, 182, 862, 81,
+        344, 805, 988, 739, 511, 655, 814, 334, 249, 515,
+        897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
+        433, 837, 553, 268, 926, 240, 102, 654, 459, 51,
+        686, 754, 806, 760, 493, 403, 415, 394, 687, 700,
+        946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
+        978, 321, 576, 617, 626, 502, 894, 679, 243, 440,
+        680, 879, 194, 572, 640, 724, 926, 56, 204, 700,
+        707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
+        297, 59, 87, 824, 713, 663, 412, 693, 342, 606,
+        134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
+        343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
+        140, 206, 73, 263, 980, 736, 876, 478, 430, 305,
+        170, 514, 364, 692, 829, 82, 855, 953, 676, 246,
+        369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
+        804, 378, 215, 828, 592, 281, 565, 555, 710, 82,
+        896, 831, 547, 261, 524, 462, 293, 465, 502, 56,
+        661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
+        768, 550, 608, 933, 378, 286, 215, 979, 792, 961,
+        61, 688, 793, 644, 986, 403, 106, 366, 905, 644,
+        372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
+        780, 773, 635, 389, 707, 100, 626, 958, 165, 504,
+        920, 176, 193, 713, 857, 265, 203, 50, 668, 108,
+        645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
+        936, 638
+    };
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/bzip2/CBZip2InputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/bzip2/CBZip2InputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/bzip2/CBZip2InputStream.java	(revision 16139)
@@ -0,0 +1,835 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Keiron Liddle, Aftex Software
+ * <keiron@aftexsw.com> to whom the Ant project is very grateful for his
+ * great code.
+ */
+package org.apache.tools.bzip2;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * An input stream that decompresses from the BZip2 format (without the file
+ * header chars) to be read as any other stream.
+ *
+ */
+public class CBZip2InputStream extends InputStream implements BZip2Constants {
+    private static void cadvise() {
+        System.out.println("CRC Error");
+        //throw new CCoruptionError();
+    }
+
+    private static void badBGLengths() {
+        cadvise();
+    }
+
+    private static void bitStreamEOF() {
+        cadvise();
+    }
+
+    private static void compressedStreamEOF() {
+        cadvise();
+    }
+
+    private void makeMaps() {
+        int i;
+        nInUse = 0;
+        for (i = 0; i < 256; i++) {
+            if (inUse[i]) {
+                seqToUnseq[nInUse] = (char) i;
+                unseqToSeq[i] = (char) nInUse;
+                nInUse++;
+            }
+        }
+    }
+
+    /*
+      index of the last char in the block, so
+      the block size == last + 1.
+    */
+    private int  last;
+
+    /*
+      index in zptr[] of original string after sorting.
+    */
+    private int  origPtr;
+
+    /*
+      always: in the range 0 .. 9.
+      The current block size is 100000 * this number.
+    */
+    private int blockSize100k;
+
+    private boolean blockRandomised;
+
+    private int bsBuff;
+    private int bsLive;
+    private CRC mCrc = new CRC();
+
+    private boolean[] inUse = new boolean[256];
+    private int nInUse;
+
+    private char[] seqToUnseq = new char[256];
+    private char[] unseqToSeq = new char[256];
+
+    private char[] selector = new char[MAX_SELECTORS];
+    private char[] selectorMtf = new char[MAX_SELECTORS];
+
+    private int[] tt;
+    private char[] ll8;
+
+    /*
+      freq table collected to save a pass over the data
+      during decompression.
+    */
+    private int[] unzftab = new int[256];
+
+    private int[][] limit = new int[N_GROUPS][MAX_ALPHA_SIZE];
+    private int[][] base = new int[N_GROUPS][MAX_ALPHA_SIZE];
+    private int[][] perm = new int[N_GROUPS][MAX_ALPHA_SIZE];
+    private int[] minLens = new int[N_GROUPS];
+
+    private InputStream bsStream;
+
+    private boolean streamEnd = false;
+
+    private int currentChar = -1;
+
+    private static final int START_BLOCK_STATE = 1;
+    private static final int RAND_PART_A_STATE = 2;
+    private static final int RAND_PART_B_STATE = 3;
+    private static final int RAND_PART_C_STATE = 4;
+    private static final int NO_RAND_PART_A_STATE = 5;
+    private static final int NO_RAND_PART_B_STATE = 6;
+    private static final int NO_RAND_PART_C_STATE = 7;
+
+    private int currentState = START_BLOCK_STATE;
+
+    private int storedBlockCRC, storedCombinedCRC;
+    private int computedBlockCRC, computedCombinedCRC;
+
+    int i2, count, chPrev, ch2;
+    int i, tPos;
+    int rNToGo = 0;
+    int rTPos  = 0;
+    int j2;
+    char z;
+
+    public CBZip2InputStream(InputStream zStream) {
+        ll8 = null;
+        tt = null;
+        bsSetStream(zStream);
+        initialize();
+        initBlock();
+        setupBlock();
+    }
+
+    public int read() {
+        if (streamEnd) {
+            return -1;
+        } else {
+            int retChar = currentChar;
+            switch(currentState) {
+            case START_BLOCK_STATE:
+                break;
+            case RAND_PART_A_STATE:
+                break;
+            case RAND_PART_B_STATE:
+                setupRandPartB();
+                break;
+            case RAND_PART_C_STATE:
+                setupRandPartC();
+                break;
+            case NO_RAND_PART_A_STATE:
+                break;
+            case NO_RAND_PART_B_STATE:
+                setupNoRandPartB();
+                break;
+            case NO_RAND_PART_C_STATE:
+                setupNoRandPartC();
+                break;
+            default:
+                break;
+            }
+            return retChar;
+        }
+    }
+
+    private void initialize() {
+        char magic3, magic4;
+        magic3 = bsGetUChar();
+        magic4 = bsGetUChar();
+        if (magic3 != 'h' || magic4 < '1' || magic4 > '9') {
+            bsFinishedWithStream();
+            streamEnd = true;
+            return;
+        }
+
+        setDecompressStructureSizes(magic4 - '0');
+        computedCombinedCRC = 0;
+    }
+
+    private void initBlock() {
+        char magic1, magic2, magic3, magic4;
+        char magic5, magic6;
+        magic1 = bsGetUChar();
+        magic2 = bsGetUChar();
+        magic3 = bsGetUChar();
+        magic4 = bsGetUChar();
+        magic5 = bsGetUChar();
+        magic6 = bsGetUChar();
+        if (magic1 == 0x17 && magic2 == 0x72 && magic3 == 0x45
+            && magic4 == 0x38 && magic5 == 0x50 && magic6 == 0x90) {
+            complete();
+            return;
+        }
+
+        if (magic1 != 0x31 || magic2 != 0x41 || magic3 != 0x59
+            || magic4 != 0x26 || magic5 != 0x53 || magic6 != 0x59) {
+            badBlockHeader();
+            streamEnd = true;
+            return;
+        }
+
+        storedBlockCRC = bsGetInt32();
+
+        if (bsR(1) == 1) {
+            blockRandomised = true;
+        } else {
+            blockRandomised = false;
+        }
+
+        //        currBlockNo++;
+        getAndMoveToFrontDecode();
+
+        mCrc.initialiseCRC();
+        currentState = START_BLOCK_STATE;
+    }
+
+    private void endBlock() {
+        computedBlockCRC = mCrc.getFinalCRC();
+        /* A bad CRC is considered a fatal error. */
+        if (storedBlockCRC != computedBlockCRC) {
+            crcError();
+        }
+
+        computedCombinedCRC = (computedCombinedCRC << 1)
+            | (computedCombinedCRC >>> 31);
+        computedCombinedCRC ^= computedBlockCRC;
+    }
+
+    private void complete() {
+        storedCombinedCRC = bsGetInt32();
+        if (storedCombinedCRC != computedCombinedCRC) {
+            crcError();
+        }
+
+        bsFinishedWithStream();
+        streamEnd = true;
+    }
+
+    private static void blockOverrun() {
+        cadvise();
+    }
+
+    private static void badBlockHeader() {
+        cadvise();
+    }
+
+    private static void crcError() {
+        cadvise();
+    }
+
+    private void bsFinishedWithStream() {
+        try {
+            if (this.bsStream != null) {
+                if (this.bsStream != System.in) {
+                    this.bsStream.close();
+                    this.bsStream = null;
+                }
+            }
+        } catch (IOException ioe) {
+            //ignore
+        }
+    }
+
+    private void bsSetStream(InputStream f) {
+        bsStream = f;
+        bsLive = 0;
+        bsBuff = 0;
+    }
+
+    private int bsR(int n) {
+        int v;
+        while (bsLive < n) {
+            int zzi;
+            char thech = 0;
+            try {
+                thech = (char) bsStream.read();
+            } catch (IOException e) {
+                compressedStreamEOF();
+            }
+            if (thech == -1) {
+                compressedStreamEOF();
+            }
+            zzi = thech;
+            bsBuff = (bsBuff << 8) | (zzi & 0xff);
+            bsLive += 8;
+        }
+
+        v = (bsBuff >> (bsLive - n)) & ((1 << n) - 1);
+        bsLive -= n;
+        return v;
+    }
+
+    private char bsGetUChar() {
+        return (char) bsR(8);
+    }
+
+    private int bsGetint() {
+        int u = 0;
+        u = (u << 8) | bsR(8);
+        u = (u << 8) | bsR(8);
+        u = (u << 8) | bsR(8);
+        u = (u << 8) | bsR(8);
+        return u;
+    }
+
+    private int bsGetIntVS(int numBits) {
+        return (int) bsR(numBits);
+    }
+
+    private int bsGetInt32() {
+        return (int) bsGetint();
+    }
+
+    private void hbCreateDecodeTables(int[] limit, int[] base,
+                                      int[] perm, char[] length,
+                                      int minLen, int maxLen, int alphaSize) {
+        int pp, i, j, vec;
+
+        pp = 0;
+        for (i = minLen; i <= maxLen; i++) {
+            for (j = 0; j < alphaSize; j++) {
+                if (length[j] == i) {
+                    perm[pp] = j;
+                    pp++;
+                }
+            }
+        }
+
+        for (i = 0; i < MAX_CODE_LEN; i++) {
+            base[i] = 0;
+        }
+        for (i = 0; i < alphaSize; i++) {
+            base[length[i] + 1]++;
+        }
+
+        for (i = 1; i < MAX_CODE_LEN; i++) {
+            base[i] += base[i - 1];
+        }
+
+        for (i = 0; i < MAX_CODE_LEN; i++) {
+            limit[i] = 0;
+        }
+        vec = 0;
+
+        for (i = minLen; i <= maxLen; i++) {
+            vec += (base[i + 1] - base[i]);
+            limit[i] = vec - 1;
+            vec <<= 1;
+        }
+        for (i = minLen + 1; i <= maxLen; i++) {
+            base[i] = ((limit[i - 1] + 1) << 1) - base[i];
+        }
+    }
+
+    private void recvDecodingTables() {
+        char len[][] = new char[N_GROUPS][MAX_ALPHA_SIZE];
+        int i, j, t, nGroups, nSelectors, alphaSize;
+        int minLen, maxLen;
+        boolean[] inUse16 = new boolean[16];
+
+        /* Receive the mapping table */
+        for (i = 0; i < 16; i++) {
+            if (bsR(1) == 1) {
+                inUse16[i] = true;
+            } else {
+                inUse16[i] = false;
+            }
+        }
+
+        for (i = 0; i < 256; i++) {
+            inUse[i] = false;
+        }
+
+        for (i = 0; i < 16; i++) {
+            if (inUse16[i]) {
+                for (j = 0; j < 16; j++) {
+                    if (bsR(1) == 1) {
+                        inUse[i * 16 + j] = true;
+                    }
+                }
+            }
+        }
+
+        makeMaps();
+        alphaSize = nInUse + 2;
+
+        /* Now the selectors */
+        nGroups = bsR(3);
+        nSelectors = bsR(15);
+        for (i = 0; i < nSelectors; i++) {
+            j = 0;
+            while (bsR(1) == 1) {
+                j++;
+            }
+            selectorMtf[i] = (char) j;
+        }
+
+        /* Undo the MTF values for the selectors. */
+        {
+            char[] pos = new char[N_GROUPS];
+            char tmp, v;
+            for (v = 0; v < nGroups; v++) {
+                pos[v] = v;
+            }
+
+            for (i = 0; i < nSelectors; i++) {
+                v = selectorMtf[i];
+                tmp = pos[v];
+                while (v > 0) {
+                    pos[v] = pos[v - 1];
+                    v--;
+                }
+                pos[0] = tmp;
+                selector[i] = tmp;
+            }
+        }
+
+        /* Now the coding tables */
+        for (t = 0; t < nGroups; t++) {
+            int curr = bsR(5);
+            for (i = 0; i < alphaSize; i++) {
+                while (bsR(1) == 1) {
+                    if (bsR(1) == 0) {
+                        curr++;
+                    } else {
+                        curr--;
+                    }
+                }
+                len[t][i] = (char) curr;
+            }
+        }
+
+        /* Create the Huffman decoding tables */
+        for (t = 0; t < nGroups; t++) {
+            minLen = 32;
+            maxLen = 0;
+            for (i = 0; i < alphaSize; i++) {
+                if (len[t][i] > maxLen) {
+                    maxLen = len[t][i];
+                }
+                if (len[t][i] < minLen) {
+                    minLen = len[t][i];
+                }
+            }
+            hbCreateDecodeTables(limit[t], base[t], perm[t], len[t], minLen,
+                                 maxLen, alphaSize);
+            minLens[t] = minLen;
+        }
+    }
+
+    private void getAndMoveToFrontDecode() {
+        char[] yy = new char[256];
+        int i, j, nextSym, limitLast;
+        int EOB, groupNo, groupPos;
+
+        limitLast = baseBlockSize * blockSize100k;
+        origPtr = bsGetIntVS(24);
+
+        recvDecodingTables();
+        EOB = nInUse + 1;
+        groupNo = -1;
+        groupPos = 0;
+
+        /*
+          Setting up the unzftab entries here is not strictly
+          necessary, but it does save having to do it later
+          in a separate pass, and so saves a block's worth of
+          cache misses.
+        */
+        for (i = 0; i <= 255; i++) {
+            unzftab[i] = 0;
+        }
+
+        for (i = 0; i <= 255; i++) {
+            yy[i] = (char) i;
+        }
+
+        last = -1;
+
+        {
+            int zt, zn, zvec, zj;
+            if (groupPos == 0) {
+                groupNo++;
+                groupPos = G_SIZE;
+            }
+            groupPos--;
+            zt = selector[groupNo];
+            zn = minLens[zt];
+            zvec = bsR(zn);
+            while (zvec > limit[zt][zn]) {
+                zn++;
+                {
+                    {
+                        while (bsLive < 1) {
+                            int zzi;
+                            char thech = 0;
+                            try {
+                                thech = (char) bsStream.read();
+                            } catch (IOException e) {
+                                compressedStreamEOF();
+                            }
+                            if (thech == -1) {
+                                compressedStreamEOF();
+                            }
+                            zzi = thech;
+                            bsBuff = (bsBuff << 8) | (zzi & 0xff);
+                            bsLive += 8;
+                        }
+                    }
+                    zj = (bsBuff >> (bsLive - 1)) & 1;
+                    bsLive--;
+                }
+                zvec = (zvec << 1) | zj;
+            }
+            nextSym = perm[zt][zvec - base[zt][zn]];
+        }
+
+        while (true) {
+
+            if (nextSym == EOB) {
+                break;
+            }
+
+            if (nextSym == RUNA || nextSym == RUNB) {
+                char ch;
+                int s = -1;
+                int N = 1;
+                do {
+                    if (nextSym == RUNA) {
+                        s = s + (0 + 1) * N;
+                    } else if (nextSym == RUNB) {
+                        s = s + (1 + 1) * N;
+                           }
+                    N = N * 2;
+                    {
+                        int zt, zn, zvec, zj;
+                        if (groupPos == 0) {
+                            groupNo++;
+                            groupPos = G_SIZE;
+                        }
+                        groupPos--;
+                        zt = selector[groupNo];
+                        zn = minLens[zt];
+                        zvec = bsR(zn);
+                        while (zvec > limit[zt][zn]) {
+                            zn++;
+                            {
+                                {
+                                    while (bsLive < 1) {
+                                        int zzi;
+                                        char thech = 0;
+                                        try {
+                                            thech = (char) bsStream.read();
+                                        } catch (IOException e) {
+                                            compressedStreamEOF();
+                                        }
+                                        if (thech == -1) {
+                                            compressedStreamEOF();
+                                        }
+                                        zzi = thech;
+                                        bsBuff = (bsBuff << 8) | (zzi & 0xff);
+                                        bsLive += 8;
+                                    }
+                                }
+                                zj = (bsBuff >> (bsLive - 1)) & 1;
+                                bsLive--;
+                            }
+                            zvec = (zvec << 1) | zj;
+                        }
+                        nextSym = perm[zt][zvec - base[zt][zn]];
+                    }
+                } while (nextSym == RUNA || nextSym == RUNB);
+
+                s++;
+                ch = seqToUnseq[yy[0]];
+                unzftab[ch] += s;
+
+                while (s > 0) {
+                    last++;
+                    ll8[last] = ch;
+                    s--;
+                }
+
+                if (last >= limitLast) {
+                    blockOverrun();
+                }
+                continue;
+            } else {
+                char tmp;
+                last++;
+                if (last >= limitLast) {
+                    blockOverrun();
+                }
+
+                tmp = yy[nextSym - 1];
+                unzftab[seqToUnseq[tmp]]++;
+                ll8[last] = seqToUnseq[tmp];
+
+                /*
+                  This loop is hammered during decompression,
+                  hence the unrolling.
+
+                  for (j = nextSym-1; j > 0; j--) yy[j] = yy[j-1];
+                */
+
+                j = nextSym - 1;
+                for (; j > 3; j -= 4) {
+                    yy[j]     = yy[j - 1];
+                    yy[j - 1] = yy[j - 2];
+                    yy[j - 2] = yy[j - 3];
+                    yy[j - 3] = yy[j - 4];
+                }
+                for (; j > 0; j--) {
+                    yy[j] = yy[j - 1];
+                }
+
+                yy[0] = tmp;
+                {
+                    int zt, zn, zvec, zj;
+                    if (groupPos == 0) {
+                        groupNo++;
+                        groupPos = G_SIZE;
+                    }
+                    groupPos--;
+                    zt = selector[groupNo];
+                    zn = minLens[zt];
+                    zvec = bsR(zn);
+                    while (zvec > limit[zt][zn]) {
+                        zn++;
+                        {
+                            {
+                                while (bsLive < 1) {
+                                    int zzi;
+                                    char thech = 0;
+                                    try {
+                                        thech = (char) bsStream.read();
+                                    } catch (IOException e) {
+                                        compressedStreamEOF();
+                                    }
+                                    zzi = thech;
+                                    bsBuff = (bsBuff << 8) | (zzi & 0xff);
+                                    bsLive += 8;
+                                }
+                            }
+                            zj = (bsBuff >> (bsLive - 1)) & 1;
+                            bsLive--;
+                        }
+                        zvec = (zvec << 1) | zj;
+                    }
+                    nextSym = perm[zt][zvec - base[zt][zn]];
+                }
+                continue;
+            }
+        }
+    }
+
+    private void setupBlock() {
+        int[] cftab = new int[257];
+        char ch;
+
+        cftab[0] = 0;
+        for (i = 1; i <= 256; i++) {
+            cftab[i] = unzftab[i - 1];
+        }
+        for (i = 1; i <= 256; i++) {
+            cftab[i] += cftab[i - 1];
+        }
+
+        for (i = 0; i <= last; i++) {
+            ch = (char) ll8[i];
+            tt[cftab[ch]] = i;
+            cftab[ch]++;
+        }
+        cftab = null;
+
+        tPos = tt[origPtr];
+
+        count = 0;
+        i2 = 0;
+        ch2 = 256;   /* not a char and not EOF */
+
+        if (blockRandomised) {
+            rNToGo = 0;
+            rTPos = 0;
+            setupRandPartA();
+        } else {
+            setupNoRandPartA();
+        }
+    }
+
+    private void setupRandPartA() {
+        if (i2 <= last) {
+            chPrev = ch2;
+            ch2 = ll8[tPos];
+            tPos = tt[tPos];
+            if (rNToGo == 0) {
+                rNToGo = rNums[rTPos];
+                rTPos++;
+                if (rTPos == 512) {
+                    rTPos = 0;
+                }
+            }
+            rNToGo--;
+            ch2 ^= (int) ((rNToGo == 1) ? 1 : 0);
+            i2++;
+
+            currentChar = ch2;
+            currentState = RAND_PART_B_STATE;
+            mCrc.updateCRC(ch2);
+        } else {
+            endBlock();
+            initBlock();
+            setupBlock();
+        }
+    }
+
+    private void setupNoRandPartA() {
+        if (i2 <= last) {
+            chPrev = ch2;
+            ch2 = ll8[tPos];
+            tPos = tt[tPos];
+            i2++;
+
+            currentChar = ch2;
+            currentState = NO_RAND_PART_B_STATE;
+            mCrc.updateCRC(ch2);
+        } else {
+            endBlock();
+            initBlock();
+            setupBlock();
+        }
+    }
+
+    private void setupRandPartB() {
+        if (ch2 != chPrev) {
+            currentState = RAND_PART_A_STATE;
+            count = 1;
+            setupRandPartA();
+        } else {
+            count++;
+            if (count >= 4) {
+                z = ll8[tPos];
+                tPos = tt[tPos];
+                if (rNToGo == 0) {
+                    rNToGo = rNums[rTPos];
+                    rTPos++;
+                    if (rTPos == 512) {
+                        rTPos = 0;
+                    }
+                }
+                rNToGo--;
+                z ^= ((rNToGo == 1) ? 1 : 0);
+                j2 = 0;
+                currentState = RAND_PART_C_STATE;
+                setupRandPartC();
+            } else {
+                currentState = RAND_PART_A_STATE;
+                setupRandPartA();
+            }
+        }
+    }
+
+    private void setupRandPartC() {
+        if (j2 < (int) z) {
+            currentChar = ch2;
+            mCrc.updateCRC(ch2);
+            j2++;
+        } else {
+            currentState = RAND_PART_A_STATE;
+            i2++;
+            count = 0;
+            setupRandPartA();
+        }
+    }
+
+    private void setupNoRandPartB() {
+        if (ch2 != chPrev) {
+            currentState = NO_RAND_PART_A_STATE;
+            count = 1;
+            setupNoRandPartA();
+        } else {
+            count++;
+            if (count >= 4) {
+                z = ll8[tPos];
+                tPos = tt[tPos];
+                currentState = NO_RAND_PART_C_STATE;
+                j2 = 0;
+                setupNoRandPartC();
+            } else {
+                currentState = NO_RAND_PART_A_STATE;
+                setupNoRandPartA();
+            }
+        }
+    }
+
+    private void setupNoRandPartC() {
+        if (j2 < (int) z) {
+            currentChar = ch2;
+            mCrc.updateCRC(ch2);
+            j2++;
+        } else {
+            currentState = NO_RAND_PART_A_STATE;
+            i2++;
+            count = 0;
+            setupNoRandPartA();
+        }
+    }
+
+    private void setDecompressStructureSizes(int newSize100k) {
+        if (!(0 <= newSize100k && newSize100k <= 9 && 0 <= blockSize100k
+               && blockSize100k <= 9)) {
+            // throw new IOException("Invalid block size");
+        }
+
+        blockSize100k = newSize100k;
+
+        if (newSize100k == 0) {
+            return;
+        }
+
+        int n = baseBlockSize * newSize100k;
+        ll8 = new char[n];
+        tt = new int[n];
+    }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/bzip2/CBZip2OutputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/bzip2/CBZip2OutputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/bzip2/CBZip2OutputStream.java	(revision 16139)
@@ -0,0 +1,1632 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Keiron Liddle, Aftex Software
+ * <keiron@aftexsw.com> to whom the Ant project is very grateful for his
+ * great code.
+ */
+
+package org.apache.tools.bzip2;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * An output stream that compresses into the BZip2 format (without the file
+ * header chars) into another stream.
+ *
+ *
+ * TODO:    Update to BZip2 1.0.1
+ */
+public class CBZip2OutputStream extends OutputStream implements BZip2Constants {
+    protected static final int SETMASK = (1 << 21);
+    protected static final int CLEARMASK = (~SETMASK);
+    protected static final int GREATER_ICOST = 15;
+    protected static final int LESSER_ICOST = 0;
+    protected static final int SMALL_THRESH = 20;
+    protected static final int DEPTH_THRESH = 10;
+
+    /*
+      If you are ever unlucky/improbable enough
+      to get a stack overflow whilst sorting,
+      increase the following constant and try
+      again.  In practice I have never seen the
+      stack go above 27 elems, so the following
+      limit seems very generous.
+    */
+    protected static final int QSORT_STACK_SIZE = 1000;
+
+    private static void panic() {
+        System.out.println("panic");
+        //throw new CError();
+    }
+
+    private void makeMaps() {
+        int i;
+        nInUse = 0;
+        for (i = 0; i < 256; i++) {
+            if (inUse[i]) {
+                seqToUnseq[nInUse] = (char) i;
+                unseqToSeq[i] = (char) nInUse;
+                nInUse++;
+            }
+        }
+    }
+
+    protected static void hbMakeCodeLengths(char[] len, int[] freq,
+                                            int alphaSize, int maxLen) {
+        /*
+          Nodes and heap entries run from 1.  Entry 0
+          for both the heap and nodes is a sentinel.
+        */
+        int nNodes, nHeap, n1, n2, i, j, k;
+        boolean  tooLong;
+
+        int[] heap = new int[MAX_ALPHA_SIZE + 2];
+        int[] weight = new int[MAX_ALPHA_SIZE * 2];
+        int[] parent = new int[MAX_ALPHA_SIZE * 2];
+
+        for (i = 0; i < alphaSize; i++) {
+            weight[i + 1] = (freq[i] == 0 ? 1 : freq[i]) << 8;
+        }
+
+        while (true) {
+            nNodes = alphaSize;
+            nHeap = 0;
+
+            heap[0] = 0;
+            weight[0] = 0;
+            parent[0] = -2;
+
+            for (i = 1; i <= alphaSize; i++) {
+                parent[i] = -1;
+                nHeap++;
+                heap[nHeap] = i;
+                {
+                    int zz, tmp;
+                    zz = nHeap;
+                    tmp = heap[zz];
+                    while (weight[tmp] < weight[heap[zz >> 1]]) {
+                        heap[zz] = heap[zz >> 1];
+                        zz >>= 1;
+                    }
+                    heap[zz] = tmp;
+                }
+            }
+            if (!(nHeap < (MAX_ALPHA_SIZE + 2))) {
+                panic();
+            }
+
+            while (nHeap > 1) {
+                n1 = heap[1];
+                heap[1] = heap[nHeap];
+                nHeap--;
+                {
+                    int zz = 0, yy = 0, tmp = 0;
+                    zz = 1;
+                    tmp = heap[zz];
+                    while (true) {
+                        yy = zz << 1;
+                        if (yy > nHeap) {
+                            break;
+                        }
+                        if (yy < nHeap
+                            && weight[heap[yy + 1]] < weight[heap[yy]]) {
+                            yy++;
+                        }
+                        if (weight[tmp] < weight[heap[yy]]) {
+                            break;
+                        }
+                        heap[zz] = heap[yy];
+                        zz = yy;
+                    }
+                    heap[zz] = tmp;
+                }
+                n2 = heap[1];
+                heap[1] = heap[nHeap];
+                nHeap--;
+                {
+                    int zz = 0, yy = 0, tmp = 0;
+                    zz = 1;
+                    tmp = heap[zz];
+                    while (true) {
+                        yy = zz << 1;
+                        if (yy > nHeap) {
+                            break;
+                        }
+                        if (yy < nHeap
+                            && weight[heap[yy + 1]] < weight[heap[yy]]) {
+                            yy++;
+                        }
+                        if (weight[tmp] < weight[heap[yy]]) {
+                            break;
+                        }
+                        heap[zz] = heap[yy];
+                        zz = yy;
+                    }
+                    heap[zz] = tmp;
+                }
+                nNodes++;
+                parent[n1] = parent[n2] = nNodes;
+
+                weight[nNodes] = ((weight[n1] & 0xffffff00)
+                                  + (weight[n2] & 0xffffff00))
+                    | (1 + (((weight[n1] & 0x000000ff)
+                            > (weight[n2] & 0x000000ff))
+                            ? (weight[n1] & 0x000000ff)
+                            : (weight[n2] & 0x000000ff)));
+
+                parent[nNodes] = -1;
+                nHeap++;
+                heap[nHeap] = nNodes;
+                {
+                    int zz = 0, tmp = 0;
+                    zz = nHeap;
+                    tmp = heap[zz];
+                    while (weight[tmp] < weight[heap[zz >> 1]]) {
+                        heap[zz] = heap[zz >> 1];
+                        zz >>= 1;
+                    }
+                    heap[zz] = tmp;
+                }
+            }
+            if (!(nNodes < (MAX_ALPHA_SIZE * 2))) {
+                panic();
+            }
+
+            tooLong = false;
+            for (i = 1; i <= alphaSize; i++) {
+                j = 0;
+                k = i;
+                while (parent[k] >= 0) {
+                    k = parent[k];
+                    j++;
+                }
+                len[i - 1] = (char) j;
+                if (j > maxLen) {
+                    tooLong = true;
+                }
+            }
+
+            if (!tooLong) {
+                break;
+            }
+
+            for (i = 1; i < alphaSize; i++) {
+                j = weight[i] >> 8;
+                j = 1 + (j / 2);
+                weight[i] = j << 8;
+            }
+        }
+    }
+
+    /*
+      index of the last char in the block, so
+      the block size == last + 1.
+    */
+    int last;
+
+    /*
+      index in zptr[] of original string after sorting.
+    */
+    int origPtr;
+
+    /*
+      always: in the range 0 .. 9.
+      The current block size is 100000 * this number.
+    */
+    int blockSize100k;
+
+    boolean blockRandomised;
+
+    int bytesOut;
+    int bsBuff;
+    int bsLive;
+    CRC mCrc = new CRC();
+
+    private boolean[] inUse = new boolean[256];
+    private int nInUse;
+
+    private char[] seqToUnseq = new char[256];
+    private char[] unseqToSeq = new char[256];
+
+    private char[] selector = new char[MAX_SELECTORS];
+    private char[] selectorMtf = new char[MAX_SELECTORS];
+
+    private char[] block;
+    private int[] quadrant;
+    private int[] zptr;
+    private short[] szptr;
+    private int[] ftab;
+
+    private int nMTF;
+
+    private int[] mtfFreq = new int[MAX_ALPHA_SIZE];
+
+    /*
+     * Used when sorting.  If too many long comparisons
+     * happen, we stop sorting, randomise the block
+     * slightly, and try again.
+     */
+    private int workFactor;
+    private int workDone;
+    private int workLimit;
+    private boolean firstAttempt;
+    private int nBlocksRandomised;
+
+    private int currentChar = -1;
+    private int runLength = 0;
+
+    public CBZip2OutputStream(OutputStream inStream) throws IOException {
+        this(inStream, 9);
+    }
+
+    public CBZip2OutputStream(OutputStream inStream, int inBlockSize)
+        throws IOException {
+        block = null;
+        quadrant = null;
+        zptr = null;
+        ftab = null;
+
+        bsSetStream(inStream);
+
+        workFactor = 50;
+        if (inBlockSize > 9) {
+            inBlockSize = 9;
+        }
+        if (inBlockSize < 1) {
+            inBlockSize = 1;
+        }
+        blockSize100k = inBlockSize;
+        allocateCompressStructures();
+        initialize();
+        initBlock();
+    }
+
+    /**
+     *
+     * modified by Oliver Merkel, 010128
+     *
+     */
+    public void write(int bv) throws IOException {
+        int b = (256 + bv) % 256;
+        if (currentChar != -1) {
+            if (currentChar == b) {
+                runLength++;
+                if (runLength > 254) {
+                    writeRun();
+                    currentChar = -1;
+                    runLength = 0;
+                }
+            } else {
+                writeRun();
+                runLength = 1;
+                currentChar = b;
+            }
+        } else {
+            currentChar = b;
+            runLength++;
+        }
+    }
+
+    private void writeRun() throws IOException {
+        if (last < allowableBlockSize) {
+            inUse[currentChar] = true;
+            for (int i = 0; i < runLength; i++) {
+                mCrc.updateCRC((char) currentChar);
+            }
+            switch (runLength) {
+            case 1:
+                last++;
+                block[last + 1] = (char) currentChar;
+                break;
+            case 2:
+                last++;
+                block[last + 1] = (char) currentChar;
+                last++;
+                block[last + 1] = (char) currentChar;
+                break;
+            case 3:
+                last++;
+                block[last + 1] = (char) currentChar;
+                last++;
+                block[last + 1] = (char) currentChar;
+                last++;
+                block[last + 1] = (char) currentChar;
+                break;
+            default:
+                inUse[runLength - 4] = true;
+                last++;
+                block[last + 1] = (char) currentChar;
+                last++;
+                block[last + 1] = (char) currentChar;
+                last++;
+                block[last + 1] = (char) currentChar;
+                last++;
+                block[last + 1] = (char) currentChar;
+                last++;
+                block[last + 1] = (char) (runLength - 4);
+                break;
+            }
+        } else {
+            endBlock();
+            initBlock();
+            writeRun();
+        }
+    }
+
+    boolean closed = false;
+
+    protected void finalize() throws Throwable {
+        close();
+        super.finalize();
+    }
+
+    public void close() throws IOException {
+        if (closed) {
+            return;
+        }
+
+        if (runLength > 0) {
+            writeRun();
+        }
+        currentChar = -1;
+        endBlock();
+        endCompression();
+        closed = true;
+        super.close();
+        bsStream.close();
+    }
+
+    public void flush() throws IOException {
+        super.flush();
+        bsStream.flush();
+    }
+
+    private int blockCRC, combinedCRC;
+
+    private void initialize() throws IOException {
+        bytesOut = 0;
+        nBlocksRandomised = 0;
+
+        /* Write `magic' bytes h indicating file-format == huffmanised,
+           followed by a digit indicating blockSize100k.
+        */
+        bsPutUChar('h');
+        bsPutUChar('0' + blockSize100k);
+
+        combinedCRC = 0;
+    }
+
+    private int allowableBlockSize;
+
+    private void initBlock() {
+        //        blockNo++;
+        mCrc.initialiseCRC();
+        last = -1;
+        //        ch = 0;
+
+        for (int i = 0; i < 256; i++) {
+            inUse[i] = false;
+        }
+
+        /* 20 is just a paranoia constant */
+        allowableBlockSize = baseBlockSize * blockSize100k - 20;
+    }
+
+    private void endBlock() throws IOException {
+        blockCRC = mCrc.getFinalCRC();
+        combinedCRC = (combinedCRC << 1) | (combinedCRC >>> 31);
+        combinedCRC ^= blockCRC;
+
+        /* sort the block and establish posn of original string */
+        doReversibleTransformation();
+
+        /*
+          A 6-byte block header, the value chosen arbitrarily
+          as 0x314159265359 :-).  A 32 bit value does not really
+          give a strong enough guarantee that the value will not
+          appear by chance in the compressed datastream.  Worst-case
+          probability of this event, for a 900k block, is about
+          2.0e-3 for 32 bits, 1.0e-5 for 40 bits and 4.0e-8 for 48 bits.
+          For a compressed file of size 100Gb -- about 100000 blocks --
+          only a 48-bit marker will do.  NB: normal compression/
+          decompression do *not* rely on these statistical properties.
+          They are only important when trying to recover blocks from
+          damaged files.
+        */
+        bsPutUChar(0x31);
+        bsPutUChar(0x41);
+        bsPutUChar(0x59);
+        bsPutUChar(0x26);
+        bsPutUChar(0x53);
+        bsPutUChar(0x59);
+
+        /* Now the block's CRC, so it is in a known place. */
+        bsPutint(blockCRC);
+
+        /* Now a single bit indicating randomisation. */
+        if (blockRandomised) {
+            bsW(1, 1);
+            nBlocksRandomised++;
+        } else {
+            bsW(1, 0);
+        }
+
+        /* Finally, block's contents proper. */
+        moveToFrontCodeAndSend();
+    }
+
+    private void endCompression() throws IOException {
+        /*
+          Now another magic 48-bit number, 0x177245385090, to
+          indicate the end of the last block.  (sqrt(pi), if
+          you want to know.  I did want to use e, but it contains
+          too much repetition -- 27 18 28 18 28 46 -- for me
+          to feel statistically comfortable.  Call me paranoid.)
+        */
+        bsPutUChar(0x17);
+        bsPutUChar(0x72);
+        bsPutUChar(0x45);
+        bsPutUChar(0x38);
+        bsPutUChar(0x50);
+        bsPutUChar(0x90);
+
+        bsPutint(combinedCRC);
+
+        bsFinishedWithStream();
+    }
+
+    private void hbAssignCodes (int[] code, char[] length, int minLen,
+                                int maxLen, int alphaSize) {
+        int n, vec, i;
+
+        vec = 0;
+        for (n = minLen; n <= maxLen; n++) {
+            for (i = 0; i < alphaSize; i++) {
+                if (length[i] == n) {
+                    code[i] = vec;
+                    vec++;
+                }
+            };
+            vec <<= 1;
+        }
+    }
+
+    private void bsSetStream(OutputStream f) {
+        bsStream = f;
+        bsLive = 0;
+        bsBuff = 0;
+        bytesOut = 0;
+    }
+
+    private void bsFinishedWithStream() throws IOException {
+        while (bsLive > 0) {
+            int ch = (bsBuff >> 24);
+            try {
+                bsStream.write(ch); // write 8-bit
+            } catch (IOException e) {
+                throw  e;
+            }
+            bsBuff <<= 8;
+            bsLive -= 8;
+            bytesOut++;
+        }
+    }
+
+    private void bsW(int n, int v) throws IOException {
+        while (bsLive >= 8) {
+            int ch = (bsBuff >> 24);
+            try {
+                bsStream.write(ch); // write 8-bit
+            } catch (IOException e) {
+                throw e;
+            }
+            bsBuff <<= 8;
+            bsLive -= 8;
+            bytesOut++;
+        }
+        bsBuff |= (v << (32 - bsLive - n));
+        bsLive += n;
+    }
+
+    private void bsPutUChar(int c) throws IOException {
+        bsW(8, c);
+    }
+
+    private void bsPutint(int u) throws IOException {
+        bsW(8, (u >> 24) & 0xff);
+        bsW(8, (u >> 16) & 0xff);
+        bsW(8, (u >>  8) & 0xff);
+        bsW(8,  u        & 0xff);
+    }
+
+    private void bsPutIntVS(int numBits, int c) throws IOException {
+        bsW(numBits, c);
+    }
+
+    private void sendMTFValues() throws IOException {
+        char len[][] = new char[N_GROUPS][MAX_ALPHA_SIZE];
+
+        int v, t, i, j, gs, ge, totc, bt, bc, iter;
+        int nSelectors = 0, alphaSize, minLen, maxLen, selCtr;
+        int nGroups, nBytes;
+
+        alphaSize = nInUse + 2;
+        for (t = 0; t < N_GROUPS; t++) {
+            for (v = 0; v < alphaSize; v++) {
+                len[t][v] = (char) GREATER_ICOST;
+            }
+        }
+
+        /* Decide how many coding tables to use */
+        if (nMTF <= 0) {
+            panic();
+        }
+
+        if (nMTF < 200) {
+            nGroups = 2;
+        } else if (nMTF < 600) {
+            nGroups = 3;
+        } else if (nMTF < 1200) {
+            nGroups = 4;
+        } else if (nMTF < 2400) {
+            nGroups = 5;
+        } else {
+            nGroups = 6;
+        }
+
+        /* Generate an initial set of coding tables */ {
+            int nPart, remF, tFreq, aFreq;
+
+            nPart = nGroups;
+            remF  = nMTF;
+            gs = 0;
+            while (nPart > 0) {
+                tFreq = remF / nPart;
+                ge = gs - 1;
+                aFreq = 0;
+                while (aFreq < tFreq && ge < alphaSize - 1) {
+                    ge++;
+                    aFreq += mtfFreq[ge];
+                }
+
+                if (ge > gs && nPart != nGroups && nPart != 1
+                    && ((nGroups - nPart) % 2 == 1)) {
+                    aFreq -= mtfFreq[ge];
+                    ge--;
+                }
+
+                for (v = 0; v < alphaSize; v++) {
+                    if (v >= gs && v <= ge) {
+                        len[nPart - 1][v] = (char) LESSER_ICOST;
+                    } else {
+                        len[nPart - 1][v] = (char) GREATER_ICOST;
+                    }
+                }
+
+                nPart--;
+                gs = ge + 1;
+                remF -= aFreq;
+            }
+        }
+
+        int[][] rfreq = new int[N_GROUPS][MAX_ALPHA_SIZE];
+        int[] fave = new int[N_GROUPS];
+        short[] cost = new short[N_GROUPS];
+        /*
+          Iterate up to N_ITERS times to improve the tables.
+        */
+        for (iter = 0; iter < N_ITERS; iter++) {
+            for (t = 0; t < nGroups; t++) {
+                fave[t] = 0;
+            }
+
+            for (t = 0; t < nGroups; t++) {
+                for (v = 0; v < alphaSize; v++) {
+                    rfreq[t][v] = 0;
+                }
+            }
+
+            nSelectors = 0;
+            totc = 0;
+            gs = 0;
+            while (true) {
+
+                /* Set group start & end marks. */
+                if (gs >= nMTF) {
+                    break;
+                }
+                ge = gs + G_SIZE - 1;
+                if (ge >= nMTF) {
+                    ge = nMTF - 1;
+                }
+
+                /*
+                  Calculate the cost of this group as coded
+                  by each of the coding tables.
+                */
+                for (t = 0; t < nGroups; t++) {
+                    cost[t] = 0;
+                }
+
+                if (nGroups == 6) {
+                    short cost0, cost1, cost2, cost3, cost4, cost5;
+                    cost0 = cost1 = cost2 = cost3 = cost4 = cost5 = 0;
+                    for (i = gs; i <= ge; i++) {
+                        short icv = szptr[i];
+                        cost0 += len[0][icv];
+                        cost1 += len[1][icv];
+                        cost2 += len[2][icv];
+                        cost3 += len[3][icv];
+                        cost4 += len[4][icv];
+                        cost5 += len[5][icv];
+                    }
+                    cost[0] = cost0;
+                    cost[1] = cost1;
+                    cost[2] = cost2;
+                    cost[3] = cost3;
+                    cost[4] = cost4;
+                    cost[5] = cost5;
+                } else {
+                    for (i = gs; i <= ge; i++) {
+                        short icv = szptr[i];
+                        for (t = 0; t < nGroups; t++) {
+                            cost[t] += len[t][icv];
+                        }
+                    }
+                }
+
+                /*
+                  Find the coding table which is best for this group,
+                  and record its identity in the selector table.
+                */
+                bc = 999999999;
+                bt = -1;
+                for (t = 0; t < nGroups; t++) {
+                    if (cost[t] < bc) {
+                        bc = cost[t];
+                        bt = t;
+                    }
+                };
+                totc += bc;
+                fave[bt]++;
+                selector[nSelectors] = (char) bt;
+                nSelectors++;
+
+                /*
+                  Increment the symbol frequencies for the selected table.
+                */
+                for (i = gs; i <= ge; i++) {
+                    rfreq[bt][szptr[i]]++;
+                }
+
+                gs = ge + 1;
+            }
+
+            /*
+              Recompute the tables based on the accumulated frequencies.
+            */
+            for (t = 0; t < nGroups; t++) {
+                hbMakeCodeLengths(len[t], rfreq[t], alphaSize, 20);
+            }
+        }
+
+        rfreq = null;
+        fave = null;
+        cost = null;
+
+        if (!(nGroups < 8)) {
+            panic();
+        }
+        if (!(nSelectors < 32768 && nSelectors <= (2 + (900000 / G_SIZE)))) {
+            panic();
+        }
+
+
+        /* Compute MTF values for the selectors. */
+        {
+            char[] pos = new char[N_GROUPS];
+            char ll_i, tmp2, tmp;
+            for (i = 0; i < nGroups; i++) {
+                pos[i] = (char) i;
+            }
+            for (i = 0; i < nSelectors; i++) {
+                ll_i = selector[i];
+                j = 0;
+                tmp = pos[j];
+                while (ll_i != tmp) {
+                    j++;
+                    tmp2 = tmp;
+                    tmp = pos[j];
+                    pos[j] = tmp2;
+                }
+                pos[0] = tmp;
+                selectorMtf[i] = (char) j;
+            }
+        }
+
+        int[][] code = new int[N_GROUPS][MAX_ALPHA_SIZE];
+
+        /* Assign actual codes for the tables. */
+        for (t = 0; t < nGroups; t++) {
+            minLen = 32;
+            maxLen = 0;
+            for (i = 0; i < alphaSize; i++) {
+                if (len[t][i] > maxLen) {
+                    maxLen = len[t][i];
+                }
+                if (len[t][i] < minLen) {
+                    minLen = len[t][i];
+                }
+            }
+            if (maxLen > 20) {
+                panic();
+            }
+            if (minLen < 1) {
+                panic();
+            }
+            hbAssignCodes(code[t], len[t], minLen, maxLen, alphaSize);
+        }
+
+        /* Transmit the mapping table. */
+        {
+            boolean[] inUse16 = new boolean[16];
+            for (i = 0; i < 16; i++) {
+                inUse16[i] = false;
+                for (j = 0; j < 16; j++) {
+                    if (inUse[i * 16 + j]) {
+                        inUse16[i] = true;
+                    }
+                }
+            }
+
+            nBytes = bytesOut;
+            for (i = 0; i < 16; i++) {
+                if (inUse16[i]) {
+                    bsW(1, 1);
+                } else {
+                    bsW(1, 0);
+                }
+            }
+
+            for (i = 0; i < 16; i++) {
+                if (inUse16[i]) {
+                    for (j = 0; j < 16; j++) {
+                        if (inUse[i * 16 + j]) {
+                            bsW(1, 1);
+                        } else {
+                            bsW(1, 0);
+                        }
+                    }
+                }
+            }
+
+        }
+
+        /* Now the selectors. */
+        nBytes = bytesOut;
+        bsW (3, nGroups);
+        bsW (15, nSelectors);
+        for (i = 0; i < nSelectors; i++) {
+            for (j = 0; j < selectorMtf[i]; j++) {
+                bsW(1, 1);
+            }
+            bsW(1, 0);
+        }
+
+        /* Now the coding tables. */
+        nBytes = bytesOut;
+
+        for (t = 0; t < nGroups; t++) {
+            int curr = len[t][0];
+            bsW(5, curr);
+            for (i = 0; i < alphaSize; i++) {
+                while (curr < len[t][i]) {
+                    bsW(2, 2);
+                    curr++; /* 10 */
+                }
+                while (curr > len[t][i]) {
+                    bsW(2, 3);
+                    curr--; /* 11 */
+                }
+                bsW (1, 0);
+            }
+        }
+
+        /* And finally, the block data proper */
+        nBytes = bytesOut;
+        selCtr = 0;
+        gs = 0;
+        while (true) {
+            if (gs >= nMTF) {
+                break;
+            }
+            ge = gs + G_SIZE - 1;
+            if (ge >= nMTF) {
+                ge = nMTF - 1;
+            }
+            for (i = gs; i <= ge; i++) {
+                bsW(len[selector[selCtr]][szptr[i]],
+                    code[selector[selCtr]][szptr[i]]);
+            }
+
+            gs = ge + 1;
+            selCtr++;
+        }
+        if (!(selCtr == nSelectors)) {
+            panic();
+        }
+    }
+
+    private void moveToFrontCodeAndSend () throws IOException {
+        bsPutIntVS(24, origPtr);
+        generateMTFValues();
+        sendMTFValues();
+    }
+
+    private OutputStream bsStream;
+
+    private void simpleSort(int lo, int hi, int d) {
+        int i, j, h, bigN, hp;
+        int v;
+
+        bigN = hi - lo + 1;
+        if (bigN < 2) {
+            return;
+        }
+
+        hp = 0;
+        while (incs[hp] < bigN) {
+            hp++;
+        }
+        hp--;
+
+        for (; hp >= 0; hp--) {
+            h = incs[hp];
+
+            i = lo + h;
+            while (true) {
+                /* copy 1 */
+                if (i > hi) {
+                    break;
+                }
+                v = zptr[i];
+                j = i;
+                while (fullGtU(zptr[j - h] + d, v + d)) {
+                    zptr[j] = zptr[j - h];
+                    j = j - h;
+                    if (j <= (lo + h - 1)) {
+                        break;
+                    }
+                }
+                zptr[j] = v;
+                i++;
+
+                /* copy 2 */
+                if (i > hi) {
+                    break;
+                }
+                v = zptr[i];
+                j = i;
+                while (fullGtU(zptr[j - h] + d, v + d)) {
+                    zptr[j] = zptr[j - h];
+                    j = j - h;
+                    if (j <= (lo + h - 1)) {
+                        break;
+                    }
+                }
+                zptr[j] = v;
+                i++;
+
+                /* copy 3 */
+                if (i > hi) {
+                    break;
+                }
+                v = zptr[i];
+                j = i;
+                while (fullGtU(zptr[j - h] + d, v + d)) {
+                    zptr[j] = zptr[j - h];
+                    j = j - h;
+                    if (j <= (lo + h - 1)) {
+                        break;
+                    }
+                }
+                zptr[j] = v;
+                i++;
+
+                if (workDone > workLimit && firstAttempt) {
+                    return;
+                }
+            }
+        }
+    }
+
+    private void vswap(int p1, int p2, int n) {
+        int temp = 0;
+        while (n > 0) {
+            temp = zptr[p1];
+            zptr[p1] = zptr[p2];
+            zptr[p2] = temp;
+            p1++;
+            p2++;
+            n--;
+        }
+    }
+
+    private char med3(char a, char b, char c) {
+        char t;
+        if (a > b) {
+            t = a;
+            a = b;
+            b = t;
+        }
+        if (b > c) {
+            t = b;
+            b = c;
+            c = t;
+        }
+        if (a > b) {
+            b = a;
+        }
+        return b;
+    }
+
+    private static class StackElem {
+        int ll;
+        int hh;
+        int dd;
+    }
+
+    private void qSort3(int loSt, int hiSt, int dSt) {
+        int unLo, unHi, ltLo, gtHi, med, n, m;
+        int sp, lo, hi, d;
+        StackElem[] stack = new StackElem[QSORT_STACK_SIZE];
+        for (int count = 0; count < QSORT_STACK_SIZE; count++) {
+            stack[count] = new StackElem();
+        }
+
+        sp = 0;
+
+        stack[sp].ll = loSt;
+        stack[sp].hh = hiSt;
+        stack[sp].dd = dSt;
+        sp++;
+
+        while (sp > 0) {
+            if (sp >= QSORT_STACK_SIZE) {
+                panic();
+            }
+
+            sp--;
+            lo = stack[sp].ll;
+            hi = stack[sp].hh;
+            d = stack[sp].dd;
+
+            if (hi - lo < SMALL_THRESH || d > DEPTH_THRESH) {
+                simpleSort(lo, hi, d);
+                if (workDone > workLimit && firstAttempt) {
+                    return;
+                }
+                continue;
+            }
+
+            med = med3(block[zptr[lo] + d + 1],
+                       block[zptr[hi            ] + d  + 1],
+                       block[zptr[(lo + hi) >> 1] + d + 1]);
+
+            unLo = ltLo = lo;
+            unHi = gtHi = hi;
+
+            while (true) {
+                while (true) {
+                    if (unLo > unHi) {
+                        break;
+                    }
+                    n = ((int) block[zptr[unLo] + d + 1]) - med;
+                    if (n == 0) {
+                        int temp = 0;
+                        temp = zptr[unLo];
+                        zptr[unLo] = zptr[ltLo];
+                        zptr[ltLo] = temp;
+                        ltLo++;
+                        unLo++;
+                        continue;
+                    };
+                    if (n >  0) {
+                        break;
+                    }
+                    unLo++;
+                }
+                while (true) {
+                    if (unLo > unHi) {
+                        break;
+                    }
+                    n = ((int) block[zptr[unHi] + d + 1]) - med;
+                    if (n == 0) {
+                        int temp = 0;
+                        temp = zptr[unHi];
+                        zptr[unHi] = zptr[gtHi];
+                        zptr[gtHi] = temp;
+                        gtHi--;
+                        unHi--;
+                        continue;
+                    };
+                    if (n <  0) {
+                        break;
+                    }
+                    unHi--;
+                }
+                if (unLo > unHi) {
+                    break;
+                }
+                int temp = 0;
+                temp = zptr[unLo];
+                zptr[unLo] = zptr[unHi];
+                zptr[unHi] = temp;
+                unLo++;
+                unHi--;
+            }
+
+            if (gtHi < ltLo) {
+                stack[sp].ll = lo;
+                stack[sp].hh = hi;
+                stack[sp].dd = d + 1;
+                sp++;
+                continue;
+            }
+
+            n = ((ltLo - lo) < (unLo - ltLo)) ? (ltLo - lo) : (unLo - ltLo);
+            vswap(lo, unLo - n, n);
+            m = ((hi - gtHi) < (gtHi - unHi)) ? (hi - gtHi) : (gtHi - unHi);
+            vswap(unLo, hi - m + 1, m);
+
+            n = lo + unLo - ltLo - 1;
+            m = hi - (gtHi - unHi) + 1;
+
+            stack[sp].ll = lo;
+            stack[sp].hh = n;
+            stack[sp].dd = d;
+            sp++;
+
+            stack[sp].ll = n + 1;
+            stack[sp].hh = m - 1;
+            stack[sp].dd = d + 1;
+            sp++;
+
+            stack[sp].ll = m;
+            stack[sp].hh = hi;
+            stack[sp].dd = d;
+            sp++;
+        }
+    }
+
+    private void mainSort() {
+        int i, j, ss, sb;
+        int[] runningOrder = new int[256];
+        int[] copy = new int[256];
+        boolean[] bigDone = new boolean[256];
+        int c1, c2;
+        int numQSorted;
+
+        /*
+          In the various block-sized structures, live data runs
+          from 0 to last+NUM_OVERSHOOT_BYTES inclusive.  First,
+          set up the overshoot area for block.
+        */
+
+        //   if (verbosity >= 4) fprintf ( stderr, "   sort initialise ...\n" );
+        for (i = 0; i < NUM_OVERSHOOT_BYTES; i++) {
+            block[last + i + 2] = block[(i % (last + 1)) + 1];
+        }
+        for (i = 0; i <= last + NUM_OVERSHOOT_BYTES; i++) {
+            quadrant[i] = 0;
+        }
+
+        block[0] = (char) (block[last + 1]);
+
+        if (last < 4000) {
+            /*
+              Use simpleSort(), since the full sorting mechanism
+              has quite a large constant overhead.
+            */
+            for (i = 0; i <= last; i++) {
+                zptr[i] = i;
+            }
+            firstAttempt = false;
+            workDone = workLimit = 0;
+            simpleSort(0, last, 0);
+        } else {
+            numQSorted = 0;
+            for (i = 0; i <= 255; i++) {
+                bigDone[i] = false;
+            }
+
+            for (i = 0; i <= 65536; i++) {
+                ftab[i] = 0;
+            }
+
+            c1 = block[0];
+            for (i = 0; i <= last; i++) {
+                c2 = block[i + 1];
+                ftab[(c1 << 8) + c2]++;
+                c1 = c2;
+            }
+
+            for (i = 1; i <= 65536; i++) {
+                ftab[i] += ftab[i - 1];
+            }
+
+            c1 = block[1];
+            for (i = 0; i < last; i++) {
+                c2 = block[i + 2];
+                j = (c1 << 8) + c2;
+                c1 = c2;
+                ftab[j]--;
+                zptr[ftab[j]] = i;
+            }
+
+            j = ((block[last + 1]) << 8) + (block[1]);
+            ftab[j]--;
+            zptr[ftab[j]] = last;
+
+            /*
+              Now ftab contains the first loc of every small bucket.
+              Calculate the running order, from smallest to largest
+              big bucket.
+            */
+
+            for (i = 0; i <= 255; i++) {
+                runningOrder[i] = i;
+            }
+
+            {
+                int vv;
+                int h = 1;
+                do {
+                    h = 3 * h + 1;
+                }
+                while (h <= 256);
+                do {
+                    h = h / 3;
+                    for (i = h; i <= 255; i++) {
+                        vv = runningOrder[i];
+                        j = i;
+                        while ((ftab[((runningOrder[j - h]) + 1) << 8]
+                                - ftab[(runningOrder[j - h]) << 8])
+                                > (ftab[((vv) + 1) << 8] - ftab[(vv) << 8])) {
+                            runningOrder[j] = runningOrder[j - h];
+                            j = j - h;
+                            if (j <= (h - 1)) {
+                                break;
+                            }
+                        }
+                        runningOrder[j] = vv;
+                    }
+                } while (h != 1);
+            }
+
+            /*
+              The main sorting loop.
+            */
+            for (i = 0; i <= 255; i++) {
+
+                /*
+                  Process big buckets, starting with the least full.
+                */
+                ss = runningOrder[i];
+
+                /*
+                  Complete the big bucket [ss] by quicksorting
+                  any unsorted small buckets [ss, j].  Hopefully
+                  previous pointer-scanning phases have already
+                  completed many of the small buckets [ss, j], so
+                  we don't have to sort them at all.
+                */
+                for (j = 0; j <= 255; j++) {
+                    sb = (ss << 8) + j;
+                    if (!((ftab[sb] & SETMASK) == SETMASK)) {
+                        int lo = ftab[sb] & CLEARMASK;
+                        int hi = (ftab[sb + 1] & CLEARMASK) - 1;
+                        if (hi > lo) {
+                            qSort3(lo, hi, 2);
+                            numQSorted += (hi - lo + 1);
+                            if (workDone > workLimit && firstAttempt) {
+                                return;
+                            }
+                        }
+                        ftab[sb] |= SETMASK;
+                    }
+                }
+
+                /*
+                  The ss big bucket is now done.  Record this fact,
+                  and update the quadrant descriptors.  Remember to
+                  update quadrants in the overshoot area too, if
+                  necessary.  The "if (i < 255)" test merely skips
+                  this updating for the last bucket processed, since
+                  updating for the last bucket is pointless.
+                */
+                bigDone[ss] = true;
+
+                if (i < 255) {
+                    int bbStart  = ftab[ss << 8] & CLEARMASK;
+                    int bbSize   = (ftab[(ss + 1) << 8] & CLEARMASK) - bbStart;
+                    int shifts   = 0;
+
+                    while ((bbSize >> shifts) > 65534) {
+                        shifts++;
+                    }
+
+                    for (j = 0; j < bbSize; j++) {
+                        int a2update = zptr[bbStart + j];
+                        int qVal = (j >> shifts);
+                        quadrant[a2update] = qVal;
+                        if (a2update < NUM_OVERSHOOT_BYTES) {
+                            quadrant[a2update + last + 1] = qVal;
+                        }
+                    }
+
+                    if (!(((bbSize - 1) >> shifts) <= 65535)) {
+                        panic();
+                    }
+                }
+
+                /*
+                  Now scan this big bucket so as to synthesise the
+                  sorted order for small buckets [t, ss] for all t != ss.
+                */
+                for (j = 0; j <= 255; j++) {
+                    copy[j] = ftab[(j << 8) + ss] & CLEARMASK;
+                }
+
+                for (j = ftab[ss << 8] & CLEARMASK;
+                     j < (ftab[(ss + 1) << 8] & CLEARMASK); j++) {
+                    c1 = block[zptr[j]];
+                    if (!bigDone[c1]) {
+                        zptr[copy[c1]] = zptr[j] == 0 ? last : zptr[j] - 1;
+                        copy[c1]++;
+                    }
+                }
+
+                for (j = 0; j <= 255; j++) {
+                    ftab[(j << 8) + ss] |= SETMASK;
+                }
+            }
+        }
+    }
+
+    private void randomiseBlock() {
+        int i;
+        int rNToGo = 0;
+        int rTPos  = 0;
+        for (i = 0; i < 256; i++) {
+            inUse[i] = false;
+        }
+
+        for (i = 0; i <= last; i++) {
+            if (rNToGo == 0) {
+                rNToGo = (char) rNums[rTPos];
+                rTPos++;
+                if (rTPos == 512) {
+                    rTPos = 0;
+                }
+            }
+            rNToGo--;
+            block[i + 1] ^= ((rNToGo == 1) ? 1 : 0);
+            // handle 16 bit signed numbers
+            block[i + 1] &= 0xFF;
+
+            inUse[block[i + 1]] = true;
+        }
+    }
+
+    private void doReversibleTransformation() {
+        int i;
+
+        workLimit = workFactor * last;
+        workDone = 0;
+        blockRandomised = false;
+        firstAttempt = true;
+
+        mainSort();
+
+        if (workDone > workLimit && firstAttempt) {
+            randomiseBlock();
+            workLimit = workDone = 0;
+            blockRandomised = true;
+            firstAttempt = false;
+            mainSort();
+        }
+
+        origPtr = -1;
+        for (i = 0; i <= last; i++) {
+            if (zptr[i] == 0) {
+                origPtr = i;
+                break;
+            }
+        };
+
+        if (origPtr == -1) {
+            panic();
+        }
+    }
+
+    private boolean fullGtU(int i1, int i2) {
+        int k;
+        char c1, c2;
+        int s1, s2;
+
+        c1 = block[i1 + 1];
+        c2 = block[i2 + 1];
+        if (c1 != c2) {
+            return (c1 > c2);
+        }
+        i1++;
+        i2++;
+
+        c1 = block[i1 + 1];
+        c2 = block[i2 + 1];
+        if (c1 != c2) {
+            return (c1 > c2);
+        }
+        i1++;
+        i2++;
+
+        c1 = block[i1 + 1];
+        c2 = block[i2 + 1];
+        if (c1 != c2) {
+            return (c1 > c2);
+        }
+        i1++;
+        i2++;
+
+        c1 = block[i1 + 1];
+        c2 = block[i2 + 1];
+        if (c1 != c2) {
+            return (c1 > c2);
+        }
+        i1++;
+        i2++;
+
+        c1 = block[i1 + 1];
+        c2 = block[i2 + 1];
+        if (c1 != c2) {
+            return (c1 > c2);
+        }
+        i1++;
+        i2++;
+
+        c1 = block[i1 + 1];
+        c2 = block[i2 + 1];
+        if (c1 != c2) {
+            return (c1 > c2);
+        }
+        i1++;
+        i2++;
+
+        k = last + 1;
+
+        do {
+            c1 = block[i1 + 1];
+            c2 = block[i2 + 1];
+            if (c1 != c2) {
+                return (c1 > c2);
+            }
+            s1 = quadrant[i1];
+            s2 = quadrant[i2];
+            if (s1 != s2) {
+                return (s1 > s2);
+            }
+            i1++;
+            i2++;
+
+            c1 = block[i1 + 1];
+            c2 = block[i2 + 1];
+            if (c1 != c2) {
+                return (c1 > c2);
+            }
+            s1 = quadrant[i1];
+            s2 = quadrant[i2];
+            if (s1 != s2) {
+                return (s1 > s2);
+            }
+            i1++;
+            i2++;
+
+            c1 = block[i1 + 1];
+            c2 = block[i2 + 1];
+            if (c1 != c2) {
+                return (c1 > c2);
+            }
+            s1 = quadrant[i1];
+            s2 = quadrant[i2];
+            if (s1 != s2) {
+                return (s1 > s2);
+            }
+            i1++;
+            i2++;
+
+            c1 = block[i1 + 1];
+            c2 = block[i2 + 1];
+            if (c1 != c2) {
+                return (c1 > c2);
+            }
+            s1 = quadrant[i1];
+            s2 = quadrant[i2];
+            if (s1 != s2) {
+                return (s1 > s2);
+            }
+            i1++;
+            i2++;
+
+            if (i1 > last) {
+                i1 -= last;
+                i1--;
+            };
+            if (i2 > last) {
+                i2 -= last;
+                i2--;
+            };
+
+            k -= 4;
+            workDone++;
+        } while (k >= 0);
+
+        return false;
+    }
+
+    /*
+      Knuth's increments seem to work better
+      than Incerpi-Sedgewick here.  Possibly
+      because the number of elems to sort is
+      usually small, typically <= 20.
+    */
+    private int[] incs = {1, 4, 13, 40, 121, 364, 1093, 3280,
+                           9841, 29524, 88573, 265720,
+                           797161, 2391484};
+
+    private void allocateCompressStructures () {
+        int n = baseBlockSize * blockSize100k;
+        block = new char[(n + 1 + NUM_OVERSHOOT_BYTES)];
+        quadrant = new int[(n + NUM_OVERSHOOT_BYTES)];
+        zptr = new int[n];
+        ftab = new int[65537];
+
+        if (block == null || quadrant == null || zptr == null
+            || ftab == null) {
+            //int totalDraw = (n + 1 + NUM_OVERSHOOT_BYTES) + (n + NUM_OVERSHOOT_BYTES) + n + 65537;
+            //compressOutOfMemory ( totalDraw, n );
+        }
+
+        /*
+          The back end needs a place to store the MTF values
+          whilst it calculates the coding tables.  We could
+          put them in the zptr array.  However, these values
+          will fit in a short, so we overlay szptr at the
+          start of zptr, in the hope of reducing the number
+          of cache misses induced by the multiple traversals
+          of the MTF values when calculating coding tables.
+          Seems to improve compression speed by about 1%.
+        */
+        //    szptr = zptr;
+
+
+        szptr = new short[2 * n];
+    }
+
+    private void generateMTFValues() {
+        char[] yy = new char[256];
+        int  i, j;
+        char tmp;
+        char tmp2;
+        int zPend;
+        int wr;
+        int EOB;
+
+        makeMaps();
+        EOB = nInUse + 1;
+
+        for (i = 0; i <= EOB; i++) {
+            mtfFreq[i] = 0;
+        }
+
+        wr = 0;
+        zPend = 0;
+        for (i = 0; i < nInUse; i++) {
+            yy[i] = (char) i;
+        }
+
+
+        for (i = 0; i <= last; i++) {
+            char ll_i;
+
+            ll_i = unseqToSeq[block[zptr[i]]];
+
+            j = 0;
+            tmp = yy[j];
+            while (ll_i != tmp) {
+                j++;
+                tmp2 = tmp;
+                tmp = yy[j];
+                yy[j] = tmp2;
+            };
+            yy[0] = tmp;
+
+            if (j == 0) {
+                zPend++;
+            } else {
+                if (zPend > 0) {
+                    zPend--;
+                    while (true) {
+                        switch (zPend % 2) {
+                        case 0:
+                            szptr[wr] = (short) RUNA;
+                            wr++;
+                            mtfFreq[RUNA]++;
+                            break;
+                        case 1:
+                            szptr[wr] = (short) RUNB;
+                            wr++;
+                            mtfFreq[RUNB]++;
+                            break;
+                        };
+                        if (zPend < 2) {
+                            break;
+                        }
+                        zPend = (zPend - 2) / 2;
+                    };
+                    zPend = 0;
+                }
+                szptr[wr] = (short) (j + 1);
+                wr++;
+                mtfFreq[j + 1]++;
+            }
+        }
+
+        if (zPend > 0) {
+            zPend--;
+            while (true) {
+                switch (zPend % 2) {
+                case 0:
+                    szptr[wr] = (short) RUNA;
+                    wr++;
+                    mtfFreq[RUNA]++;
+                    break;
+                case 1:
+                    szptr[wr] = (short) RUNB;
+                    wr++;
+                    mtfFreq[RUNB]++;
+                    break;
+                }
+                if (zPend < 2) {
+                    break;
+                }
+                zPend = (zPend - 2) / 2;
+            }
+        }
+
+        szptr[wr] = (short) EOB;
+        wr++;
+        mtfFreq[EOB]++;
+
+        nMTF = wr;
+    }
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/bzip2/CRC.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/bzip2/CRC.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/bzip2/CRC.java	(revision 16139)
@@ -0,0 +1,129 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Keiron Liddle, Aftex Software
+ * <keiron@aftexsw.com> to whom the Ant project is very grateful for his
+ * great code.
+ */
+
+package org.apache.tools.bzip2;
+
+/**
+ * A simple class the hold and calculate the CRC for sanity checking
+ * of the data.
+ *
+ */
+class CRC {
+    public static int crc32Table[] = {
+        0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
+        0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
+        0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
+        0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+        0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
+        0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+        0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
+        0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
+        0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+        0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
+        0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
+        0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+        0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
+        0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+        0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+        0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
+        0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
+        0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+        0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
+        0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
+        0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
+        0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
+        0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
+        0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+        0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
+        0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
+        0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
+        0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
+        0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
+        0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+        0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
+        0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
+        0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
+        0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+        0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
+        0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+        0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
+        0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
+        0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+        0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
+        0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
+        0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+        0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
+        0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+        0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+        0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
+        0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
+        0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+        0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
+        0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
+        0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
+        0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
+        0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
+        0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+        0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
+        0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
+        0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
+        0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
+        0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
+        0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+        0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
+        0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
+        0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
+        0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+    };
+
+    public CRC() {
+        initialiseCRC();
+    }
+
+    void initialiseCRC() {
+        globalCrc = 0xffffffff;
+    }
+
+    int getFinalCRC() {
+        return ~globalCrc;
+    }
+
+    int getGlobalCRC() {
+        return globalCrc;
+    }
+
+    void setGlobalCRC(int newCrc) {
+        globalCrc = newCrc;
+    }
+
+    void updateCRC(int inCh) {
+        int temp = (globalCrc >> 24) ^ inCh;
+        if (temp < 0) {
+            temp = 256 + temp;
+        }
+        globalCrc = (globalCrc << 8) ^ CRC.crc32Table[temp];
+    }
+
+    int globalCrc;
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/mail/ErrorInQuitException.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/mail/ErrorInQuitException.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/mail/ErrorInQuitException.java	(revision 16139)
@@ -0,0 +1,42 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.mail;
+
+import java.io.IOException;
+
+/**
+ * Specialized IOException that get thrown if SMPT's QUIT command fails.
+ *
+ * <p>This seems to happen with some version of MS Exchange that
+ * doesn't respond with a 221 code immediately.  See <a
+ * href="http://nagoya.apache.org/bugzilla/show_bug.cgi?id=5273">Bug
+ * report 5273</a>.</p>
+ *
+ */
+public class ErrorInQuitException extends IOException {
+
+    /**
+     * Initialise from an IOException
+     *
+     * @param e the IO Exception.
+     */
+    public ErrorInQuitException(IOException e) {
+        super(e.getMessage());
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/mail/MailMessage.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/mail/MailMessage.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/mail/MailMessage.java	(revision 16139)
@@ -0,0 +1,524 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * The original version of this class was donated by Jason Hunter,
+ * who wrote the class as part of the com.oreilly.servlet
+ * package for his book "Java Servlet Programming" (O'Reilly).
+ * See http://www.servlets.com.
+ *
+ */
+
+package org.apache.tools.mail;
+
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.BufferedOutputStream;
+import java.io.OutputStream;
+import java.net.Socket;
+import java.net.InetAddress;
+import java.util.Vector;
+import java.util.Enumeration;
+
+/**
+ * A class to help send SMTP email.
+ * This class is an improvement on the sun.net.smtp.SmtpClient class
+ * found in the JDK.  This version has extra functionality, and can be used
+ * with JVMs that did not extend from the JDK.  It's not as robust as
+ * the JavaMail Standard Extension classes, but it's easier to use and
+ * easier to install, and has an Open Source license.
+ * <p>
+ * It can be used like this:
+ * <blockquote><pre>
+ * String mailhost = "localhost";  // or another mail host
+ * String from = "Mail Message Servlet &lt;MailMessage@server.com&gt;";
+ * String to = "to@you.com";
+ * String cc1 = "cc1@you.com";
+ * String cc2 = "cc2@you.com";
+ * String bcc = "bcc@you.com";
+ * &nbsp;
+ * MailMessage msg = new MailMessage(mailhost);
+ * msg.setPort(25);
+ * msg.from(from);
+ * msg.to(to);
+ * msg.cc(cc1);
+ * msg.cc(cc2);
+ * msg.bcc(bcc);
+ * msg.setSubject("Test subject");
+ * PrintStream out = msg.getPrintStream();
+ * &nbsp;
+ * Enumeration enum = req.getParameterNames();
+ * while (enum.hasMoreElements()) {
+ *   String name = (String)enum.nextElement();
+ *   String value = req.getParameter(name);
+ *   out.println(name + " = " + value);
+ * }
+ * &nbsp;
+ * msg.sendAndClose();
+ * </pre></blockquote>
+ * <p>
+ * Be sure to set the from address, then set the recepient
+ * addresses, then set the subject and other headers, then get the
+ * PrintStream, then write the message, and finally send and close.
+ * The class does minimal error checking internally; it counts on the mail
+ * host to complain if there's any malformatted input or out of order
+ * execution.
+ * <p>
+ * An attachment mechanism based on RFC 1521 could be implemented on top of
+ * this class.  In the meanwhile, JavaMail is the best solution for sending
+ * email with attachments.
+ * <p>
+ * Still to do:
+ * <ul>
+ * <li>Figure out how to close the connection in case of error
+ * </ul>
+ *
+ * @version 1.1, 2000/03/19, added angle brackets to address, helps some servers
+ * version 1.0, 1999/12/29
+ */
+public class MailMessage {
+
+    /** default mailhost */
+    public static final String DEFAULT_HOST = "localhost";
+
+    /** default port for SMTP: 25 */
+    public static final int DEFAULT_PORT = 25;
+
+    /** host name for the mail server */
+    private String host;
+
+    /** host port for the mail server */
+    private int port = DEFAULT_PORT;
+
+    /** sender email address */
+    private String from;
+
+    /** list of email addresses to reply to */
+    private Vector replyto;
+
+    /** list of email addresses to send to */
+    private Vector to;
+
+    /** list of email addresses to cc to */
+    private Vector cc;
+
+    /** headers to send in the mail */
+    private Vector headersKeys;
+    private Vector headersValues;
+
+    private MailPrintStream out;
+
+    private SmtpResponseReader in;
+
+    private Socket socket;
+    private static final int OK_READY = 220;
+    private static final int OK_HELO = 250;
+    private static final int OK_FROM = 250;
+    private static final int OK_RCPT_1 = 250;
+    private static final int OK_RCPT_2 = 251;
+    private static final int OK_DATA = 354;
+    private static final int OK_DOT = 250;
+    private static final int OK_QUIT = 221;
+
+  /**
+   * Constructs a new MailMessage to send an email.
+   * Use localhost as the mail server with port 25.
+   *
+   * @exception IOException if there's any problem contacting the mail server
+   */
+  public MailMessage() throws IOException {
+    this(DEFAULT_HOST, DEFAULT_PORT);
+  }
+
+  /**
+   * Constructs a new MailMessage to send an email.
+   * Use the given host as the mail server with port 25.
+   *
+   * @param host the mail server to use
+   * @exception IOException if there's any problem contacting the mail server
+   */
+  public MailMessage(String host) throws IOException {
+    this(host, DEFAULT_PORT);
+  }
+
+  /**
+   * Constructs a new MailMessage to send an email.
+   * Use the given host and port as the mail server.
+   *
+   * @param host the mail server to use
+   * @param port the port to connect to
+   * @exception IOException if there's any problem contacting the mail server
+   */
+  public MailMessage(String host, int port) throws IOException {
+    this.port = port;
+    this.host = host;
+    replyto = new Vector();
+    to = new Vector();
+    cc = new Vector();
+    headersKeys = new Vector();
+    headersValues = new Vector();
+    connect();
+    sendHelo();
+  }
+
+    /**
+     * Set the port to connect to the SMTP host.
+     * @param port the port to use for connection.
+     * @see #DEFAULT_PORT
+     */
+    public void setPort(int port) {
+        this.port = port;
+    }
+
+    /**
+     * Sets the from address.  Also sets the "From" header.  This method should
+     * be called only once.
+     * @param from the from address
+     * @exception IOException if there's any problem reported by the mail server
+     */
+    public void from(String from) throws IOException {
+        sendFrom(from);
+        this.from = from;
+    }
+
+    /**
+     * Sets the replyto address
+     * This method may be
+     * called multiple times.
+     * @param rto the replyto address
+     *
+     */
+    public void replyto(String rto) {
+      this.replyto.addElement(rto);
+    }
+
+  /**
+   * Sets the to address.  Also sets the "To" header.  This method may be
+   * called multiple times.
+   *
+   * @param to the to address
+   * @exception IOException if there's any problem reported by the mail server
+   */
+  public void to(String to) throws IOException {
+    sendRcpt(to);
+    this.to.addElement(to);
+  }
+
+  /**
+   * Sets the cc address.  Also sets the "Cc" header.  This method may be
+   * called multiple times.
+   *
+   * @param cc the cc address
+   * @exception IOException if there's any problem reported by the mail server
+   */
+  public void cc(String cc) throws IOException {
+    sendRcpt(cc);
+    this.cc.addElement(cc);
+  }
+
+  /**
+   * Sets the bcc address.  Does NOT set any header since it's a *blind* copy.
+   * This method may be called multiple times.
+   *
+   * @param bcc the bcc address
+   * @exception IOException if there's any problem reported by the mail server
+   */
+  public void bcc(String bcc) throws IOException {
+    sendRcpt(bcc);
+    // No need to keep track of Bcc'd addresses
+  }
+
+  /**
+   * Sets the subject of the mail message.  Actually sets the "Subject"
+   * header.
+   * @param subj the subject of the mail message
+   */
+  public void setSubject(String subj) {
+    setHeader("Subject", subj);
+  }
+
+  /**
+   * Sets the named header to the given value.  RFC 822 provides the rules for
+   * what text may constitute a header name and value.
+   * @param name name of the header
+   * @param value contents of the header
+   */
+  public void setHeader(String name, String value) {
+    // Blindly trust the user doesn't set any invalid headers
+    headersKeys.add(name);
+    headersValues.add(value);
+  }
+
+  /**
+   * Returns a PrintStream that can be used to write the body of the message.
+   * A stream is used since email bodies are byte-oriented.  A writer can
+   * be wrapped on top if necessary for internationalization.
+   * This is actually done in Message.java
+   *
+   * @return a printstream containing the data and the headers of the email
+   * @exception IOException if there's any problem reported by the mail server
+   * @see org.apache.tools.ant.taskdefs.email.Message
+   */
+  public PrintStream getPrintStream() throws IOException {
+    setFromHeader();
+    setReplyToHeader();
+    setToHeader();
+    setCcHeader();
+    setHeader("X-Mailer", "org.apache.tools.mail.MailMessage (ant.apache.org)");
+    sendData();
+    flushHeaders();
+    return out;
+  }
+
+
+  // RFC 822 s4.1: "From:" header must be sent
+  // We rely on error checking by the MTA
+  void setFromHeader() {
+    setHeader("From", from);
+  }
+
+  // RFC 822 s4.1: "Reply-To:" header is optional
+  void setReplyToHeader() {
+    if (!replyto.isEmpty()) {
+      setHeader("Reply-To", vectorToList(replyto));
+    }
+  }
+
+  void setToHeader() {
+    if (!to.isEmpty()) {
+      setHeader("To", vectorToList(to));
+    }
+  }
+
+  void setCcHeader() {
+    if (!cc.isEmpty()) {
+      setHeader("Cc", vectorToList(cc));
+    }
+  }
+
+  String vectorToList(Vector v) {
+    StringBuffer buf = new StringBuffer();
+    Enumeration e = v.elements();
+    while (e.hasMoreElements()) {
+      buf.append(e.nextElement());
+      if (e.hasMoreElements()) {
+        buf.append(", ");
+      }
+    }
+    return buf.toString();
+  }
+
+  void flushHeaders() throws IOException {
+    // RFC 822 s4.1:
+    //   "Header fields are NOT required to occur in any particular order,
+    //    except that the message body MUST occur AFTER the headers"
+    // (the same section specifies a reccommended order, which we ignore)
+   for (int i = 0; i < headersKeys.size(); i++) {
+      String name = (String) headersKeys.elementAt(i);
+      String value = (String) headersValues.elementAt(i);
+      out.println(name + ": " + value);
+    }
+    out.println();
+    out.flush();
+  }
+
+  /**
+   * Sends the message and closes the connection to the server.
+   * The MailMessage object cannot be reused.
+   *
+   * @exception IOException if there's any problem reported by the mail server
+   */
+  public void sendAndClose() throws IOException {
+      try {
+          sendDot();
+          sendQuit();
+      } finally {
+          disconnect();
+      }
+  }
+
+  // Make a limited attempt to extract a sanitized email address
+  // Prefer text in <brackets>, ignore anything in (parentheses)
+  static String sanitizeAddress(String s) {
+    int paramDepth = 0;
+    int start = 0;
+    int end = 0;
+    int len = s.length();
+
+    for (int i = 0; i < len; i++) {
+      char c = s.charAt(i);
+      if (c == '(') {
+        paramDepth++;
+        if (start == 0) {
+          end = i;  // support "address (name)"
+        }
+      } else if (c == ')') {
+        paramDepth--;
+        if (end == 0) {
+          start = i + 1;  // support "(name) address"
+        }
+      } else if (paramDepth == 0 && c == '<') {
+        start = i + 1;
+      } else if (paramDepth == 0 && c == '>') {
+        end = i;
+      }
+    }
+
+    if (end == 0) {
+      end = len;
+    }
+
+    return s.substring(start, end);
+  }
+
+  // * * * * * Raw protocol methods below here * * * * *
+
+  void connect() throws IOException {
+    socket = new Socket(host, port);
+    out = new MailPrintStream(
+          new BufferedOutputStream(
+          socket.getOutputStream()));
+    in = new SmtpResponseReader(socket.getInputStream());
+    getReady();
+  }
+
+  void getReady() throws IOException {
+    String response = in.getResponse();
+    int[] ok = {OK_READY};
+    if (!isResponseOK(response, ok)) {
+      throw new IOException(
+        "Didn't get introduction from server: " + response);
+    }
+  }
+  void sendHelo() throws IOException {
+    String local = InetAddress.getLocalHost().getHostName();
+    int[] ok = {OK_HELO};
+    send("HELO " + local, ok);
+  }
+  void sendFrom(String from) throws IOException {
+    int[] ok = {OK_FROM};
+    send("MAIL FROM: " + "<" + sanitizeAddress(from) + ">", ok);
+  }
+  void sendRcpt(String rcpt) throws IOException {
+    int[] ok = {OK_RCPT_1, OK_RCPT_2};
+    send("RCPT TO: " + "<" + sanitizeAddress(rcpt) + ">", ok);
+  }
+
+  void sendData() throws IOException {
+    int[] ok = {OK_DATA};
+    send("DATA", ok);
+  }
+
+  void sendDot() throws IOException {
+    int[] ok = {OK_DOT};
+    send("\r\n.", ok);  // make sure dot is on new line
+  }
+
+    void sendQuit() throws IOException {
+        int[] ok = {OK_QUIT};
+        try {
+            send("QUIT", ok);
+        } catch (IOException e) {
+            throw new ErrorInQuitException(e);
+        }
+    }
+
+    void send(String msg, int[] ok) throws IOException {
+        out.rawPrint(msg + "\r\n");  // raw supports <CRLF>.<CRLF>
+        String response = in.getResponse();
+        if (!isResponseOK(response, ok)) {
+            throw new IOException("Unexpected reply to command: "
+                                  + msg + ": " + response);
+        }
+    }
+
+  boolean isResponseOK(String response, int[] ok) {
+    // Check that the response is one of the valid codes
+    for (int i = 0; i < ok.length; i++) {
+      if (response.startsWith("" + ok[i])) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+    void disconnect() throws IOException {
+        if (out != null) {
+            out.close();
+        }
+        if (in != null) {
+            try {
+                in.close();
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+        if (socket != null) {
+            try {
+                socket.close();
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+    }
+}
+
+/**
+ * This PrintStream subclass makes sure that <CRLF>. becomes <CRLF>..
+ *  per RFC 821.  It also ensures that new lines are always \r\n.
+*/
+class MailPrintStream extends PrintStream {
+
+  private int lastChar;
+
+  public MailPrintStream(OutputStream out) {
+    super(out, true);  // deprecated, but email is byte-oriented
+  }
+
+  // Mac does \n\r, but that's tough to distinguish from Windows \r\n\r\n.
+  // Don't tackle that problem right now.
+  public void write(int b) {
+    if (b == '\n' && lastChar != '\r') {
+      rawWrite('\r');  // ensure always \r\n
+      rawWrite(b);
+    } else if (b == '.' && lastChar == '\n') {
+      rawWrite('.');  // add extra dot
+      rawWrite(b);
+    } else {
+      rawWrite(b);
+    }
+    lastChar = b;
+  }
+
+  public void write(byte[] buf, int off, int len) {
+    for (int i = 0; i < len; i++) {
+      write(buf[off + i]);
+    }
+  }
+
+  void rawWrite(int b) {
+    super.write(b);
+  }
+
+  void rawPrint(String s) {
+    int len = s.length();
+    for (int i = 0; i < len; i++) {
+      rawWrite(s.charAt(i));
+    }
+  }
+}
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/mail/SmtpResponseReader.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/mail/SmtpResponseReader.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/mail/SmtpResponseReader.java	(revision 16139)
@@ -0,0 +1,98 @@
+/*
+ * Copyright  2000-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.mail;
+
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+/**
+ * A wrapper around the raw input from the SMTP server that assembles
+ * multi line responses into a single String.
+ *
+ * <p>The same rules used here would apply to FTP and other Telnet
+ * based protocols as well.</p>
+ *
+ */
+public class SmtpResponseReader {
+
+    protected BufferedReader reader = null;
+    private StringBuffer result = new StringBuffer();
+
+    /**
+     * Wrap this input stream.
+     * @param in the stream to wrap.
+     */
+    public SmtpResponseReader(InputStream in) {
+        reader = new BufferedReader(new InputStreamReader(in));
+    }
+
+    /**
+     * Read until the server indicates that the response is complete.
+     *
+     * @return Responsecode (3 digits) + Blank + Text from all
+     *         response line concatenated (with blanks replacing the \r\n
+     *         sequences).
+     * @throws IOException on error.
+     */
+    public String getResponse() throws IOException {
+        result.setLength(0);
+        String line = reader.readLine();
+        if (line != null && line.length() >= 3) {
+            result.append(line.substring(0, 3));
+            result.append(" ");
+        }
+
+        while (line != null) {
+            append(line);
+            if (!hasMoreLines(line)) {
+                break;
+            }
+            line = reader.readLine();
+        }
+        return result.toString().trim();
+    }
+
+    /**
+     * Closes the underlying stream.
+     * @throws IOException on error.
+     */
+    public void close() throws IOException {
+        reader.close();
+    }
+
+    /**
+     * Should we expect more input?
+     * @param line the line to check.
+     * @return true if there are more lines to check.
+     */
+    protected boolean hasMoreLines(String line) {
+        return line.length() > 3 && line.charAt(3) == '-';
+    }
+
+    /**
+     * Append the text from this line of the resonse.
+     */
+    private void append(String line) {
+        if (line.length() > 4) {
+            result.append(line.substring(4));
+            result.append(" ");
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/tar/TarBuffer.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/tar/TarBuffer.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/tar/TarBuffer.java	(revision 16139)
@@ -0,0 +1,413 @@
+/*
+ * Copyright  2000,2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Timothy Gerard Endres
+ * (time@ice.com) to whom the Ant project is very grateful for his great code.
+ */
+
+package org.apache.tools.tar;
+
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+
+/**
+ * The TarBuffer class implements the tar archive concept
+ * of a buffered input stream. This concept goes back to the
+ * days of blocked tape drives and special io devices. In the
+ * Java universe, the only real function that this class
+ * performs is to ensure that files have the correct "block"
+ * size, or other tars will complain.
+ * <p>
+ * You should never have a need to access this class directly.
+ * TarBuffers are created by Tar IO Streams.
+ *
+ */
+
+public class TarBuffer {
+
+    public static final int DEFAULT_RCDSIZE = (512);
+    public static final int DEFAULT_BLKSIZE = (DEFAULT_RCDSIZE * 20);
+
+    private InputStream     inStream;
+    private OutputStream    outStream;
+    private byte[]          blockBuffer;
+    private int             currBlkIdx;
+    private int             currRecIdx;
+    private int             blockSize;
+    private int             recordSize;
+    private int             recsPerBlock;
+    private boolean         debug;
+
+    public TarBuffer(InputStream inStream) {
+        this(inStream, TarBuffer.DEFAULT_BLKSIZE);
+    }
+
+    public TarBuffer(InputStream inStream, int blockSize) {
+        this(inStream, blockSize, TarBuffer.DEFAULT_RCDSIZE);
+    }
+
+    public TarBuffer(InputStream inStream, int blockSize, int recordSize) {
+        this.inStream = inStream;
+        this.outStream = null;
+
+        this.initialize(blockSize, recordSize);
+    }
+
+    public TarBuffer(OutputStream outStream) {
+        this(outStream, TarBuffer.DEFAULT_BLKSIZE);
+    }
+
+    public TarBuffer(OutputStream outStream, int blockSize) {
+        this(outStream, blockSize, TarBuffer.DEFAULT_RCDSIZE);
+    }
+
+    public TarBuffer(OutputStream outStream, int blockSize, int recordSize) {
+        this.inStream = null;
+        this.outStream = outStream;
+
+        this.initialize(blockSize, recordSize);
+    }
+
+    /**
+     * Initialization common to all constructors.
+     */
+    private void initialize(int blockSize, int recordSize) {
+        this.debug = false;
+        this.blockSize = blockSize;
+        this.recordSize = recordSize;
+        this.recsPerBlock = (this.blockSize / this.recordSize);
+        this.blockBuffer = new byte[this.blockSize];
+
+        if (this.inStream != null) {
+            this.currBlkIdx = -1;
+            this.currRecIdx = this.recsPerBlock;
+        } else {
+            this.currBlkIdx = 0;
+            this.currRecIdx = 0;
+        }
+    }
+
+    /**
+     * Get the TAR Buffer's block size. Blocks consist of multiple records.
+     */
+    public int getBlockSize() {
+        return this.blockSize;
+    }
+
+    /**
+     * Get the TAR Buffer's record size.
+     */
+    public int getRecordSize() {
+        return this.recordSize;
+    }
+
+    /**
+     * Set the debugging flag for the buffer.
+     *
+     * @param debug If true, print debugging output.
+     */
+    public void setDebug(boolean debug) {
+        this.debug = debug;
+    }
+
+    /**
+     * Determine if an archive record indicate End of Archive. End of
+     * archive is indicated by a record that consists entirely of null bytes.
+     *
+     * @param record The record data to check.
+     */
+    public boolean isEOFRecord(byte[] record) {
+        for (int i = 0, sz = this.getRecordSize(); i < sz; ++i) {
+            if (record[i] != 0) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    /**
+     * Skip over a record on the input stream.
+     */
+    public void skipRecord() throws IOException {
+        if (this.debug) {
+            System.err.println("SkipRecord: recIdx = " + this.currRecIdx
+                               + " blkIdx = " + this.currBlkIdx);
+        }
+
+        if (this.inStream == null) {
+            throw new IOException("reading (via skip) from an output buffer");
+        }
+
+        if (this.currRecIdx >= this.recsPerBlock) {
+            if (!this.readBlock()) {
+                return;    // UNDONE
+            }
+        }
+
+        this.currRecIdx++;
+    }
+
+    /**
+     * Read a record from the input stream and return the data.
+     *
+     * @return The record data.
+     */
+    public byte[] readRecord() throws IOException {
+        if (this.debug) {
+            System.err.println("ReadRecord: recIdx = " + this.currRecIdx
+                               + " blkIdx = " + this.currBlkIdx);
+        }
+
+        if (this.inStream == null) {
+            throw new IOException("reading from an output buffer");
+        }
+
+        if (this.currRecIdx >= this.recsPerBlock) {
+            if (!this.readBlock()) {
+                return null;
+            }
+        }
+
+        byte[] result = new byte[this.recordSize];
+
+        System.arraycopy(this.blockBuffer,
+                         (this.currRecIdx * this.recordSize), result, 0,
+                         this.recordSize);
+
+        this.currRecIdx++;
+
+        return result;
+    }
+
+    /**
+     * @return false if End-Of-File, else true
+     */
+    private boolean readBlock() throws IOException {
+        if (this.debug) {
+            System.err.println("ReadBlock: blkIdx = " + this.currBlkIdx);
+        }
+
+        if (this.inStream == null) {
+            throw new IOException("reading from an output buffer");
+        }
+
+        this.currRecIdx = 0;
+
+        int offset = 0;
+        int bytesNeeded = this.blockSize;
+
+        while (bytesNeeded > 0) {
+            long numBytes = this.inStream.read(this.blockBuffer, offset,
+                                               bytesNeeded);
+
+            //
+            // NOTE
+            // We have fit EOF, and the block is not full!
+            //
+            // This is a broken archive. It does not follow the standard
+            // blocking algorithm. However, because we are generous, and
+            // it requires little effort, we will simply ignore the error
+            // and continue as if the entire block were read. This does
+            // not appear to break anything upstream. We used to return
+            // false in this case.
+            //
+            // Thanks to 'Yohann.Roussel@alcatel.fr' for this fix.
+            //
+            if (numBytes == -1) {
+                // However, just leaving the unread portion of the buffer dirty does 
+                // cause problems in some cases.  This problem is described in
+                // http://issues.apache.org/bugzilla/show_bug.cgi?id=29877
+                //
+                // The solution is to fill the unused portion of the buffer with zeros.
+
+                Arrays.fill(blockBuffer, offset, offset + bytesNeeded, (byte) 0);
+
+                break;
+            }
+
+            offset += numBytes;
+            bytesNeeded -= numBytes;
+
+            if (numBytes != this.blockSize) {
+                if (this.debug) {
+                    System.err.println("ReadBlock: INCOMPLETE READ "
+                                       + numBytes + " of " + this.blockSize
+                                       + " bytes read.");
+                }
+            }
+        }
+
+        this.currBlkIdx++;
+
+        return true;
+    }
+
+    /**
+     * Get the current block number, zero based.
+     *
+     * @return The current zero based block number.
+     */
+    public int getCurrentBlockNum() {
+        return this.currBlkIdx;
+    }
+
+    /**
+     * Get the current record number, within the current block, zero based.
+     * Thus, current offset = (currentBlockNum * recsPerBlk) + currentRecNum.
+     *
+     * @return The current zero based record number.
+     */
+    public int getCurrentRecordNum() {
+        return this.currRecIdx - 1;
+    }
+
+    /**
+     * Write an archive record to the archive.
+     *
+     * @param record The record data to write to the archive.
+     */
+    public void writeRecord(byte[] record) throws IOException {
+        if (this.debug) {
+            System.err.println("WriteRecord: recIdx = " + this.currRecIdx
+                               + " blkIdx = " + this.currBlkIdx);
+        }
+
+        if (this.outStream == null) {
+            throw new IOException("writing to an input buffer");
+        }
+
+        if (record.length != this.recordSize) {
+            throw new IOException("record to write has length '"
+                                  + record.length
+                                  + "' which is not the record size of '"
+                                  + this.recordSize + "'");
+        }
+
+        if (this.currRecIdx >= this.recsPerBlock) {
+            this.writeBlock();
+        }
+
+        System.arraycopy(record, 0, this.blockBuffer,
+                         (this.currRecIdx * this.recordSize),
+                         this.recordSize);
+
+        this.currRecIdx++;
+    }
+
+    /**
+     * Write an archive record to the archive, where the record may be
+     * inside of a larger array buffer. The buffer must be "offset plus
+     * record size" long.
+     *
+     * @param buf The buffer containing the record data to write.
+     * @param offset The offset of the record data within buf.
+     */
+    public void writeRecord(byte[] buf, int offset) throws IOException {
+        if (this.debug) {
+            System.err.println("WriteRecord: recIdx = " + this.currRecIdx
+                               + " blkIdx = " + this.currBlkIdx);
+        }
+
+        if (this.outStream == null) {
+            throw new IOException("writing to an input buffer");
+        }
+
+        if ((offset + this.recordSize) > buf.length) {
+            throw new IOException("record has length '" + buf.length
+                                  + "' with offset '" + offset
+                                  + "' which is less than the record size of '"
+                                  + this.recordSize + "'");
+        }
+
+        if (this.currRecIdx >= this.recsPerBlock) {
+            this.writeBlock();
+        }
+
+        System.arraycopy(buf, offset, this.blockBuffer,
+                         (this.currRecIdx * this.recordSize),
+                         this.recordSize);
+
+        this.currRecIdx++;
+    }
+
+    /**
+     * Write a TarBuffer block to the archive.
+     */
+    private void writeBlock() throws IOException {
+        if (this.debug) {
+            System.err.println("WriteBlock: blkIdx = " + this.currBlkIdx);
+        }
+
+        if (this.outStream == null) {
+            throw new IOException("writing to an input buffer");
+        }
+
+        this.outStream.write(this.blockBuffer, 0, this.blockSize);
+        this.outStream.flush();
+
+        this.currRecIdx = 0;
+        this.currBlkIdx++;
+    }
+
+    /**
+     * Flush the current data block if it has any data in it.
+     */
+    private void flushBlock() throws IOException {
+        if (this.debug) {
+            System.err.println("TarBuffer.flushBlock() called.");
+        }
+
+        if (this.outStream == null) {
+            throw new IOException("writing to an input buffer");
+        }
+
+        if (this.currRecIdx > 0) {
+            this.writeBlock();
+        }
+    }
+
+    /**
+     * Close the TarBuffer. If this is an output buffer, also flush the
+     * current block before closing.
+     */
+    public void close() throws IOException {
+        if (this.debug) {
+            System.err.println("TarBuffer.closeBuffer().");
+        }
+
+        if (this.outStream != null) {
+            this.flushBlock();
+
+            if (this.outStream != System.out
+                    && this.outStream != System.err) {
+                this.outStream.close();
+
+                this.outStream = null;
+            }
+        } else if (this.inStream != null) {
+            if (this.inStream != System.in) {
+                this.inStream.close();
+
+                this.inStream = null;
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/tar/TarConstants.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/tar/TarConstants.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/tar/TarConstants.java	(revision 16139)
@@ -0,0 +1,152 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Timothy Gerard Endres
+ * (time@ice.com) to whom the Ant project is very grateful for his great code.
+ */
+
+package org.apache.tools.tar;
+
+/**
+ * This interface contains all the definitions used in the package.
+ *
+ */
+
+public interface TarConstants {
+
+    /**
+     * The length of the name field in a header buffer.
+     */
+    int    NAMELEN = 100;
+
+    /**
+     * The length of the mode field in a header buffer.
+     */
+    int    MODELEN = 8;
+
+    /**
+     * The length of the user id field in a header buffer.
+     */
+    int    UIDLEN = 8;
+
+    /**
+     * The length of the group id field in a header buffer.
+     */
+    int    GIDLEN = 8;
+
+    /**
+     * The length of the checksum field in a header buffer.
+     */
+    int    CHKSUMLEN = 8;
+
+    /**
+     * The length of the size field in a header buffer.
+     */
+    int    SIZELEN = 12;
+
+    /**
+     * The length of the magic field in a header buffer.
+     */
+    int    MAGICLEN = 8;
+
+    /**
+     * The length of the modification time field in a header buffer.
+     */
+    int    MODTIMELEN = 12;
+
+    /**
+     * The length of the user name field in a header buffer.
+     */
+    int    UNAMELEN = 32;
+
+    /**
+     * The length of the group name field in a header buffer.
+     */
+    int    GNAMELEN = 32;
+
+    /**
+     * The length of the devices field in a header buffer.
+     */
+    int    DEVLEN = 8;
+
+    /**
+     * LF_ constants represent the "link flag" of an entry, or more commonly,
+     * the "entry type". This is the "old way" of indicating a normal file.
+     */
+    byte   LF_OLDNORM = 0;
+
+    /**
+     * Normal file type.
+     */
+    byte   LF_NORMAL = (byte) '0';
+
+    /**
+     * Link file type.
+     */
+    byte   LF_LINK = (byte) '1';
+
+    /**
+     * Symbolic link file type.
+     */
+    byte   LF_SYMLINK = (byte) '2';
+
+    /**
+     * Character device file type.
+     */
+    byte   LF_CHR = (byte) '3';
+
+    /**
+     * Block device file type.
+     */
+    byte   LF_BLK = (byte) '4';
+
+    /**
+     * Directory file type.
+     */
+    byte   LF_DIR = (byte) '5';
+
+    /**
+     * FIFO (pipe) file type.
+     */
+    byte   LF_FIFO = (byte) '6';
+
+    /**
+     * Contiguous file type.
+     */
+    byte   LF_CONTIG = (byte) '7';
+
+    /**
+     * The magic tag representing a POSIX tar archive.
+     */
+    String TMAGIC = "ustar";
+
+    /**
+     * The magic tag representing a GNU tar archive.
+     */
+    String GNU_TMAGIC = "ustar  ";
+
+    /**
+     * The namr of the GNU tar entry which contains a long name.
+     */
+    String GNU_LONGLINK = "././@LongLink";
+
+    /**
+     * Identifies the *next* file on the tape as having a long name.
+     */
+    byte LF_GNUTYPE_LONGNAME = (byte) 'L';
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/tar/TarEntry.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/tar/TarEntry.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/tar/TarEntry.java	(revision 16139)
@@ -0,0 +1,643 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Timothy Gerard Endres
+ * (time@ice.com) to whom the Ant project is very grateful for his great code.
+ */
+
+package org.apache.tools.tar;
+
+import java.io.File;
+import java.util.Date;
+import java.util.Locale;
+
+/**
+ * This class represents an entry in a Tar archive. It consists
+ * of the entry's header, as well as the entry's File. Entries
+ * can be instantiated in one of three ways, depending on how
+ * they are to be used.
+ * <p>
+ * TarEntries that are created from the header bytes read from
+ * an archive are instantiated with the TarEntry( byte[] )
+ * constructor. These entries will be used when extracting from
+ * or listing the contents of an archive. These entries have their
+ * header filled in using the header bytes. They also set the File
+ * to null, since they reference an archive entry not a file.
+ * <p>
+ * TarEntries that are created from Files that are to be written
+ * into an archive are instantiated with the TarEntry( File )
+ * constructor. These entries have their header filled in using
+ * the File's information. They also keep a reference to the File
+ * for convenience when writing entries.
+ * <p>
+ * Finally, TarEntries can be constructed from nothing but a name.
+ * This allows the programmer to construct the entry by hand, for
+ * instance when only an InputStream is available for writing to
+ * the archive, and the header information is constructed from
+ * other information. In this case the header fields are set to
+ * defaults and the File is set to null.
+ *
+ * <p>
+ * The C structure for a Tar Entry's header is:
+ * <pre>
+ * struct header {
+ * char name[NAMSIZ];
+ * char mode[8];
+ * char uid[8];
+ * char gid[8];
+ * char size[12];
+ * char mtime[12];
+ * char chksum[8];
+ * char linkflag;
+ * char linkname[NAMSIZ];
+ * char magic[8];
+ * char uname[TUNMLEN];
+ * char gname[TGNMLEN];
+ * char devmajor[8];
+ * char devminor[8];
+ * } header;
+ * </pre>
+ *
+ */
+
+public class TarEntry implements TarConstants {
+    /** The entry's name. */
+    private StringBuffer name;
+
+    /** The entry's permission mode. */
+    private int mode;
+
+    /** The entry's user id. */
+    private int userId;
+
+    /** The entry's group id. */
+    private int groupId;
+
+    /** The entry's size. */
+    private long size;
+
+    /** The entry's modification time. */
+    private long modTime;
+
+    /** The entry's checksum. */
+    private int checkSum;
+
+    /** The entry's link flag. */
+    private byte linkFlag;
+
+    /** The entry's link name. */
+    private StringBuffer linkName;
+
+    /** The entry's magic tag. */
+    private StringBuffer magic;
+
+    /** The entry's user name. */
+    private StringBuffer userName;
+
+    /** The entry's group name. */
+    private StringBuffer groupName;
+
+    /** The entry's major device number. */
+    private int devMajor;
+
+    /** The entry's minor device number. */
+    private int devMinor;
+
+    /** The entry's file reference */
+    private File file;
+
+    /** Maximum length of a user's name in the tar file */
+    public static final int MAX_NAMELEN = 31;
+
+    /** Default permissions bits for directories */
+    public static final int DEFAULT_DIR_MODE = 040755;
+
+    /** Default permissions bits for files */
+    public static final int DEFAULT_FILE_MODE = 0100644;
+
+    /** Convert millis to seconds */
+    public static final int MILLIS_PER_SECOND = 1000;
+
+    /**
+     * Construct an empty entry and prepares the header values.
+     */
+    private TarEntry () {
+        this.magic = new StringBuffer(TMAGIC);
+        this.name = new StringBuffer();
+        this.linkName = new StringBuffer();
+
+        String user = System.getProperty("user.name", "");
+
+        if (user.length() > MAX_NAMELEN) {
+            user = user.substring(0, MAX_NAMELEN);
+        }
+
+        this.userId = 0;
+        this.groupId = 0;
+        this.userName = new StringBuffer(user);
+        this.groupName = new StringBuffer("");
+        this.file = null;
+    }
+
+    /**
+     * Construct an entry with only a name. This allows the programmer
+     * to construct the entry's header "by hand". File is set to null.
+     *
+     * @param name the entry name
+     */
+    public TarEntry(String name) {
+        this();
+
+        boolean isDir = name.endsWith("/");
+
+        this.devMajor = 0;
+        this.devMinor = 0;
+        this.name = new StringBuffer(name);
+        this.mode = isDir ? DEFAULT_DIR_MODE : DEFAULT_FILE_MODE;
+        this.linkFlag = isDir ? LF_DIR : LF_NORMAL;
+        this.userId = 0;
+        this.groupId = 0;
+        this.size = 0;
+        this.checkSum = 0;
+        this.modTime = (new Date()).getTime() / MILLIS_PER_SECOND;
+        this.linkName = new StringBuffer("");
+        this.userName = new StringBuffer("");
+        this.groupName = new StringBuffer("");
+        this.devMajor = 0;
+        this.devMinor = 0;
+
+    }
+
+    /**
+     * Construct an entry with a name an a link flag.
+     *
+     * @param name the entry name
+     * @param linkFlag the entry link flag.
+     */
+    public TarEntry(String name, byte linkFlag) {
+        this(name);
+        this.linkFlag = linkFlag;
+    }
+
+    /**
+     * Construct an entry for a file. File is set to file, and the
+     * header is constructed from information from the file.
+     *
+     * @param file The file that the entry represents.
+     */
+    public TarEntry(File file) {
+        this();
+
+        this.file = file;
+
+        String name = file.getPath();
+        String osname = System.getProperty("os.name").toLowerCase(Locale.US);
+
+        if (osname != null) {
+
+            // Strip off drive letters!
+            // REVIEW Would a better check be "(File.separator == '\')"?
+
+            if (osname.startsWith("windows")) {
+                if (name.length() > 2) {
+                    char ch1 = name.charAt(0);
+                    char ch2 = name.charAt(1);
+
+                    if (ch2 == ':'
+                            && ((ch1 >= 'a' && ch1 <= 'z')
+                                || (ch1 >= 'A' && ch1 <= 'Z'))) {
+                        name = name.substring(2);
+                    }
+                }
+            } else if (osname.indexOf("netware") > -1) {
+                int colon = name.indexOf(':');
+                if (colon != -1) {
+                    name = name.substring(colon + 1);
+                }
+            }
+        }
+
+        name = name.replace(File.separatorChar, '/');
+
+        // No absolute pathnames
+        // Windows (and Posix?) paths can start with "\\NetworkDrive\",
+        // so we loop on starting /'s.
+        while (name.startsWith("/")) {
+            name = name.substring(1);
+        }
+
+        this.linkName = new StringBuffer("");
+        this.name = new StringBuffer(name);
+
+        if (file.isDirectory()) {
+            this.mode = DEFAULT_DIR_MODE;
+            this.linkFlag = LF_DIR;
+
+            if (this.name.charAt(this.name.length() - 1) != '/') {
+                this.name.append("/");
+            }
+        } else {
+            this.mode = DEFAULT_FILE_MODE;
+            this.linkFlag = LF_NORMAL;
+        }
+
+        this.size = file.length();
+        this.modTime = file.lastModified() / MILLIS_PER_SECOND;
+        this.checkSum = 0;
+        this.devMajor = 0;
+        this.devMinor = 0;
+    }
+
+    /**
+     * Construct an entry from an archive's header bytes. File is set
+     * to null.
+     *
+     * @param headerBuf The header bytes from a tar archive entry.
+     */
+    public TarEntry(byte[] headerBuf) {
+        this();
+        this.parseTarHeader(headerBuf);
+    }
+
+    /**
+     * Determine if the two entries are equal. Equality is determined
+     * by the header names being equal.
+     *
+     * @param it Entry to be checked for equality.
+     * @return True if the entries are equal.
+     */
+    public boolean equals(TarEntry it) {
+        return this.getName().equals(it.getName());
+    }
+
+    /**
+     * Determine if the two entries are equal. Equality is determined
+     * by the header names being equal.
+     *
+     * @param it Entry to be checked for equality.
+     * @return True if the entries are equal.
+     */
+    public boolean equals(Object it) {
+        if (it == null || getClass() != it.getClass()) {
+            return false;
+        }
+        return equals((TarEntry) it);
+    }
+
+    /**
+     * Hashcodes are based on entry names.
+     *
+     * @return the entry hashcode
+     */
+    public int hashCode() {
+        return getName().hashCode();
+    }
+
+    /**
+     * Determine if the given entry is a descendant of this entry.
+     * Descendancy is determined by the name of the descendant
+     * starting with this entry's name.
+     *
+     * @param desc Entry to be checked as a descendent of this.
+     * @return True if entry is a descendant of this.
+     */
+    public boolean isDescendent(TarEntry desc) {
+        return desc.getName().startsWith(this.getName());
+    }
+
+    /**
+     * Get this entry's name.
+     *
+     * @return This entry's name.
+     */
+    public String getName() {
+        return this.name.toString();
+    }
+
+    /**
+     * Set this entry's name.
+     *
+     * @param name This entry's new name.
+     */
+    public void setName(String name) {
+        this.name = new StringBuffer(name);
+    }
+
+    /**
+     * Set the mode for this entry
+     *
+     * @param mode the mode for this entry
+     */
+    public void setMode(int mode) {
+        this.mode = mode;
+    }
+
+    /**
+     * Get this entry's link name.
+     *
+     * @return This entry's link name.
+     */
+    public String getLinkName() {
+        return this.linkName.toString();
+    }
+
+    /**
+     * Get this entry's user id.
+     *
+     * @return This entry's user id.
+     */
+    public int getUserId() {
+        return this.userId;
+    }
+
+    /**
+     * Set this entry's user id.
+     *
+     * @param userId This entry's new user id.
+     */
+    public void setUserId(int userId) {
+        this.userId = userId;
+    }
+
+    /**
+     * Get this entry's group id.
+     *
+     * @return This entry's group id.
+     */
+    public int getGroupId() {
+        return this.groupId;
+    }
+
+    /**
+     * Set this entry's group id.
+     *
+     * @param groupId This entry's new group id.
+     */
+    public void setGroupId(int groupId) {
+        this.groupId = groupId;
+    }
+
+    /**
+     * Get this entry's user name.
+     *
+     * @return This entry's user name.
+     */
+    public String getUserName() {
+        return this.userName.toString();
+    }
+
+    /**
+     * Set this entry's user name.
+     *
+     * @param userName This entry's new user name.
+     */
+    public void setUserName(String userName) {
+        this.userName = new StringBuffer(userName);
+    }
+
+    /**
+     * Get this entry's group name.
+     *
+     * @return This entry's group name.
+     */
+    public String getGroupName() {
+        return this.groupName.toString();
+    }
+
+    /**
+     * Set this entry's group name.
+     *
+     * @param groupName This entry's new group name.
+     */
+    public void setGroupName(String groupName) {
+        this.groupName = new StringBuffer(groupName);
+    }
+
+    /**
+     * Convenience method to set this entry's group and user ids.
+     *
+     * @param userId This entry's new user id.
+     * @param groupId This entry's new group id.
+     */
+    public void setIds(int userId, int groupId) {
+        this.setUserId(userId);
+        this.setGroupId(groupId);
+    }
+
+    /**
+     * Convenience method to set this entry's group and user names.
+     *
+     * @param userName This entry's new user name.
+     * @param groupName This entry's new group name.
+     */
+    public void setNames(String userName, String groupName) {
+        this.setUserName(userName);
+        this.setGroupName(groupName);
+    }
+
+    /**
+     * Set this entry's modification time. The parameter passed
+     * to this method is in "Java time".
+     *
+     * @param time This entry's new modification time.
+     */
+    public void setModTime(long time) {
+        this.modTime = time / MILLIS_PER_SECOND;
+    }
+
+    /**
+     * Set this entry's modification time.
+     *
+     * @param time This entry's new modification time.
+     */
+    public void setModTime(Date time) {
+        this.modTime = time.getTime() / MILLIS_PER_SECOND;
+    }
+
+    /**
+     * Set this entry's modification time.
+     *
+     * @return time This entry's new modification time.
+     */
+    public Date getModTime() {
+        return new Date(this.modTime * MILLIS_PER_SECOND);
+    }
+
+    /**
+     * Get this entry's file.
+     *
+     * @return This entry's file.
+     */
+    public File getFile() {
+        return this.file;
+    }
+
+    /**
+     * Get this entry's mode.
+     *
+     * @return This entry's mode.
+     */
+    public int getMode() {
+        return this.mode;
+    }
+
+    /**
+     * Get this entry's file size.
+     *
+     * @return This entry's file size.
+     */
+    public long getSize() {
+        return this.size;
+    }
+
+    /**
+     * Set this entry's file size.
+     *
+     * @param size This entry's new file size.
+     */
+    public void setSize(long size) {
+        this.size = size;
+    }
+
+
+    /**
+     * Indicate if this entry is a GNU long name block
+     *
+     * @return true if this is a long name extension provided by GNU tar
+     */
+    public boolean isGNULongNameEntry() {
+        return linkFlag == LF_GNUTYPE_LONGNAME
+                           && name.toString().equals(GNU_LONGLINK);
+    }
+
+    /**
+     * Return whether or not this entry represents a directory.
+     *
+     * @return True if this entry is a directory.
+     */
+    public boolean isDirectory() {
+        if (this.file != null) {
+            return this.file.isDirectory();
+        }
+
+        if (this.linkFlag == LF_DIR) {
+            return true;
+        }
+
+        if (this.getName().endsWith("/")) {
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * If this entry represents a file, and the file is a directory, return
+     * an array of TarEntries for this entry's children.
+     *
+     * @return An array of TarEntry's for this entry's children.
+     */
+    public TarEntry[] getDirectoryEntries() {
+        if (this.file == null || !this.file.isDirectory()) {
+            return new TarEntry[0];
+        }
+
+        String[]   list = this.file.list();
+        TarEntry[] result = new TarEntry[list.length];
+
+        for (int i = 0; i < list.length; ++i) {
+            result[i] = new TarEntry(new File(this.file, list[i]));
+        }
+
+        return result;
+    }
+
+    /**
+     * Write an entry's header information to a header buffer.
+     *
+     * @param outbuf The tar entry header buffer to fill in.
+     */
+    public void writeEntryHeader(byte[] outbuf) {
+        int offset = 0;
+
+        offset = TarUtils.getNameBytes(this.name, outbuf, offset, NAMELEN);
+        offset = TarUtils.getOctalBytes(this.mode, outbuf, offset, MODELEN);
+        offset = TarUtils.getOctalBytes(this.userId, outbuf, offset, UIDLEN);
+        offset = TarUtils.getOctalBytes(this.groupId, outbuf, offset, GIDLEN);
+        offset = TarUtils.getLongOctalBytes(this.size, outbuf, offset, SIZELEN);
+        offset = TarUtils.getLongOctalBytes(this.modTime, outbuf, offset, MODTIMELEN);
+
+        int csOffset = offset;
+
+        for (int c = 0; c < CHKSUMLEN; ++c) {
+            outbuf[offset++] = (byte) ' ';
+        }
+
+        outbuf[offset++] = this.linkFlag;
+        offset = TarUtils.getNameBytes(this.linkName, outbuf, offset, NAMELEN);
+        offset = TarUtils.getNameBytes(this.magic, outbuf, offset, MAGICLEN);
+        offset = TarUtils.getNameBytes(this.userName, outbuf, offset, UNAMELEN);
+        offset = TarUtils.getNameBytes(this.groupName, outbuf, offset, GNAMELEN);
+        offset = TarUtils.getOctalBytes(this.devMajor, outbuf, offset, DEVLEN);
+        offset = TarUtils.getOctalBytes(this.devMinor, outbuf, offset, DEVLEN);
+
+        while (offset < outbuf.length) {
+            outbuf[offset++] = 0;
+        }
+
+        long checkSum = TarUtils.computeCheckSum(outbuf);
+
+        TarUtils.getCheckSumOctalBytes(checkSum, outbuf, csOffset, CHKSUMLEN);
+    }
+
+    /**
+     * Parse an entry's header information from a header buffer.
+     *
+     * @param header The tar entry header buffer to get information from.
+     */
+    public void parseTarHeader(byte[] header) {
+        int offset = 0;
+
+        this.name = TarUtils.parseName(header, offset, NAMELEN);
+        offset += NAMELEN;
+        this.mode = (int) TarUtils.parseOctal(header, offset, MODELEN);
+        offset += MODELEN;
+        this.userId = (int) TarUtils.parseOctal(header, offset, UIDLEN);
+        offset += UIDLEN;
+        this.groupId = (int) TarUtils.parseOctal(header, offset, GIDLEN);
+        offset += GIDLEN;
+        this.size = TarUtils.parseOctal(header, offset, SIZELEN);
+        offset += SIZELEN;
+        this.modTime = TarUtils.parseOctal(header, offset, MODTIMELEN);
+        offset += MODTIMELEN;
+        this.checkSum = (int) TarUtils.parseOctal(header, offset, CHKSUMLEN);
+        offset += CHKSUMLEN;
+        this.linkFlag = header[offset++];
+        this.linkName = TarUtils.parseName(header, offset, NAMELEN);
+        offset += NAMELEN;
+        this.magic = TarUtils.parseName(header, offset, MAGICLEN);
+        offset += MAGICLEN;
+        this.userName = TarUtils.parseName(header, offset, UNAMELEN);
+        offset += UNAMELEN;
+        this.groupName = TarUtils.parseName(header, offset, GNAMELEN);
+        offset += GNAMELEN;
+        this.devMajor = (int) TarUtils.parseOctal(header, offset, DEVLEN);
+        offset += DEVLEN;
+        this.devMinor = (int) TarUtils.parseOctal(header, offset, DEVLEN);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/tar/TarInputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/tar/TarInputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/tar/TarInputStream.java	(revision 16139)
@@ -0,0 +1,390 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Timothy Gerard Endres
+ * (time@ice.com) to whom the Ant project is very grateful for his great code.
+ */
+
+package org.apache.tools.tar;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * The TarInputStream reads a UNIX tar archive as an InputStream.
+ * methods are provided to position at each successive entry in
+ * the archive, and the read each entry as a normal input stream
+ * using read().
+ *
+ */
+public class TarInputStream extends FilterInputStream {
+
+    protected boolean debug;
+    protected boolean hasHitEOF;
+    protected int entrySize;
+    protected int entryOffset;
+    protected byte[] readBuf;
+    protected TarBuffer buffer;
+    protected TarEntry currEntry;
+    private boolean v7Format;
+
+    /**
+     * This contents of this array is not used at all in this class,
+     * it is only here to avoid repreated object creation during calls
+     * to the no-arg read method.
+     */
+    protected byte[] oneBuf;
+
+    /**
+     * Constructor for TarInputStream.
+     * @param is the input stream to use
+     */
+    public TarInputStream(InputStream is) {
+        this(is, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE);
+    }
+
+    /**
+     * Constructor for TarInputStream.
+     * @param is the input stream to use
+     * @param blockSize the block size to use
+     */
+    public TarInputStream(InputStream is, int blockSize) {
+        this(is, blockSize, TarBuffer.DEFAULT_RCDSIZE);
+    }
+
+    /**
+     * Constructor for TarInputStream.
+     * @param is the input stream to use
+     * @param blockSize the block size to use
+     * @param recordSize the record size to use
+     */
+    public TarInputStream(InputStream is, int blockSize, int recordSize) {
+        super(is);
+
+        this.buffer = new TarBuffer(is, blockSize, recordSize);
+        this.readBuf = null;
+        this.oneBuf = new byte[1];
+        this.debug = false;
+        this.hasHitEOF = false;
+        this.v7Format = false;
+    }
+
+    /**
+     * Sets the debugging flag.
+     *
+     * @param debug True to turn on debugging.
+     */
+    public void setDebug(boolean debug) {
+        this.debug = debug;
+        this.buffer.setDebug(debug);
+    }
+
+    /**
+     * Closes this stream. Calls the TarBuffer's close() method.
+     * @throws IOException on error
+     */
+    public void close() throws IOException {
+        this.buffer.close();
+    }
+
+    /**
+     * Get the record size being used by this stream's TarBuffer.
+     *
+     * @return The TarBuffer record size.
+     */
+    public int getRecordSize() {
+        return this.buffer.getRecordSize();
+    }
+
+    /**
+     * Get the available data that can be read from the current
+     * entry in the archive. This does not indicate how much data
+     * is left in the entire archive, only in the current entry.
+     * This value is determined from the entry's size header field
+     * and the amount of data already read from the current entry.
+     *
+     *
+     * @return The number of available bytes for the current entry.
+     * @throws IOException for signature
+     */
+    public int available() throws IOException {
+        return this.entrySize - this.entryOffset;
+    }
+
+    /**
+     * Skip bytes in the input buffer. This skips bytes in the
+     * current entry's data, not the entire archive, and will
+     * stop at the end of the current entry's data if the number
+     * to skip extends beyond that point.
+     *
+     * @param numToSkip The number of bytes to skip.
+     * @return the number actually skipped
+     * @throws IOException on error
+     */
+    public long skip(long numToSkip) throws IOException {
+        // REVIEW
+        // This is horribly inefficient, but it ensures that we
+        // properly skip over bytes via the TarBuffer...
+        //
+        byte[] skipBuf = new byte[8 * 1024];
+        long skip = numToSkip;
+        while (skip > 0) {
+            int realSkip = (int) (skip > skipBuf.length ? skipBuf.length : skip);
+            int numRead = this.read(skipBuf, 0, realSkip);
+            if (numRead == -1) {
+                break;
+            }
+            skip -= numRead;
+        }
+        return (numToSkip - skip);
+    }
+
+    /**
+     * Since we do not support marking just yet, we return false.
+     *
+     * @return False.
+     */
+    public boolean markSupported() {
+        return false;
+    }
+
+    /**
+     * Since we do not support marking just yet, we do nothing.
+     *
+     * @param markLimit The limit to mark.
+     */
+    public void mark(int markLimit) {
+    }
+
+    /**
+     * Since we do not support marking just yet, we do nothing.
+     */
+    public void reset() {
+    }
+
+    /**
+     * Get the next entry in this tar archive. This will skip
+     * over any remaining data in the current entry, if there
+     * is one, and place the input stream at the header of the
+     * next entry, and read the header and instantiate a new
+     * TarEntry from the header bytes and return that entry.
+     * If there are no more entries in the archive, null will
+     * be returned to indicate that the end of the archive has
+     * been reached.
+     *
+     * @return The next TarEntry in the archive, or null.
+     * @throws IOException on error
+     */
+    public TarEntry getNextEntry() throws IOException {
+        if (this.hasHitEOF) {
+            return null;
+        }
+
+        if (this.currEntry != null) {
+            int numToSkip = this.entrySize - this.entryOffset;
+
+            if (this.debug) {
+                System.err.println("TarInputStream: SKIP currENTRY '"
+                        + this.currEntry.getName() + "' SZ "
+                        + this.entrySize + " OFF "
+                        + this.entryOffset + "  skipping "
+                        + numToSkip + " bytes");
+            }
+
+            if (numToSkip > 0) {
+                this.skip(numToSkip);
+            }
+
+            this.readBuf = null;
+        }
+
+        byte[] headerBuf = this.buffer.readRecord();
+
+        if (headerBuf == null) {
+            if (this.debug) {
+                System.err.println("READ NULL RECORD");
+            }
+            this.hasHitEOF = true;
+        } else if (this.buffer.isEOFRecord(headerBuf)) {
+            if (this.debug) {
+                System.err.println("READ EOF RECORD");
+            }
+            this.hasHitEOF = true;
+        }
+
+        if (this.hasHitEOF) {
+            this.currEntry = null;
+        } else {
+            this.currEntry = new TarEntry(headerBuf);
+
+            if (!(headerBuf[257] == 'u' && headerBuf[258] == 's'
+                    && headerBuf[259] == 't' && headerBuf[260] == 'a'
+                    && headerBuf[261] == 'r')) {
+                this.v7Format = true;
+            }
+
+            if (this.debug) {
+                System.err.println("TarInputStream: SET CURRENTRY '"
+                        + this.currEntry.getName()
+                        + "' size = "
+                        + this.currEntry.getSize());
+            }
+
+            this.entryOffset = 0;
+
+            // REVIEW How do we resolve this discrepancy?!
+            this.entrySize = (int) this.currEntry.getSize();
+        }
+
+        if (this.currEntry != null && this.currEntry.isGNULongNameEntry()) {
+            // read in the name
+            StringBuffer longName = new StringBuffer();
+            byte[] buf = new byte[256];
+            int length = 0;
+            while ((length = read(buf)) >= 0) {
+                longName.append(new String(buf, 0, length));
+            }
+            getNextEntry();
+
+            // remove trailing null terminator
+            if (longName.length() > 0
+                && longName.charAt(longName.length() - 1) == 0) {
+                longName.deleteCharAt(longName.length() - 1);
+            }
+            this.currEntry.setName(longName.toString());
+        }
+
+        return this.currEntry;
+    }
+
+    /**
+     * Reads a byte from the current tar archive entry.
+     *
+     * This method simply calls read( byte[], int, int ).
+     *
+     * @return The byte read, or -1 at EOF.
+     * @throws IOException on error
+     */
+    public int read() throws IOException {
+        int num = this.read(this.oneBuf, 0, 1);
+        return num == -1 ? -1 : ((int) this.oneBuf[0]) & 0xFF;
+    }
+
+    /**
+     * Reads bytes from the current tar archive entry.
+     *
+     * This method is aware of the boundaries of the current
+     * entry in the archive and will deal with them as if they
+     * were this stream's start and EOF.
+     *
+     * @param buf The buffer into which to place bytes read.
+     * @param offset The offset at which to place bytes read.
+     * @param numToRead The number of bytes to read.
+     * @return The number of bytes read, or -1 at EOF.
+     * @throws IOException on error
+     */
+    public int read(byte[] buf, int offset, int numToRead) throws IOException {
+        int totalRead = 0;
+
+        if (this.entryOffset >= this.entrySize) {
+            return -1;
+        }
+
+        if ((numToRead + this.entryOffset) > this.entrySize) {
+            numToRead = (this.entrySize - this.entryOffset);
+        }
+
+        if (this.readBuf != null) {
+            int sz = (numToRead > this.readBuf.length) ? this.readBuf.length
+                    : numToRead;
+
+            System.arraycopy(this.readBuf, 0, buf, offset, sz);
+
+            if (sz >= this.readBuf.length) {
+                this.readBuf = null;
+            } else {
+                int newLen = this.readBuf.length - sz;
+                byte[] newBuf = new byte[newLen];
+
+                System.arraycopy(this.readBuf, sz, newBuf, 0, newLen);
+
+                this.readBuf = newBuf;
+            }
+
+            totalRead += sz;
+            numToRead -= sz;
+            offset += sz;
+        }
+
+        while (numToRead > 0) {
+            byte[] rec = this.buffer.readRecord();
+
+            if (rec == null) {
+                // Unexpected EOF!
+                throw new IOException("unexpected EOF with " + numToRead
+                        + " bytes unread");
+            }
+
+            int sz = numToRead;
+            int recLen = rec.length;
+
+            if (recLen > sz) {
+                System.arraycopy(rec, 0, buf, offset, sz);
+
+                this.readBuf = new byte[recLen - sz];
+
+                System.arraycopy(rec, sz, this.readBuf, 0, recLen - sz);
+            } else {
+                sz = recLen;
+
+                System.arraycopy(rec, 0, buf, offset, recLen);
+            }
+
+            totalRead += sz;
+            numToRead -= sz;
+            offset += sz;
+        }
+
+        this.entryOffset += totalRead;
+
+        return totalRead;
+    }
+
+    /**
+     * Copies the contents of the current tar archive entry directly into
+     * an output stream.
+     *
+     * @param out The OutputStream into which to write the entry's data.
+     * @throws IOException on error
+     */
+    public void copyEntryContents(OutputStream out) throws IOException {
+        byte[] buf = new byte[32 * 1024];
+
+        while (true) {
+            int numRead = this.read(buf, 0, buf.length);
+
+            if (numRead == -1) {
+                break;
+            }
+
+            out.write(buf, 0, numRead);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/tar/TarOutputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/tar/TarOutputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/tar/TarOutputStream.java	(revision 16139)
@@ -0,0 +1,313 @@
+/*
+ * Copyright  2000-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Timothy Gerard Endres
+ * (time@ice.com) to whom the Ant project is very grateful for his great code.
+ */
+
+package org.apache.tools.tar;
+
+import java.io.FilterOutputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * The TarOutputStream writes a UNIX tar archive as an OutputStream.
+ * Methods are provided to put entries, and then write their contents
+ * by writing to this stream using write().
+ *
+ */
+public class TarOutputStream extends FilterOutputStream {
+    /** Fail if a long file name is required in the archive. */
+    public static final int LONGFILE_ERROR = 0;
+
+    /** Long paths will be truncated in the archive. */
+    public static final int LONGFILE_TRUNCATE = 1;
+
+    /** GNU tar extensions are used to store long file names in the archive. */
+    public static final int LONGFILE_GNU = 2;
+
+    protected boolean   debug;
+    protected int       currSize;
+    protected int       currBytes;
+    protected byte[]    oneBuf;
+    protected byte[]    recordBuf;
+    protected int       assemLen;
+    protected byte[]    assemBuf;
+    protected TarBuffer buffer;
+    protected int       longFileMode = LONGFILE_ERROR;
+
+    public TarOutputStream(OutputStream os) {
+        this(os, TarBuffer.DEFAULT_BLKSIZE, TarBuffer.DEFAULT_RCDSIZE);
+    }
+
+    public TarOutputStream(OutputStream os, int blockSize) {
+        this(os, blockSize, TarBuffer.DEFAULT_RCDSIZE);
+    }
+
+    public TarOutputStream(OutputStream os, int blockSize, int recordSize) {
+        super(os);
+
+        this.buffer = new TarBuffer(os, blockSize, recordSize);
+        this.debug = false;
+        this.assemLen = 0;
+        this.assemBuf = new byte[recordSize];
+        this.recordBuf = new byte[recordSize];
+        this.oneBuf = new byte[1];
+    }
+
+    public void setLongFileMode(int longFileMode) {
+        this.longFileMode = longFileMode;
+    }
+
+
+    /**
+     * Sets the debugging flag.
+     *
+     * @param debugF True to turn on debugging.
+     */
+    public void setDebug(boolean debugF) {
+        this.debug = debugF;
+    }
+
+    /**
+     * Sets the debugging flag in this stream's TarBuffer.
+     *
+     * @param debug True to turn on debugging.
+     */
+    public void setBufferDebug(boolean debug) {
+        this.buffer.setDebug(debug);
+    }
+
+    /**
+     * Ends the TAR archive without closing the underlying OutputStream.
+     * The result is that the two EOF records of nulls are written.
+     */
+    public void finish() throws IOException {
+        // See Bugzilla 28776 for a discussion on this
+        // http://issues.apache.org/bugzilla/show_bug.cgi?id=28776
+        this.writeEOFRecord();
+        this.writeEOFRecord();
+    }
+
+    /**
+     * Ends the TAR archive and closes the underlying OutputStream.
+     * This means that finish() is called followed by calling the
+     * TarBuffer's close().
+     */
+    public void close() throws IOException {
+        this.finish();
+        this.buffer.close();
+    }
+
+    /**
+     * Get the record size being used by this stream's TarBuffer.
+     *
+     * @return The TarBuffer record size.
+     */
+    public int getRecordSize() {
+        return this.buffer.getRecordSize();
+    }
+
+    /**
+     * Put an entry on the output stream. This writes the entry's
+     * header record and positions the output stream for writing
+     * the contents of the entry. Once this method is called, the
+     * stream is ready for calls to write() to write the entry's
+     * contents. Once the contents are written, closeEntry()
+     * <B>MUST</B> be called to ensure that all buffered data
+     * is completely written to the output stream.
+     *
+     * @param entry The TarEntry to be written to the archive.
+     */
+    public void putNextEntry(TarEntry entry) throws IOException {
+        if (entry.getName().length() >= TarConstants.NAMELEN) {
+
+            if (longFileMode == LONGFILE_GNU) {
+                // create a TarEntry for the LongLink, the contents
+                // of which are the entry's name
+                TarEntry longLinkEntry = new TarEntry(TarConstants.GNU_LONGLINK,
+                                                      TarConstants.LF_GNUTYPE_LONGNAME);
+
+                longLinkEntry.setSize(entry.getName().length() + 1);
+                putNextEntry(longLinkEntry);
+                write(entry.getName().getBytes());
+                write(0);
+                closeEntry();
+            } else if (longFileMode != LONGFILE_TRUNCATE) {
+                throw new RuntimeException("file name '" + entry.getName()
+                                             + "' is too long ( > "
+                                             + TarConstants.NAMELEN + " bytes)");
+            }
+        }
+
+        entry.writeEntryHeader(this.recordBuf);
+        this.buffer.writeRecord(this.recordBuf);
+
+        this.currBytes = 0;
+
+        if (entry.isDirectory()) {
+            this.currSize = 0;
+        } else {
+            this.currSize = (int) entry.getSize();
+        }
+    }
+
+    /**
+     * Close an entry. This method MUST be called for all file
+     * entries that contain data. The reason is that we must
+     * buffer data written to the stream in order to satisfy
+     * the buffer's record based writes. Thus, there may be
+     * data fragments still being assembled that must be written
+     * to the output stream before this entry is closed and the
+     * next entry written.
+     */
+    public void closeEntry() throws IOException {
+        if (this.assemLen > 0) {
+            for (int i = this.assemLen; i < this.assemBuf.length; ++i) {
+                this.assemBuf[i] = 0;
+            }
+
+            this.buffer.writeRecord(this.assemBuf);
+
+            this.currBytes += this.assemLen;
+            this.assemLen = 0;
+        }
+
+        if (this.currBytes < this.currSize) {
+            throw new IOException("entry closed at '" + this.currBytes
+                                  + "' before the '" + this.currSize
+                                  + "' bytes specified in the header were written");
+        }
+    }
+
+    /**
+     * Writes a byte to the current tar archive entry.
+     *
+     * This method simply calls read( byte[], int, int ).
+     *
+     * @param b The byte written.
+     */
+    public void write(int b) throws IOException {
+        this.oneBuf[0] = (byte) b;
+
+        this.write(this.oneBuf, 0, 1);
+    }
+
+    /**
+     * Writes bytes to the current tar archive entry.
+     *
+     * This method simply calls write( byte[], int, int ).
+     *
+     * @param wBuf The buffer to write to the archive.
+     */
+    public void write(byte[] wBuf) throws IOException {
+        this.write(wBuf, 0, wBuf.length);
+    }
+
+    /**
+     * Writes bytes to the current tar archive entry. This method
+     * is aware of the current entry and will throw an exception if
+     * you attempt to write bytes past the length specified for the
+     * current entry. The method is also (painfully) aware of the
+     * record buffering required by TarBuffer, and manages buffers
+     * that are not a multiple of recordsize in length, including
+     * assembling records from small buffers.
+     *
+     * @param wBuf The buffer to write to the archive.
+     * @param wOffset The offset in the buffer from which to get bytes.
+     * @param numToWrite The number of bytes to write.
+     */
+    public void write(byte[] wBuf, int wOffset, int numToWrite) throws IOException {
+        if ((this.currBytes + numToWrite) > this.currSize) {
+            throw new IOException("request to write '" + numToWrite
+                                  + "' bytes exceeds size in header of '"
+                                  + this.currSize + "' bytes");
+
+            //
+            // We have to deal with assembly!!!
+            // The programmer can be writing little 32 byte chunks for all
+            // we know, and we must assemble complete records for writing.
+            // REVIEW Maybe this should be in TarBuffer? Could that help to
+            // eliminate some of the buffer copying.
+            //
+        }
+
+        if (this.assemLen > 0) {
+            if ((this.assemLen + numToWrite) >= this.recordBuf.length) {
+                int aLen = this.recordBuf.length - this.assemLen;
+
+                System.arraycopy(this.assemBuf, 0, this.recordBuf, 0,
+                                 this.assemLen);
+                System.arraycopy(wBuf, wOffset, this.recordBuf,
+                                 this.assemLen, aLen);
+                this.buffer.writeRecord(this.recordBuf);
+
+                this.currBytes += this.recordBuf.length;
+                wOffset += aLen;
+                numToWrite -= aLen;
+                this.assemLen = 0;
+            } else {
+                System.arraycopy(wBuf, wOffset, this.assemBuf, this.assemLen,
+                                 numToWrite);
+
+                wOffset += numToWrite;
+                this.assemLen += numToWrite;
+                numToWrite -= numToWrite;
+            }
+        }
+
+        //
+        // When we get here we have EITHER:
+        // o An empty "assemble" buffer.
+        // o No bytes to write (numToWrite == 0)
+        //
+        while (numToWrite > 0) {
+            if (numToWrite < this.recordBuf.length) {
+                System.arraycopy(wBuf, wOffset, this.assemBuf, this.assemLen,
+                                 numToWrite);
+
+                this.assemLen += numToWrite;
+
+                break;
+            }
+
+            this.buffer.writeRecord(wBuf, wOffset);
+
+            int num = this.recordBuf.length;
+
+            this.currBytes += num;
+            numToWrite -= num;
+            wOffset += num;
+        }
+    }
+
+    /**
+     * Write an EOF (end of archive) record to the tar archive.
+     * An EOF record consists of a record of all zeros.
+     */
+    private void writeEOFRecord() throws IOException {
+        for (int i = 0; i < this.recordBuf.length; ++i) {
+            this.recordBuf[i] = 0;
+        }
+
+        this.buffer.writeRecord(this.recordBuf);
+    }
+}
+
+
Index: /release-kits/shared/ant/src/main/org/apache/tools/tar/TarUtils.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/tar/TarUtils.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/tar/TarUtils.java	(revision 16139)
@@ -0,0 +1,194 @@
+/*
+ * Copyright  2000,2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Timothy Gerard Endres
+ * (time@ice.com) to whom the Ant project is very grateful for his great code.
+ */
+
+package org.apache.tools.tar;
+
+/**
+ * This class provides static utility methods to work with byte streams.
+ *
+ */
+public class TarUtils {
+
+    /**
+     * Parse an octal string from a header buffer. This is used for the
+     * file permission mode value.
+     *
+     * @param header The header buffer from which to parse.
+     * @param offset The offset into the buffer from which to parse.
+     * @param length The number of header bytes to parse.
+     * @return The long value of the octal string.
+     */
+    public static long parseOctal(byte[] header, int offset, int length) {
+        long    result = 0;
+        boolean stillPadding = true;
+        int     end = offset + length;
+
+        for (int i = offset; i < end; ++i) {
+            if (header[i] == 0) {
+                break;
+            }
+
+            if (header[i] == (byte) ' ' || header[i] == '0') {
+                if (stillPadding) {
+                    continue;
+                }
+
+                if (header[i] == (byte) ' ') {
+                    break;
+                }
+            }
+
+            stillPadding = false;
+            result = (result << 3) + (header[i] - '0');
+        }
+
+        return result;
+    }
+
+    /**
+     * Parse an entry name from a header buffer.
+     *
+     * @param header The header buffer from which to parse.
+     * @param offset The offset into the buffer from which to parse.
+     * @param length The number of header bytes to parse.
+     * @return The header's entry name.
+     */
+    public static StringBuffer parseName(byte[] header, int offset, int length) {
+        StringBuffer result = new StringBuffer(length);
+        int          end = offset + length;
+
+        for (int i = offset; i < end; ++i) {
+            if (header[i] == 0) {
+                break;
+            }
+
+            result.append((char) header[i]);
+        }
+
+        return result;
+    }
+
+    /**
+     * Determine the number of bytes in an entry name.
+     *
+     * @param name The header name from which to parse.
+     * @param offset The offset into the buffer from which to parse.
+     * @param length The number of header bytes to parse.
+     * @return The number of bytes in a header's entry name.
+     */
+    public static int getNameBytes(StringBuffer name, byte[] buf, int offset, int length) {
+        int i;
+
+        for (i = 0; i < length && i < name.length(); ++i) {
+            buf[offset + i] = (byte) name.charAt(i);
+        }
+
+        for (; i < length; ++i) {
+            buf[offset + i] = 0;
+        }
+
+        return offset + length;
+    }
+
+    /**
+     * Parse an octal integer from a header buffer.
+     *
+     * @param value The header value
+     * @param offset The offset into the buffer from which to parse.
+     * @param length The number of header bytes to parse.
+     * @return The integer value of the octal bytes.
+     */
+    public static int getOctalBytes(long value, byte[] buf, int offset, int length) {
+        int    idx = length - 1;
+
+        buf[offset + idx] = 0;
+        --idx;
+        buf[offset + idx] = (byte) ' ';
+        --idx;
+
+        if (value == 0) {
+            buf[offset + idx] = (byte) '0';
+            --idx;
+        } else {
+            for (long val = value; idx >= 0 && val > 0; --idx) {
+                buf[offset + idx] = (byte) ((byte) '0' + (byte) (val & 7));
+                val = val >> 3;
+            }
+        }
+
+        for (; idx >= 0; --idx) {
+            buf[offset + idx] = (byte) ' ';
+        }
+
+        return offset + length;
+    }
+
+    /**
+     * Parse an octal long integer from a header buffer.
+     *
+     * @param value The header value
+     * @param offset The offset into the buffer from which to parse.
+     * @param length The number of header bytes to parse.
+     * @return The long value of the octal bytes.
+     */
+    public static int getLongOctalBytes(long value, byte[] buf, int offset, int length) {
+        byte[] temp = new byte[length + 1];
+
+        getOctalBytes(value, temp, 0, length + 1);
+        System.arraycopy(temp, 0, buf, offset, length);
+
+        return offset + length;
+    }
+
+    /**
+     * Parse the checksum octal integer from a header buffer.
+     *
+     * @param value The header value
+     * @param offset The offset into the buffer from which to parse.
+     * @param length The number of header bytes to parse.
+     * @return The integer value of the entry's checksum.
+     */
+    public static int getCheckSumOctalBytes(long value, byte[] buf, int offset, int length) {
+        getOctalBytes(value, buf, offset, length);
+
+        buf[offset + length - 1] = (byte) ' ';
+        buf[offset + length - 2] = 0;
+
+        return offset + length;
+    }
+
+    /**
+     * Compute the checksum of a tar entry header.
+     *
+     * @param buf The tar entry's header buffer.
+     * @return The computed checksum.
+     */
+    public static long computeCheckSum(byte[] buf) {
+        long sum = 0;
+
+        for (int i = 0; i < buf.length; ++i) {
+            sum += 255 & buf[i];
+        }
+
+        return sum;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/zip/AsiExtraField.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/zip/AsiExtraField.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/zip/AsiExtraField.java	(revision 16139)
@@ -0,0 +1,327 @@
+/*
+ * Copyright  2001-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.zip;
+
+import java.util.zip.CRC32;
+import java.util.zip.ZipException;
+
+/**
+ * Adds Unix file permission and UID/GID fields as well as symbolic
+ * link handling.
+ *
+ * <p>This class uses the ASi extra field in the format:
+ * <pre>
+ *         Value         Size            Description
+ *         -----         ----            -----------
+ * (Unix3) 0x756e        Short           tag for this extra block type
+ *         TSize         Short           total data size for this block
+ *         CRC           Long            CRC-32 of the remaining data
+ *         Mode          Short           file permissions
+ *         SizDev        Long            symlink'd size OR major/minor dev num
+ *         UID           Short           user ID
+ *         GID           Short           group ID
+ *         (var.)        variable        symbolic link filename
+ * </pre>
+ * taken from appnote.iz (Info-ZIP note, 981119) found at <a
+ * href="ftp://ftp.uu.net/pub/archiving/zip/doc/">ftp://ftp.uu.net/pub/archiving/zip/doc/</a></p>
+
+ *
+ * <p>Short is two bytes and Long is four bytes in big endian byte and
+ * word order, device numbers are currently not supported.</p>
+ *
+ */
+public class AsiExtraField implements ZipExtraField, UnixStat, Cloneable {
+
+    private static final ZipShort HEADER_ID = new ZipShort(0x756E);
+
+    /**
+     * Standard Unix stat(2) file mode.
+     *
+     * @since 1.1
+     */
+    private int mode = 0;
+    /**
+     * User ID.
+     *
+     * @since 1.1
+     */
+    private int uid = 0;
+    /**
+     * Group ID.
+     *
+     * @since 1.1
+     */
+    private int gid = 0;
+    /**
+     * File this entry points to, if it is a symbolic link.
+     *
+     * <p>empty string - if entry is not a symbolic link.</p>
+     *
+     * @since 1.1
+     */
+    private String link = "";
+    /**
+     * Is this an entry for a directory?
+     *
+     * @since 1.1
+     */
+    private boolean dirFlag = false;
+
+    /**
+     * Instance used to calculate checksums.
+     *
+     * @since 1.1
+     */
+    private CRC32 crc = new CRC32();
+
+    public AsiExtraField() {
+    }
+
+    /**
+     * The Header-ID.
+     *
+     * @since 1.1
+     */
+    public ZipShort getHeaderId() {
+        return HEADER_ID;
+    }
+
+    /**
+     * Length of the extra field in the local file data - without
+     * Header-ID or length specifier.
+     *
+     * @since 1.1
+     */
+    public ZipShort getLocalFileDataLength() {
+        return new ZipShort(4         // CRC
+                          + 2         // Mode
+                          + 4         // SizDev
+                          + 2         // UID
+                          + 2         // GID
+                          + getLinkedFile().getBytes().length);
+    }
+
+    /**
+     * Delegate to local file data.
+     *
+     * @since 1.1
+     */
+    public ZipShort getCentralDirectoryLength() {
+        return getLocalFileDataLength();
+    }
+
+    /**
+     * The actual data to put into local file data - without Header-ID
+     * or length specifier.
+     *
+     * @since 1.1
+     */
+    public byte[] getLocalFileDataData() {
+        // CRC will be added later
+        byte[] data = new byte[getLocalFileDataLength().getValue() - 4];
+        System.arraycopy((new ZipShort(getMode())).getBytes(), 0, data, 0, 2);
+
+        byte[] linkArray = getLinkedFile().getBytes();
+        System.arraycopy((new ZipLong(linkArray.length)).getBytes(),
+                         0, data, 2, 4);
+
+        System.arraycopy((new ZipShort(getUserId())).getBytes(),
+                         0, data, 6, 2);
+        System.arraycopy((new ZipShort(getGroupId())).getBytes(),
+                         0, data, 8, 2);
+
+        System.arraycopy(linkArray, 0, data, 10, linkArray.length);
+
+        crc.reset();
+        crc.update(data);
+        long checksum = crc.getValue();
+
+        byte[] result = new byte[data.length + 4];
+        System.arraycopy((new ZipLong(checksum)).getBytes(), 0, result, 0, 4);
+        System.arraycopy(data, 0, result, 4, data.length);
+        return result;
+    }
+
+    /**
+     * Delegate to local file data.
+     *
+     * @since 1.1
+     */
+    public byte[] getCentralDirectoryData() {
+        return getLocalFileDataData();
+    }
+
+    /**
+     * Set the user id.
+     *
+     * @since 1.1
+     */
+    public void setUserId(int uid) {
+        this.uid = uid;
+    }
+
+    /**
+     * Get the user id.
+     *
+     * @since 1.1
+     */
+    public int getUserId() {
+        return uid;
+    }
+
+    /**
+     * Set the group id.
+     *
+     * @since 1.1
+     */
+    public void setGroupId(int gid) {
+        this.gid = gid;
+    }
+
+    /**
+     * Get the group id.
+     *
+     * @since 1.1
+     */
+    public int getGroupId() {
+        return gid;
+    }
+
+    /**
+     * Indicate that this entry is a symbolic link to the given filename.
+     *
+     * @param name Name of the file this entry links to, empty String
+     *             if it is not a symbolic link.
+     *
+     * @since 1.1
+     */
+    public void setLinkedFile(String name) {
+        link = name;
+        mode = getMode(mode);
+    }
+
+    /**
+     * Name of linked file
+     *
+     * @return name of the file this entry links to if it is a
+     *         symbolic link, the empty string otherwise.
+     *
+     * @since 1.1
+     */
+    public String getLinkedFile() {
+        return link;
+    }
+
+    /**
+     * Is this entry a symbolic link?
+     *
+     * @since 1.1
+     */
+    public boolean isLink() {
+        return getLinkedFile().length() != 0;
+    }
+
+    /**
+     * File mode of this file.
+     *
+     * @since 1.1
+     */
+    public void setMode(int mode) {
+        this.mode = getMode(mode);
+    }
+
+    /**
+     * File mode of this file.
+     *
+     * @since 1.1
+     */
+    public int getMode() {
+        return mode;
+    }
+
+    /**
+     * Indicate whether this entry is a directory.
+     *
+     * @since 1.1
+     */
+    public void setDirectory(boolean dirFlag) {
+        this.dirFlag = dirFlag;
+        mode = getMode(mode);
+    }
+
+    /**
+     * Is this entry a directory?
+     *
+     * @since 1.1
+     */
+    public boolean isDirectory() {
+        return dirFlag && !isLink();
+    }
+
+    /**
+     * Populate data from this array as if it was in local file data.
+     *
+     * @since 1.1
+     */
+    public void parseFromLocalFileData(byte[] data, int offset, int length)
+        throws ZipException {
+
+        long givenChecksum = (new ZipLong(data, offset)).getValue();
+        byte[] tmp = new byte[length - 4];
+        System.arraycopy(data, offset + 4, tmp, 0, length - 4);
+        crc.reset();
+        crc.update(tmp);
+        long realChecksum = crc.getValue();
+        if (givenChecksum != realChecksum) {
+            throw new ZipException("bad CRC checksum "
+                                   + Long.toHexString(givenChecksum)
+                                   + " instead of "
+                                   + Long.toHexString(realChecksum));
+        }
+
+        int newMode = (new ZipShort(tmp, 0)).getValue();
+        byte[] linkArray = new byte[(int) (new ZipLong(tmp, 2)).getValue()];
+        uid = (new ZipShort(tmp, 6)).getValue();
+        gid = (new ZipShort(tmp, 8)).getValue();
+
+        if (linkArray.length == 0) {
+            link = "";
+        } else {
+            System.arraycopy(tmp, 10, linkArray, 0, linkArray.length);
+            link = new String(linkArray);
+        }
+        setDirectory((newMode & DIR_FLAG) != 0);
+        setMode(newMode);
+    }
+
+    /**
+     * Get the file mode for given permissions with the correct file type.
+     *
+     * @since 1.1
+     */
+    protected int getMode(int mode) {
+        int type = FILE_FLAG;
+        if (isLink()) {
+            type = LINK_FLAG;
+        } else if (isDirectory()) {
+            type = DIR_FLAG;
+        }
+        return type | (mode & PERM_MASK);
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/zip/ExtraFieldUtils.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/zip/ExtraFieldUtils.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/zip/ExtraFieldUtils.java	(revision 16139)
@@ -0,0 +1,165 @@
+/*
+ * Copyright  2001-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.zip;
+
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.zip.ZipException;
+
+/**
+ * ZipExtraField related methods
+ *
+ */
+public class ExtraFieldUtils {
+
+    /**
+     * Static registry of known extra fields.
+     *
+     * @since 1.1
+     */
+    private static Hashtable implementations;
+
+    static {
+        implementations = new Hashtable();
+        register(AsiExtraField.class);
+        register(JarMarker.class);
+    }
+
+    /**
+     * Register a ZipExtraField implementation.
+     *
+     * <p>The given class must have a no-arg constructor and implement
+     * the {@link ZipExtraField ZipExtraField interface}.</p>
+     *
+     * @since 1.1
+     */
+    public static void register(Class c) {
+        try {
+            ZipExtraField ze = (ZipExtraField) c.newInstance();
+            implementations.put(ze.getHeaderId(), c);
+        } catch (ClassCastException cc) {
+            throw new RuntimeException(c + " doesn\'t implement ZipExtraField");
+        } catch (InstantiationException ie) {
+            throw new RuntimeException(c + " is not a concrete class");
+        } catch (IllegalAccessException ie) {
+            throw new RuntimeException(c + "\'s no-arg constructor is not public");
+        }
+    }
+
+    /**
+     * Create an instance of the approriate ExtraField, falls back to
+     * {@link UnrecognizedExtraField UnrecognizedExtraField}.
+     *
+     * @since 1.1
+     */
+    public static ZipExtraField createExtraField(ZipShort headerId)
+        throws InstantiationException, IllegalAccessException {
+        Class c = (Class) implementations.get(headerId);
+        if (c != null) {
+            return (ZipExtraField) c.newInstance();
+        }
+        UnrecognizedExtraField u = new UnrecognizedExtraField();
+        u.setHeaderId(headerId);
+        return u;
+    }
+
+    /**
+     * Split the array into ExtraFields and populate them with the
+     * give data.
+     *
+     * @since 1.1
+     */
+    public static ZipExtraField[] parse(byte[] data) throws ZipException {
+        Vector v = new Vector();
+        int start = 0;
+        while (start <= data.length - 4) {
+            ZipShort headerId = new ZipShort(data, start);
+            int length = (new ZipShort(data, start + 2)).getValue();
+            if (start + 4 + length > data.length) {
+                throw new ZipException("data starting at " + start
+                    + " is in unknown format");
+            }
+            try {
+                ZipExtraField ze = createExtraField(headerId);
+                ze.parseFromLocalFileData(data, start + 4, length);
+                v.addElement(ze);
+            } catch (InstantiationException ie) {
+                throw new ZipException(ie.getMessage());
+            } catch (IllegalAccessException iae) {
+                throw new ZipException(iae.getMessage());
+            }
+            start += (length + 4);
+        }
+        if (start != data.length) { // array not exhausted
+            throw new ZipException("data starting at " + start
+                + " is in unknown format");
+        }
+
+        ZipExtraField[] result = new ZipExtraField[v.size()];
+        v.copyInto(result);
+        return result;
+    }
+
+    /**
+     * Merges the local file data fields of the given ZipExtraFields.
+     *
+     * @since 1.1
+     */
+    public static byte[] mergeLocalFileDataData(ZipExtraField[] data) {
+        int sum = 4 * data.length;
+        for (int i = 0; i < data.length; i++) {
+            sum += data[i].getLocalFileDataLength().getValue();
+        }
+        byte[] result = new byte[sum];
+        int start = 0;
+        for (int i = 0; i < data.length; i++) {
+            System.arraycopy(data[i].getHeaderId().getBytes(),
+                             0, result, start, 2);
+            System.arraycopy(data[i].getLocalFileDataLength().getBytes(),
+                             0, result, start + 2, 2);
+            byte[] local = data[i].getLocalFileDataData();
+            System.arraycopy(local, 0, result, start + 4, local.length);
+            start += (local.length + 4);
+        }
+        return result;
+    }
+
+    /**
+     * Merges the central directory fields of the given ZipExtraFields.
+     *
+     * @since 1.1
+     */
+    public static byte[] mergeCentralDirectoryData(ZipExtraField[] data) {
+        int sum = 4 * data.length;
+        for (int i = 0; i < data.length; i++) {
+            sum += data[i].getCentralDirectoryLength().getValue();
+        }
+        byte[] result = new byte[sum];
+        int start = 0;
+        for (int i = 0; i < data.length; i++) {
+            System.arraycopy(data[i].getHeaderId().getBytes(),
+                             0, result, start, 2);
+            System.arraycopy(data[i].getCentralDirectoryLength().getBytes(),
+                             0, result, start + 2, 2);
+            byte[] local = data[i].getCentralDirectoryData();
+            System.arraycopy(local, 0, result, start + 4, local.length);
+            start += (local.length + 4);
+        }
+        return result;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/zip/JarMarker.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/zip/JarMarker.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/zip/JarMarker.java	(revision 16139)
@@ -0,0 +1,105 @@
+/*
+ * Copyright  2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.zip;
+
+import java.util.zip.ZipException;
+
+/**
+ * If this extra field is added as the very first extra field of the
+ * archive, Solaris will consider it an executable jar file.
+ *
+ * @since Ant 1.6.3
+ */
+public final class JarMarker implements ZipExtraField {
+
+    private static ZipShort ID = new ZipShort(0xCAFE);
+    private static ZipShort NULL = new ZipShort(0);
+    private static byte[] NO_BYTES = new byte[0];
+    private static JarMarker DEFAULT = new JarMarker();
+
+    public JarMarker() {
+        // empty
+    }
+
+    /**
+     * Since JarMarker is stateless we can always use the same instance.
+     */
+    public static JarMarker getInstance() {
+        return DEFAULT;
+    }
+
+    /**
+     * The Header-ID.
+     * @return the header id
+     */
+    public ZipShort getHeaderId() {
+        return ID;
+    }
+
+    /**
+     * Length of the extra field in the local file data - without
+     * Header-ID or length specifier.
+     * @return 0
+     */
+    public ZipShort getLocalFileDataLength() {
+        return NULL;
+    }
+
+    /**
+     * Length of the extra field in the central directory - without
+     * Header-ID or length specifier.
+     * @return 0
+     */
+    public ZipShort getCentralDirectoryLength() {
+        return NULL;
+    }
+
+    /**
+     * The actual data to put into local file data - without Header-ID
+     * or length specifier.
+     * @return the data
+     * @since 1.1
+     */
+    public byte[] getLocalFileDataData() {
+        return NO_BYTES;
+    }
+
+    /**
+     * The actual data to put central directory - without Header-ID or
+     * length specifier.
+     * @return the data
+     */
+    public byte[] getCentralDirectoryData() {
+        return NO_BYTES;
+    }
+
+    /**
+     * Populate data from this array as if it was in local file data.
+     * @param data an array of bytes
+     * @param offset the start offset
+     * @param length the number of bytes in the array from offset
+     *
+     * @throws ZipException on error
+     */
+    public void parseFromLocalFileData(byte[] data, int offset, int length)
+        throws ZipException {
+        if (length != 0) {
+            throw new ZipException("JarMarker doesn't expect any data");
+        }
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/zip/UnixStat.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/zip/UnixStat.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/zip/UnixStat.java	(revision 16139)
@@ -0,0 +1,74 @@
+/*
+ * Copyright  2001,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.zip;
+
+/**
+ * Constants from stat.h on Unix systems.
+ *
+ */
+public interface UnixStat {
+
+    /**
+     * Bits used for permissions (and sticky bit)
+     *
+     * @since 1.1
+     */
+    int PERM_MASK =           07777;
+    /**
+     * Indicates symbolic links.
+     *
+     * @since 1.1
+     */
+    int LINK_FLAG =         0120000;
+    /**
+     * Indicates plain files.
+     *
+     * @since 1.1
+     */
+    int FILE_FLAG =         0100000;
+    /**
+     * Indicates directories.
+     *
+     * @since 1.1
+     */
+    int DIR_FLAG =           040000;
+
+    // ----------------------------------------------------------
+    // somewhat arbitrary choices that are quite common for shared
+    // installations
+    // -----------------------------------------------------------
+
+    /**
+     * Default permissions for symbolic links.
+     *
+     * @since 1.1
+     */
+    int DEFAULT_LINK_PERM =    0777;
+    /**
+     * Default permissions for directories.
+     *
+     * @since 1.1
+     */
+    int DEFAULT_DIR_PERM =     0755;
+    /**
+     * Default permissions for plain files.
+     *
+     * @since 1.1
+     */
+    int DEFAULT_FILE_PERM =    0644;
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/zip/UnrecognizedExtraField.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/zip/UnrecognizedExtraField.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/zip/UnrecognizedExtraField.java	(revision 16139)
@@ -0,0 +1,96 @@
+/*
+ * Copyright  2001-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.zip;
+
+/**
+ * Simple placeholder for all those extra fields we don't want to deal
+ * with.
+ *
+ * <p>Assumes local file data and central directory entries are
+ * identical - unless told the opposite.</p>
+ *
+ */
+public class UnrecognizedExtraField implements ZipExtraField {
+
+    /**
+     * The Header-ID.
+     *
+     * @since 1.1
+     */
+    private ZipShort headerId;
+
+    public void setHeaderId(ZipShort headerId) {
+        this.headerId = headerId;
+    }
+
+    public ZipShort getHeaderId() {
+        return headerId;
+    }
+
+    /**
+     * Extra field data in local file data - without
+     * Header-ID or length specifier.
+     *
+     * @since 1.1
+     */
+    private byte[] localData;
+
+    public void setLocalFileDataData(byte[] data) {
+        localData = data;
+    }
+
+    public ZipShort getLocalFileDataLength() {
+        return new ZipShort(localData.length);
+    }
+
+    public byte[] getLocalFileDataData() {
+        return localData;
+    }
+
+    /**
+     * Extra field data in central directory - without
+     * Header-ID or length specifier.
+     *
+     * @since 1.1
+     */
+    private byte[] centralData;
+
+    public void setCentralDirectoryData(byte[] data) {
+        centralData = data;
+    }
+
+    public ZipShort getCentralDirectoryLength() {
+        if (centralData != null) {
+            return new ZipShort(centralData.length);
+        }
+        return getLocalFileDataLength();
+    }
+
+    public byte[] getCentralDirectoryData() {
+        if (centralData != null) {
+            return centralData;
+        }
+        return getLocalFileDataData();
+    }
+
+    public void parseFromLocalFileData(byte[] data, int offset, int length) {
+        byte[] tmp = new byte[length];
+        System.arraycopy(data, offset, tmp, 0, length);
+        setLocalFileDataData(tmp);
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipEntry.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipEntry.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipEntry.java	(revision 16139)
@@ -0,0 +1,457 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.zip;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Vector;
+import java.util.zip.ZipException;
+
+/**
+ * Extension that adds better handling of extra fields and provides
+ * access to the internal and external file attributes.
+ *
+ */
+public class ZipEntry extends java.util.zip.ZipEntry implements Cloneable {
+
+    private static final int PLATFORM_UNIX = 3;
+    private static final int PLATFORM_FAT  = 0;
+
+    private int internalAttributes = 0;
+    private int platform = PLATFORM_FAT;
+    private long externalAttributes = 0;
+    private Vector extraFields = new Vector();
+    private String name = null;
+
+    /**
+     * Creates a new zip entry with the specified name.
+     *
+     * @since 1.1
+     */
+    public ZipEntry(String name) {
+        super(name);
+    }
+
+    /**
+     * Creates a new zip entry with fields taken from the specified zip entry.
+     *
+     * @since 1.1
+     */
+    public ZipEntry(java.util.zip.ZipEntry entry) throws ZipException {
+        /*
+         * REVISIT: call super(entry) instead of this stuff in Ant2,
+         *          "copy constructor" has not been available in JDK 1.1
+         */
+        super(entry.getName());
+
+        setComment(entry.getComment());
+        setMethod(entry.getMethod());
+        setTime(entry.getTime());
+
+        long size = entry.getSize();
+        if (size > 0) {
+            setSize(size);
+        }
+        long cSize = entry.getCompressedSize();
+        if (cSize > 0) {
+            setComprSize(cSize);
+        }
+        long crc = entry.getCrc();
+        if (crc > 0) {
+            setCrc(crc);
+        }
+
+        byte[] extra = entry.getExtra();
+        if (extra != null) {
+            setExtraFields(ExtraFieldUtils.parse(extra));
+        } else {
+            // initializes extra data to an empty byte array
+            setExtra();
+        }
+    }
+
+    /**
+     * Creates a new zip entry with fields taken from the specified zip entry.
+     *
+     * @since 1.1
+     */
+    public ZipEntry(ZipEntry entry) throws ZipException {
+        this((java.util.zip.ZipEntry) entry);
+        setInternalAttributes(entry.getInternalAttributes());
+        setExternalAttributes(entry.getExternalAttributes());
+        setExtraFields(entry.getExtraFields());
+    }
+
+    /**
+     * @since 1.9
+     */
+    protected ZipEntry() {
+        super("");
+    }
+
+    /**
+     * Overwrite clone
+     *
+     * @since 1.1
+     */
+    public Object clone() {
+        try {
+            ZipEntry e = (ZipEntry) super.clone();
+
+            e.setName(getName());
+            e.setComment(getComment());
+            e.setMethod(getMethod());
+            e.setTime(getTime());
+            long size = getSize();
+            if (size > 0) {
+                e.setSize(size);
+            }
+            long cSize = getCompressedSize();
+            if (cSize > 0) {
+                e.setComprSize(cSize);
+            }
+            long crc = getCrc();
+            if (crc > 0) {
+                e.setCrc(crc);
+            }
+
+            e.extraFields = (Vector) extraFields.clone();
+            e.setInternalAttributes(getInternalAttributes());
+            e.setExternalAttributes(getExternalAttributes());
+            e.setExtraFields(getExtraFields());
+            return e;
+        } catch (Throwable t) {
+            // in JDK 1.1 ZipEntry is not Cloneable, so super.clone declares
+            // to throw CloneNotSupported - since JDK 1.2 it is overridden to
+            // not throw that exception
+            return null;
+        }
+    }
+
+    /**
+     * Retrieves the internal file attributes.
+     *
+     * @since 1.1
+     */
+    public int getInternalAttributes() {
+        return internalAttributes;
+    }
+
+    /**
+     * Sets the internal file attributes.
+     *
+     * @since 1.1
+     */
+    public void setInternalAttributes(int value) {
+        internalAttributes = value;
+    }
+
+    /**
+     * Retrieves the external file attributes.
+     *
+     * @since 1.1
+     */
+    public long getExternalAttributes() {
+        return externalAttributes;
+    }
+
+    /**
+     * Sets the external file attributes.
+     *
+     * @since 1.1
+     */
+    public void setExternalAttributes(long value) {
+        externalAttributes = value;
+    }
+
+    /**
+     * Sets Unix permissions in a way that is understood by Info-Zip's
+     * unzip command.
+     *
+     * @since Ant 1.5.2
+     */
+    public void setUnixMode(int mode) {
+        setExternalAttributes((mode << 16)
+                              // MS-DOS read-only attribute
+                              | ((mode & 0200) == 0 ? 1 : 0)
+                              // MS-DOS directory flag
+                              | (isDirectory() ? 0x10 : 0));
+        platform = PLATFORM_UNIX;
+    }
+
+    /**
+     * Unix permission.
+     *
+     * @since Ant 1.6
+     */
+    public int getUnixMode() {
+        return (int) ((getExternalAttributes() >> 16) & 0xFFFF);
+    }
+
+    /**
+     * Platform specification to put into the &quot;version made
+     * by&quot; part of the central file header.
+     *
+     * @return 0 (MS-DOS FAT) unless {@link #setUnixMode setUnixMode}
+     * has been called, in which case 3 (Unix) will be returned.
+     *
+     * @since Ant 1.5.2
+     */
+    public int getPlatform() {
+        return platform;
+    }
+
+    /**
+     * @since 1.9
+     */
+    protected void setPlatform(int platform) {
+        this.platform = platform;
+    }
+
+    /**
+     * Replaces all currently attached extra fields with the new array.
+     *
+     * @since 1.1
+     */
+    public void setExtraFields(ZipExtraField[] fields) {
+        extraFields.removeAllElements();
+        for (int i = 0; i < fields.length; i++) {
+            extraFields.addElement(fields[i]);
+        }
+        setExtra();
+    }
+
+    /**
+     * Retrieves extra fields.
+     *
+     * @since 1.1
+     */
+    public ZipExtraField[] getExtraFields() {
+        ZipExtraField[] result = new ZipExtraField[extraFields.size()];
+        extraFields.copyInto(result);
+        return result;
+    }
+
+    /**
+     * Adds an extra fields - replacing an already present extra field
+     * of the same type.
+     *
+     * @since 1.1
+     */
+    public void addExtraField(ZipExtraField ze) {
+        ZipShort type = ze.getHeaderId();
+        boolean done = false;
+        for (int i = 0; !done && i < extraFields.size(); i++) {
+            if (((ZipExtraField) extraFields.elementAt(i)).getHeaderId().equals(type)) {
+                extraFields.setElementAt(ze, i);
+                done = true;
+            }
+        }
+        if (!done) {
+            extraFields.addElement(ze);
+        }
+        setExtra();
+    }
+
+    /**
+     * Remove an extra fields.
+     *
+     * @since 1.1
+     */
+    public void removeExtraField(ZipShort type) {
+        boolean done = false;
+        for (int i = 0; !done && i < extraFields.size(); i++) {
+            if (((ZipExtraField) extraFields.elementAt(i)).getHeaderId().equals(type)) {
+                extraFields.removeElementAt(i);
+                done = true;
+            }
+        }
+        if (!done) {
+            throw new java.util.NoSuchElementException();
+        }
+        setExtra();
+    }
+
+    /**
+     * Throws an Exception if extra data cannot be parsed into extra fields.
+     *
+     * @since 1.1
+     */
+    public void setExtra(byte[] extra) throws RuntimeException {
+        try {
+            setExtraFields(ExtraFieldUtils.parse(extra));
+        } catch (Exception e) {
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
+    /**
+     * Unfortunately {@link java.util.zip.ZipOutputStream
+     * java.util.zip.ZipOutputStream} seems to access the extra data
+     * directly, so overriding getExtra doesn't help - we need to
+     * modify super's data directly.
+     *
+     * @since 1.1
+     */
+    protected void setExtra() {
+        super.setExtra(ExtraFieldUtils.mergeLocalFileDataData(getExtraFields()));
+    }
+
+    /**
+     * Retrieves the extra data for the local file data.
+     *
+     * @since 1.1
+     */
+    public byte[] getLocalFileDataExtra() {
+        byte[] extra = getExtra();
+        return extra != null ? extra : new byte[0];
+    }
+
+    /**
+     * Retrieves the extra data for the central directory.
+     *
+     * @since 1.1
+     */
+    public byte[] getCentralDirectoryExtra() {
+        return ExtraFieldUtils.mergeCentralDirectoryData(getExtraFields());
+    }
+
+    /**
+     * Helper for JDK 1.1 <-> 1.2 incompatibility.
+     *
+     * @since 1.2
+     */
+    private Long compressedSize = null;
+
+    /**
+     * Make this class work in JDK 1.1 like a 1.2 class.
+     *
+     * <p>This either stores the size for later usage or invokes
+     * setCompressedSize via reflection.</p>
+     *
+     * @since 1.2
+     */
+    public void setComprSize(long size) {
+        if (haveSetCompressedSize()) {
+            performSetCompressedSize(this, size);
+        } else {
+            compressedSize = new Long(size);
+        }
+    }
+
+    /**
+     * Override to make this class work in JDK 1.1 like a 1.2 class.
+     *
+     * @since 1.2
+     */
+    public long getCompressedSize() {
+        if (compressedSize != null) {
+            // has been set explicitly and we are running in a 1.1 VM
+            return compressedSize.longValue();
+        }
+        return super.getCompressedSize();
+    }
+
+    /**
+     * @since 1.9
+     */
+    public String getName() {
+        return name == null ? super.getName() : name;
+    }
+
+    /**
+     * @since 1.10
+     */
+    public boolean isDirectory() {
+        return getName().endsWith("/");
+    }
+
+    protected void setName(String name) {
+        this.name = name;
+    }
+
+    /**
+     * Helper for JDK 1.1
+     *
+     * @since 1.2
+     */
+    private static Method setCompressedSizeMethod = null;
+    /**
+     * Helper for JDK 1.1
+     *
+     * @since 1.2
+     */
+    private static Object lockReflection = new Object();
+    /**
+     * Helper for JDK 1.1
+     *
+     * @since 1.2
+     */
+    private static boolean triedToGetMethod = false;
+
+    /**
+     * Are we running JDK 1.2 or higher?
+     *
+     * @since 1.2
+     */
+    private static boolean haveSetCompressedSize() {
+        checkSCS();
+        return setCompressedSizeMethod != null;
+    }
+
+    /**
+     * Invoke setCompressedSize via reflection.
+     *
+     * @since 1.2
+     */
+    private static void performSetCompressedSize(ZipEntry ze, long size) {
+        Long[] s = {new Long(size)};
+        try {
+            setCompressedSizeMethod.invoke(ze, s);
+        } catch (InvocationTargetException ite) {
+            Throwable nested = ite.getTargetException();
+            throw new RuntimeException("Exception setting the compressed size "
+                                       + "of " + ze + ": "
+                                       + nested.getMessage());
+        } catch (Throwable other) {
+            throw new RuntimeException("Exception setting the compressed size "
+                                       + "of " + ze + ": "
+                                       + other.getMessage());
+        }
+    }
+
+    /**
+     * Try to get a handle to the setCompressedSize method.
+     *
+     * @since 1.2
+     */
+    private static void checkSCS() {
+        if (!triedToGetMethod) {
+            synchronized (lockReflection) {
+                triedToGetMethod = true;
+                try {
+                    setCompressedSizeMethod =
+                        java.util.zip.ZipEntry.class.getMethod("setCompressedSize",
+                                                               new Class[] {Long.TYPE});
+                } catch (NoSuchMethodException nse) {
+                }
+            }
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipExtraField.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipExtraField.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipExtraField.java	(revision 16139)
@@ -0,0 +1,80 @@
+/*
+ * Copyright  2001,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.zip;
+
+import java.util.zip.ZipException;
+
+/**
+ * General format of extra field data.
+ *
+ * <p>Extra fields usually appear twice per file, once in the local
+ * file data and once in the central directory.  Usually they are the
+ * same, but they don't have to be.  {@link
+ * java.util.zip.ZipOutputStream java.util.zip.ZipOutputStream} will
+ * only use the local file data in both places.</p>
+ *
+ */
+public interface ZipExtraField {
+
+    /**
+     * The Header-ID.
+     *
+     * @since 1.1
+     */
+    ZipShort getHeaderId();
+
+    /**
+     * Length of the extra field in the local file data - without
+     * Header-ID or length specifier.
+     *
+     * @since 1.1
+     */
+    ZipShort getLocalFileDataLength();
+
+    /**
+     * Length of the extra field in the central directory - without
+     * Header-ID or length specifier.
+     *
+     * @since 1.1
+     */
+    ZipShort getCentralDirectoryLength();
+
+    /**
+     * The actual data to put into local file data - without Header-ID
+     * or length specifier.
+     *
+     * @since 1.1
+     */
+    byte[] getLocalFileDataData();
+
+    /**
+     * The actual data to put central directory - without Header-ID or
+     * length specifier.
+     *
+     * @since 1.1
+     */
+    byte[] getCentralDirectoryData();
+
+    /**
+     * Populate data from this array as if it was in local file data.
+     *
+     * @since 1.1
+     */
+    void parseFromLocalFileData(byte[] data, int offset, int length)
+        throws ZipException;
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipFile.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipFile.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipFile.java	(revision 16139)
@@ -0,0 +1,526 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.zip;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.RandomAccessFile;
+import java.io.UnsupportedEncodingException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.zip.Inflater;
+import java.util.zip.InflaterInputStream;
+import java.util.zip.ZipException;
+
+/**
+ * Replacement for <code>java.util.ZipFile</code>.
+ *
+ * <p>This class adds support for file name encodings other than UTF-8
+ * (which is required to work on ZIP files created by native zip tools
+ * and is able to skip a preamble like the one found in self
+ * extracting archives.  Furthermore it returns instances of
+ * <code>org.apache.tools.zip.ZipEntry</code> instead of
+ * <code>java.util.zip.ZipEntry</code>.</p>
+ *
+ * <p>It doesn't extend <code>java.util.zip.ZipFile</code> as it would
+ * have to reimplement all methods anyway.  Like
+ * <code>java.util.ZipFile</code>, it uses RandomAccessFile under the
+ * covers and supports compressed and uncompressed entries.</p>
+ *
+ * <p>The method signatures mimic the ones of
+ * <code>java.util.zip.ZipFile</code>, with a couple of exceptions:
+ *
+ * <ul>
+ *   <li>There is no getName method.</li>
+ *   <li>entries has been renamed to getEntries.</li>
+ *   <li>getEntries and getEntry return
+ *   <code>org.apache.tools.zip.ZipEntry</code> instances.</li>
+ *   <li>close is allowed to throw IOException.</li>
+ * </ul>
+ *
+ */
+public class ZipFile {
+
+    /**
+     * Maps ZipEntrys to Longs, recording the offsets of the local
+     * file headers.
+     */
+    private Hashtable entries = new Hashtable();
+
+    /**
+     * Maps String to ZipEntrys, name -> actual entry.
+     */
+    private Hashtable nameMap = new Hashtable();
+
+    /**
+     * Maps ZipEntrys to Longs, recording the offsets of the actual file data.
+     */
+    private Hashtable dataOffsets = new Hashtable();
+
+    /**
+     * The encoding to use for filenames and the file comment.
+     *
+     * <p>For a list of possible values see <a
+     * href="http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html">http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html</a>.
+     * Defaults to the platform's default character encoding.</p>
+     */
+    private String encoding = null;
+
+    /**
+     * The actual data source.
+     */
+    private RandomAccessFile archive;
+
+    /**
+     * Opens the given file for reading, assuming the platform's
+     * native encoding for file names.
+     *
+     * @param f the archive.
+     *
+     * @throws IOException if an error occurs while reading the file.
+     */
+    public ZipFile(File f) throws IOException {
+        this(f, null);
+    }
+
+    /**
+     * Opens the given file for reading, assuming the platform's
+     * native encoding for file names.
+     *
+     * @param name name of the archive.
+     *
+     * @throws IOException if an error occurs while reading the file.
+     */
+    public ZipFile(String name) throws IOException {
+        this(new File(name), null);
+    }
+
+    /**
+     * Opens the given file for reading, assuming the specified
+     * encoding for file names.
+     *
+     * @param name name of the archive.
+     * @param encoding the encoding to use for file names
+     *
+     * @throws IOException if an error occurs while reading the file.
+     */
+    public ZipFile(String name, String encoding) throws IOException {
+        this(new File(name), encoding);
+    }
+
+    /**
+     * Opens the given file for reading, assuming the specified
+     * encoding for file names.
+     *
+     * @param f the archive.
+     * @param encoding the encoding to use for file names
+     *
+     * @throws IOException if an error occurs while reading the file.
+     */
+    public ZipFile(File f, String encoding) throws IOException {
+        this.encoding = encoding;
+        archive = new RandomAccessFile(f, "r");
+        try {
+            populateFromCentralDirectory();
+            resolveLocalFileHeaderData();
+        } catch (IOException e) {
+            try {
+                archive.close();
+            } catch (IOException e2) {
+                // swallow, throw the original exception instead
+            }
+            throw e;
+        }
+    }
+
+    /**
+     * The encoding to use for filenames and the file comment.
+     *
+     * @return null if using the platform's default character encoding.
+     */
+    public String getEncoding() {
+        return encoding;
+    }
+
+    /**
+     * Closes the archive.
+     * @throws IOException if an error occurs closing the archive.
+     */
+    public void close() throws IOException {
+        archive.close();
+    }
+
+    /**
+     * Returns all entries.
+     * @return all entries as {@link ZipEntry} instances
+     */
+    public Enumeration getEntries() {
+        return entries.keys();
+    }
+
+    /**
+     * Returns a named entry - or <code>null</code> if no entry by
+     * that name exists.
+     * @param name name of the entry.
+     * @return the ZipEntry corresponding to the given name - or
+     * <code>null</code> if not present.
+     */
+    public ZipEntry getEntry(String name) {
+        return (ZipEntry) nameMap.get(name);
+    }
+
+    /**
+     * Returns an InputStream for reading the contents of the given entry.
+     * @param ze the entry to get the stream for.
+     * @return a stream to read the entry from.
+     */
+    public InputStream getInputStream(ZipEntry ze)
+        throws IOException, ZipException {
+        Long start = (Long) dataOffsets.get(ze);
+        if (start == null) {
+            return null;
+        }
+        BoundedInputStream bis =
+            new BoundedInputStream(start.longValue(), ze.getCompressedSize());
+        switch (ze.getMethod()) {
+            case ZipEntry.STORED:
+                return bis;
+            case ZipEntry.DEFLATED:
+                bis.addDummy();
+                return new InflaterInputStream(bis, new Inflater(true));
+            default:
+                throw new ZipException("Found unsupported compression method "
+                                       + ze.getMethod());
+        }
+    }
+
+    private static final int CFH_LEN =
+        /* version made by                 */ 2 +
+        /* version needed to extract       */ 2 +
+        /* general purpose bit flag        */ 2 +
+        /* compression method              */ 2 +
+        /* last mod file time              */ 2 +
+        /* last mod file date              */ 2 +
+        /* crc-32                          */ 4 +
+        /* compressed size                 */ 4 +
+        /* uncompressed size               */ 4 +
+        /* filename length                 */ 2 +
+        /* extra field length              */ 2 +
+        /* file comment length             */ 2 +
+        /* disk number start               */ 2 +
+        /* internal file attributes        */ 2 +
+        /* external file attributes        */ 4 +
+        /* relative offset of local header */ 4;
+
+    /**
+     * Reads the central directory of the given archive and populates
+     * the internal tables with ZipEntry instances.
+     *
+     * <p>The ZipEntrys will know all data that can be obtained from
+     * the central directory alone, but not the data that requires the
+     * local file header or additional data to be read.</p>
+     */
+    private void populateFromCentralDirectory()
+        throws IOException {
+        positionAtCentralDirectory();
+
+        byte[] cfh = new byte[CFH_LEN];
+
+        byte[] signatureBytes = new byte[4];
+        archive.readFully(signatureBytes);
+        ZipLong sig = new ZipLong(signatureBytes);
+        while (sig.equals(ZipOutputStream.CFH_SIG)) {
+            archive.readFully(cfh);
+            int off = 0;
+            ZipEntry ze = new ZipEntry();
+
+            ZipShort versionMadeBy = new ZipShort(cfh, off);
+            off += 2;
+            ze.setPlatform((versionMadeBy.getValue() >> 8) & 0x0F);
+
+            off += 4; // skip version info and general purpose byte
+
+            ze.setMethod((new ZipShort(cfh, off)).getValue());
+            off += 2;
+
+            ze.setTime(fromDosTime(new ZipLong(cfh, off)).getTime());
+            off += 4;
+
+            ze.setCrc((new ZipLong(cfh, off)).getValue());
+            off += 4;
+
+            ze.setCompressedSize((new ZipLong(cfh, off)).getValue());
+            off += 4;
+
+            ze.setSize((new ZipLong(cfh, off)).getValue());
+            off += 4;
+
+            int fileNameLen = (new ZipShort(cfh, off)).getValue();
+            off += 2;
+
+            int extraLen = (new ZipShort(cfh, off)).getValue();
+            off += 2;
+
+            int commentLen = (new ZipShort(cfh, off)).getValue();
+            off += 2;
+
+            off += 2; // disk number
+
+            ze.setInternalAttributes((new ZipShort(cfh, off)).getValue());
+            off += 2;
+
+            ze.setExternalAttributes((new ZipLong(cfh, off)).getValue());
+            off += 4;
+
+            // LFH offset
+            entries.put(ze, new Long((new ZipLong(cfh, off)).getValue()));
+
+            byte[] fileName = new byte[fileNameLen];
+            archive.readFully(fileName);
+            ze.setName(getString(fileName));
+
+            nameMap.put(ze.getName(), ze);
+
+            archive.skipBytes(extraLen);
+
+            byte[] comment = new byte[commentLen];
+            archive.readFully(comment);
+            ze.setComment(getString(comment));
+
+            archive.readFully(signatureBytes);
+            sig = new ZipLong(signatureBytes);
+        }
+    }
+
+    private static final int MIN_EOCD_SIZE =
+        /* end of central dir signature    */ 4 +
+        /* number of this disk             */ 2 +
+        /* number of the disk with the     */   +
+        /* start of the central directory  */ 2 +
+        /* total number of entries in      */   +
+        /* the central dir on this disk    */ 2 +
+        /* total number of entries in      */   +
+        /* the central dir                 */ 2 +
+        /* size of the central directory   */ 4 +
+        /* offset of start of central      */   +
+        /* directory with respect to       */   +
+        /* the starting disk number        */ 4 +
+        /* zipfile comment length          */ 2;
+
+    private static final int CFD_LOCATOR_OFFSET =
+        /* end of central dir signature    */ 4 +
+        /* number of this disk             */ 2 +
+        /* number of the disk with the     */   +
+        /* start of the central directory  */ 2 +
+        /* total number of entries in      */   +
+        /* the central dir on this disk    */ 2 +
+        /* total number of entries in      */   +
+        /* the central dir                 */ 2 +
+        /* size of the central directory   */ 4;
+
+    /**
+     * Searches for the &quot;End of central dir record&quot;, parses
+     * it and positions the stream at the first central directory
+     * record.
+     */
+    private void positionAtCentralDirectory()
+        throws IOException {
+        long off = archive.length() - MIN_EOCD_SIZE;
+        archive.seek(off);
+        byte[] sig = ZipOutputStream.EOCD_SIG.getBytes();
+        int curr = archive.read();
+        boolean found = false;
+        while (curr != -1) {
+            if (curr == sig[0]) {
+                curr = archive.read();
+                if (curr == sig[1]) {
+                    curr = archive.read();
+                    if (curr == sig[2]) {
+                        curr = archive.read();
+                        if (curr == sig[3]) {
+                            found = true;
+                            break;
+                        }
+                    }
+                }
+            }
+            archive.seek(--off);
+            curr = archive.read();
+        }
+        if (!found) {
+            throw new ZipException("archive is not a ZIP archive");
+        }
+        archive.seek(off + CFD_LOCATOR_OFFSET);
+        byte[] cfdOffset = new byte[4];
+        archive.readFully(cfdOffset);
+        archive.seek((new ZipLong(cfdOffset)).getValue());
+    }
+
+    /**
+     * Number of bytes in local file header up to the &quot;length of
+     * filename&quot; entry.
+     */
+    private static final long LFH_OFFSET_FOR_FILENAME_LENGTH =
+        /* local file header signature     */ 4 +
+        /* version needed to extract       */ 2 +
+        /* general purpose bit flag        */ 2 +
+        /* compression method              */ 2 +
+        /* last mod file time              */ 2 +
+        /* last mod file date              */ 2 +
+        /* crc-32                          */ 4 +
+        /* compressed size                 */ 4 +
+        /* uncompressed size               */ 4;
+
+    /**
+     * Walks through all recorded entries and adds the data available
+     * from the local file header.
+     *
+     * <p>Also records the offsets for the data to read from the
+     * entries.</p>
+     */
+    private void resolveLocalFileHeaderData()
+        throws IOException {
+        Enumeration e = getEntries();
+        while (e.hasMoreElements()) {
+            ZipEntry ze = (ZipEntry) e.nextElement();
+            long offset = ((Long) entries.get(ze)).longValue();
+            archive.seek(offset + LFH_OFFSET_FOR_FILENAME_LENGTH);
+            byte[] b = new byte[2];
+            archive.readFully(b);
+            int fileNameLen = (new ZipShort(b)).getValue();
+            archive.readFully(b);
+            int extraFieldLen = (new ZipShort(b)).getValue();
+            archive.skipBytes(fileNameLen);
+            byte[] localExtraData = new byte[extraFieldLen];
+            archive.readFully(localExtraData);
+            ze.setExtra(localExtraData);
+            dataOffsets.put(ze,
+                            new Long(offset + LFH_OFFSET_FOR_FILENAME_LENGTH
+                                     + 2 + 2 + fileNameLen + extraFieldLen));
+        }
+    }
+
+    /**
+     * Convert a DOS date/time field to a Date object.
+     *
+     * @param l contains the stored DOS time.
+     * @return a Date instance corresponding to the given time.
+     */
+    protected static Date fromDosTime(ZipLong l) {
+        long dosTime = l.getValue();
+        Calendar cal = Calendar.getInstance();
+        cal.set(Calendar.YEAR, (int) ((dosTime >> 25) & 0x7f) + 1980);
+        cal.set(Calendar.MONTH, (int) ((dosTime >> 21) & 0x0f) - 1);
+        cal.set(Calendar.DATE, (int) (dosTime >> 16) & 0x1f);
+        cal.set(Calendar.HOUR_OF_DAY, (int) (dosTime >> 11) & 0x1f);
+        cal.set(Calendar.MINUTE, (int) (dosTime >> 5) & 0x3f);
+        cal.set(Calendar.SECOND, (int) (dosTime << 1) & 0x3e);
+        return cal.getTime();
+    }
+
+    /**
+     * Retrieve a String from the given bytes using the encoding set
+     * for this ZipFile.
+     *
+     * @param bytes the byte array to transform
+     * @return String obtained by using the given encoding
+     * @throws ZipException if the encoding cannot be recognized.
+     */
+    protected String getString(byte[] bytes) throws ZipException {
+        if (encoding == null) {
+            return new String(bytes);
+        } else {
+            try {
+                return new String(bytes, encoding);
+            } catch (UnsupportedEncodingException uee) {
+                throw new ZipException(uee.getMessage());
+            }
+        }
+    }
+
+    /**
+     * InputStream that delegates requests to the underlying
+     * RandomAccessFile, making sure that only bytes from a certain
+     * range can be read.
+     */
+    private class BoundedInputStream extends InputStream {
+        private long remaining;
+        private long loc;
+        private boolean addDummyByte = false;
+
+        BoundedInputStream(long start, long remaining) {
+            this.remaining = remaining;
+            loc = start;
+        }
+
+        public int read() throws IOException {
+            if (remaining-- <= 0) {
+                if (addDummyByte) {
+                    addDummyByte = false;
+                    return 0;
+                }
+                return -1;
+            }
+            synchronized (archive) {
+                archive.seek(loc++);
+                return archive.read();
+            }
+        }
+
+        public int read(byte[] b, int off, int len) throws IOException {
+            if (remaining <= 0) {
+                if (addDummyByte) {
+                    addDummyByte = false;
+                    b[off] = 0;
+                    return 1;
+                }
+                return -1;
+            }
+
+            if (len <= 0) {
+                return 0;
+            }
+
+            if (len > remaining) {
+                len = (int) remaining;
+            }
+            int ret = -1;
+            synchronized (archive) {
+                archive.seek(loc);
+                ret = archive.read(b, off, len);
+            }
+            if (ret > 0) {
+                loc += ret;
+                remaining -= ret;
+            }
+            return ret;
+        }
+
+        /**
+         * Inflater needs an extra dummy byte for nowrap - see
+         * Inflater's javadocs.
+         */
+        void addDummy() {
+            addDummyByte = true;
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipLong.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipLong.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipLong.java	(revision 16139)
@@ -0,0 +1,102 @@
+/*
+ * Copyright  2001-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.zip;
+
+/**
+ * Utility class that represents a four byte integer with conversion
+ * rules for the big endian byte order of ZIP files.
+ *
+ */
+public final class ZipLong implements Cloneable {
+
+    private long value;
+
+    /**
+     * Create instance from a number.
+     *
+     * @since 1.1
+     */
+    public ZipLong(long value) {
+        this.value = value;
+    }
+
+    /**
+     * Create instance from bytes.
+     *
+     * @since 1.1
+     */
+    public ZipLong (byte[] bytes) {
+        this(bytes, 0);
+    }
+
+    /**
+     * Create instance from the four bytes starting at offset.
+     *
+     * @since 1.1
+     */
+    public ZipLong (byte[] bytes, int offset) {
+        value = (bytes[offset + 3] << 24) & 0xFF000000L;
+        value += (bytes[offset + 2] << 16) & 0xFF0000;
+        value += (bytes[offset + 1] << 8) & 0xFF00;
+        value += (bytes[offset] & 0xFF);
+    }
+
+    /**
+     * Get value as two bytes in big endian byte order.
+     *
+     * @since 1.1
+     */
+    public byte[] getBytes() {
+        byte[] result = new byte[4];
+        result[0] = (byte) ((value & 0xFF));
+        result[1] = (byte) ((value & 0xFF00) >> 8);
+        result[2] = (byte) ((value & 0xFF0000) >> 16);
+        result[3] = (byte) ((value & 0xFF000000l) >> 24);
+        return result;
+    }
+
+    /**
+     * Get value as Java int.
+     *
+     * @since 1.1
+     */
+    public long getValue() {
+        return value;
+    }
+
+    /**
+     * Override to make two instances with same value equal.
+     *
+     * @since 1.1
+     */
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof ZipLong)) {
+            return false;
+        }
+        return value == ((ZipLong) o).getValue();
+    }
+
+    /**
+     * Override to make two instances with same value equal.
+     *
+     * @since 1.1
+     */
+    public int hashCode() {
+        return (int) value;
+    }
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipOutputStream.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipOutputStream.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipOutputStream.java	(revision 16139)
@@ -0,0 +1,846 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.zip;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import java.io.UnsupportedEncodingException;
+import java.util.Date;
+import java.util.Hashtable;
+import java.util.Vector;
+import java.util.zip.CRC32;
+import java.util.zip.Deflater;
+import java.util.zip.ZipException;
+
+/**
+ * Reimplementation of {@link java.util.zip.ZipOutputStream
+ * java.util.zip.ZipOutputStream} that does handle the extended
+ * functionality of this package, especially internal/external file
+ * attributes and extra fields with different layouts for local file
+ * data and central directory entries.
+ *
+ * <p>This class will try to use {@link java.io.RandomAccessFile
+ * RandomAccessFile} when you know that the output is going to go to a
+ * file.</p>
+ *
+ * <p>If RandomAccessFile cannot be used, this implementation will use
+ * a Data Descriptor to store size and CRC information for {@link
+ * #DEFLATED DEFLATED} entries, this means, you don't need to
+ * calculate them yourself.  Unfortunately this is not possible for
+ * the {@link #STORED STORED} method, here setting the CRC and
+ * uncompressed size information is required before {@link
+ * #putNextEntry putNextEntry} can be called.</p>
+ *
+ */
+public class ZipOutputStream extends FilterOutputStream {
+
+    /**
+     * Current entry.
+     *
+     * @since 1.1
+     */
+    private ZipEntry entry;
+
+    /**
+     * The file comment.
+     *
+     * @since 1.1
+     */
+    private String comment = "";
+
+    /**
+     * Compression level for next entry.
+     *
+     * @since 1.1
+     */
+    private int level = Deflater.DEFAULT_COMPRESSION;
+
+    /**
+     * Has the compression level changed when compared to the last
+     * entry?
+     *
+     * @since 1.5
+     */
+    private boolean hasCompressionLevelChanged = false;
+
+    /**
+     * Default compression method for next entry.
+     *
+     * @since 1.1
+     */
+    private int method = DEFLATED;
+
+    /**
+     * List of ZipEntries written so far.
+     *
+     * @since 1.1
+     */
+    private Vector entries = new Vector();
+
+    /**
+     * CRC instance to avoid parsing DEFLATED data twice.
+     *
+     * @since 1.1
+     */
+    private CRC32 crc = new CRC32();
+
+    /**
+     * Count the bytes written to out.
+     *
+     * @since 1.1
+     */
+    private long written = 0;
+
+    /**
+     * Data for local header data
+     *
+     * @since 1.1
+     */
+    private long dataStart = 0;
+
+    /**
+     * Offset for CRC entry in the local file header data for the
+     * current entry starts here.
+     *
+     * @since 1.15
+     */
+    private long localDataStart = 0;
+
+    /**
+     * Start of central directory.
+     *
+     * @since 1.1
+     */
+    private ZipLong cdOffset = new ZipLong(0);
+
+    /**
+     * Length of central directory.
+     *
+     * @since 1.1
+     */
+    private ZipLong cdLength = new ZipLong(0);
+
+    /**
+     * Helper, a 0 as ZipShort.
+     *
+     * @since 1.1
+     */
+    private static final byte[] ZERO = {0, 0};
+
+    /**
+     * Helper, a 0 as ZipLong.
+     *
+     * @since 1.1
+     */
+    private static final byte[] LZERO = {0, 0, 0, 0};
+
+    /**
+     * Holds the offsets of the LFH starts for each entry.
+     *
+     * @since 1.1
+     */
+    private Hashtable offsets = new Hashtable();
+
+    /**
+     * The encoding to use for filenames and the file comment.
+     *
+     * <p>For a list of possible values see <a
+     * href="http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html">http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html</a>.
+     * Defaults to the platform's default character encoding.</p>
+     *
+     * @since 1.3
+     */
+    private String encoding = null;
+
+    /**
+     * This Deflater object is used for output.
+     *
+     * <p>This attribute is only protected to provide a level of API
+     * backwards compatibility.  This class used to extend {@link
+     * java.util.zip.DeflaterOutputStream DeflaterOutputStream} up to
+     * Revision 1.13.</p>
+     *
+     * @since 1.14
+     */
+    protected Deflater def = new Deflater(Deflater.DEFAULT_COMPRESSION, true);
+
+    /**
+     * This buffer servers as a Deflater.
+     *
+     * <p>This attribute is only protected to provide a level of API
+     * backwards compatibility.  This class used to extend {@link
+     * java.util.zip.DeflaterOutputStream DeflaterOutputStream} up to
+     * Revision 1.13.</p>
+     *
+     * @since 1.14
+     */
+    protected byte[] buf = new byte[512];
+
+    /**
+     * Optional random access output.
+     *
+     * @since 1.14
+     */
+    private RandomAccessFile raf = null;
+
+    /**
+     * Compression method for deflated entries.
+     *
+     * @since 1.1
+     */
+    public static final int DEFLATED = ZipEntry.DEFLATED;
+
+    /**
+     * Compression method for deflated entries.
+     *
+     * @since 1.1
+     */
+    public static final int STORED = ZipEntry.STORED;
+
+    /**
+     * Creates a new ZIP OutputStream filtering the underlying stream.
+     *
+     * @since 1.1
+     */
+    public ZipOutputStream(OutputStream out) {
+        super(out);
+    }
+
+    /**
+     * Creates a new ZIP OutputStream writing to a File.  Will use
+     * random access if possible.
+     *
+     * @since 1.14
+     */
+    public ZipOutputStream(File file) throws IOException {
+        super(null);
+
+        try {
+            raf = new RandomAccessFile(file, "rw");
+            raf.setLength(0);
+        } catch (IOException e) {
+            if (raf != null) {
+                try {
+                    raf.close();
+                } catch (IOException inner) {
+                    // ignore
+                }
+                raf = null;
+            }
+            out = new FileOutputStream(file);
+        }
+    }
+
+    /**
+     * This method indicates whether this archive is writing to a seekable stream (i.e., to a random
+     * access file).
+     *
+     * <p>For seekable streams, you don't need to calculate the CRC or
+     * uncompressed size for {@link #STORED} entries before
+     * invoking {@link #putNextEntry}.
+     *
+     * @since 1.17
+     */
+    public boolean isSeekable() {
+        return raf != null;
+    }
+
+    /**
+     * The encoding to use for filenames and the file comment.
+     *
+     * <p>For a list of possible values see <a
+     * href="http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html">http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html</a>.
+     * Defaults to the platform's default character encoding.</p>
+     *
+     * @since 1.3
+     */
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * The encoding to use for filenames and the file comment.
+     *
+     * @return null if using the platform's default character encoding.
+     *
+     * @since 1.3
+     */
+    public String getEncoding() {
+        return encoding;
+    }
+
+    /**
+     * Finishs writing the contents and closes this as well as the
+     * underlying stream.
+     *
+     * @since 1.1
+     */
+    public void finish() throws IOException {
+        closeEntry();
+        cdOffset = new ZipLong(written);
+        for (int i = 0; i < entries.size(); i++) {
+            writeCentralFileHeader((ZipEntry) entries.elementAt(i));
+        }
+        cdLength = new ZipLong(written - cdOffset.getValue());
+        writeCentralDirectoryEnd();
+        offsets.clear();
+        entries.removeAllElements();
+    }
+
+    /**
+     * Writes all necessary data for this entry.
+     *
+     * @since 1.1
+     */
+    public void closeEntry() throws IOException {
+        if (entry == null) {
+            return;
+        }
+
+        long realCrc = crc.getValue();
+        crc.reset();
+
+        if (entry.getMethod() == DEFLATED) {
+            def.finish();
+            while (!def.finished()) {
+                deflate();
+            }
+
+            entry.setSize(adjustToLong(def.getTotalIn()));
+            entry.setComprSize(adjustToLong(def.getTotalOut()));
+            entry.setCrc(realCrc);
+
+            def.reset();
+
+            written += entry.getCompressedSize();
+        } else if (raf == null) {
+            if (entry.getCrc() != realCrc) {
+                throw new ZipException("bad CRC checksum for entry "
+                                       + entry.getName() + ": "
+                                       + Long.toHexString(entry.getCrc())
+                                       + " instead of "
+                                       + Long.toHexString(realCrc));
+            }
+
+            if (entry.getSize() != written - dataStart) {
+                throw new ZipException("bad size for entry "
+                                       + entry.getName() + ": "
+                                       + entry.getSize()
+                                       + " instead of "
+                                       + (written - dataStart));
+            }
+        } else { /* method is STORED and we used RandomAccessFile */
+            long size = written - dataStart;
+
+            entry.setSize(size);
+            entry.setComprSize(size);
+            entry.setCrc(realCrc);
+        }
+
+        // If random access output, write the local file header containing
+        // the correct CRC and compressed/uncompressed sizes
+        if (raf != null) {
+            long save = raf.getFilePointer();
+
+            raf.seek(localDataStart);
+            writeOut((new ZipLong(entry.getCrc())).getBytes());
+            writeOut((new ZipLong(entry.getCompressedSize())).getBytes());
+            writeOut((new ZipLong(entry.getSize())).getBytes());
+            raf.seek(save);
+        }
+
+        writeDataDescriptor(entry);
+        entry = null;
+    }
+
+    /**
+     * Begin writing next entry.
+     *
+     * @since 1.1
+     */
+    public void putNextEntry(ZipEntry ze) throws IOException {
+        closeEntry();
+
+        entry = ze;
+        entries.addElement(entry);
+
+        if (entry.getMethod() == -1) { // not specified
+            entry.setMethod(method);
+        }
+
+        if (entry.getTime() == -1) { // not specified
+            entry.setTime(System.currentTimeMillis());
+        }
+
+        // Size/CRC not required if RandomAccessFile is used
+        if (entry.getMethod() == STORED && raf == null) {
+            if (entry.getSize() == -1) {
+                throw new ZipException("uncompressed size is required for"
+                                       + " STORED method when not writing to a"
+                                       + " file");
+            }
+            if (entry.getCrc() == -1) {
+                throw new ZipException("crc checksum is required for STORED"
+                                       + " method when not writing to a file");
+            }
+            entry.setComprSize(entry.getSize());
+        }
+
+        if (entry.getMethod() == DEFLATED && hasCompressionLevelChanged) {
+            def.setLevel(level);
+            hasCompressionLevelChanged = false;
+        }
+        writeLocalFileHeader(entry);
+    }
+
+    /**
+     * Set the file comment.
+     *
+     * @since 1.1
+     */
+    public void setComment(String comment) {
+        this.comment = comment;
+    }
+
+    /**
+     * Sets the compression level for subsequent entries.
+     *
+     * <p>Default is Deflater.DEFAULT_COMPRESSION.</p>
+     *
+     * @since 1.1
+     */
+    public void setLevel(int level) {
+        hasCompressionLevelChanged = (this.level != level);
+        this.level = level;
+    }
+
+    /**
+     * Sets the default compression method for subsequent entries.
+     *
+     * <p>Default is DEFLATED.</p>
+     *
+     * @since 1.1
+     */
+    public void setMethod(int method) {
+        this.method = method;
+    }
+
+    /**
+     * Writes bytes to ZIP entry.
+     */
+    public void write(byte[] b, int offset, int length) throws IOException {
+        if (entry.getMethod() == DEFLATED) {
+            if (length > 0) {
+                if (!def.finished()) {
+                    def.setInput(b, offset, length);
+                    while (!def.needsInput()) {
+                        deflate();
+                    }
+                }
+            }
+        } else {
+            writeOut(b, offset, length);
+            written += length;
+        }
+        crc.update(b, offset, length);
+    }
+
+    /**
+     * Writes a single byte to ZIP entry.
+     *
+     * <p>Delegates to the three arg method.</p>
+     *
+     * @since 1.14
+     */
+    public void write(int b) throws IOException {
+        byte[] buf = new byte[1];
+        buf[0] = (byte) (b & 0xff);
+        write(buf, 0, 1);
+    }
+
+    /**
+     * Closes this output stream and releases any system resources
+     * associated with the stream.
+     *
+     * @exception  IOException  if an I/O error occurs.
+     * @since 1.14
+     */
+    public void close() throws IOException {
+        finish();
+
+        if (raf != null) {
+            raf.close();
+        }
+        if (out != null) {
+            out.close();
+        }
+    }
+
+    /**
+     * Flushes this output stream and forces any buffered output bytes
+     * to be written out to the stream.
+     *
+     * @exception  IOException  if an I/O error occurs.
+     * @since 1.14
+     */
+    public void flush() throws IOException {
+        if (out != null) {
+            out.flush();
+        }
+    }
+
+    /*
+     * Various ZIP constants
+     */
+    /**
+     * local file header signature
+     *
+     * @since 1.1
+     */
+    protected static final ZipLong LFH_SIG = new ZipLong(0X04034B50L);
+    /**
+     * data descriptor signature
+     *
+     * @since 1.1
+     */
+    protected static final ZipLong DD_SIG = new ZipLong(0X08074B50L);
+    /**
+     * central file header signature
+     *
+     * @since 1.1
+     */
+    protected static final ZipLong CFH_SIG = new ZipLong(0X02014B50L);
+    /**
+     * end of central dir signature
+     *
+     * @since 1.1
+     */
+    protected static final ZipLong EOCD_SIG = new ZipLong(0X06054B50L);
+
+    /**
+     * Writes next block of compressed data to the output stream.
+     *
+     * @since 1.14
+     */
+    protected final void deflate() throws IOException {
+        int len = def.deflate(buf, 0, buf.length);
+        if (len > 0) {
+            writeOut(buf, 0, len);
+        }
+    }
+
+    /**
+     * Writes the local file header entry
+     *
+     * @since 1.1
+     */
+    protected void writeLocalFileHeader(ZipEntry ze) throws IOException {
+        offsets.put(ze, new ZipLong(written));
+
+        writeOut(LFH_SIG.getBytes());
+        written += 4;
+
+        // version needed to extract
+        // general purpose bit flag
+        if (ze.getMethod() == DEFLATED && raf == null) {
+            // requires version 2 as we are going to store length info
+            // in the data descriptor
+            writeOut((new ZipShort(20)).getBytes());
+
+            // bit3 set to signal, we use a data descriptor
+            writeOut((new ZipShort(8)).getBytes());
+        } else {
+            writeOut((new ZipShort(10)).getBytes());
+            writeOut(ZERO);
+        }
+        written += 4;
+
+        // compression method
+        writeOut((new ZipShort(ze.getMethod())).getBytes());
+        written += 2;
+
+        // last mod. time and date
+        writeOut(toDosTime(new Date(ze.getTime())).getBytes());
+        written += 4;
+
+        // CRC
+        // compressed length
+        // uncompressed length
+        localDataStart = written;
+        if (ze.getMethod() == DEFLATED || raf != null) {
+            writeOut(LZERO);
+            writeOut(LZERO);
+            writeOut(LZERO);
+        } else {
+            writeOut((new ZipLong(ze.getCrc())).getBytes());
+            writeOut((new ZipLong(ze.getSize())).getBytes());
+            writeOut((new ZipLong(ze.getSize())).getBytes());
+        }
+        written += 12;
+
+        // file name length
+        byte[] name = getBytes(ze.getName());
+        writeOut((new ZipShort(name.length)).getBytes());
+        written += 2;
+
+        // extra field length
+        byte[] extra = ze.getLocalFileDataExtra();
+        writeOut((new ZipShort(extra.length)).getBytes());
+        written += 2;
+
+        // file name
+        writeOut(name);
+        written += name.length;
+
+        // extra field
+        writeOut(extra);
+        written += extra.length;
+
+        dataStart = written;
+    }
+
+    /**
+     * Writes the data descriptor entry
+     *
+     * @since 1.1
+     */
+    protected void writeDataDescriptor(ZipEntry ze) throws IOException {
+        if (ze.getMethod() != DEFLATED || raf != null) {
+            return;
+        }
+        writeOut(DD_SIG.getBytes());
+        writeOut((new ZipLong(entry.getCrc())).getBytes());
+        writeOut((new ZipLong(entry.getCompressedSize())).getBytes());
+        writeOut((new ZipLong(entry.getSize())).getBytes());
+        written += 16;
+    }
+
+    /**
+     * Writes the central file header entry
+     *
+     * @since 1.1
+     */
+    protected void writeCentralFileHeader(ZipEntry ze) throws IOException {
+        writeOut(CFH_SIG.getBytes());
+        written += 4;
+
+        // version made by
+        writeOut((new ZipShort((ze.getPlatform() << 8) | 20)).getBytes());
+        written += 2;
+
+        // version needed to extract
+        // general purpose bit flag
+        if (ze.getMethod() == DEFLATED && raf == null) {
+            // requires version 2 as we are going to store length info
+            // in the data descriptor
+            writeOut((new ZipShort(20)).getBytes());
+
+            // bit3 set to signal, we use a data descriptor
+            writeOut((new ZipShort(8)).getBytes());
+        } else {
+            writeOut((new ZipShort(10)).getBytes());
+            writeOut(ZERO);
+        }
+        written += 4;
+
+        // compression method
+        writeOut((new ZipShort(ze.getMethod())).getBytes());
+        written += 2;
+
+        // last mod. time and date
+        writeOut(toDosTime(new Date(ze.getTime())).getBytes());
+        written += 4;
+
+        // CRC
+        // compressed length
+        // uncompressed length
+        writeOut((new ZipLong(ze.getCrc())).getBytes());
+        writeOut((new ZipLong(ze.getCompressedSize())).getBytes());
+        writeOut((new ZipLong(ze.getSize())).getBytes());
+        written += 12;
+
+        // file name length
+        byte[] name = getBytes(ze.getName());
+        writeOut((new ZipShort(name.length)).getBytes());
+        written += 2;
+
+        // extra field length
+        byte[] extra = ze.getCentralDirectoryExtra();
+        writeOut((new ZipShort(extra.length)).getBytes());
+        written += 2;
+
+        // file comment length
+        String comm = ze.getComment();
+        if (comm == null) {
+            comm = "";
+        }
+        byte[] comment = getBytes(comm);
+        writeOut((new ZipShort(comment.length)).getBytes());
+        written += 2;
+
+        // disk number start
+        writeOut(ZERO);
+        written += 2;
+
+        // internal file attributes
+        writeOut((new ZipShort(ze.getInternalAttributes())).getBytes());
+        written += 2;
+
+        // external file attributes
+        writeOut((new ZipLong(ze.getExternalAttributes())).getBytes());
+        written += 4;
+
+        // relative offset of LFH
+        writeOut(((ZipLong) offsets.get(ze)).getBytes());
+        written += 4;
+
+        // file name
+        writeOut(name);
+        written += name.length;
+
+        // extra field
+        writeOut(extra);
+        written += extra.length;
+
+        // file comment
+        writeOut(comment);
+        written += comment.length;
+    }
+
+    /**
+     * Writes the &quot;End of central dir record&quot;
+     *
+     * @since 1.1
+     */
+    protected void writeCentralDirectoryEnd() throws IOException {
+        writeOut(EOCD_SIG.getBytes());
+
+        // disk numbers
+        writeOut(ZERO);
+        writeOut(ZERO);
+
+        // number of entries
+        byte[] num = (new ZipShort(entries.size())).getBytes();
+        writeOut(num);
+        writeOut(num);
+
+        // length and location of CD
+        writeOut(cdLength.getBytes());
+        writeOut(cdOffset.getBytes());
+
+        // ZIP file comment
+        byte[] data = getBytes(comment);
+        writeOut((new ZipShort(data.length)).getBytes());
+        writeOut(data);
+    }
+
+    /**
+     * Smallest date/time ZIP can handle.
+     *
+     * @since 1.1
+     */
+    private static final ZipLong DOS_TIME_MIN = new ZipLong(0x00002100L);
+
+    /**
+     * Convert a Date object to a DOS date/time field.
+     *
+     * <p>Stolen from InfoZip's <code>fileio.c</code></p>
+     *
+     * @since 1.1
+     */
+    protected static ZipLong toDosTime(Date time) {
+        int year = time.getYear() + 1900;
+        int month = time.getMonth() + 1;
+        if (year < 1980) {
+            return DOS_TIME_MIN;
+        }
+        long value =  ((year - 1980) << 25)
+            |         (month << 21)
+            |         (time.getDate() << 16)
+            |         (time.getHours() << 11)
+            |         (time.getMinutes() << 5)
+            |         (time.getSeconds() >> 1);
+
+        byte[] result = new byte[4];
+        result[0] = (byte) ((value & 0xFF));
+        result[1] = (byte) ((value & 0xFF00) >> 8);
+        result[2] = (byte) ((value & 0xFF0000) >> 16);
+        result[3] = (byte) ((value & 0xFF000000L) >> 24);
+        return new ZipLong(result);
+    }
+
+    /**
+     * Retrieve the bytes for the given String in the encoding set for
+     * this Stream.
+     *
+     * @since 1.3
+     */
+    protected byte[] getBytes(String name) throws ZipException {
+        if (encoding == null) {
+            return name.getBytes();
+        } else {
+            try {
+                return name.getBytes(encoding);
+            } catch (UnsupportedEncodingException uee) {
+                throw new ZipException(uee.getMessage());
+            }
+        }
+    }
+
+    /**
+     * Write bytes to output or random access file
+     *
+     * @since 1.14
+     */
+    protected final void writeOut(byte [] data) throws IOException {
+        writeOut(data, 0, data.length);
+    }
+
+    /**
+     * Write bytes to output or random access file
+     *
+     * @since 1.14
+     */
+    protected final void writeOut(byte [] data, int offset, int length)
+        throws IOException {
+        if (raf != null) {
+            raf.write(data, offset, length);
+        } else {
+            out.write(data, offset, length);
+        }
+    }
+
+    /**
+     * Assumes a negative integer really is a positive integer that
+     * has wrapped around and re-creates the original value.
+     *
+     * @since 1.17.2.8
+     */
+    protected static long adjustToLong(int i) {
+        if (i < 0) {
+            return 2 * ((long) Integer.MAX_VALUE) + 2 + i;
+        } else {
+            return i;
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipShort.java
===================================================================
--- /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipShort.java	(revision 16139)
+++ /release-kits/shared/ant/src/main/org/apache/tools/zip/ZipShort.java	(revision 16139)
@@ -0,0 +1,98 @@
+/*
+ * Copyright  2001-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.zip;
+
+/**
+ * Utility class that represents a two byte integer with conversion
+ * rules for the big endian byte order of ZIP files.
+ *
+ */
+public final class ZipShort implements Cloneable {
+
+    private int value;
+
+    /**
+     * Create instance from a number.
+     *
+     * @since 1.1
+     */
+    public ZipShort (int value) {
+        this.value = value;
+    }
+
+    /**
+     * Create instance from bytes.
+     *
+     * @since 1.1
+     */
+    public ZipShort (byte[] bytes) {
+        this(bytes, 0);
+    }
+
+    /**
+     * Create instance from the two bytes starting at offset.
+     *
+     * @since 1.1
+     */
+    public ZipShort (byte[] bytes, int offset) {
+        value = (bytes[offset + 1] << 8) & 0xFF00;
+        value += (bytes[offset] & 0xFF);
+    }
+
+    /**
+     * Get value as two bytes in big endian byte order.
+     *
+     * @since 1.1
+     */
+    public byte[] getBytes() {
+        byte[] result = new byte[2];
+        result[0] = (byte) (value & 0xFF);
+        result[1] = (byte) ((value & 0xFF00) >> 8);
+        return result;
+    }
+
+    /**
+     * Get value as Java int.
+     *
+     * @since 1.1
+     */
+    public int getValue() {
+        return value;
+    }
+
+    /**
+     * Override to make two instances with same value equal.
+     *
+     * @since 1.1
+     */
+    public boolean equals(Object o) {
+        if (o == null || !(o instanceof ZipShort)) {
+            return false;
+        }
+        return value == ((ZipShort) o).getValue();
+    }
+
+    /**
+     * Override to make two instances with same value equal.
+     *
+     * @since 1.1
+     */
+    public int hashCode() {
+        return value;
+    }
+}
Index: /release-kits/shared/ant/src/script/ant
===================================================================
--- /release-kits/shared/ant/src/script/ant	(revision 16139)
+++ /release-kits/shared/ant/src/script/ant	(revision 16139)
@@ -0,0 +1,299 @@
+#! /bin/sh
+
+#   Copyright 2001-2005 The Apache Software Foundation
+#
+#   Licensed 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.
+
+# Extract launch and ant arguments, (see details below).
+ant_exec_args=
+no_config=false
+use_jikes_default=false
+ant_exec_debug=false
+show_help=false
+for arg in "$@" ; do
+  if [ "$arg" = "--noconfig" ] ; then
+    no_config=true
+  elif [ "$arg" = "--usejikes" ] ; then
+    use_jikes_default=true
+  elif [ "$arg" = "--execdebug" ] ; then
+    ant_exec_debug=true
+  elif [ my"$arg" = my"--h"  -o my"$arg" = my"--help"  ] ; then
+    show_help=true
+    ant_exec_args="$ant_exec_args -h"
+  else
+    if [  my"$arg" = my"-h"  -o  my"$arg" = my"-help" ] ; then
+      show_help=true
+    fi
+    ant_exec_args="$ant_exec_args \"$arg\""
+  fi
+done
+
+# Source/default ant configuration
+if $no_config ; then
+  rpm_mode=false
+  usejikes=$use_jikes_default
+else
+  # load system-wide ant configuration
+#  if [ -f "/etc/ant.conf" ] ; then
+#    . /etc/ant.conf
+#  fi
+
+  # load user ant configuration
+  if [ -f "$HOME/.ant/ant.conf" ] ; then
+    . $HOME/.ant/ant.conf
+  fi
+  if [ -f "$HOME/.antrc" ] ; then
+    . "$HOME/.antrc"
+  fi
+
+  # provide default configuration values
+  if [ -z "$rpm_mode" ] ; then
+    rpm_mode=false
+  fi
+  if [ -z "$usejikes" ] ; then
+    usejikes=$use_jikes_default
+  fi
+fi
+
+# Setup Java environment in rpm mode
+if $rpm_mode ; then
+  if [ -f /usr/share/java-utils/java-functions ] ; then
+    . /usr/share/java-utils/java-functions
+    set_jvm
+    set_javacmd
+  fi
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false;
+darwin=false;
+case "`uname`" in
+  CYGWIN*) cygwin=true ;;
+  Darwin*) darwin=true
+           if [ -z "$JAVA_HOME" ] ; then
+             JAVA_HOME=/System/Library/Frameworks/JavaVM.framework/Home
+           fi
+           ;;
+esac
+
+if [ -z "$ANT_HOME" -o ! -d "$ANT_HOME" ] ; then
+  ## resolve links - $0 may be a link to ant's home
+  PRG="$0"
+  progname=`basename "$0"`
+
+  # need this for relative symlinks
+  while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+    PRG="$link"
+    else
+    PRG=`dirname "$PRG"`"/$link"
+    fi
+  done
+
+  ANT_HOME=`dirname "$PRG"`/..
+
+  # make it fully qualified
+  ANT_HOME=`cd "$ANT_HOME" && pwd`
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin ; then
+  [ -n "$ANT_HOME" ] &&
+    ANT_HOME=`cygpath --unix "$ANT_HOME"`
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# set ANT_LIB location
+ANT_LIB="${ANT_HOME}/lib"
+
+if [ -z "$JAVACMD" ] ; then
+  if [ -n "$JAVA_HOME"  ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD=`which java 2> /dev/null `
+    if [ -z "$JAVACMD" ] ; then
+        JAVACMD=java
+    fi
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ] ; then
+  echo "Error: JAVA_HOME is not defined correctly."
+  echo "  We cannot execute $JAVACMD"
+  exit 1
+fi
+
+# Build local classpath using just the launcher in non-rpm mode or
+# use the Jpackage helper in rpm mode with basic and default jars
+# specified in the ant.conf configuration. Because the launcher is
+# used, libraries linked in ANT_HOME will also be include, but this
+# is discouraged as it is not java-version safe. A user should
+# request optional jars and their dependencies via the OPT_JAR_LIST
+# variable
+if $rpm_mode && [ -f /usr/bin/build-classpath ] ; then
+  LOCALCLASSPATH="$(/usr/bin/build-classpath ant ant-launcher jaxp_parser_impl xml-commons-apis)"
+  # If the user requested to try to add some other jars to the classpath
+  if [ -n "$OPT_JAR_LIST" ] ; then
+    _OPTCLASSPATH="$(/usr/bin/build-classpath $OPT_JAR_LIST 2> /dev/null)"
+    if [ -n "$_OPTCLASSPATH" ] ; then 
+      LOCALCLASSPATH="$LOCALCLASSPATH:$_OPTCLASSPATH"
+    fi
+  fi
+
+  # Explicitly add javac path to classpath, assume JAVA_HOME set
+  # properly in rpm mode
+  if [ -f "$JAVA_HOME/lib/tools.jar" ] ; then
+    LOCALCLASSPATH="$LOCALCLASSPATH:$JAVA_HOME/lib/tools.jar"
+  fi
+  if [ -f "$JAVA_HOME/lib/classes.zip" ] ; then
+    LOCALCLASSPATH="$LOCALCLASSPATH:$JAVA_HOME/lib/classes.zip"
+  fi
+
+  # if CLASSPATH_OVERRIDE env var is set, LOCALCLASSPATH will be
+  # user CLASSPATH first and ant-found jars after.
+  # In that case, the user CLASSPATH will override ant-found jars
+  #
+  # if CLASSPATH_OVERRIDE is not set, we'll have the normal behaviour
+  # with ant-found jars first and user CLASSPATH after
+  if [ -n "$CLASSPATH" ] ; then
+    # merge local and specified classpath 
+    if [ -z "$LOCALCLASSPATH" ] ; then 
+      LOCALCLASSPATH="$CLASSPATH"
+    elif [ -n "$CLASSPATH_OVERRIDE" ] ; then
+      LOCALCLASSPATH="$CLASSPATH:$LOCALCLASSPATH"
+    else
+      LOCALCLASSPATH="$LOCALCLASSPATH:$CLASSPATH"
+    fi
+
+    # remove class path from launcher -cp option
+    CLASSPATH=""
+  fi
+else
+  # not using rpm_mode; use launcher to determine classpaths
+  if [ -z "$LOCALCLASSPATH" ] ; then
+      LOCALCLASSPATH=$ANT_LIB/ant-launcher.jar
+  else
+      LOCALCLASSPATH=$ANT_LIB/ant-launcher.jar:$LOCALCLASSPATH
+  fi
+fi
+
+if [ -n "$JAVA_HOME" ] ; then
+  # OSX hack to make Ant work with jikes
+  if $darwin ; then
+    OSXHACK="${JAVA_HOME}/../Classes"
+    if [ -d "${OSXHACK}" ] ; then
+      for i in "${OSXHACK}"/*.jar
+      do
+        JIKESPATH="$JIKESPATH:$i"
+      done
+    fi
+  fi
+fi
+
+# Allow Jikes support (off by default)
+if $usejikes; then
+  ANT_OPTS="$ANT_OPTS -Dbuild.compiler=jikes"
+fi
+
+# For Cygwin, switch paths to appropriate format before running java
+# For PATHs convert to unix format first, then to windows format to ensure
+# both formats are supported. Probably this will fail on directories with ;
+# in the name in the path. Let's assume that paths containing ; are more
+# rare than windows style paths on cygwin.
+if $cygwin; then
+  if [ "$OS" = "Windows_NT" ] && cygpath -m .>/dev/null 2>/dev/null ; then
+    format=mixed
+  else
+    format=windows
+  fi
+  ANT_HOME=`cygpath --$format "$ANT_HOME"`
+  ANT_LIB=`cygpath --$format "$ANT_LIB"`
+  JAVA_HOME=`cygpath --$format "$JAVA_HOME"`
+  LCP_TEMP=`cygpath --path --unix "$LOCALCLASSPATH"`
+  LOCALCLASSPATH=`cygpath --path --$format "$LCP_TEMP"`
+  if [ -n "$CLASSPATH" ] ; then
+    CP_TEMP=`cygpath --path --unix "$CLASSPATH"`
+    CLASSPATH=`cygpath --path --$format "$CP_TEMP"`
+  fi
+  CYGHOME=`cygpath --$format "$HOME"`
+fi
+
+# Show script help if requested
+if $show_help ; then
+  echo $0 '[script options] [options] [target [target2 [target3] ..]]'
+  echo 'Script Options:'
+  echo '  --help, --h            print this message and ant help'
+  echo '  --noconfig             suppress sourcing of /etc/ant.conf,'
+  echo '                         $HOME/.ant/ant.conf, and $HOME/.antrc'
+  echo '                         configuration files'
+  echo '  --usejikes             enable use of jikes by default, unless'
+  echo '                         set explicitly in configuration files'
+  echo '  --execdebug            print ant exec line generated by this'
+  echo '                         launch script'
+  echo '  '
+fi
+# add a second backslash to variables terminated by a backslash under cygwin
+if $cygwin; then
+  case "$ANT_HOME" in
+    *\\ )
+    ANT_HOME="$ANT_HOME\\"
+    ;;
+  esac
+  case "$CYGHOME" in
+    *\\ )
+    CYGHOME="$CYGHOME\\"
+    ;;
+  esac
+  case "$JIKESPATH" in
+    *\\ )
+    JIKESPATH="$JIKESPATH\\"
+    ;;
+  esac
+  case "$LOCALCLASSPATH" in
+    *\\ )
+    LOCALCLASSPATH="$LOCALCLASSPATH\\"
+    ;;
+  esac
+  case "$CLASSPATH" in
+    *\\ )
+    CLASSPATH="$CLASSPATH\\"
+    ;;
+  esac
+fi
+# Execute ant using eval/exec to preserve spaces in paths,
+# java options, and ant args
+ant_sys_opts=
+if [ -n "$CYGHOME" ]; then
+  if [ -n "$JIKESPATH" ]; then
+    ant_sys_opts="-Djikes.class.path=\"$JIKESPATH\" -Dcygwin.user.home=\"$CYGHOME\""
+  else
+    ant_sys_opts="-Dcygwin.user.home=\"$CYGHOME\""
+  fi
+else
+  if [ -n "$JIKESPATH" ]; then
+    ant_sys_opts="-Djikes.class.path=\"$JIKESPATH\""
+  fi
+fi
+ant_exec_command="exec \"$JAVACMD\" $ANT_OPTS -classpath \"$LOCALCLASSPATH\" -Dant.home=\"$ANT_HOME\" -Dant.library.dir=\"$ANT_LIB\" $ant_sys_opts org.apache.tools.ant.launch.Launcher $ANT_ARGS -cp \"$CLASSPATH\" $ant_exec_args"
+if $ant_exec_debug ; then
+    echo $ant_exec_command
+fi
+eval $ant_exec_command
Index: /release-kits/shared/ant/src/script/ant.bat
===================================================================
--- /release-kits/shared/ant/src/script/ant.bat	(revision 16139)
+++ /release-kits/shared/ant/src/script/ant.bat	(revision 16139)
@@ -0,0 +1,126 @@
+@echo off
+
+REM  Copyright 2001,2004-2005 The Apache Software Foundation
+REM
+REM  Licensed under the Apache License, Version 2.0 (the "License");
+REM  you may not use this file except in compliance with the License.
+REM  You may obtain a copy of the License at
+REM
+REM      http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM  Unless required by applicable law or agreed to in writing, software
+REM  distributed under the License is distributed on an "AS IS" BASIS,
+REM  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM  See the License for the specific language governing permissions and
+REM  limitations under the License.
+
+if exist "%HOME%\antrc_pre.bat" call "%HOME%\antrc_pre.bat"
+
+if "%OS%"=="Windows_NT" @setlocal
+if "%OS%"=="WINNT" @setlocal
+
+rem %~dp0 is expanded pathname of the current script under NT
+set DEFAULT_ANT_HOME=%~dp0..
+
+if "%ANT_HOME%"=="" set ANT_HOME=%DEFAULT_ANT_HOME%
+set DEFAULT_ANT_HOME=
+
+set _USE_CLASSPATH=yes
+
+rem Slurp the command line arguments. This loop allows for an unlimited number
+rem of arguments (up to the command line limit, anyway).
+set ANT_CMD_LINE_ARGS=%1
+if ""%1""=="""" goto doneStart
+shift
+:setupArgs
+if ""%1""=="""" goto doneStart
+if ""%1""==""-noclasspath"" goto clearclasspath
+set ANT_CMD_LINE_ARGS=%ANT_CMD_LINE_ARGS% %1
+shift
+goto setupArgs
+
+rem here is there is a -noclasspath in the options
+:clearclasspath
+set _USE_CLASSPATH=no
+shift
+goto setupArgs
+
+rem This label provides a place for the argument list loop to break out
+rem and for NT handling to skip to.
+
+:doneStart
+rem find ANT_HOME if it does not exist due to either an invalid value passed
+rem by the user or the %0 problem on Windows 9x
+if exist "%ANT_HOME%\lib\ant.jar" goto checkJava
+
+rem check for ant in Program Files
+if not exist "%ProgramFiles%\ant" goto checkSystemDrive
+set ANT_HOME=%ProgramFiles%\ant
+goto checkJava
+
+:checkSystemDrive
+rem check for ant in root directory of system drive
+if not exist %SystemDrive%\ant\lib\ant.jar goto checkCDrive
+set ANT_HOME=%SystemDrive%\ant
+goto checkJava
+
+:checkCDrive
+rem check for ant in C:\ant for Win9X users
+if not exist C:\ant\lib\ant.jar goto noAntHome
+set ANT_HOME=C:\ant
+goto checkJava
+
+:noAntHome
+echo ANT_HOME is set incorrectly or ant could not be located. Please set ANT_HOME.
+goto end
+
+:checkJava
+set _JAVACMD=%JAVACMD%
+
+if "%JAVA_HOME%" == "" goto noJavaHome
+if not exist "%JAVA_HOME%\bin\java.exe" goto noJavaHome
+if "%_JAVACMD%" == "" set _JAVACMD=%JAVA_HOME%\bin\java.exe
+goto checkJikes
+
+:noJavaHome
+if "%_JAVACMD%" == "" set _JAVACMD=java.exe
+
+:checkJikes
+if not "%JIKESPATH%"=="" goto runAntWithJikes
+
+:runAnt
+if "%_USE_CLASSPATH%"=="no" goto runAntNoClasspath
+if not "%CLASSPATH%"=="" goto runAntWithClasspath
+"%_JAVACMD%" %ANT_OPTS% -classpath "%ANT_HOME%\lib\ant-launcher.jar" "-Dant.home=%ANT_HOME%" org.apache.tools.ant.launch.Launcher %ANT_ARGS% %ANT_CMD_LINE_ARGS%
+goto end
+
+:runAntNoClasspath
+"%_JAVACMD%" %ANT_OPTS% -classpath "%ANT_HOME%\lib\ant-launcher.jar" "-Dant.home=%ANT_HOME%" org.apache.tools.ant.launch.Launcher %ANT_ARGS% %ANT_CMD_LINE_ARGS%
+goto end
+
+:runAntWithClasspath
+"%_JAVACMD%" %ANT_OPTS% -classpath "%ANT_HOME%\lib\ant-launcher.jar" "-Dant.home=%ANT_HOME%" org.apache.tools.ant.launch.Launcher %ANT_ARGS% -cp "%CLASSPATH%" %ANT_CMD_LINE_ARGS%
+goto end
+
+:runAntWithJikes
+if "%_USE_CLASSPATH%"=="no" goto runAntWithJikesNoClasspath
+if not "%CLASSPATH%"=="" goto runAntWithJikesAndClasspath
+
+:runAntWithJikesNoClasspath
+"%_JAVACMD%" %ANT_OPTS% -classpath "%ANT_HOME%\lib\ant-launcher.jar" "-Dant.home=%ANT_HOME%" "-Djikes.class.path=%JIKESPATH%" org.apache.tools.ant.launch.Launcher %ANT_ARGS% %ANT_CMD_LINE_ARGS%
+goto end
+
+:runAntWithJikesAndClasspath
+"%_JAVACMD%" %ANT_OPTS% -classpath "%ANT_HOME%\lib\ant-launcher.jar" "-Dant.home=%ANT_HOME%" "-Djikes.class.path=%JIKESPATH%" org.apache.tools.ant.launch.Launcher %ANT_ARGS%  -cp "%CLASSPATH%" %ANT_CMD_LINE_ARGS%
+goto end
+
+:end
+set _JAVACMD=
+set ANT_CMD_LINE_ARGS=
+
+if "%OS%"=="Windows_NT" @endlocal
+if "%OS%"=="WINNT" @endlocal
+
+:mainEnd
+if exist "%HOME%\antrc_post.bat" call "%HOME%\antrc_post.bat"
+
Index: /release-kits/shared/ant/src/script/ant.cmd
===================================================================
--- /release-kits/shared/ant/src/script/ant.cmd	(revision 16139)
+++ /release-kits/shared/ant/src/script/ant.cmd	(revision 16139)
@@ -0,0 +1,92 @@
+/* 
+    Copyright 2003-2004 The Apache Software Foundation
+  
+    Licensed 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.
+ 
+    Run ant
+*/
+
+'@echo off'
+parse arg mode envarg '::' antarg
+
+if mode\='.' & mode\='..' & mode\='/' then do
+  envarg = mode envarg
+  mode = ''
+end
+
+if antarg = '' then do
+  antarg = envarg
+  envarg = ''
+end
+
+x = setlocal()
+
+env="OS2ENVIRONMENT"
+antenv = _getenv_('antenv')
+if _testenv_() = 0 then interpret 'call "' || antenv || '"' '"' || envarg || '"'
+
+if mode = '' then mode = _getenv_('ANT_MODE' '..')
+if mode \= '/' then do
+  runrc = _getenv_('runrc')
+  antrc = _getenv_('antrc' 'antrc.cmd')
+  if mode = '..' then mode = '-r'
+  else mode = ''
+  interpret 'call "' || runrc || '"' antrc '"' || mode || '"'
+end
+
+if _testenv_() = 0 then do
+  say 'Ant environment is not set properly'
+  x = endlocal()
+  exit 16
+end
+
+settings = '-Dant.home=' || ANT_HOME '-Djava.home=' || JAVA_HOME
+
+java = _getenv_('javacmd' 'java')
+opts = value('ANT_OPTS',,env)
+args = value('ANT_ARGS',,env)
+lcp = value('LOCALCLASSPATH',,env)
+cp = value('CLASSPATH',,env)
+if value('ANT_USE_CP',,env) \= '' then do
+  if lcp \= '' & right(lcp, 1) \= ';' then lcp = lcp || ';'
+  lcp = lcp || cp
+  'SET CLASSPATH='
+end
+if lcp\='' then lcp = '-classpath' lcp
+
+cmd = java opts lcp '-jar' ANT_HOME ||'\lib\ant-launcher.jar' settings args antarg
+launcher = stream(ANT_HOME ||'\lib\ant-launcher.jar', 'C', 'query exists')
+if launcher = '' then entry = 'org.apache.tools.ant.Main'
+else entry = 'org.apache.tools.ant.launch.Launcher'
+java opts lcp entry settings args antarg
+
+x = endlocal()
+
+return rc
+
+_testenv_: procedure expose env ANT_HOME JAVA_HOME
+ANT_HOME = value('ANT_HOME',,env)
+if ANT_HOME = '' then return 0
+JAVA_HOME = value('JAVA_HOME',,env)
+if JAVA_HOME = '' then return 0
+cp = translate(value('CLASSPATH',,env))
+if pos(translate(ANT_HOME), cp) = 0 then return 0
+if pos(translate(JAVA_HOME), cp) = 0 then return 0
+return 1
+
+_getenv_: procedure expose env
+parse arg envar default
+if default = '' then default = envar
+var = value(translate(envar),,env)
+if var = '' then var = default
+return var
Index: /release-kits/shared/ant/src/script/antRun
===================================================================
--- /release-kits/shared/ant/src/script/antRun	(revision 16139)
+++ /release-kits/shared/ant/src/script/antRun	(revision 16139)
@@ -0,0 +1,26 @@
+#!/bin/sh
+
+#
+#  Copyright  2001-2002,2004 The Apache Software Foundation
+# 
+#   Licensed 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.
+# 
+#
+
+# Args: DIR command
+cd "$1"
+CMD="$2"
+shift
+shift
+
+exec "$CMD" "$@"
Index: /release-kits/shared/ant/src/script/antRun.bat
===================================================================
--- /release-kits/shared/ant/src/script/antRun.bat	(revision 16139)
+++ /release-kits/shared/ant/src/script/antRun.bat	(revision 16139)
@@ -0,0 +1,47 @@
+@echo off
+
+REM
+REM Copyright  2001-2002,2004-2005 The Apache Software Foundation
+REM
+REM  Licensed under the Apache License, Version 2.0 (the "License");
+REM  you may not use this file except in compliance with the License.
+REM  You may obtain a copy of the License at
+REM
+REM      http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM  Unless required by applicable law or agreed to in writing, software
+REM  distributed under the License is distributed on an "AS IS" BASIS,
+REM  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM  See the License for the specific language governing permissions and
+REM  limitations under the License.
+REM
+REM
+
+if "%OS%"=="Windows_NT" @setlocal
+if "%OS%"=="WINNT" @setlocal
+
+if ""%1""=="""" goto runCommand
+
+rem Change drive and directory to %1
+if "%OS%"=="Windows_NT" cd /d ""%1""
+if not "%OS%"=="Windows_NT" cd ""%1""
+shift
+
+rem Slurp the command line arguments. This loop allows for an unlimited number
+rem of agruments (up to the command line limit, anyway).
+set ANT_RUN_CMD=%1
+if ""%1""=="""" goto runCommand
+shift
+:loop
+if ""%1""=="""" goto runCommand
+set ANT_RUN_CMD=%ANT_RUN_CMD% %1
+shift
+goto loop
+
+:runCommand
+rem echo %ANT_RUN_CMD%
+%ANT_RUN_CMD%
+
+if "%OS%"=="Windows_NT" @endlocal
+if "%OS%"=="WINNT" @endlocal
+
Index: /release-kits/shared/ant/src/script/antRun.pl
===================================================================
--- /release-kits/shared/ant/src/script/antRun.pl	(revision 16139)
+++ /release-kits/shared/ant/src/script/antRun.pl	(revision 16139)
@@ -0,0 +1,65 @@
+#!/usr/bin/perl
+#
+# Copyright 2001,2003-2004 The Apache Software Foundation
+#
+#  Licensed 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.
+#
+#######################################################################
+#
+# antRun.pl
+#
+# wrapper script for invoking commands on a platform with Perl installed
+# this is akin to antRun.bat, and antRun the SH script 
+#
+# created:         2001-10-18
+# author:          Jeff Tulley jtulley@novell.com 
+#######################################################################
+#be fussy about variables
+use strict;
+
+#turn warnings on during dev; generates a few spurious uninitialised var access warnings
+#use warnings;
+
+#and set $debug to 1 to turn on trace info (currently unused)
+my $debug=1;
+
+#######################################################################
+# change drive and directory to "%1"
+my $ANT_RUN_CMD = @ARGV[0];
+
+# assign current run command to "%2"
+chdir (@ARGV[0]) || die "Can't cd to $ARGV[0]: $!\n";
+if ($^O eq "NetWare") {
+    # There is a bug in Perl 5 on NetWare, where chdir does not
+    # do anything.  On NetWare, the following path-prefixed form should 
+    # always work. (afaict)
+    $ANT_RUN_CMD .= "/".@ARGV[1];
+}
+else {
+    $ANT_RUN_CMD = @ARGV[1];
+}
+
+# dispose of the first two arguments, leaving only the command's args.
+shift;
+shift;
+
+# run the command
+my $returnValue = system $ANT_RUN_CMD, @ARGV;
+if ($returnValue eq 0) {
+    exit 0;
+}
+else {
+    # only 0 and 1 are widely recognized as exit values
+    # so change the exit value to 1
+    exit 1;
+}
Index: /release-kits/shared/ant/src/script/antenv.cmd
===================================================================
--- /release-kits/shared/ant/src/script/antenv.cmd	(revision 16139)
+++ /release-kits/shared/ant/src/script/antenv.cmd	(revision 16139)
@@ -0,0 +1,99 @@
+/* 
+    Copyright 2003-2004 The Apache Software Foundation
+  
+    Licensed 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.
+
+    Ant environment
+*/
+
+'@echo off'
+call RxFuncAdd "SysLoadFuncs", "RexxUtil", "SysLoadFuncs"
+call SysLoadFuncs
+
+/* Prepare the parameters for later use */
+parse arg argv
+mode = ''
+args = ''
+opts = ''
+cp = ''
+lcp = ''
+
+do i = 1 to words(argv)
+  param = word(argv, i)
+  select
+    when param='-lcp' then mode = 'l'
+    when param='-cp' | param='-classpath' then mode = 'c'
+    when abbrev('-opts', param, 4) then mode = 'o'
+    when abbrev('-args', param, 4) then mode = 'a'
+  otherwise
+    select
+      when mode = 'a' then args = space(args param, 1)
+      when mode = 'c' then cp = space(cp param, 1)
+      when mode = 'l' then lcp = space(lcp param, 1)
+      when mode = 'o' then opts = space(opts param, 1)
+    otherwise
+      say 'Option' param 'ignored'
+    end
+  end
+end
+
+env="OS2ENVIRONMENT"
+antconf = _getenv_('antconf' 'antconf.cmd')
+runrc = _getenv_('runrc')
+interpret 'call "' || runrc || '"' '"' || antconf || '"' 'ETC'
+ANT_HOME = value('ANT_HOME',,env)
+JAVA_HOME = value('JAVA_HOME',,env)
+classpath = value('CLASSPATH',,env)
+classes = stream(JAVA_HOME || "\lib\classes.zip", "C", "QUERY EXISTS")
+if classes \= '' then classpath = prepend(classpath classes)
+classes = stream(JAVA_HOME || "\lib\tools.jar", "C", "QUERY EXISTS")
+if classes \= '' then classpath = prepend(classpath classes)
+
+classpath = prepend(classpath ANT_HOME || '\lib\ant-launcher.jar')
+'SET CLASSPATH=' || classpath
+
+/* Setting classpathes, options and arguments */
+envset = _getenv_('envset')
+if cp\=''   then interpret 'call "' || envset || '"' '"; CLASSPATH"' '"' || cp || '"'
+if lcp\=''  then interpret 'call "' || envset || '"' '"; LOCALCLASSPATH"' '"' || lcp || '"'
+if opts\='' then interpret 'call "' || envset || '"' '"-D ANT_OPTS"' '"' || opts || '"'
+if args\='' then interpret 'call "' || envset || '"' '"ANT_ARGS"' '"' || args || '"'
+
+exit 0
+
+addpath: procedure
+parse arg path elem
+if elem = '' then do
+  if path\='' & right(path, 1)\=';' then path = path || ';'
+  return path
+end
+if substr(path, length(path)) = ';' then glue = ''
+else glue = ';'
+if pos(translate(elem), translate(path)) = 0 then path = path || glue || elem || ';'
+return path
+
+prepend: procedure
+parse arg path elem
+if elem = '' then do
+  if path\='' & right(path, 1)\=';' then path = path || ';'
+  return path
+end
+if pos(translate(elem), translate(path)) = 0 then path = elem || ';' || path
+return path
+
+_getenv_: procedure expose env
+parse arg envar default
+if default = '' then default = envar
+var = value(translate(envar),,env)
+if var = '' then var = default
+return var
Index: /release-kits/shared/ant/src/script/complete-ant-cmd.pl
===================================================================
--- /release-kits/shared/ant/src/script/complete-ant-cmd.pl	(revision 16139)
+++ /release-kits/shared/ant/src/script/complete-ant-cmd.pl	(revision 16139)
@@ -0,0 +1,113 @@
+#!/usr/bin/perl
+#
+# Copyright 2001,2004 The Apache Software Foundation
+#
+#  Licensed 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.
+#
+# A script to allow Bash or Z-Shell to complete an Ant command-line.  
+#
+# To install for Bash 2.0 or better, add the following to ~/.bashrc:
+# 
+#     $ complete -C complete-ant-cmd ant build.sh
+#
+# To install for Z-Shell 2.5 or better, add the following to ~/.zshrc:
+#
+#     function ant_complete () {
+#         local args_line args
+#         read -l args_line
+#         set -A args $args_line
+#         set -A reply $(COMP_LINE=$args_line complete-ant-cmd ${args[1]} $1)
+#     }
+#     compctl -K ant_complete ant build.sh
+#     
+# @author Mike Williams <mikew@cortexebusiness.com.au>
+
+my $cmdLine = $ENV{'COMP_LINE'};
+my $antCmd = $ARGV[0];
+my $word = $ARGV[1];
+
+my @completions;
+if ($word =~ /^-/) {
+    list( restrict( $word, getArguments() ));
+} elsif ($cmdLine =~ /-(f|buildfile)\s+\S*$/) {
+    list( getBuildFiles($word) );
+} else {
+    list( restrict( $word, getTargets() ));
+}
+
+exit(0);
+
+sub list {
+    for (@_) {
+        print "$_\n";
+    }
+}
+
+sub restrict {
+    my ($word, @completions) = @_;
+    grep( /^\Q$word\E/, @completions );
+}
+
+sub getArguments {
+    qw(-buildfile -debug -emacs -f -find -help -listener -logfile 
+       -logger -projecthelp -quiet -verbose -version); 
+}
+
+
+sub getBuildFiles {
+    my ($word) = @_;
+    grep( /\.xml$/, glob( "$word*" ));
+}
+
+sub getTargets {
+
+    # Look for build-file
+    my $buildFile = 'build.xml';
+    if ($cmdLine =~ /-(f|buildfile)\s+(\S+)/) {
+        $buildFile = $2;
+    }
+    return () unless (-f $buildFile);
+
+    # Run "ant -projecthelp" to list targets.  Keep a cache of results in a
+    # cache-file.
+    my $cacheFile = $buildFile;
+    $cacheFile =~ s|(.*/)?(.*)|${1}.ant-targets-${2}|;
+    if ((!-e $cacheFile) || (-M $buildFile) < (-M $cacheFile)) {
+        open( CACHE, '>'.$cacheFile ) || die "can\'t write $cacheFile: $!\n";
+        open( HELP, "$antCmd -projecthelp -f '$buildFile'|" ) || return(); 
+        my %targets;
+        while( <HELP> ) {
+            if (/^\s+(\S+)/) {
+                $targets{$1}++;
+            }
+        }
+        my @targets = sort keys %targets;
+        for (@targets) { print CACHE "$_\n"; }
+        return @targets;
+    }
+    
+    # Read the target-cache
+    open( CACHE, $cacheFile ) || die "can\'t read $cacheFile: $!\n";
+    my @targets;
+    while (<CACHE>) {
+        chop;
+        s/\r$//;  # for Cygwin
+        push( @targets, $_ );
+    }
+    close( CACHE );
+    @targets;
+
+}
+
+
+
Index: /release-kits/shared/ant/src/script/envset.cmd
===================================================================
--- /release-kits/shared/ant/src/script/envset.cmd	(revision 16139)
+++ /release-kits/shared/ant/src/script/envset.cmd	(revision 16139)
@@ -0,0 +1,130 @@
+/*
+
+    Copyright 2003-2004 The Apache Software Foundation
+  
+    Licensed 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.
+
+SET environment variables
+First optional parameter:
+   ;     parameters are considered parts of a path variable, semicolons are
+         appended to each element if not already present
+   -D    parameters are properties for Java or Makefile etc., -D will be
+         prepended and the parameters will be separated by a space
+   =D    the same as above but equal sign is not required
+   ,     parameters should be comma separated in the environment variable
+   -     parameters should be separated by the next parameter
+   Other values mean that the first parameter is missing and the environment
+   variable will be set to the space separated parameters
+
+Second parameter: name of the environment variable
+
+Next parameters: values
+; implies that the equal sign is considered a part of the parameter and is
+not interpreted
+
+-D requires parameters in the form name=value. If the equal sign is not found,
+the parameters are changed to name=expanded_name
+
+Other options have optional equal sign. If it is found, only the part after
+the equal sign will be oprionally expanded.
+
+If the parameter is the minus sign, the next parameter will not be expanded.
+If the parameter is a single dot, it will be replaced with the value of the
+environment variable as it existed before envset was invoked.
+
+For other parameters the batch looks for the environment variable with the
+same name (in uppercase). If it is found, it forms the expanded_name. If
+the environment variable with such a name does not exist, the expanded_name
+will hold the parameter name without case conversion.
+*/
+
+parse arg mode envar args
+
+equal = 0
+sep = ' '
+
+/* Parse command line parameters */
+select
+  when mode='-' then do
+    sep = envar
+    parse var args envar args
+  end
+  when mode=';' then do
+    sep = ''
+    equal = -1
+  end
+  when mode='-D' then equal = 1
+  when mode='=D' then mode = '-D'
+  when mode=',' then sep = ','
+otherwise
+  args = envar args
+  envar = mode
+  mode = ''
+end
+
+env = 'OS2ENVIRONMENT'
+envar = translate(envar)
+orig = value(envar,,env)
+newval = ''
+expand = 1
+
+/* for each parameter... */
+do i = 1 to words(args)
+  if expand > 0 & word(args, i) = '-' then expand = 0
+  else call addval word(args, i)
+end
+
+/* Optionally enclose path variable by quotes */
+if mode = ';' & pos(' ', newval) > 0 then newval = '"' || newval || '"'
+
+/* Set the new value, 'SET' cannot be used since it does not allow '=' */
+x = value(envar, newval, env)
+exit 0
+
+addval: procedure expose sep equal orig expand newval mode env
+parse arg var
+
+if var = '.' then expvar = orig
+else do
+  if equal >= 0 then do
+    parse var var name '=' val
+    if val = '' then var = name
+    else var = val
+  end
+  if expand = 0 then expvar = var
+  else expvar = value(translate(var),,env)
+  if expvar = '' then expvar = var
+  if equal >= 0 then do
+    if val = '' then do
+      parse var expvar key '=' val
+      if val <> '' then name = key
+      else do
+        if equal > 0 then val = key
+        else name = key
+      end
+    end
+    else val = expvar
+    if pos(' ', val) > 0 | pos('=', val) > 0 then val = '"' || val || '"'
+    if val = '' then expvar = name
+    else expvar = name || '=' || val
+  end
+  if mode = '-D' then expvar = '-D' || expvar
+  if mode = ';' then do
+    if right(expvar, 1) <> ';' then expvar = expvar || ';'
+  end
+end
+
+if newval = '' then newval = expvar
+else newval = newval || sep || expvar
+expand = 1
+return
Index: /release-kits/shared/ant/src/script/lcp.bat
===================================================================
--- /release-kits/shared/ant/src/script/lcp.bat	(revision 16139)
+++ /release-kits/shared/ant/src/script/lcp.bat	(revision 16139)
@@ -0,0 +1,30 @@
+REM
+REM Copyright  2001-2004 The Apache Software Foundation
+REM
+REM  Licensed under the Apache License, Version 2.0 (the "License");
+REM  you may not use this file except in compliance with the License.
+REM  You may obtain a copy of the License at
+REM
+REM      http://www.apache.org/licenses/LICENSE-2.0
+REM
+REM  Unless required by applicable law or agreed to in writing, software
+REM  distributed under the License is distributed on an "AS IS" BASIS,
+REM  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+REM  See the License for the specific language governing permissions and
+REM  limitations under the License.
+REM
+REM
+
+set _CLASSPATHCOMPONENT=%1
+if ""%1""=="""" goto gotAllArgs
+shift
+
+:argCheck
+if ""%1""=="""" goto gotAllArgs
+set _CLASSPATHCOMPONENT=%_CLASSPATHCOMPONENT% %1
+shift
+goto argCheck
+
+:gotAllArgs
+set LOCALCLASSPATH=%_CLASSPATHCOMPONENT%;%LOCALCLASSPATH%
+
Index: /release-kits/shared/ant/src/script/runant.pl
===================================================================
--- /release-kits/shared/ant/src/script/runant.pl	(revision 16139)
+++ /release-kits/shared/ant/src/script/runant.pl	(revision 16139)
@@ -0,0 +1,152 @@
+#!/usr/bin/perl
+#
+# Copyright 2000-2004 The Apache Software Foundation
+#
+#  Licensed 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.
+#
+#######################################################################
+#
+# runant.pl
+#
+# wrapper script for invoking ant in a platform with Perl installed
+# this may include cgi-bin invocation, which is considered somewhat daft.
+# (slo: that should be a separate file which can be derived from this
+# and returns the XML formatted output)
+#
+# the code is not totally portable due to classpath and directory splitting
+# issues. oops. (NB, use File::Spec::Functions  will help and the code is
+# structured for the catfile() call, but because of perl version funnies
+# the code is not included. 
+#
+# created:         2000-8-24
+# author:          Steve Loughran steve_l@sourceforge.net
+#######################################################################
+#
+# Assumptions:
+#
+# - the "java" executable/script is on the command path
+# - ANT_HOME has been set
+# - target platform uses ":" as classpath separator or perl indicates it is dos/win32
+# - target platform uses "/" as directory separator.
+
+#be fussy about variables
+use strict;
+
+#platform specifics (disabled)
+#use File::Spec::Functions;
+
+#turn warnings on during dev; generates a few spurious uninitialised var access warnings
+#use warnings;
+
+#and set $debug to 1 to turn on trace info
+my $debug=1;
+
+#######################################################################
+#
+# check to make sure environment is setup
+#
+
+my $HOME = $ENV{ANT_HOME};
+if ($HOME eq "")
+        {
+    die "\n\nANT_HOME *MUST* be set!\n\n";
+        }
+
+my $JAVACMD = $ENV{JAVACMD};
+$JAVACMD = "java" if $JAVACMD eq "";
+
+my $onnetware = 0;
+if ($^O eq "NetWare")
+{
+  $onnetware = 1;
+}
+
+my $oncygwin = ($^O eq "cygwin");
+
+#ISSUE: what java wants to split up classpath varies from platform to platform 
+#and perl is not too hot at hinting which box it is on.
+#here I assume ":" 'cept on win32, dos, and netware. Add extra tests here as needed.
+my $s=":";
+if(($^O eq "MSWin32") || ($^O eq "dos") || ($^O eq "cygwin") ||
+   ($onnetware == 1))
+        {
+        $s=";";
+        }
+
+#build up standard classpath
+my $localpath = "$HOME/lib/ant-launcher.jar";
+#set JVM options and Ant arguments, if any
+my @ANT_OPTS=split(" ", $ENV{ANT_OPTS});
+my @ANT_ARGS=split(" ", $ENV{ANT_ARGS});
+
+#jikes
+if($ENV{JIKESPATH} ne "")
+        {
+        push @ANT_OPTS, "-Djikes.class.path=$ENV{JIKESPATH}";
+        }
+
+#construct arguments to java
+my @ARGS;
+push @ARGS, @ANT_OPTS;
+
+my $CYGHOME = "";
+
+my $classpath=$ENV{CLASSPATH};
+if ($oncygwin == 1) {
+  $localpath = `cygpath --path --windows $localpath`;
+  chomp ($localpath);
+  if (! $classpath eq "")
+  {
+    $classpath = `cygpath --path --windows "$classpath"`;
+    chomp ($classpath);
+  }
+  $HOME = `cygpath --path --windows $HOME`;
+  chomp ($HOME);
+  $CYGHOME = `cygpath --path --windows $ENV{HOME}`;
+  chomp ($CYGHOME);
+}
+push @ARGS, "-classpath", "$localpath";
+push @ARGS, "-Dant.home=$HOME";
+if ( ! $CYGHOME eq "" )
+{
+  push @ARGS, "-Dcygwin.user.home=\"$CYGHOME\""
+}
+push @ARGS, "org.apache.tools.ant.launch.Launcher", @ANT_ARGS;
+push @ARGS, @ARGV;
+if (! $classpath eq "")
+{
+  if ($onnetware == 1)
+  {
+    # make classpath literally $CLASSPATH
+    # this is to avoid pushing us over the 512 character limit
+    # even skip the ; - that is already in $localpath
+    push @ARGS, "-lib", "\$CLASSPATH";
+  }
+  else
+  {
+    push @ARGS, "-lib", "$classpath";
+  }
+}
+print "\n $JAVACMD @ARGS\n\n" if ($debug);
+
+my $returnValue = system $JAVACMD, @ARGS;
+if ($returnValue eq 0)
+        {
+        exit 0;
+        }
+else
+        {
+        # only 0 and 1 are widely recognized as exit values
+        # so change the exit value to 1
+        exit 1;
+        }
Index: /release-kits/shared/ant/src/script/runant.py
===================================================================
--- /release-kits/shared/ant/src/script/runant.py	(revision 16139)
+++ /release-kits/shared/ant/src/script/runant.py	(revision 16139)
@@ -0,0 +1,100 @@
+#!/usr/bin/python
+# Copyright 2001,2003-2004 The Apache Software Foundation
+#
+#  Licensed 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.
+#
+
+"""
+
+ runant.py
+
+    This script is a translation of the runant.pl written by Steve Loughran.
+    It runs ant with/out arguments, it should be quite portable (thanks to
+    the python os library)
+    This script has been tested with Python2.0/Win2K
+
+ created:         2001-04-11
+ author:          Pierre Dittgen pierre.dittgen@criltelecom.com
+
+ Assumptions:
+
+ - the "java" executable/script is on the command path
+"""
+import os, os.path, string, sys
+
+# Change it to 1 to get extra debug information
+debug = 0
+
+#######################################################################
+
+# If ANT_HOME is not set default to script's parent directory
+if os.environ.has_key('ANT_HOME'):
+    ANT_HOME = os.environ['ANT_HOME']
+else:
+    ANT_HOME = os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[0])))
+
+# set ANT_LIB location
+ANT_LIB = os.path.join(ANT_HOME, 'lib')
+
+# set JAVACMD (check variables JAVACMD and JAVA_HOME)
+JAVACMD = None
+if not os.environ.has_key('JAVACMD'):
+    if os.environ.has_key('JAVA_HOME'):
+        if not os.path.exists(os.environ['JAVA_HOME']):
+            print "Warning: JAVA_HOME is not defined correctly."
+        else:
+            JAVACMD = os.path.join(os.environ['JAVA_HOME'], 'bin', 'java')
+    else:
+        print "Warning: JAVA_HOME not set."
+else:
+    JAVACMD = os.environ['JAVACMD']
+if not JAVACMD:
+    JAVACMD = 'java'
+
+launcher_jar = os.path.join(ANT_LIB, 'ant-launcher.jar')
+if not os.path.exists(launcher_jar):
+    print 'Unable to locate ant-launcher.jar. Expected to find it in %s' % \
+        ANT_LIB
+
+# Build up standard classpath (LOCALCLASSPATH)
+LOCALCLASSPATH = launcher_jar
+if os.environ.has_key('LOCALCLASSPATH'):
+    LOCALCLASSPATH += os.pathsep + os.environ['LOCALCLASSPATH']
+
+ANT_OPTS = ""
+if os.environ.has_key('ANT_OPTS'):
+    ANT_OPTS = os.environ['ANT_OPTS']
+
+OPTS = ""
+if os.environ.has_key('JIKESPATH'):
+    OPTS = '-Djikes.class.path=\"%s\"' % os.environ['JIKESPATH']
+
+ANT_ARGS = ""
+if os.environ.has_key('ANT_ARGS'):
+    ANT_ARGS = os.environ['ANT_ARGS']
+
+CLASSPATH = ""
+if os.environ.has_key('CLASSPATH'):
+    CLASSPATH = os.environ['CLASSPATH']
+
+# Builds the commandline
+cmdline = ('%s %s -classpath %s -Dant.home=%s %s ' + \
+    'org.apache.tools.ant.launch.Launcher %s -lib %s %s') \
+     % (JAVACMD, ANT_OPTS, LOCALCLASSPATH, ANT_HOME, OPTS, ANT_ARGS, \
+        CLASSPATH, string.join(sys.argv[1:], ' '))
+
+if debug:
+    print '\n%s\n\n' % (cmdline)
+
+# Run the biniou!
+os.system(cmdline)
Index: /release-kits/shared/ant/src/script/runrc.cmd
===================================================================
--- /release-kits/shared/ant/src/script/runrc.cmd	(revision 16139)
+++ /release-kits/shared/ant/src/script/runrc.cmd	(revision 16139)
@@ -0,0 +1,59 @@
+/* 
+    Copyright 2003-2004 The Apache Software Foundation
+  
+    Licensed 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.
+
+    Run RC file, name is in the first arg, second arg is either PATH
+    ENV  or -r or nothing 
+*/
+
+parse arg name path rest
+
+if name = '' then do
+  say 'RC file name is missing'
+  exit 1
+end
+
+if rest \= '' then do
+  say 'Too many parameters'
+  exit 1
+end
+
+call runit name path
+exit 0
+
+runit: procedure
+parse arg name path dir
+
+if path \= '' & path \= '-r' then do
+  dir = value(translate(path),,'OS2ENVIRONMENT')
+  if dir = '' then return
+  dir = translate(dir, '\', '/') /* change UNIX-like path to OS/2 */
+end
+
+if dir = '' then dir = directory()
+
+if path = '-r' then do /* recursive call */
+  subdir = filespec('path', dir)
+  if subdir \= '\' then do
+    subdir = left(subdir, length(subdir)-1)
+    call runit name path filespec('drive', dir) || subdir
+  end
+end
+
+/* Look for the file and run it */
+if right(dir, 1) \= '\' then dir = dir || '\'
+rcfile = stream(dir || name, 'c', 'query exists')
+if rcfile \= '' then interpret 'call "' || rcfile || '"'
+
+return
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/AntClassLoaderDelegationTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/AntClassLoaderDelegationTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/AntClassLoaderDelegationTest.java	(revision 16139)
@@ -0,0 +1,102 @@
+/*
+ * Copyright  2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import junit.framework.TestCase;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Test case for ant class loader
+ *
+ */
+public class AntClassLoaderDelegationTest extends TestCase {
+
+    private Project p;
+
+    public AntClassLoaderDelegationTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        p = new Project();
+        p.init();
+    }
+
+    /** Sample resource present in build/testcases/ */
+    private static final String TEST_RESOURCE = "org/apache/tools/ant/IncludeTest.class";
+    
+    public void testFindResources() throws Exception {
+        //System.err.println("loading from: " + AntClassLoader.class.getProtectionDomain().getCodeSource().getLocation());
+        // See bug #30161.
+        // This path should contain the class files for these testcases:
+        String buildTestcases = System.getProperty("build.tests");
+        assertNotNull("defined ${build.tests}", buildTestcases);
+        assertTrue("have a dir " + buildTestcases, new File(buildTestcases).isDirectory());
+        Path path = new Path(p, buildTestcases);
+        // A special parent loader which is not the system class loader:
+        ClassLoader parent = new ParentLoader();
+        // An AntClassLoader which is supposed to delegate to the parent and then to the disk path:
+        ClassLoader acl = new AntClassLoader(parent, p, path, true);
+        // The intended result URLs:
+        URL urlFromPath = new URL(FileUtils.newFileUtils().toURI(buildTestcases) + TEST_RESOURCE);
+        URL urlFromParent = new URL("http://ant.apache.org/" + TEST_RESOURCE);
+        assertEquals("correct resources (regular delegation order)",
+            Arrays.asList(new URL[] {urlFromParent, urlFromPath}),
+            enum2List(acl.getResources(TEST_RESOURCE)));
+        acl = new AntClassLoader(parent, p, path, false);
+        assertEquals("correct resources (reverse delegation order)",
+            Arrays.asList(new URL[] {urlFromPath, urlFromParent}),
+            enum2List(acl.getResources(TEST_RESOURCE)));
+    }
+    
+    private static List enum2List(Enumeration e) {
+        // JDK 1.4: return Collections.list(e);
+        List l = new ArrayList();
+        while (e.hasMoreElements()) {
+            l.add(e.nextElement());
+        }
+        return l;
+    }
+    
+    /** Special loader that just knows how to find TEST_RESOURCE. */
+    private static final class ParentLoader extends ClassLoader {
+        
+        public ParentLoader() {}
+        
+        protected Enumeration findResources(String name) throws IOException {
+            if (name.equals(TEST_RESOURCE)) {
+                return Collections.enumeration(Collections.singleton(new URL("http://ant.apache.org/" + name)));
+            } else {
+                return Collections.enumeration(Collections.EMPTY_SET);
+            }
+        }
+        
+    }
+    
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/AntClassLoaderTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/AntClassLoaderTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/AntClassLoaderTest.java	(revision 16139)
@@ -0,0 +1,85 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+import junit.framework.TestCase;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Test case for ant class loader
+ *
+ */
+public class AntClassLoaderTest extends TestCase {
+
+    private Project p;
+
+    public AntClassLoaderTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        p = new Project();
+        p.init();
+    }
+
+    public void testCleanup() throws BuildException {
+        Path path = new Path(p, ".");
+        AntClassLoader loader = new AntClassLoader(p, path);
+        try {
+            // we don't expect to find this
+            loader.findClass("fubar");
+            fail("Did not expect to find fubar class");
+        } catch (ClassNotFoundException e) {
+            // ignore expected
+        }
+
+        loader.cleanup();
+        try {
+            // we don't expect to find this
+            loader.findClass("fubar");
+            fail("Did not expect to find fubar class");
+        } catch (ClassNotFoundException e) {
+            // ignore expected
+        } catch (NullPointerException e) {
+            fail("loader should not fail even if cleaned up");
+        }
+
+        // tell the build it is finished
+        p.fireBuildFinished(null);
+        try {
+            // we don't expect to find this
+            loader.findClass("fubar");
+            fail("Did not expect to find fubar class");
+        } catch (ClassNotFoundException e) {
+            // ignore expected
+        } catch (NullPointerException e) {
+            fail("loader should not fail even if project finished");
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/BuildFileTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/BuildFileTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/BuildFileTest.java	(revision 16139)
@@ -0,0 +1,478 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import junit.framework.TestCase;
+import org.apache.tools.ant.Project;
+import java.io.File;
+import java.io.PrintStream;
+import java.net.URL;
+
+/**
+ * A BuildFileTest is a TestCase which executes targets from an Ant buildfile
+ * for testing.
+ *
+ * This class provides a number of utility methods for particular build file
+ * tests which extend this class.
+ *
+ */
+public abstract class BuildFileTest extends TestCase {
+
+    protected Project project;
+
+    private StringBuffer logBuffer;
+    private StringBuffer fullLogBuffer;
+    private StringBuffer outBuffer;
+    private StringBuffer errBuffer;
+    private BuildException buildException;
+
+    /**
+     *  Constructor for the BuildFileTest object
+     *
+     *@param  name string to pass up to TestCase constructor
+     */
+    public BuildFileTest(String name) {
+        super(name);
+    }
+
+    /**
+     *  run a target, expect for any build exception
+     *
+     *@param  target target to run
+     *@param  cause  information string to reader of report
+     */
+    protected void expectBuildException(String target, String cause) {
+        expectSpecificBuildException(target, cause, null);
+    }
+
+    /**
+     * Assert that only the given message has been logged with a
+     * priority &gt;= INFO when running the given target.
+     */
+    protected void expectLog(String target, String log) {
+        executeTarget(target);
+        String realLog = getLog();
+        assertEquals(log, realLog);
+    }
+
+    /**
+     * Assert that the given substring is in the log messages
+     */
+
+    protected void assertLogContaining(String substring) {
+        String realLog = getLog();
+        assertTrue("expecting log to contain \"" + substring + "\" log was \""
+                   + realLog + "\"",
+                   realLog.indexOf(substring) >= 0);
+    }
+
+    /**
+     * Assert that the given message has been logged with a priority
+     * &gt;= INFO when running the given target.
+     */
+    protected void expectLogContaining(String target, String log) {
+        executeTarget(target);
+        assertLogContaining(log);
+    }
+
+    /**
+     *  Gets the log the BuildFileTest object.
+     *  only valid if configureProject() has
+     *  been called.
+     * @pre logBuffer!=null
+     * @return    The log value
+     */
+    protected String getLog() {
+        return logBuffer.toString();
+    }
+
+    /**
+     * Assert that the given message has been logged with a priority
+     * &gt;= DEBUG when running the given target.
+     */
+    protected void expectDebuglog(String target, String log) {
+        executeTarget(target);
+        String realLog = getFullLog();
+        assertEquals(log, realLog);
+    }
+
+    /**
+     *  Gets the log the BuildFileTest object.
+     *  only valid if configureProject() has
+     *  been called.
+     * @pre fullLogBuffer!=null
+     * @return    The log value
+     */
+    protected String getFullLog() {
+        return fullLogBuffer.toString();
+    }
+
+    /**
+     *  execute the target, verify output matches expectations
+     *
+     *@param  target  target to execute
+     *@param  output  output to look for
+     */
+
+    protected void expectOutput(String target, String output) {
+        executeTarget(target);
+        String realOutput = getOutput();
+        assertEquals(output, realOutput.trim());
+    }
+
+    /**
+     *  execute the target, verify output matches expectations
+     *  and that we got the named error at the end
+     *@param  target  target to execute
+     *@param  output  output to look for
+     *@param  error   Description of Parameter
+     */
+
+    protected void expectOutputAndError(String target, String output, String error) {
+        executeTarget(target);
+        String realOutput = getOutput();
+        assertEquals(output, realOutput);
+        String realError = getError();
+        assertEquals(error, realError);
+    }
+
+    protected String getOutput() {
+        return cleanBuffer(outBuffer);
+    }
+
+    protected String getError() {
+        return cleanBuffer(errBuffer);
+    }
+
+    protected BuildException getBuildException() {
+        return buildException;
+    }
+
+    private String cleanBuffer(StringBuffer buffer) {
+        StringBuffer cleanedBuffer = new StringBuffer();
+        boolean cr = false;
+        for (int i = 0; i < buffer.length(); i++) {
+            char ch = buffer.charAt(i);
+            if (ch == '\r') {
+                cr = true;
+                continue;
+            }
+
+            if (!cr) {
+                cleanedBuffer.append(ch);
+            } else {
+                cleanedBuffer.append(ch);
+            }
+        }
+        return cleanedBuffer.toString();
+    }
+
+    /**
+     *  set up to run the named project
+     *
+     * @param  filename name of project file to run
+     */
+    protected void configureProject(String filename) throws BuildException {
+        configureProject(filename, Project.MSG_DEBUG);
+    }
+
+    /**
+     *  set up to run the named project
+     *
+     * @param  filename name of project file to run
+     */
+    protected void configureProject(String filename, int logLevel)
+        throws BuildException {
+        logBuffer = new StringBuffer();
+        fullLogBuffer = new StringBuffer();
+        project = new Project();
+        project.init();
+        project.setUserProperty( "ant.file" , new File(filename).getAbsolutePath() );
+        project.addBuildListener(new AntTestListener(logLevel));
+        ProjectHelper.configureProject(project, new File(filename));
+    }
+
+    /**
+     *  execute a target we have set up
+     * @pre configureProject has been called
+     * @param  targetName  target to run
+     */
+    protected void executeTarget(String targetName) {
+        PrintStream sysOut = System.out;
+        PrintStream sysErr = System.err;
+        try {
+            sysOut.flush();
+            sysErr.flush();
+            outBuffer = new StringBuffer();
+            PrintStream out = new PrintStream(new AntOutputStream(outBuffer));
+            System.setOut(out);
+            errBuffer = new StringBuffer();
+            PrintStream err = new PrintStream(new AntOutputStream(errBuffer));
+            System.setErr(err);
+            logBuffer = new StringBuffer();
+            fullLogBuffer = new StringBuffer();
+            buildException = null;
+            project.executeTarget(targetName);
+        } finally {
+            System.setOut(sysOut);
+            System.setErr(sysErr);
+        }
+
+    }
+
+    /**
+     * Get the project which has been configured for a test.
+     *
+     * @return the Project instance for this test.
+     */
+    protected Project getProject() {
+        return project;
+    }
+
+    /**
+     * get the directory of the project
+     * @return the base dir of the project
+     */
+    protected File getProjectDir() {
+        return project.getBaseDir();
+    }
+
+    /**
+     *  run a target, wait for a build exception
+     *
+     *@param  target target to run
+     *@param  cause  information string to reader of report
+     *@param  msg    the message value of the build exception we are waiting for
+              set to null for any build exception to be valid
+     */
+    protected void expectSpecificBuildException(String target, String cause, String msg) {
+        try {
+            executeTarget(target);
+        } catch (org.apache.tools.ant.BuildException ex) {
+            buildException = ex;
+            if ((null != msg) && (!ex.getMessage().equals(msg))) {
+                fail("Should throw BuildException because '" + cause
+                        + "' with message '" + msg
+                        + "' (actual message '" + ex.getMessage() + "' instead)");
+            }
+            return;
+        }
+        fail("Should throw BuildException because: " + cause);
+    }
+
+    /**
+     *  run a target, expect an exception string
+     *  containing the substring we look for (case sensitive match)
+     *
+     *@param  target target to run
+     *@param  cause  information string to reader of report
+     *@param  contains  substring of the build exception to look for
+     */
+    protected void expectBuildExceptionContaining(String target, String cause, String contains) {
+        try {
+            executeTarget(target);
+        } catch (org.apache.tools.ant.BuildException ex) {
+            buildException = ex;
+            if ((null != contains) && (ex.getMessage().indexOf(contains) == -1)) {
+                fail("Should throw BuildException because '" + cause + "' with message containing '" + contains + "' (actual message '" + ex.getMessage() + "' instead)");
+            }
+            return;
+        }
+        fail("Should throw BuildException because: " + cause);
+    }
+
+
+    /**
+     * call a target, verify property is as expected
+     *
+     * @param target build file target
+     * @param property property name
+     * @param value expected value
+     */
+
+    protected void expectPropertySet(String target, String property, String value) {
+        executeTarget(target);
+        assertPropertyEquals(property, value);
+    }
+
+    /**
+     * assert that a property equals a value; comparison is case sensitive.
+     * @param property property name
+     * @param value expected value
+     */
+    protected void assertPropertyEquals(String property, String value) {
+        String result = project.getProperty(property);
+        assertEquals("property " + property,value,result);
+    }
+
+    /**
+     * assert that a property equals &quot;true&quot;
+     * @param property property name
+     */
+    protected void assertPropertySet(String property) {
+        assertPropertyEquals(property, "true");
+    }
+
+    /**
+     * assert that a property is null
+     * @param property property name
+     */
+    protected void assertPropertyUnset(String property) {
+        assertPropertyEquals(property, null);
+    }
+
+
+    /**
+     * call a target, verify named property is "true".
+     *
+     * @param target build file target
+     * @param property property name
+     */
+    protected void expectPropertySet(String target, String property) {
+        expectPropertySet(target, property, "true");
+    }
+
+
+    /**
+     * call a target, verify property is null
+     * @param target build file target
+     * @param property property name
+     */
+    protected void expectPropertyUnset(String target, String property) {
+        expectPropertySet(target, property, null);
+    }
+
+    /**
+     * Retrieve a resource from the caller classloader to avoid
+     * assuming a vm working directory. The resource path must be
+     * relative to the package name or absolute from the root path.
+     * @param resource the resource to retrieve its url.
+     * @throws AssertionFailureException if resource is not found.
+     */
+    protected URL getResource(String resource){
+        URL url = getClass().getResource(resource);
+        assertNotNull("Could not find resource :" + resource, url);
+        return url;
+    }
+
+    /**
+     * an output stream which saves stuff to our buffer.
+     */
+    private static class AntOutputStream extends java.io.OutputStream {
+        private StringBuffer buffer;
+
+        public AntOutputStream( StringBuffer buffer ) {
+            this.buffer = buffer;
+        }
+
+        public void write(int b) {
+            buffer.append((char)b);
+        }
+    }
+
+    /**
+     * our own personal build listener
+     */
+    private class AntTestListener implements BuildListener {
+        private int logLevel;
+
+        /**
+         * Constructs a test listener which will ignore log events
+         * above the given level
+         */
+        public AntTestListener(int logLevel) {
+            this.logLevel = logLevel;
+        }
+
+        /**
+         *  Fired before any targets are started.
+         */
+        public void buildStarted(BuildEvent event) {
+        }
+
+        /**
+         *  Fired after the last target has finished. This event
+         *  will still be thrown if an error occured during the build.
+         *
+         *  @see BuildEvent#getException()
+         */
+        public void buildFinished(BuildEvent event) {
+        }
+
+        /**
+         *  Fired when a target is started.
+         *
+         *  @see BuildEvent#getTarget()
+         */
+        public void targetStarted(BuildEvent event) {
+            //System.out.println("targetStarted " + event.getTarget().getName());
+        }
+
+        /**
+         *  Fired when a target has finished. This event will
+         *  still be thrown if an error occured during the build.
+         *
+         *  @see BuildEvent#getException()
+         */
+        public void targetFinished(BuildEvent event) {
+            //System.out.println("targetFinished " + event.getTarget().getName());
+        }
+
+        /**
+         *  Fired when a task is started.
+         *
+         *  @see BuildEvent#getTask()
+         */
+        public void taskStarted(BuildEvent event) {
+            //System.out.println("taskStarted " + event.getTask().getTaskName());
+        }
+
+        /**
+         *  Fired when a task has finished. This event will still
+         *  be throw if an error occured during the build.
+         *
+         *  @see BuildEvent#getException()
+         */
+        public void taskFinished(BuildEvent event) {
+            //System.out.println("taskFinished " + event.getTask().getTaskName());
+        }
+
+        /**
+         *  Fired whenever a message is logged.
+         *
+         *  @see BuildEvent#getMessage()
+         *  @see BuildEvent#getPriority()
+         */
+        public void messageLogged(BuildEvent event) {
+            if (event.getPriority() > logLevel) {
+                // ignore event
+                return;
+            }
+
+            if (event.getPriority() == Project.MSG_INFO ||
+                event.getPriority() == Project.MSG_WARN ||
+                event.getPriority() == Project.MSG_ERR) {
+                logBuffer.append(event.getMessage());
+            }
+            fullLogBuffer.append(event.getMessage());
+
+        }
+    }
+
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/CaseTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/CaseTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/CaseTest.java	(revision 16139)
@@ -0,0 +1,54 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ *
+ * Simple tests of build file processing
+ */
+public class CaseTest extends BuildFileTest {
+
+    public CaseTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/core/case.xml");
+    }
+
+    /**
+     * Test whether the build file treats nested elements without
+     * regard to case. This should not cause an exception.
+     */
+    public void testCaseSensitivity() {
+        executeTarget("case-sensitivity");
+    }
+
+    /**
+     * Test whether the build file uses case when determining
+     * task names.
+     */
+    public void testTaskCase() {
+        expectBuildExceptionContaining("taskcase",
+            "Task names are case sensitive",
+            "Could not create task or type of type: ecHO");
+    }
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DirectoryScannerTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DirectoryScannerTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DirectoryScannerTest.java	(revision 16139)
@@ -0,0 +1,455 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+import java.io.File;
+import java.io.IOException;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.Iterator;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.ant.DirectoryScanner
+ *
+ */
+public class DirectoryScannerTest extends BuildFileTest {
+
+    public DirectoryScannerTest(String name) {super(name);}
+
+    // keep track of what operating systems are supported here.
+    private boolean supportsSymlinks = Os.isFamily("unix");
+
+    public void setUp() {
+        configureProject("src/etc/testcases/core/directoryscanner.xml");
+        getProject().executeTarget("setup");
+    }
+
+    public void tearDown() {
+        getProject().executeTarget("cleanup");
+    }
+
+    public void test1() {
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha"});
+        ds.scan();
+        compareFiles(ds, new String[] {} ,new String[] {"alpha"});
+    }
+
+    public void test2() {
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/"});
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/beta.xml",
+                                       "alpha/beta/gamma/gamma.xml"},
+                     new String[] {"alpha", "alpha/beta", "alpha/beta/gamma"});
+    }
+
+    public void test3() {
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/beta.xml",
+                                       "alpha/beta/gamma/gamma.xml"},
+                     new String[] {"", "alpha", "alpha/beta",
+                                   "alpha/beta/gamma"});
+    }
+
+    public void testFullPathMatchesCaseSensitive() {
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/beta/gamma/GAMMA.XML"});
+        ds.scan();
+        compareFiles(ds, new String[] {}, new String[] {});
+    }
+
+    public void testFullPathMatchesCaseInsensitive() {
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setCaseSensitive(false);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/beta/gamma/GAMMA.XML"});
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/gamma/gamma.xml"},
+            new String[] {});
+    }
+
+    public void test2ButCaseInsensitive() {
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"ALPHA/"});
+        ds.setCaseSensitive(false);
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/beta.xml",
+                                       "alpha/beta/gamma/gamma.xml"},
+                     new String[] {"alpha", "alpha/beta", "alpha/beta/gamma"});
+    }
+
+    public void testAllowSymlinks() {
+        if (!supportsSymlinks) {
+            return;
+        }
+
+        getProject().executeTarget("symlink-setup");
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/beta/gamma/"});
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/gamma/gamma.xml"},
+                     new String[] {"alpha/beta/gamma"});
+    }
+
+    public void testProhibitSymlinks() {
+        if (!supportsSymlinks) {
+            return;
+        }
+
+        getProject().executeTarget("symlink-setup");
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/beta/gamma/"});
+        ds.setFollowSymlinks(false);
+        ds.scan();
+        compareFiles(ds, new String[] {}, new String[] {});
+    }
+
+    // father and child pattern test
+    public void testOrderOfIncludePatternsIrrelevant() {
+        String [] expectedFiles = {"alpha/beta/beta.xml",
+                                   "alpha/beta/gamma/gamma.xml"};
+        String [] expectedDirectories = {"alpha/beta", "alpha/beta/gamma" };
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/be?a/**", "alpha/beta/gamma/"});
+        ds.scan();
+        compareFiles(ds, expectedFiles, expectedDirectories);
+        // redo the test, but the 2 include patterns are inverted
+        ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/beta/gamma/", "alpha/be?a/**"});
+        ds.scan();
+        compareFiles(ds, expectedFiles, expectedDirectories);
+    }
+
+    public void testPatternsDifferInCaseScanningSensitive() {
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/", "ALPHA/"});
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/beta.xml",
+                                       "alpha/beta/gamma/gamma.xml"},
+                     new String[] {"alpha", "alpha/beta", "alpha/beta/gamma"});
+    }
+
+    public void testPatternsDifferInCaseScanningInsensitive() {
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/", "ALPHA/"});
+        ds.setCaseSensitive(false);
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/beta.xml",
+                                       "alpha/beta/gamma/gamma.xml"},
+                     new String[] {"alpha", "alpha/beta", "alpha/beta/gamma"});
+    }
+
+    public void testFullpathDiffersInCaseScanningSensitive() {
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {
+            "alpha/beta/gamma/gamma.xml",
+            "alpha/beta/gamma/GAMMA.XML"
+        });
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/gamma/gamma.xml"},
+                     new String[] {});
+    }
+
+    public void testFullpathDiffersInCaseScanningInsensitive() {
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {
+            "alpha/beta/gamma/gamma.xml",
+            "alpha/beta/gamma/GAMMA.XML"
+        });
+        ds.setCaseSensitive(false);
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/gamma/gamma.xml"},
+                     new String[] {});
+    }
+
+    public void testParentDiffersInCaseScanningSensitive() {
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/", "ALPHA/beta/"});
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/beta.xml",
+                                       "alpha/beta/gamma/gamma.xml"},
+                     new String[] {"alpha", "alpha/beta", "alpha/beta/gamma"});
+    }
+
+    public void testParentDiffersInCaseScanningInsensitive() {
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/", "ALPHA/beta/"});
+        ds.setCaseSensitive(false);
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/beta.xml",
+                                       "alpha/beta/gamma/gamma.xml"},
+                     new String[] {"alpha", "alpha/beta", "alpha/beta/gamma"});
+    }
+
+    /**
+     * Test case for setFollowLinks() and associated functionality.
+     * Only supports test on Linux at the moment because Java has
+     * no real notion of symlinks built in, so an os-specfic call
+     * to Runtime.exec() must be made to create a link to test against.
+     */
+    public void testSetFollowLinks() throws IOException {
+        if (supportsSymlinks) {
+            File linkFile = new File(System.getProperty("root"), "src/main/org/apache/tools/ThisIsALink");
+            System.err.println("link exists pre-test? " + linkFile.exists());
+            
+            try {
+                // add conditions and more commands as soon as the need arises
+                String[] command = new String[] {
+                    "ln", "-s", "ant", linkFile.getAbsolutePath()
+                };
+                try {
+                    Runtime.getRuntime().exec(command);
+                    // give ourselves some time for the system call
+                    // to execute... tweak if you have a really over
+                    // loaded system.
+                    Thread.sleep(1000);
+                } catch (IOException ioe) {
+                    fail("IOException making link "+ioe);
+                } catch (InterruptedException ie) {
+                }
+
+                File dir = new File(System.getProperty("root"), "src/main/org/apache/tools");
+                System.err.println("link exists after exec? " + linkFile.exists());
+                System.err.println("Ant knows it is a link? " + FileUtils.getFileUtils().isSymbolicLink(dir, "ThisIsALink"));
+
+                DirectoryScanner ds = new DirectoryScanner();
+
+                // followLinks should be true by default, but if this ever
+                // changes we will need this line.
+                ds.setFollowSymlinks(true);
+
+                ds.setBasedir(dir);
+                ds.setExcludes(new String[] {"ant/**"});
+                ds.scan();
+
+                boolean haveZipPackage = false;
+                boolean haveTaskdefsPackage = false;
+
+                String[] included = ds.getIncludedDirectories();
+                for (int i=0; i<included.length; i++) {
+                    if (included[i].equals("zip")) {
+                        haveZipPackage = true;
+                    } else if (included[i].equals("ThisIsALink"
+                                                  + File.separator
+                                                  + "taskdefs")) {
+                        haveTaskdefsPackage = true;
+                    }
+                }
+
+                // if we followed the symlink we just made we should
+                // bypass the excludes.
+
+                assertTrue("(1) zip package included", haveZipPackage);
+                assertTrue("(1) taskdefs package included",
+                           haveTaskdefsPackage);
+
+
+                ds = new DirectoryScanner();
+                ds.setFollowSymlinks(false);
+
+                ds.setBasedir(dir);
+                ds.setExcludes(new String[] {"ant/**"});
+                ds.scan();
+
+                haveZipPackage = false;
+                haveTaskdefsPackage = false;
+                included = ds.getIncludedDirectories();
+                for (int i=0; i<included.length; i++) {
+                    if (included[i].equals("zip")) {
+                        haveZipPackage = true;
+                    } else if (included[i].equals("ThisIsALink"
+                                                  + File.separator
+                                                  + "taskdefs")) {
+                        haveTaskdefsPackage = true;
+                    }
+                }
+                assertTrue("(2) zip package included", haveZipPackage);
+                assertTrue("(2) taskdefs package not included",
+                           !haveTaskdefsPackage);
+
+            } finally {
+                System.err.println("link exists pre-delete? " + linkFile.exists());
+                if (!linkFile.delete()) {
+                    throw new RuntimeException("Failed to delete " + linkFile);
+                }
+                System.err.println("link exists post-delete? " + linkFile.exists());
+            }
+        }
+    }
+
+    public void testExcludeOneFile() {
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {
+            "**/*.xml"
+        });
+        ds.setExcludes(new String[] {
+            "alpha/beta/b*xml"
+        });
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/gamma/gamma.xml"},
+                     new String[] {});
+    }
+
+    public void testExcludeHasPrecedence() {
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {
+            "alpha/**"
+        });
+        ds.setExcludes(new String[] {
+            "alpha/**"
+        });
+        ds.scan();
+        compareFiles(ds, new String[] {},
+                     new String[] {});
+
+    }
+
+    public void testAlternateIncludeExclude() {
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {
+            "alpha/**",
+            "alpha/beta/gamma/**"
+        });
+        ds.setExcludes(new String[] {
+            "alpha/beta/**"
+        });
+        ds.scan();
+        compareFiles(ds, new String[] {},
+                     new String[] {"alpha"});
+
+    }
+
+    public void testAlternateExcludeInclude() {
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setExcludes(new String[] {
+            "alpha/**",
+            "alpha/beta/gamma/**"
+        });
+        ds.setIncludes(new String[] {
+            "alpha/beta/**"
+        });
+        ds.scan();
+        compareFiles(ds, new String[] {},
+                     new String[] {});
+
+    }
+
+    /**
+     * Test inspired by Bug#1415.
+     */
+    public void testChildrenOfExcludedDirectory() {
+        getProject().executeTarget("children-of-excluded-dir-setup");
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setExcludes(new String[] {"alpha/**"});
+        ds.setFollowSymlinks(false);
+        ds.scan();
+        compareFiles(ds, new String[] {"delta/delta.xml"},
+                    new String[] {"", "delta"});
+
+        ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setExcludes(new String[] {"alpha"});
+        ds.setFollowSymlinks(false);
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/beta.xml",
+                                       "alpha/beta/gamma/gamma.xml",
+                                        "delta/delta.xml"},
+                     new String[] {"", "alpha/beta", "alpha/beta/gamma", "delta"});
+
+    }
+
+    public void testIsExcludedDirectoryScanned() {
+        getProject().executeTarget("children-of-excluded-dir-setup");
+        DirectoryScanner ds = new DirectoryScanner();
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setExcludes(new String[] {"**/gamma/**"});
+        ds.setFollowSymlinks(false);
+        ds.scan();
+        Set set = ds.getScannedDirs();
+        assertFalse("empty set", set.isEmpty());
+        String s = "alpha/beta/gamma/".replace('/', File.separatorChar);
+        assertFalse("scanned " + s, set.contains(s));
+    }
+
+    private void compareFiles(DirectoryScanner ds, String[] expectedFiles,
+                              String[] expectedDirectories) {
+        String includedFiles[] = ds.getIncludedFiles();
+        String includedDirectories[] = ds.getIncludedDirectories();
+        assertEquals("file present: ", expectedFiles.length,
+                     includedFiles.length);
+        assertEquals("directories present: ", expectedDirectories.length,
+                     includedDirectories.length);
+
+        TreeSet files = new TreeSet();
+        for (int counter=0; counter < includedFiles.length; counter++) {
+            files.add(includedFiles[counter].replace(File.separatorChar, '/'));
+        }
+        TreeSet directories = new TreeSet();
+        for (int counter=0; counter < includedDirectories.length; counter++) {
+            directories.add(includedDirectories[counter]
+                            .replace(File.separatorChar, '/'));
+        }
+
+        String currentfile;
+        Iterator i = files.iterator();
+        int counter = 0;
+        while (i.hasNext()) {
+            currentfile = (String) i.next();
+            assertEquals(expectedFiles[counter], currentfile);
+            counter++;
+        }
+        String currentdirectory;
+        Iterator dirit = directories.iterator();
+        counter = 0;
+        while (dirit.hasNext()) {
+            currentdirectory = (String) dirit.next();
+            assertEquals(expectedDirectories[counter], currentdirectory);
+            counter++;
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskAbstract.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskAbstract.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskAbstract.java	(revision 16139)
@@ -0,0 +1,31 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import org.apache.tools.ant.Task;
+
+public abstract class DummyTaskAbstract extends Task {
+
+    public DummyTaskAbstract() {
+    }
+
+    public void execute() {
+    }
+
+    public abstract void abstractDummy();
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskInterface.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskInterface.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskInterface.java	(revision 16139)
@@ -0,0 +1,24 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+public interface DummyTaskInterface {
+
+    void execute();
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskOk.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskOk.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskOk.java	(revision 16139)
@@ -0,0 +1,30 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import org.apache.tools.ant.Task;
+
+public class DummyTaskOk extends Task {
+
+    public DummyTaskOk() {
+    }
+
+    public void execute() {
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskOkNonTask.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskOkNonTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskOkNonTask.java	(revision 16139)
@@ -0,0 +1,28 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+public class DummyTaskOkNonTask {
+
+    public DummyTaskOkNonTask() {
+    }
+
+    public void execute() {
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskWithNonPublicExecute.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskWithNonPublicExecute.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskWithNonPublicExecute.java	(revision 16139)
@@ -0,0 +1,28 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+public class DummyTaskWithNonPublicExecute {
+
+    public DummyTaskWithNonPublicExecute() {
+    }
+
+    void execute() {
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskWithNonVoidExecute.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskWithNonVoidExecute.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskWithNonVoidExecute.java	(revision 16139)
@@ -0,0 +1,29 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+public class DummyTaskWithNonVoidExecute {
+
+    public DummyTaskWithNonVoidExecute() {
+    }
+
+    public int execute() {
+        return 0;
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskWithoutDefaultConstructor.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskWithoutDefaultConstructor.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskWithoutDefaultConstructor.java	(revision 16139)
@@ -0,0 +1,30 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import org.apache.tools.ant.Task;
+
+public class DummyTaskWithoutDefaultConstructor extends Task {
+
+    public DummyTaskWithoutDefaultConstructor(int dummy) {
+    }
+
+    public void execute() {
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskWithoutExecute.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskWithoutExecute.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskWithoutExecute.java	(revision 16139)
@@ -0,0 +1,28 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+public class DummyTaskWithoutExecute {
+
+    public DummyTaskWithoutExecute() {
+    }
+
+    public void execute(String dummy) {
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskWithoutPublicConstructor.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskWithoutPublicConstructor.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/DummyTaskWithoutPublicConstructor.java	(revision 16139)
@@ -0,0 +1,30 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import org.apache.tools.ant.Task;
+
+public class DummyTaskWithoutPublicConstructor extends Task {
+
+    DummyTaskWithoutPublicConstructor() {
+    }
+
+    public void execute() {
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/ExecutorTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/ExecutorTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/ExecutorTest.java	(revision 16139)
@@ -0,0 +1,129 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.util.Vector;
+
+import org.apache.tools.ant.taskdefs.Exit;
+
+/**
+ * Executor tests
+ */
+public class ExecutorTest extends BuildFileTest implements BuildListener {
+    private static final String SINGLE_CHECK
+        = "org.apache.tools.ant.helper.SingleCheckExecutor";
+    private static final Vector targetNames;
+    static {
+        targetNames = new Vector();
+        targetNames.add("a");
+        targetNames.add("b");
+    }
+
+    private int targetCount;
+
+    /* BuildListener stuff */
+    public void targetStarted(BuildEvent event) {
+        targetCount++;
+    }
+    public void buildStarted(BuildEvent event) {}
+    public void buildFinished(BuildEvent event) {}
+    public void targetFinished(BuildEvent event) {}
+    public void taskStarted(BuildEvent event) {}
+    public void taskFinished(BuildEvent event) {}
+    public void messageLogged(BuildEvent event) {}
+
+    public ExecutorTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/core/executor.xml");
+        targetCount = 0;
+        getProject().addBuildListener(this);
+    }
+
+    private Project getProject(String e) {
+        return getProject(e, false);
+    }
+
+    private Project getProject(String e, boolean f) {
+        return getProject(e, f, false);
+    }
+
+    private Project getProject(String e, boolean f, boolean k) {
+        Project p = getProject();
+        p.setNewProperty("ant.executor.class", e);
+        p.setKeepGoingMode(k);
+        if (f) {
+            p.setNewProperty("failfoo", "foo");
+        }
+        return p;
+    }
+
+    public void testDefaultExecutor() {
+        getProject().executeTargets(targetNames);
+        assertEquals(targetCount, 4);
+    }
+
+    public void testSingleCheckExecutor() {
+        getProject(SINGLE_CHECK).executeTargets(targetNames);
+        assertEquals(targetCount, 3);
+    }
+
+    public void testDefaultFailure() {
+        try {
+            getProject(null, true).executeTargets(targetNames);
+            fail("should fail");
+        } catch (BuildException e) {
+            assertTrue(e.getMessage().equals("failfoo"));
+            assertEquals(targetCount, 1);
+        }
+    }
+
+    public void testSingleCheckFailure() {
+        try {
+            getProject(SINGLE_CHECK, true).executeTargets(targetNames);
+            fail("should fail");
+        } catch (BuildException e) {
+            assertTrue(e.getMessage().equals("failfoo"));
+            assertEquals(targetCount, 1);
+        }
+    }
+
+    public void testKeepGoingDefault() {
+        try {
+            getProject(null, true, true).executeTargets(targetNames);
+            fail("should fail");
+        } catch (BuildException e) {
+            assertTrue(e.getMessage().equals("failfoo"));
+            assertEquals(targetCount, 2);
+        }
+    }
+
+    public void testKeepGoingSingleCheck() {
+        try {
+            getProject(SINGLE_CHECK, true, true).executeTargets(targetNames);
+            fail("should fail");
+        } catch (BuildException e) {
+            assertTrue(e.getMessage().equals("failfoo"));
+            assertEquals(targetCount, 1);
+        }
+    }
+
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/ImmutableTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/ImmutableTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/ImmutableTest.java	(revision 16139)
@@ -0,0 +1,77 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class ImmutableTest extends BuildFileTest {
+
+    public ImmutableTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/core/immutable.xml");
+    }
+
+    // override allowed on <available>
+    public void test1() {
+        executeTarget("test1");
+        assertEquals("override", project.getProperty("test"));
+    }
+
+    // ensure <tstamp>'s new prefix attribute is working
+    public void test2() {
+        executeTarget("test2");
+        assertNotNull(project.getProperty("DSTAMP"));
+        assertNotNull(project.getProperty("start.DSTAMP"));
+    }
+
+    // ensure <tstamp> follows the immutability rule
+    public void test3() {
+        executeTarget("test3");
+        assertEquals("original", project.getProperty("DSTAMP"));
+    }
+
+    // ensure <condition> follows the immutability rule
+    public void test4() {
+        executeTarget("test4");
+        assertEquals("original", project.getProperty("test"));
+    }
+    // ensure <checksum> follows the immutability rule
+    public void test5() {
+        executeTarget("test5");
+        assertEquals("original", project.getProperty("test"));
+    }
+
+    // ensure <exec> follows the immutability rule
+    public void test6() {
+        executeTarget("test6");
+        assertEquals("original", project.getProperty("test1"));
+        assertEquals("original", project.getProperty("test2"));
+    }
+
+    // ensure <pathconvert> follows the immutability rule
+    public void test7() {
+        executeTarget("test7");
+        assertEquals("original", project.getProperty("test"));
+    }
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/IncludeTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/IncludeTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/IncludeTest.java	(revision 16139)
@@ -0,0 +1,125 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * Test the build file inclusion using XML entities.
+ *
+ */
+public class IncludeTest extends BuildFileTest {
+
+    public IncludeTest(String name) {
+        super(name);
+    }
+
+    public void test1() {
+        configureProject("src/etc/testcases/core/include/basic/include.xml");
+        expectLog("test1", "from included entity");
+    }
+
+    public void test2() {
+        configureProject("src/etc/testcases/core/include/frag#ment/include.xml");
+        expectLog("test1", "from included entity");
+    }
+
+    public void test3() {
+        configureProject("src/etc/testcases/core/include/frag#ment/simple.xml");
+        expectLog("test1", "from simple buildfile");
+    }
+
+    public void test4() {
+        configureProject("src/etc/testcases/core/include/basic/relative.xml");
+        expectLog("test1", "from included entity");
+    }
+
+    public void test5() {
+        configureProject("src/etc/testcases/core/include/frag#ment/relative.xml");
+        expectLog("test1", "from included entity");
+    }
+
+    public void testParseErrorInIncluding() {
+        try {
+            configureProject("src/etc/testcases/core/include/including_file_parse_error/build.xml");
+            fail("should have caused a parser exception");
+        } catch (BuildException e) {
+            assertTrue(e.getLocation().toString()
+                       + " should refer to build.xml",
+                       e.getLocation().toString().indexOf("build.xml:") > -1);
+        }
+    }
+
+    public void testTaskErrorInIncluding() {
+        configureProject("src/etc/testcases/core/include/including_file_task_error/build.xml");
+        try {
+            executeTarget("test");
+            fail("should have cause a build failure");
+        } catch (BuildException e) {
+            assertTrue(e.getMessage()
+                       + " should start with \'Warning: Could not find",
+                         e.getMessage().startsWith("Warning: Could not find file "));
+            assertTrue(e.getLocation().toString()
+                       + " should end with build.xml:14: ",
+                       e.getLocation().toString().endsWith("build.xml:14: "));
+        }
+    }
+
+    public void testParseErrorInIncluded() {
+        try {
+            configureProject("src/etc/testcases/core/include/included_file_parse_error/build.xml");
+            fail("should have caused a parser exception");
+        } catch (BuildException e) {
+            assertTrue(e.getLocation().toString()
+                       + " should refer to included_file.xml",
+                       e.getLocation().toString()
+                       .indexOf("included_file.xml:") > -1);
+        }
+    }
+
+    public void testTaskErrorInIncluded() {
+        configureProject("src/etc/testcases/core/include/included_file_task_error/build.xml");
+        try {
+            executeTarget("test");
+            fail("should have cause a build failure");
+        } catch (BuildException e) {
+            assertTrue(e.getMessage()
+                       + " should start with \'Warning: Could not find",
+                         e.getMessage().startsWith("Warning: Could not find file "));
+            assertTrue(e.getLocation().toString()
+                       + " should end with included_file.xml:2: ",
+                       e.getLocation().toString().endsWith("included_file.xml:2: "));
+        }
+    }
+
+    public void testWithSpaceInclude() {
+        configureProject("src/etc/testcases/core/include/with space/include.xml");
+        expectLog("test1", "from included entity in 'with space'");
+    }
+
+    public void testWithSpaceSimple() {
+        configureProject("src/etc/testcases/core/include/with space/simple.xml");
+        expectLog("test1", "from simple buildfile in 'with space'");
+    }
+
+    public void testWithSpaceRelative() {
+        configureProject("src/etc/testcases/core/include/with space/relative.xml");
+        expectLog("test1", "from included entity in 'with space'");
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/IntrospectionHelperTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/IntrospectionHelperTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/IntrospectionHelperTest.java	(revision 16139)
@@ -0,0 +1,683 @@
+/*
+ * Copyright  2000-2001,2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+import java.io.File;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import org.apache.tools.ant.taskdefs.condition.Os;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.ant.IntrospectionHelper.
+ *
+ */
+
+public class IntrospectionHelperTest extends TestCase {
+
+    private Project p;
+    private IntrospectionHelper ih;
+    private static final String projectBasedir = File.separator;
+
+    public IntrospectionHelperTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        p = new Project();
+        p.setBasedir(projectBasedir);
+        ih = IntrospectionHelper.getHelper(getClass());
+    }
+
+    public void testIsDynamic() {
+        assertTrue("Not dynamic", false == ih.isDynamic());
+    }
+
+    public void testIsContainer() {
+        assertTrue("Not a container", false == ih.isContainer());
+    }
+
+    public void testAddText() throws BuildException {
+        ih.addText(p, this, "test");
+        try {
+            ih.addText(p, this, "test2");
+            fail("test2 shouldn\'t be equal to test");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof AssertionFailedError);
+        }
+
+        ih = IntrospectionHelper.getHelper(String.class);
+        try {
+            ih.addText(p, "", "test");
+            fail("String doesn\'t support addText");
+        } catch (BuildException be) {
+        }
+    }
+
+    public void testGetAddTextMethod() {
+        Method m = ih.getAddTextMethod();
+        assertMethod(m, "addText", String.class, "test", "bing!");
+
+        ih = IntrospectionHelper.getHelper(String.class);
+        try {
+            m = ih.getAddTextMethod();
+        } catch (BuildException e) {}
+    }
+
+    public void testSupportsCharacters() {
+        assertTrue("IntrospectionHelperTest supports addText",
+                   ih.supportsCharacters());
+
+        ih = IntrospectionHelper.getHelper(String.class);
+        assertTrue("String doesn\'t support addText", !ih.supportsCharacters());
+    }
+
+    public void addText(String text) {
+        assertEquals("test", text);
+    }
+
+    public void testElementCreators() throws BuildException {
+        try {
+            ih.getElementType("one");
+            fail("don't have element type one");
+        } catch (BuildException be) {
+        }
+        try {
+            ih.getElementType("two");
+            fail("createTwo takes arguments");
+        } catch (BuildException be) {
+        }
+        try {
+            ih.getElementType("three");
+            fail("createThree returns void");
+        } catch (BuildException be) {
+        }
+        try {
+            ih.getElementType("four");
+            fail("createFour returns array");
+        } catch (BuildException be) {
+        }
+        try {
+            ih.getElementType("five");
+            fail("createFive returns primitive type");
+        } catch (BuildException be) {
+        }
+        assertEquals(String.class, ih.getElementType("six"));
+        assertEquals("test", ih.createElement(p, this, "six"));
+
+        try {
+            ih.getElementType("seven");
+            fail("addSeven takes two arguments");
+        } catch (BuildException be) {
+        }
+        try {
+            ih.getElementType("eight");
+            fail("addEight takes no arguments");
+        } catch (BuildException be) {
+        }
+        try {
+            ih.getElementType("nine");
+            fail("nine return non void");
+        } catch (BuildException be) {
+        }
+        try {
+            ih.getElementType("ten");
+            fail("addTen takes array argument");
+        } catch (BuildException be) {
+        }
+        try {
+            ih.getElementType("eleven");
+            fail("addEleven takes primitive argument");
+        } catch (BuildException be) {
+        }
+        try {
+            ih.getElementType("twelve");
+            fail("no primitive constructor for java.lang.Class");
+        } catch (BuildException be) {
+        }
+        assertEquals(StringBuffer.class, ih.getElementType("thirteen"));
+        assertEquals("test", ih.createElement(p, this, "thirteen").toString());
+
+        try {
+            ih.createElement(p, this, "fourteen");
+            fail("fourteen throws NullPointerException");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof NullPointerException);
+        }
+
+        try {
+            ih.createElement(p, this, "fourteen");
+            fail("fifteen throws NullPointerException");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof NullPointerException);
+        }
+    }
+
+    private Map getExpectedNestedElements() {
+        Map elemMap = new Hashtable();
+        elemMap.put("six", String.class);
+        elemMap.put("thirteen", StringBuffer.class);
+        elemMap.put("fourteen", StringBuffer.class);
+        elemMap.put("fifteen", StringBuffer.class);
+        return elemMap;
+    }
+
+    public void testGetNestedElements() {
+        Map elemMap = getExpectedNestedElements();
+        Enumeration e = ih.getNestedElements();
+        while (e.hasMoreElements()) {
+            String name = (String) e.nextElement();
+            Class expect = (Class) elemMap.get(name);
+            assertNotNull("Support for "+name+" in IntrospectioNHelperTest?",
+                          expect);
+            assertEquals("Return type of "+name, expect, ih.getElementType(name));
+            elemMap.remove(name);
+        }
+        assertTrue("Found all", elemMap.isEmpty());
+    }
+
+    public void testGetNestedElementMap() {
+        Map elemMap = getExpectedNestedElements();
+        Map actualMap = ih.getNestedElementMap();
+        for (Iterator i = actualMap.entrySet().iterator(); i.hasNext();) {
+            Map.Entry entry = (Map.Entry) i.next();
+            String elemName = (String) entry.getKey();
+            Class elemClass = (Class) elemMap.get(elemName);
+            assertNotNull("Support for " + elemName +
+                          " in IntrospectionHelperTest?", elemClass);
+            assertEquals("Type of " + elemName, elemClass, entry.getValue());
+            elemMap.remove(elemName);
+        }
+        assertTrue("Found all", elemMap.isEmpty());
+
+        // Check it's a read-only map.
+        try {
+            actualMap.clear();
+        } catch (UnsupportedOperationException e) {}
+    }
+
+    public void testGetElementMethod() {
+        assertElemMethod("six", "createSix", String.class, null);
+        assertElemMethod("thirteen", "addThirteen", null, StringBuffer.class);
+        assertElemMethod("fourteen", "addFourteen", null, StringBuffer.class);
+        assertElemMethod("fifteen", "createFifteen", StringBuffer.class, null);
+    }
+
+    private void assertElemMethod(String elemName, String methodName,
+                                  Class returnType, Class methodArg) {
+        Method m = ih.getElementMethod(elemName);
+        assertEquals("Method name", methodName, m.getName());
+        Class expectedReturnType = (returnType == null)? Void.TYPE: returnType;
+        assertEquals("Return type", expectedReturnType, m.getReturnType());
+        Class[] args = m.getParameterTypes();
+        if (methodArg != null) {
+            assertEquals("Arg Count", 1, args.length);
+            assertEquals("Arg Type", methodArg, args[0]);
+        } else {
+            assertEquals("Arg Count", 0, args.length);
+        }
+    }
+
+    public Object createTwo(String s) {
+        return null;
+    }
+
+    public void createThree() {}
+
+    public Object[] createFour() {
+        return null;
+    }
+
+    public int createFive() {
+        return 0;
+    }
+
+    public String createSix() {
+        return "test";
+    }
+
+    public StringBuffer createFifteen() {
+        throw new NullPointerException();
+    }
+
+    public void addSeven(String s, String s2) {}
+
+    public void addEight() {}
+
+    public String addNine(String s) {
+        return null;
+    }
+
+    public void addTen(String[] s) {}
+
+    public void addEleven(int i) {}
+
+    public void addTwelve(Class c) {}
+
+    public void addThirteen(StringBuffer sb) {
+        sb.append("test");
+    }
+
+    public void addFourteen(StringBuffer s) {
+        throw new NullPointerException();
+    }
+
+    public void testAttributeSetters() throws BuildException {
+        try {
+            ih.setAttribute(p, this, "one", "test");
+            fail("setOne doesn't exist");
+        } catch (BuildException be) {
+        }
+        try {
+            ih.setAttribute(p, this, "two", "test");
+            fail("setTwo returns non void");
+        } catch (BuildException be) {
+        }
+        try {
+            ih.setAttribute(p, this, "three", "test");
+            fail("setThree takes no args");
+        } catch (BuildException be) {
+        }
+        try {
+            ih.setAttribute(p, this, "four", "test");
+            fail("setFour takes two args");
+        } catch (BuildException be) {
+        }
+        try {
+            ih.setAttribute(p, this, "five", "test");
+            fail("setFive takes array arg");
+        } catch (BuildException be) {
+        }
+        try {
+            ih.setAttribute(p, this, "six", "test");
+            fail("Project doesn't have a String constructor");
+        } catch (BuildException be) {
+        }
+        ih.setAttribute(p, this, "seven", "2");
+        try {
+            ih.setAttribute(p, this, "seven", "3");
+            fail("2 shouldn't be equals to three");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof AssertionFailedError);
+        }
+        ih.setAttribute(p, this, "eight", "2");
+        try {
+            ih.setAttribute(p, this, "eight", "3");
+            fail("2 shouldn't be equals to three - as int");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof AssertionFailedError);
+        }
+        ih.setAttribute(p, this, "nine", "2");
+        try {
+            ih.setAttribute(p, this, "nine", "3");
+            fail("2 shouldn't be equals to three - as Integer");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof AssertionFailedError);
+        }
+        ih.setAttribute(p, this, "ten", "2");
+        try {
+            ih.setAttribute(p, this, "ten", "3");
+            fail(projectBasedir+"2 shouldn't be equals to "+projectBasedir+"3");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof AssertionFailedError);
+        }
+        ih.setAttribute(p, this, "eleven", "2");
+        try {
+            ih.setAttribute(p, this, "eleven", "on");
+            fail("on shouldn't be false");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof AssertionFailedError);
+        }
+        ih.setAttribute(p, this, "twelve", "2");
+        try {
+            ih.setAttribute(p, this, "twelve", "on");
+            fail("on shouldn't be false");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof AssertionFailedError);
+        }
+        ih.setAttribute(p, this, "thirteen", "org.apache.tools.ant.Project");
+        try {
+            ih.setAttribute(p, this, "thirteen", "org.apache.tools.ant.ProjectHelper");
+            fail("org.apache.tools.ant.Project shouldn't be equal to org.apache.tools.ant.ProjectHelper");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof AssertionFailedError);
+        }
+        try {
+            ih.setAttribute(p, this, "thirteen", "org.apache.tools.ant.Project2");
+            fail("org.apache.tools.ant.Project2 doesn't exist");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof ClassNotFoundException);
+        }
+        ih.setAttribute(p, this, "fourteen", "2");
+        try {
+            ih.setAttribute(p, this, "fourteen", "on");
+            fail("2 shouldn't be equals to three - as StringBuffer");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof AssertionFailedError);
+        }
+        ih.setAttribute(p, this, "fifteen", "abcd");
+        try {
+            ih.setAttribute(p, this, "fifteen", "on");
+            fail("o shouldn't be equal to a");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof AssertionFailedError);
+        }
+        ih.setAttribute(p, this, "sixteen", "abcd");
+        try {
+            ih.setAttribute(p, this, "sixteen", "on");
+            fail("o shouldn't be equal to a");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof AssertionFailedError);
+        }
+        ih.setAttribute(p, this, "seventeen", "17");
+        try {
+            ih.setAttribute(p, this, "seventeen", "3");
+            fail("17 shouldn't be equals to three");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof AssertionFailedError);
+        }
+        ih.setAttribute(p, this, "eightteen", "18");
+        try {
+            ih.setAttribute(p, this, "eightteen", "3");
+            fail("18 shouldn't be equals to three");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof AssertionFailedError);
+        }
+        ih.setAttribute(p, this, "nineteen", "19");
+        try {
+            ih.setAttribute(p, this, "nineteen", "3");
+            fail("19 shouldn't be equals to three");
+        } catch (BuildException be) {
+            assertTrue(be.getException() instanceof AssertionFailedError);
+        }
+    }
+
+    private Map getExpectedAttributes() {
+        Map attrMap = new Hashtable();
+        attrMap.put("seven", String.class);
+        attrMap.put("eight", Integer.TYPE);
+        attrMap.put("nine", Integer.class);
+        attrMap.put("ten", File.class);
+        attrMap.put("eleven", Boolean.TYPE);
+        attrMap.put("twelve", Boolean.class);
+        attrMap.put("thirteen", Class.class);
+        attrMap.put("fourteen", StringBuffer.class);
+        attrMap.put("fifteen", Character.TYPE);
+        attrMap.put("sixteen", Character.class);
+        attrMap.put("seventeen", Byte.TYPE);
+        attrMap.put("eightteen", Short.TYPE);
+        attrMap.put("nineteen", Double.TYPE);
+
+        /*
+         * JUnit 3.7 adds a getName method to TestCase - so we now
+         * have a name attribute in IntrospectionHelperTest if we run
+         * under JUnit 3.7 but not in earlier versions.
+         *
+         * Simply add it here and remove it after the tests.
+         */
+        attrMap.put("name", String.class);
+
+        return attrMap;
+    }
+
+    public void testGetAttributes() {
+        Map attrMap = getExpectedAttributes();
+        Enumeration e = ih.getAttributes();
+        while (e.hasMoreElements()) {
+            String name = (String) e.nextElement();
+            Class expect = (Class) attrMap.get(name);
+            assertNotNull("Support for "+name+" in IntrospectionHelperTest?",
+                          expect);
+            assertEquals("Type of "+name, expect, ih.getAttributeType(name));
+            attrMap.remove(name);
+        }
+        attrMap.remove("name");
+        assertTrue("Found all", attrMap.isEmpty());
+    }
+
+    public void testGetAttributeMap() {
+        Map attrMap = getExpectedAttributes();
+        Map actualMap = ih.getAttributeMap();
+        for (Iterator i = actualMap.entrySet().iterator(); i.hasNext();) {
+            Map.Entry entry = (Map.Entry) i.next();
+            String attrName = (String) entry.getKey();
+            Class attrClass = (Class) attrMap.get(attrName);
+            assertNotNull("Support for " + attrName +
+                          " in IntrospectionHelperTest?", attrClass);
+            assertEquals("Type of " + attrName, attrClass, entry.getValue());
+            attrMap.remove(attrName);
+        }
+        attrMap.remove("name");
+        assertTrue("Found all", attrMap.isEmpty());
+
+        // Check it's a read-only map.
+        try {
+            actualMap.clear();
+        } catch (UnsupportedOperationException e) {}
+    }
+
+    public void testGetAttributeMethod() {
+        assertAttrMethod("seven", "setSeven", String.class,
+                         "2", "3");
+        assertAttrMethod("eight", "setEight", Integer.TYPE,
+                         new Integer(2), new Integer(3));
+        assertAttrMethod("nine", "setNine", Integer.class,
+                         new Integer(2), new Integer(3));
+        assertAttrMethod("ten", "setTen", File.class,
+                         new File(projectBasedir + 2), new File("toto"));
+        assertAttrMethod("eleven", "setEleven", Boolean.TYPE,
+                         Boolean.FALSE, Boolean.TRUE);
+        assertAttrMethod("twelve", "setTwelve", Boolean.class,
+                         Boolean.FALSE, Boolean.TRUE);
+        assertAttrMethod("thirteen", "setThirteen", Class.class,
+                         Project.class, Map.class);
+        assertAttrMethod("fourteen", "setFourteen", StringBuffer.class,
+                         new StringBuffer("2"), new StringBuffer("3"));
+        assertAttrMethod("fifteen", "setFifteen", Character.TYPE,
+                         new Character('a'), new Character('b'));
+        assertAttrMethod("sixteen", "setSixteen", Character.class,
+                         new Character('a'), new Character('b'));
+        assertAttrMethod("seventeen", "setSeventeen", Byte.TYPE,
+                         new Byte((byte)17), new Byte((byte)10));
+        assertAttrMethod("eightteen", "setEightteen", Short.TYPE,
+                         new Short((short)18), new Short((short)10));
+        assertAttrMethod("nineteen", "setNineteen", Double.TYPE,
+                         new Double(19), new Double((short)10));
+
+        try {
+            assertAttrMethod("onehundred", null, null, null, null);
+            fail("Should have raised a BuildException!");
+        } catch (BuildException e) {}
+    }
+
+    private void assertAttrMethod(String attrName, String methodName,
+                                  Class methodArg, Object arg, Object badArg) {
+        Method m = ih.getAttributeMethod(attrName);
+        assertMethod(m, methodName, methodArg, arg, badArg);
+    }
+
+    public int setTwo(String s) {
+        return 0;
+    }
+
+    public void setThree() {}
+
+    public void setFour(String s1, String s2) {}
+
+    public void setFive(String[] s) {}
+
+    public void setSix(Project p) {}
+
+    public void setSeven(String s) {
+        assertEquals("2", s);
+    }
+
+    public void setEight(int i) {
+        assertEquals(2, i);
+    }
+
+    public void setNine(Integer i) {
+        assertEquals(2, i.intValue());
+    }
+
+    public void setTen(File f) {
+        String path = f.getAbsolutePath();
+        if (Os.isFamily("unix") || Os.isFamily("openvms")) {
+            assertEquals(projectBasedir+"2", path);
+        } else if (Os.isFamily("netware")) {
+            assertEquals(projectBasedir+"2", path.toLowerCase(Locale.US));
+        } else {
+            assertEquals(":"+projectBasedir+"2",
+                         path.toLowerCase(Locale.US).substring(1));
+        }
+    }
+
+    public void setEleven(boolean b) {
+        assertTrue(!b);
+    }
+
+    public void setTwelve(Boolean b) {
+        assertTrue(!b.booleanValue());
+    }
+
+    public void setThirteen(Class c) {
+        assertEquals(Project.class, c);
+    }
+
+    public void setFourteen(StringBuffer sb) {
+        assertEquals("2", sb.toString());
+    }
+
+    public void setFifteen(char c) {
+        assertEquals(c, 'a');
+    }
+
+    public void setSixteen(Character c) {
+        assertEquals(c.charValue(), 'a');
+    }
+
+    public void setSeventeen(byte b) {
+        assertEquals(17, b);
+    }
+
+    public void setEightteen(short s) {
+        assertEquals(18, s);
+    }
+
+    public void setNineteen(double d) {
+        assertEquals(19, d, 1e-6);
+    }
+
+    public void testGetExtensionPoints() {
+        List extensions = ih.getExtensionPoints();
+        final int adders = 2;
+        assertEquals("extension count", adders, extensions.size());
+
+        // this original test assumed something about the order of
+        // add(Number) and addConfigured(Map) returned by reflection.
+        // Unfortunately the assumption doesn't hold for all VMs
+        // (failed on MacOS X using JDK 1.4.2_05) and the possible
+        // combinatorics are too hard to check.  We really only want
+        // to ensure that the more derived Hashtable can be found
+        // before Map.
+//        assertExtMethod(extensions.get(0), "add", Number.class,
+//                        new Integer(2), new Integer(3));
+
+        // addConfigured(Hashtable) should come before addConfigured(Map)
+        assertExtMethod(extensions.get(adders - 2),
+                        "addConfigured", Hashtable.class,
+                        makeTable("key", "value"), makeTable("1", "2"));
+
+        assertExtMethod(extensions.get(adders - 1), "addConfigured", Map.class,
+                        new HashMap(), makeTable("1", "2"));
+    }
+
+    private void assertExtMethod(Object mo, String methodName, Class methodArg,
+                                 Object arg, Object badArg) {
+        assertMethod((Method) mo, methodName, methodArg, arg, badArg);
+    }
+
+    private void assertMethod(Method m, String methodName, Class methodArg,
+                              Object arg, Object badArg) {
+        assertEquals("Method name", methodName, m.getName());
+        assertEquals("Return type", Void.TYPE, m.getReturnType());
+        Class[] args = m.getParameterTypes();
+        assertEquals("Arg Count", 1, args.length);
+        assertEquals("Arg Type", methodArg, args[0]);
+
+        try {
+            m.invoke(this, new Object[] { arg });
+        } catch (IllegalAccessException e) {
+            throw new BuildException(e);
+        } catch (InvocationTargetException e) {
+            throw new BuildException(e);
+        }
+
+        try {
+            m.invoke(this, new Object[] { badArg });
+            fail("Should have raised an assertion exception");
+        } catch (IllegalAccessException e) {
+            throw new BuildException(e);
+        } catch (InvocationTargetException e) {
+            Throwable t = e.getTargetException();
+            assertTrue(t instanceof junit.framework.AssertionFailedError);
+        }
+    }
+
+    public List add(List l) {
+        // INVALID extension point
+        return null;
+    }
+
+    // see comments in testGetExtensionPoints
+//    public void add(Number n) {
+//        // Valid extension point
+//        assertEquals(2, n.intValue());
+//    }
+
+    public void add(List l, int i) {
+        // INVALID extension point
+    }
+
+    public void addConfigured(Map m) {
+        // Valid extension point
+        assertTrue(m.size() == 0);
+    }
+
+    public void addConfigured(Hashtable h) {
+        // Valid extension point, more derived than Map above, but *after* it!
+        assertEquals(makeTable("key", "value"), h);
+    }
+
+    private Hashtable makeTable(Object key, Object value) {
+        Hashtable table = new Hashtable();
+        table.put(key, value);
+        return table;
+    }
+
+} // IntrospectionHelperTest
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/LoaderRefTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/LoaderRefTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/LoaderRefTest.java	(revision 16139)
@@ -0,0 +1,44 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class LoaderRefTest extends BuildFileTest {
+
+    public LoaderRefTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/core/loaderref/loaderref.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("clean");
+    }
+
+    // override allowed on <available>
+    public void testBadRef() {
+        expectBuildExceptionContaining("testbadref", "Should fail due to ref "
+            + "not being a class loader", "does not reference a class loader");
+    }
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/MockBuildListener.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/MockBuildListener.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/MockBuildListener.java	(revision 16139)
@@ -0,0 +1,62 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import java.util.Vector;
+
+import junit.framework.Assert;
+
+public class MockBuildListener extends Assert implements BuildListener {
+
+    private final Vector buffer = new Vector();
+    private final Project project;
+
+    public MockBuildListener(final Project project) {
+        this.project = project;
+    }
+
+    public void buildStarted(BuildEvent event) {}
+    public void buildFinished(BuildEvent event) {}
+    public void targetStarted(BuildEvent event) {}
+    public void targetFinished(BuildEvent event) {}
+    public void taskStarted(BuildEvent event) {}
+    public void taskFinished(BuildEvent event) {}
+
+    public void messageLogged(final BuildEvent actual) {
+        if(actual.getPriority()==Project.MSG_DEBUG)
+            return;
+        assertTrue("unexpected messageLogged: "+actual.getMessage(), !buffer.isEmpty());
+        assertEquals("unexpected project ", project, actual.getProject());
+
+        BuildEvent expected = (BuildEvent) buffer.elementAt(0);
+        buffer.removeElementAt(0);
+        assertEquals("unexpected messageLogged ", expected.getMessage(), actual.getMessage());
+        assertEquals("unexpected priority ", expected.getPriority(), actual.getPriority());
+    }
+
+    public void assertEmpty() {
+        assertTrue("MockBuildListener is not empty", buffer.isEmpty());
+    }
+
+    public void addBuildEvent(final String message, final int priority) {
+        final BuildEvent be = new BuildEvent(project);
+        be.setMessage(message, priority);
+        buffer.addElement(be);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/ProjectTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/ProjectTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/ProjectTest.java	(revision 16139)
@@ -0,0 +1,223 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+import org.apache.tools.ant.input.DefaultInputHandler;
+import org.apache.tools.ant.input.InputHandler;
+import org.apache.tools.ant.input.PropertyFileInputHandler;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.types.*;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+import java.io.File;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Very limited test class for Project. Waiting to be extended.
+ *
+ */
+public class ProjectTest extends TestCase {
+
+    private Project p;
+    private String root;
+    private MockBuildListener mbl;
+
+    public ProjectTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        p = new Project();
+        p.init();
+        root = new File(File.separator).getAbsolutePath();
+        mbl = new MockBuildListener(p);
+    }
+
+    public void testDataTypes() throws BuildException {
+        assertNull("dummy is not a known data type",
+                   p.createDataType("dummy"));
+        Object o = p.createDataType("fileset");
+        assertNotNull("fileset is a known type", o);
+        assertTrue("fileset creates FileSet", o instanceof FileSet);
+        assertTrue("PatternSet",
+               p.createDataType("patternset") instanceof PatternSet);
+        assertTrue("Path", p.createDataType("path") instanceof Path);
+    }
+
+    /**
+     * This test has been a starting point for moving the code to FileUtils.
+     */
+    public void testResolveFile() {
+        /*
+         * Start with simple absolute file names.
+         */
+        assertEquals(File.separator,
+                     p.resolveFile("/", null).getPath());
+        assertEquals(File.separator,
+                     p.resolveFile("\\", null).getPath());
+
+        if (!Os.isFamily("unix")) {
+        /*
+         * throw in drive letters
+         */
+        String driveSpec = "C:";
+        assertEquals(driveSpec + "\\",
+                     p.resolveFile(driveSpec + "/", null).getPath());
+        assertEquals(driveSpec + "\\",
+                     p.resolveFile(driveSpec + "\\", null).getPath());
+        String driveSpecLower = "c:";
+        assertEquals(driveSpec + "\\",
+                     p.resolveFile(driveSpecLower + "/", null).getPath());
+        assertEquals(driveSpec + "\\",
+                     p.resolveFile(driveSpecLower + "\\", null).getPath());
+        /*
+         * promised to eliminate consecutive slashes after drive letter.
+         */
+        assertEquals(driveSpec + "\\",
+                     p.resolveFile(driveSpec + "/////", null).getPath());
+        assertEquals(driveSpec + "\\",
+                     p.resolveFile(driveSpec + "\\\\\\\\\\\\", null).getPath());
+        }
+
+        /*
+         * Now test some relative file name magic.
+         */
+        assertEquals(localize("/1/2/3/4"),
+                     p.resolveFile("4", new File(localize("/1/2/3"))).getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     p.resolveFile("./4", new File(localize("/1/2/3"))).getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     p.resolveFile(".\\4", new File(localize("/1/2/3"))).getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     p.resolveFile("./.\\4", new File(localize("/1/2/3"))).getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     p.resolveFile("../3/4", new File(localize("/1/2/3"))).getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     p.resolveFile("..\\3\\4", new File(localize("/1/2/3"))).getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     p.resolveFile("../../5/.././2/./3/6/../4", new File(localize("/1/2/3"))).getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     p.resolveFile("..\\../5/..\\./2/./3/6\\../4", new File(localize("/1/2/3"))).getPath());
+
+    }
+
+    /**
+     * adapt file separators to local conventions
+     */
+    private String localize(String path) {
+        path = root + path.substring(1);
+        return path.replace('\\', File.separatorChar).replace('/', File.separatorChar);
+    }
+
+
+    private void assertTaskDefFails(final Class taskClass,
+                                       final String message) {
+        final String dummyName = "testTaskDefinitionDummy";
+        try {
+            mbl.addBuildEvent(message, Project.MSG_ERR);
+            p.addTaskDefinition(dummyName, taskClass);
+            fail("expected BuildException(\""+message+"\", Project.MSG_ERR) when adding task " + taskClass);
+        }
+        catch(BuildException e) {
+            assertEquals(message, e.getMessage());
+            mbl.assertEmpty();
+            assertTrue(!p.getTaskDefinitions().containsKey(dummyName));
+        }
+    }
+
+    public void testAddTaskDefinition() {
+        p.addBuildListener(mbl);
+
+        p.addTaskDefinition("Ok", DummyTaskOk.class);
+        assertEquals(DummyTaskOk.class, p.getTaskDefinitions().get("Ok"));
+        p.addTaskDefinition("OkNonTask", DummyTaskOkNonTask.class);
+        assertEquals(DummyTaskOkNonTask.class, p.getTaskDefinitions().get("OkNonTask"));
+        mbl.assertEmpty();
+
+        assertTaskDefFails(DummyTaskPrivate.class,   DummyTaskPrivate.class   + " is not public");
+
+        try {
+            assertTaskDefFails(DummyTaskProtected.class,
+                               DummyTaskProtected.class + " is not public");
+        } catch (AssertionFailedError e) {
+            /*
+             * I don't understand this, but this is what happens with
+             * > java -fullversion
+             * java full version "Linux_JDK_1.1.8_v3_green_threads"
+             * from time to time
+             */
+            assertTrue(JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1));
+            assertTaskDefFails(DummyTaskProtected.class,
+                               "No public no-arg constructor in "
+                               + DummyTaskProtected.class);
+        }
+
+        assertTaskDefFails(DummyTaskPackage.class,   DummyTaskPackage.class   + " is not public");
+
+        assertTaskDefFails(DummyTaskAbstract.class,  DummyTaskAbstract.class  + " is abstract");
+        assertTaskDefFails(DummyTaskInterface.class, DummyTaskInterface.class + " is abstract");
+
+        assertTaskDefFails(DummyTaskWithoutDefaultConstructor.class, "No public no-arg constructor in " + DummyTaskWithoutDefaultConstructor.class);
+        assertTaskDefFails(DummyTaskWithoutPublicConstructor.class,  "No public no-arg constructor in " + DummyTaskWithoutPublicConstructor.class);
+
+        assertTaskDefFails(DummyTaskWithoutExecute.class,       "No public execute() in " + DummyTaskWithoutExecute.class);
+        assertTaskDefFails(DummyTaskWithNonPublicExecute.class, "No public execute() in " + DummyTaskWithNonPublicExecute.class);
+
+        mbl.addBuildEvent("return type of execute() should be void but was \"int\" in " + DummyTaskWithNonVoidExecute.class, Project.MSG_WARN);
+        p.addTaskDefinition("NonVoidExecute", DummyTaskWithNonVoidExecute.class);
+        mbl.assertEmpty();
+        assertEquals(DummyTaskWithNonVoidExecute.class, p.getTaskDefinitions().get("NonVoidExecute"));
+    }
+
+    public void testInputHandler() {
+        InputHandler ih = p.getInputHandler();
+        assertNotNull(ih);
+        assertTrue(ih instanceof DefaultInputHandler);
+        InputHandler pfih = new PropertyFileInputHandler();
+        p.setInputHandler(pfih);
+        assertSame(pfih, p.getInputHandler());
+    }
+
+    public void testTaskDefinitionContainsKey() {
+        assertTrue(p.getTaskDefinitions().containsKey("echo"));
+    }
+
+    public void testTaskDefinitionContains() {
+        assertTrue(p.getTaskDefinitions().contains(org.apache.tools.ant.taskdefs.Echo.class));
+    }
+
+    private class DummyTaskPrivate extends Task {
+        public DummyTaskPrivate() {}
+        public void execute() {}
+    }
+
+    protected class DummyTaskProtected extends Task {
+        public DummyTaskProtected() {}
+        public void execute() {}
+    }
+
+}
+
+class DummyTaskPackage extends Task {
+    public DummyTaskPackage() {}
+    public void execute() {}
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/PropertyExpansionTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/PropertyExpansionTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/PropertyExpansionTest.java	(revision 16139)
@@ -0,0 +1,89 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+
+package org.apache.tools.ant;
+
+/**
+ * class to look at how we expand properties
+ */
+public class PropertyExpansionTest extends BuildFileTest {
+
+
+    public PropertyExpansionTest(String name) {
+        super(name);
+    }
+
+    /**
+     * we bind to an existing test file because we are too lazy to write our
+     * own, and we don't really care what it is
+     */
+    public void setUp() {
+        configureProject("src/etc/testcases/core/immutable.xml");
+    }
+
+    /**
+     * run through the test cases of expansion
+     */
+    public void testPropertyExpansion() {
+        assertExpandsTo("","");
+        assertExpandsTo("$","$");
+        assertExpandsTo("$$-","$-");
+        assertExpandsTo("$$","$");
+        project.setProperty("expanded","EXPANDED");
+        assertExpandsTo("a${expanded}b","aEXPANDEDb");
+        assertExpandsTo("${expanded}${expanded}","EXPANDEDEXPANDED");
+        assertExpandsTo("$$$","$$");
+        assertExpandsTo("$$$$-","$$-");
+        assertExpandsTo("","");
+        assertExpandsTo("Class$$subclass","Class$subclass");
+    }
+
+    /**
+     * new things we want
+     */
+    public void testDollarPassthru() {
+        assertExpandsTo("$-","$-");
+        assertExpandsTo("Class$subclass","Class$subclass");
+        assertExpandsTo("$$$-","$$-");
+        assertExpandsTo("$$$$$","$$$");
+        assertExpandsTo("${unassigned.property}","${unassigned.property}");
+        assertExpandsTo("a$b","a$b");
+        assertExpandsTo("$}}","$}}");
+    }
+
+
+    /**
+     * old things we dont want; not a test no more
+     */
+    public void oldtestQuirkyLegacyBehavior() {
+        assertExpandsTo("Class$subclass","Classsubclass");
+        assertExpandsTo("$$$-","$-");
+        assertExpandsTo("a$b","ab");
+        assertExpandsTo("$}}","}}");
+    }
+
+    /**
+     * little helper method to validate stuff
+     */
+    private void assertExpandsTo(String source,String expected) {
+        String actual=project.replaceProperties(source);
+        assertEquals(source,expected,actual);
+    }
+
+//end class
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/TaskContainerTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/TaskContainerTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/TaskContainerTest.java	(revision 16139)
@@ -0,0 +1,60 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+public class TaskContainerTest extends BuildFileTest {
+
+    public TaskContainerTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/core/taskcontainer.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testPropertyExpansion() {
+        executeTarget("testPropertyExpansion");
+        assertTrue("attribute worked",
+                   getLog().indexOf("As attribute: it worked") > -1);
+        assertTrue("nested text worked",
+                   getLog().indexOf("As nested text: it worked") > -1);
+    }
+
+    public void testTaskdef() {
+        executeTarget("testTaskdef");
+        assertTrue("attribute worked",
+                   getLog().indexOf("As attribute: it worked") > -1);
+        assertTrue("nested text worked",
+                   getLog().indexOf("As nested text: it worked") > -1);
+        assertTrue("nested text worked",
+                   getLog().indexOf("As nested task: it worked") > -1);
+    }
+
+    public void testCaseInsensitive() {
+        executeTarget("testCaseInsensitive");
+        assertTrue("works outside of container",
+                   getLog().indexOf("hello ") > -1);
+        assertTrue("works inside of container",
+                   getLog().indexOf("world") > -1);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/TopLevelTaskTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/TopLevelTaskTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/TopLevelTaskTest.java	(revision 16139)
@@ -0,0 +1,48 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant;
+
+// This test will fail with embed, or if top-level is moved out of
+// dependency - as 'echo' happens as part of configureProject stage.
+
+/**
+ * Tests for builds with tasks at the top level
+ *
+ * @since Ant 1.6
+ */
+public class TopLevelTaskTest extends BuildFileTest {
+
+    public TopLevelTaskTest(String name) {
+        super(name);
+    }
+
+    public void testNoTarget() {
+        configureProject("src/etc/testcases/core/topleveltasks/notarget.xml");
+        expectLog("", "Called");
+    }
+
+    public void testCalledFromTopLevelAnt() {
+        configureProject("src/etc/testcases/core/topleveltasks/toplevelant.xml");
+        expectLog("", "Called");
+    }
+
+    public void testCalledFromTargetLevelAnt() {
+        configureProject("src/etc/testcases/core/topleveltasks/targetlevelant.xml");
+        expectLog("foo", "Called");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/ConcatFilterTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/ConcatFilterTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/ConcatFilterTest.java	(revision 16139)
@@ -0,0 +1,154 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.filters;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.taskdefs.condition.Os;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * JUnit Testcases for ConcatReader
+ */
+public class ConcatFilterTest extends BuildFileTest {
+
+    private static FileUtils fu = FileUtils.newFileUtils();
+    private static final String lSep = 
+        Os.isFamily("mac") ? "\r" : System.getProperty("line.separator");
+
+    private static final String FILE_PREPEND_WITH =
+          "this-should-be-the-first-line" + lSep
+        + "Line  1" + lSep
+        + "Line  2" + lSep
+        + "Line  3" + lSep
+        + "Line  4" + lSep
+    ;
+
+    private static final String FILE_PREPEND =
+          "Line  1" + lSep
+        + "Line  2" + lSep
+        + "Line  3" + lSep
+        + "Line  4" + lSep
+        + "Line  5" + lSep
+    ;
+
+    private static final String FILE_APPEND_WITH =
+          "Line 57" + lSep
+        + "Line 58" + lSep
+        + "Line 59" + lSep
+        + "Line 60" + lSep
+        + "this-should-be-the-last-line" + lSep
+    ;
+
+    private static final String FILE_APPEND =
+          "Line 56" + lSep
+        + "Line 57" + lSep
+        + "Line 58" + lSep
+        + "Line 59" + lSep
+        + "Line 60" + lSep
+    ;
+
+
+    public ConcatFilterTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/filters/concat.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testFilterReaderNoArgs() throws IOException {
+        executeTarget("testFilterReaderNoArgs");
+        File expected = getProject().resolveFile("input/concatfilter.test");
+        File result = getProject().resolveFile("result/concat.FilterReaderNoArgs.test");
+        assertTrue("testFilterReaderNoArgs: Result not like expected", fu.contentEquals(expected, result));
+    }
+
+    public void testFilterReaderBefore() {
+        doTest("testFilterReaderPrepend", FILE_PREPEND_WITH, FILE_APPEND);
+    }
+
+    public void testFilterReaderAfter() {
+        doTest("testFilterReaderAppend", FILE_PREPEND, FILE_APPEND_WITH);
+    }
+
+    public void testFilterReaderBeforeAfter() {
+        doTest("testFilterReaderPrependAppend", FILE_PREPEND_WITH, FILE_APPEND_WITH);
+    }
+
+    public void testConcatFilter() {
+        doTest("testConcatFilter", FILE_PREPEND, FILE_APPEND);
+    }
+
+    public void testConcatFilterBefore() {
+        doTest("testConcatFilterPrepend", FILE_PREPEND_WITH, FILE_APPEND);
+    }
+
+    public void testConcatFilterAfter() {
+        doTest("testConcatFilterAppend", FILE_PREPEND, FILE_APPEND_WITH);
+    }
+
+    public void testConcatFilterBeforeAfter() {
+        doTest("testConcatFilterPrependAppend", FILE_PREPEND_WITH, FILE_APPEND_WITH);
+    }
+
+
+    /**
+     * Executes a target and checks the beginning and the ending of a file.
+     * The filename depends on the target name: target name <i>testHelloWorld</i>
+     * will search for a file <i>result/concat.HelloWorld.test</i>.
+     * @param target The target to invoke
+     * @param expectedStart The string which should be at the beginning of the file
+     * @param expectedEnd The string which should be at the end of the file
+     */
+    protected void doTest(String target, String expectedStart, String expectedEnd) {
+        executeTarget(target);
+        String resultContent = read("result/concat." + target.substring(4) + ".test");
+        assertTrue("First 5 lines differs.", resultContent.startsWith(expectedStart));
+        assertTrue("Last 5 lines differs.", resultContent.endsWith(expectedEnd));
+    }
+
+
+    /**
+     * Wrapper for FileUtils.readFully().
+     * Additionally it resolves the filename according the the projects basedir
+     * and closes the used reader.
+     * @param filename The name of the file to read
+     * @return the content of the file or <i>null</i> if something goes wrong
+     */
+    protected String read(String filename) {
+        String content = null;
+        try {
+            File file = getProject().resolveFile(filename);
+            java.io.FileReader rdr = new java.io.FileReader(file);
+            content = fu.readFully(rdr);
+            rdr.close();
+            rdr = null;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return content;
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/DynamicFilterTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/DynamicFilterTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/DynamicFilterTest.java	(revision 16139)
@@ -0,0 +1,129 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.filters;
+
+import java.io.File;
+import java.io.Reader;
+import java.io.FileReader;
+import java.io.IOException;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ */
+public class DynamicFilterTest extends BuildFileTest {
+
+    public DynamicFilterTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/filters/dynamicfilter.xml");
+        executeTarget("init");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+    public void testCustomFilter() throws IOException {
+        expectFileContains("dynamicfilter", "result/dynamicfilter",
+                           "hellO wOrld");
+    }
+
+    // ------------------------------------------------------
+    //   Helper methods
+    // -----------------------------------------------------
+
+
+    private void assertStringContains(String string, String contains) {
+        assertTrue("[" + string + "] does not contain [" + contains +"]",
+                   string.indexOf(contains) > -1);
+    }
+
+    private void assertStringNotContains(String string, String contains) {
+        assertTrue("[" + string + "] does contain [" + contains +"]",
+                   string.indexOf(contains) == -1);
+    }
+
+    private String getFileString(String filename)
+        throws IOException
+    {
+        Reader r = null;
+        try {
+            r = new FileReader(getProject().resolveFile(filename));
+            return  FileUtils.newFileUtils().readFully(r);
+        }
+        finally {
+            try {r.close();} catch (Throwable ignore) {}
+        }
+
+    }
+
+    private String getFileString(String target, String filename)
+        throws IOException
+    {
+        executeTarget(target);
+        return getFileString(filename);
+    }
+
+    private void expectFileContains(String name, String contains)
+        throws IOException
+    {
+        String content = getFileString(name);
+        assertTrue(
+            "expecting file " + name + " to contain " + contains +
+            " but got " + content, content.indexOf(contains) > -1);
+    }
+
+    private void expectFileContains(
+        String target, String name, String contains)
+        throws IOException
+    {
+        executeTarget(target);
+        expectFileContains(name, contains);
+    }
+
+    public static class CustomFilter implements ChainableReader {
+        char replace = 'x';
+        char with    = 'y';
+
+        public void setReplace(char replace) {
+            this.replace = replace;
+        }
+
+        public void setWith(char with) {
+            this.with = with;
+        }
+
+        public Reader chain(final Reader rdr) {
+            return new BaseFilterReader(rdr) {
+                public int read()
+                    throws IOException
+                {
+                    int c = in.read();
+                    if (c == replace)
+                        return with;
+                    else
+                        return c;
+                }
+            };
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/EscapeUnicodeTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/EscapeUnicodeTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/EscapeUnicodeTest.java	(revision 16139)
@@ -0,0 +1,50 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.filters;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ */
+public class EscapeUnicodeTest extends BuildFileTest {
+
+    public EscapeUnicodeTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/filters/build.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testEscapeUnicode() throws IOException {
+        executeTarget("testEscapeUnicode");
+        File expected = getProject().resolveFile("expected/escapeunicode.test");
+        File result = getProject().resolveFile("result/escapeunicode.test");
+        FileUtils fu = FileUtils.newFileUtils();
+        assertTrue(fu.contentEquals(expected, result));
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/HeadTailTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/HeadTailTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/HeadTailTest.java	(revision 16139)
@@ -0,0 +1,139 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.filters;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+/** JUnit Testcases for TailFilter and HeadFilter
+ */
+/* I wrote the testcases in one java file because I want also to test the
+ * combined behaviour (see end of the class).
+*/
+public class HeadTailTest extends BuildFileTest {
+
+    public HeadTailTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/filters/head-tail.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testHead() throws IOException {
+        executeTarget("testHead");
+        File expected = getProject().resolveFile("expected/head-tail.head.test");
+        File result = getProject().resolveFile("result/head-tail.head.test");
+        FileUtils fu = FileUtils.newFileUtils();
+        assertTrue("testHead: Result not like expected", fu.contentEquals(expected, result));
+    }
+
+    public void testHeadLines() throws IOException {
+        executeTarget("testHeadLines");
+        File expected = getProject().resolveFile("expected/head-tail.headLines.test");
+        File result = getProject().resolveFile("result/head-tail.headLines.test");
+        FileUtils fu = FileUtils.newFileUtils();
+        assertTrue("testHeadLines: Result not like expected", fu.contentEquals(expected, result));
+    }
+
+    public void testHeadSkip() throws IOException {
+        executeTarget("testHeadSkip");
+        File expected = getProject().resolveFile("expected/head-tail.headSkip.test");
+        File result = getProject().resolveFile("result/head-tail.headSkip.test");
+        FileUtils fu = FileUtils.newFileUtils();
+        assertTrue("testHeadSkip: Result not like expected", fu.contentEquals(expected, result));
+    }
+
+    public void testHeadLinesSkip() throws IOException {
+        executeTarget("testHeadLinesSkip");
+        File expected = getProject().resolveFile("expected/head-tail.headLinesSkip.test");
+        File result = getProject().resolveFile("result/head-tail.headLinesSkip.test");
+        FileUtils fu = FileUtils.newFileUtils();
+        assertTrue("testHeadLinesSkip: Result not like expected", fu.contentEquals(expected, result));
+    }
+
+    public void testFilterReaderHeadLinesSkip() throws IOException {
+        executeTarget("testFilterReaderHeadLinesSkip");
+        File expected = getProject().resolveFile(
+            "expected/head-tail.headLinesSkip.test");
+        File result = getProject().resolveFile(
+            "result/head-tail.filterReaderHeadLinesSkip.test");
+        FileUtils fu = FileUtils.newFileUtils();
+        assertTrue("testFilterReaderHeadLinesSkip: Result not like expected",
+                   fu.contentEquals(expected, result));
+    }
+
+    public void testTail() throws IOException {
+        executeTarget("testTail");
+        File expected = getProject().resolveFile("expected/head-tail.tail.test");
+        File result = getProject().resolveFile("result/head-tail.tail.test");
+        FileUtils fu = FileUtils.newFileUtils();
+        assertTrue("testTail: Result not like expected", fu.contentEquals(expected, result));
+    }
+
+    public void testTailLines() throws IOException {
+        executeTarget("testTailLines");
+        File expected = getProject().resolveFile("expected/head-tail.tailLines.test");
+        File result = getProject().resolveFile("result/head-tail.tailLines.test");
+        FileUtils fu = FileUtils.newFileUtils();
+        assertTrue("testTailLines: Result not like expected", fu.contentEquals(expected, result));
+    }
+
+    public void testTailSkip() throws IOException {
+        executeTarget("testTailSkip");
+        File expected = getProject().resolveFile("expected/head-tail.tailSkip.test");
+        File result = getProject().resolveFile("result/head-tail.tailSkip.test");
+        FileUtils fu = FileUtils.newFileUtils();
+        assertTrue("testTailSkip: Result not like expected", fu.contentEquals(expected, result));
+    }
+
+    public void testTailLinesSkip() throws IOException {
+        executeTarget("testTailLinesSkip");
+        File expected = getProject().resolveFile("expected/head-tail.tailLinesSkip.test");
+        File result = getProject().resolveFile("result/head-tail.tailLinesSkip.test");
+        FileUtils fu = FileUtils.newFileUtils();
+        assertTrue("testTailLinesSkip: Result not like expected", fu.contentEquals(expected, result));
+    }
+
+    public void testFilterReaderTailLinesSkip() throws IOException {
+        executeTarget("testFilterReaderTailLinesSkip");
+        File expected = getProject().resolveFile(
+            "expected/head-tail.tailLinesSkip.test");
+        File result = getProject().resolveFile(
+            "result/head-tail.filterReaderTailLinesSkip.test");
+        FileUtils fu = FileUtils.newFileUtils();
+        assertTrue("testFilterReaderTailLinesSkip: Result not like expected",
+                   fu.contentEquals(expected, result));
+    }
+
+    public void testHeadTail() throws IOException {
+        executeTarget("testHeadTail");
+        File expected = getProject().resolveFile("expected/head-tail.headtail.test");
+        File result = getProject().resolveFile("result/head-tail.headtail.test");
+        FileUtils fu = FileUtils.newFileUtils();
+        assertTrue("testHeadTail: Result not like expected", fu.contentEquals(expected, result));
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/LineContainsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/LineContainsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/LineContainsTest.java	(revision 16139)
@@ -0,0 +1,50 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.filters;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ */
+public class LineContainsTest extends BuildFileTest {
+
+    public LineContainsTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/filters/build.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testLineContains() throws IOException {
+        executeTarget("testLineContains");
+        File expected = getProject().resolveFile("expected/linecontains.test");
+        File result = getProject().resolveFile("result/linecontains.test");
+        FileUtils fu = FileUtils.newFileUtils();
+        assertTrue(fu.contentEquals(expected, result));
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/NoNewLineTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/NoNewLineTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/NoNewLineTest.java	(revision 16139)
@@ -0,0 +1,50 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.filters;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+/** JUnit Testcases for No new line when filterchain used
+ */
+
+
+public class NoNewLineTest extends BuildFileTest {
+
+    public NoNewLineTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/filters/build.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testNoAddNewLine() throws IOException {
+        executeTarget("testNoAddNewLine");
+    }
+
+
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/ReplaceTokensTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/ReplaceTokensTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/ReplaceTokensTest.java	(revision 16139)
@@ -0,0 +1,50 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.filters;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ */
+public class ReplaceTokensTest extends BuildFileTest {
+
+    public ReplaceTokensTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/filters/build.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testReplaceTokens() throws IOException {
+        executeTarget("testReplaceTokens");
+        File expected = getProject().resolveFile("expected/replacetokens.test");
+        File result = getProject().resolveFile("result/replacetokens.test");
+        FileUtils fu = FileUtils.newFileUtils();
+        assertTrue(fu.contentEquals(expected, result));
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/StripJavaCommentsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/StripJavaCommentsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/StripJavaCommentsTest.java	(revision 16139)
@@ -0,0 +1,50 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.filters;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ */
+public class StripJavaCommentsTest extends BuildFileTest {
+
+    public StripJavaCommentsTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/filters/build.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testStripJavaComments() throws IOException {
+        executeTarget("testStripJavaComments");
+        File expected = getProject().resolveFile("expected/stripjavacomments.test");
+        File result = getProject().resolveFile("result/stripjavacomments.test");
+        FileUtils fu = FileUtils.newFileUtils();
+        assertTrue(fu.contentEquals(expected, result));
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/TokenFilterTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/TokenFilterTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/filters/TokenFilterTest.java	(revision 16139)
@@ -0,0 +1,294 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.filters;
+
+import java.io.File;
+import java.io.Reader;
+import java.io.FileReader;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ */
+public class TokenFilterTest extends BuildFileTest {
+
+    public TokenFilterTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/filters/tokenfilter.xml");
+        executeTarget("init");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    /** make sure tokenfilter exists */
+    public void testTokenfilter() throws IOException {
+        executeTarget("tokenfilter");
+    }
+
+    public void testTrimignore() throws IOException {
+        expectLogContaining("trimignore", "Hello-World");
+    }
+
+    public void testStringTokenizer() throws IOException {
+        expectLogContaining(
+            "stringtokenizer", "#This#is#a#number#of#words#");
+    }
+
+    public void testUnixLineOutput() throws IOException {
+        expectFileContains(
+            "unixlineoutput", "result/unixlineoutput",
+            "\nThis\nis\na\nnumber\nof\nwords\n");
+    }
+
+    public void testDosLineOutput() throws IOException {
+        expectFileContains(
+            "doslineoutput", "result/doslineoutput",
+            "\r\nThis\r\nis\r\na\r\nnumber\r\nof\r\nwords\r\n");
+    }
+
+    public void testFileTokenizer() throws IOException {
+        String contents = getFileString(
+            "filetokenizer", "result/filetokenizer");
+        assertStringContains(contents, "   of words");
+        assertStringNotContains(contents, " This is");
+    }
+
+    public void testReplaceString() throws IOException {
+        expectFileContains(
+            "replacestring", "result/replacestring",
+            "this is the moon");
+    }
+
+    public void testReplaceStrings() throws IOException {
+        expectLogContaining("replacestrings", "bar bar bar");
+    }
+
+    public void testContainsString() throws IOException {
+        String contents = getFileString(
+            "containsstring", "result/containsstring");
+        assertStringContains(contents, "this is a line contains foo");
+        assertStringNotContains(contents, "this line does not");
+    }
+
+    public void testReplaceRegex() throws IOException {
+        if (! hasRegex("testReplaceRegex"))
+            return;
+        String contents = getFileString(
+            "replaceregex", "result/replaceregex");
+        assertStringContains(contents, "world world world world");
+        assertStringContains(contents, "dog Cat dog");
+        assertStringContains(contents, "moon Sun Sun");
+        assertStringContains(contents, "found WhiteSpace");
+        assertStringContains(contents, "Found digits [1234]");
+        assertStringNotContains(contents, "This is a line with digits");
+    }
+
+    public void testFilterReplaceRegex() throws IOException {
+        if (! hasRegex("testFilterReplaceRegex"))
+            return;
+        String contents = getFileString(
+            "filterreplaceregex", "result/filterreplaceregex");
+        assertStringContains(contents, "world world world world");
+    }
+
+    public void testHandleDollerMatch() throws IOException {
+        if (! hasRegex("testFilterReplaceRegex"))
+            return;
+        executeTarget("dollermatch");
+    }
+
+    public void testTrimFile() throws IOException {
+        String contents = getFileString(
+            "trimfile", "result/trimfile");
+        assertTrue("no ws at start", contents.startsWith("This is th"));
+        assertTrue("no ws at end", contents.endsWith("second line."));
+        assertStringContains(contents, "  This is the second");
+    }
+
+    public void testTrimFileByLine() throws IOException {
+        String contents = getFileString(
+            "trimfilebyline", "result/trimfilebyline");
+        assertFalse("no ws at start", contents.startsWith("This is th"));
+        assertFalse("no ws at end", contents.endsWith("second line."));
+        assertStringNotContains(contents, "  This is the second");
+        assertStringContains(contents, "file.\nThis is the second");
+    }
+
+    public void testFilterReplaceString() throws IOException {
+        String contents = getFileString(
+            "filterreplacestring", "result/filterreplacestring");
+        assertStringContains(contents, "This is the moon");
+    }
+
+    public void testFilterReplaceStrings() throws IOException {
+        expectLogContaining("filterreplacestrings", "bar bar bar");
+    }
+
+    public void testContainsRegex() throws IOException {
+        if (! hasRegex("testContainsRegex"))
+            return;
+        String contents = getFileString(
+            "containsregex", "result/containsregex");
+        assertStringContains(contents, "hello world");
+        assertStringNotContains(contents, "this is the moon");
+        assertStringContains(contents, "World here");
+    }
+
+    public void testFilterContainsRegex() throws IOException {
+        if (! hasRegex("testFilterContainsRegex"))
+            return;
+        String contents = getFileString(
+            "filtercontainsregex", "result/filtercontainsregex");
+        assertStringContains(contents, "hello world");
+        assertStringNotContains(contents, "this is the moon");
+        assertStringContains(contents, "World here");
+    }
+
+    public void testContainsRegex2() throws IOException {
+        if (! hasRegex("testContainsRegex2"))
+            return;
+        String contents = getFileString(
+            "containsregex2", "result/containsregex2");
+        assertStringContains(contents, "void register_bits();");
+    }
+
+    public void testDeleteCharacters() throws IOException {
+        String contents = getFileString(
+            "deletecharacters", "result/deletechars");
+        assertStringNotContains(contents, "#");
+        assertStringNotContains(contents, "*");
+        assertStringContains(contents, "This is some ");
+    }
+
+    public void testScriptFilter() throws IOException {
+        if (! hasScript("testScriptFilter"))
+            return;
+
+        expectFileContains("scriptfilter", "result/scriptfilter",
+                           "HELLO WORLD");
+    }
+
+
+    public void testScriptFilter2() throws IOException {
+        if (! hasScript("testScriptFilter"))
+            return;
+
+        expectFileContains("scriptfilter2", "result/scriptfilter2",
+                           "HELLO MOON");
+    }
+
+    public void testCustomTokenFilter() throws IOException {
+        expectFileContains("customtokenfilter", "result/custom",
+                           "Hello World");
+    }
+
+    // ------------------------------------------------------
+    //   Helper methods
+    // -----------------------------------------------------
+    private boolean hasScript(String test) {
+        try {
+            executeTarget("hasscript");
+        }
+        catch (Throwable ex) {
+            System.out.println(
+                test + ": skipped - script not present ");
+            return false;
+        }
+        return true;
+    }
+
+    private boolean hasRegex(String test) {
+        try {
+            executeTarget("hasregex");
+            expectFileContains("result/replaceregexp", "bye world");
+        }
+        catch (Throwable ex) {
+            System.out.println(test + ": skipped - regex not present "
+                               + ex);
+            return false;
+        }
+        return true;
+    }
+
+    private void assertStringContains(String string, String contains) {
+        assertTrue("[" + string + "] does not contain [" + contains +"]",
+                   string.indexOf(contains) > -1);
+    }
+
+    private void assertStringNotContains(String string, String contains) {
+        assertTrue("[" + string + "] does contain [" + contains +"]",
+                   string.indexOf(contains) == -1);
+    }
+
+    private String getFileString(String filename)
+        throws IOException
+    {
+        Reader r = null;
+        try {
+            r = new FileReader(getProject().resolveFile(filename));
+            return  FileUtils.newFileUtils().readFully(r);
+        }
+        finally {
+            try {r.close();} catch (Throwable ignore) {}
+        }
+
+    }
+
+    private String getFileString(String target, String filename)
+        throws IOException
+    {
+        executeTarget(target);
+        return getFileString(filename);
+    }
+
+    private void expectFileContains(String name, String contains)
+        throws IOException
+    {
+        String content = getFileString(name);
+        assertTrue(
+            "expecting file " + name + " to contain " + contains +
+            " but got " + content, content.indexOf(contains) > -1);
+    }
+
+    private void expectFileContains(
+        String target, String name, String contains)
+        throws IOException
+    {
+        executeTarget(target);
+        expectFileContains(name, contains);
+    }
+
+    public static class Capitalize
+        implements TokenFilter.Filter
+    {
+        public String filter(String token) {
+            if (token.length() == 0)
+                return token;
+            return token.substring(0, 1).toUpperCase() +
+                token.substring(1);
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AbstractCvsTaskTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AbstractCvsTaskTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AbstractCvsTaskTest.java	(revision 16139)
@@ -0,0 +1,60 @@
+/*
+ * Copyright  2002-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class AbstractCvsTaskTest extends BuildFileTest {
+
+    public AbstractCvsTaskTest() {
+        this( "AbstractCvsTaskTest" );
+    }
+
+    public AbstractCvsTaskTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/abstractcvstask.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testAbstractCvsTask() {
+        executeTarget( "all" );
+    }
+
+    public void testPackageAttribute() {
+        File f = getProject().resolveFile("tmpdir/ant/build.xml");
+        assertTrue("starting empty", !f.exists());
+        expectLogContaining("package-attribute", "U ant/build.xml");
+        assertTrue("now it is there", f.exists());
+    }
+
+    public void testTagAttribute() {
+        File f = getProject().resolveFile("tmpdir/ant/build.xml");
+        assertTrue("starting empty", !f.exists());
+        expectLogContaining("tag-attribute", "ANT_141 (revision: 1.175.2.13)");
+        assertTrue("now it is there", f.exists());
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AntLikeTasksAtTopLevelTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AntLikeTasksAtTopLevelTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AntLikeTasksAtTopLevelTest.java	(revision 16139)
@@ -0,0 +1,61 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * @since Ant 1.6
+ */
+public class AntLikeTasksAtTopLevelTest extends BuildFileTest {
+    public AntLikeTasksAtTopLevelTest(String name) {
+        super(name);
+    }
+
+    public void testAnt() {
+        try {
+            configureProject("src/etc/testcases/taskdefs/toplevelant.xml");
+            fail("no exception thrown");
+        } catch (BuildException e) {
+            assertEquals("ant task at the top level must not invoke its own"
+                         + " build file.", e.getMessage());
+        }
+    }
+
+    public void testSubant() {
+        try {
+            configureProject("src/etc/testcases/taskdefs/toplevelsubant.xml");
+            fail("no exception thrown");
+        } catch (BuildException e) {
+            assertEquals("subant task at the top level must not invoke its own"
+                         + " build file.", e.getMessage());
+        }
+    }
+
+    public void testAntcall() {
+        try {
+            configureProject("src/etc/testcases/taskdefs/toplevelantcall.xml");
+            fail("no exception thrown");
+        } catch (BuildException e) {
+            assertEquals("antcall must not be used at the top level.",
+                         e.getMessage());
+        }
+    }
+
+}// AntLikeTasksAtTopLevelTest
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AntStructureTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AntStructureTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AntStructureTest.java	(revision 16139)
@@ -0,0 +1,36 @@
+/*
+ * Copyright  2000,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+/**
+ */
+public class AntStructureTest extends BuildFileTest {
+
+    public AntStructureTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/antstructure.xml");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument not specified");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AntTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AntTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AntTest.java	(revision 16139)
@@ -0,0 +1,511 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+
+import junit.framework.AssertionFailedError;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.input.InputHandler;
+import org.apache.tools.ant.input.PropertyFileInputHandler;
+import org.apache.tools.ant.types.Path;
+
+/**
+ */
+public class AntTest extends BuildFileTest {
+
+    public AntTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/ant.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "recursive call");
+    }
+
+    // target must be specified
+    public void test2() {
+        expectBuildException("test2", "required argument not specified");
+    }
+
+    // Should fail since a recursion will occur...
+    public void test3() {
+        expectBuildException("test1", "recursive call");
+    }
+
+    public void test4() {
+        expectBuildException("test4", "target attribute must not be empty");
+    }
+
+    public void test4b() {
+        expectBuildException("test4b", "target doesn't exist");
+    }
+
+    public void test5() {
+        executeTarget("test5");
+    }
+
+    public void test6() {
+        executeTarget("test6");
+    }
+
+    public void testExplicitBasedir1() {
+        File dir1 = getProjectDir();
+        File dir2 = project.resolveFile("..");
+        testBaseDirs("explicitBasedir1",
+                     new String[] {dir1.getAbsolutePath(),
+                                   dir2.getAbsolutePath()
+                     });
+    }
+
+    public void testExplicitBasedir2() {
+        File dir1 = getProjectDir();
+        File dir2 = project.resolveFile("..");
+        testBaseDirs("explicitBasedir2",
+                     new String[] {dir1.getAbsolutePath(),
+                                   dir2.getAbsolutePath()
+                     });
+    }
+
+    public void testInheritBasedir() {
+        String basedir = getProjectDir().getAbsolutePath();
+        testBaseDirs("inheritBasedir", new String[] {basedir, basedir});
+    }
+
+    public void testDoNotInheritBasedir() {
+        File dir1 = getProjectDir();
+        File dir2 = project.resolveFile("ant");
+        String basedir = getProjectDir().getAbsolutePath();
+        testBaseDirs("doNotInheritBasedir",
+                     new String[] {dir1.getAbsolutePath(),
+                                   dir2.getAbsolutePath()
+                     });
+    }
+
+    public void testBasedirTripleCall() {
+        File dir1 = getProjectDir();
+        File dir2 = project.resolveFile("ant");
+        testBaseDirs("tripleCall",
+                     new String[] {dir1.getAbsolutePath(),
+                                   dir2.getAbsolutePath(),
+                                   dir1.getAbsolutePath()
+                     });
+    }
+
+    protected void testBaseDirs(String target, String[] dirs) {
+        BasedirChecker bc = new BasedirChecker(dirs);
+        project.addBuildListener(bc);
+        executeTarget(target);
+        AssertionFailedError ae = bc.getError();
+        if (ae != null) {
+            throw ae;
+        }
+        project.removeBuildListener(bc);
+    }
+
+    public void testReferenceInheritance() {
+        Path p = Path.systemClasspath;
+        p.setProject(project);
+        project.addReference("path", p);
+        project.addReference("no-override", p);
+        testReference("testInherit", new String[] {"path", "path"},
+                      new boolean[] {true, true}, p);
+        testReference("testInherit",
+                      new String[] {"no-override", "no-override"},
+                      new boolean[] {true, false}, p);
+        testReference("testInherit",
+                      new String[] {"no-override", "no-override"},
+                      new boolean[] {false, false}, null);
+    }
+
+    public void testReferenceNoInheritance() {
+        Path p = Path.systemClasspath;
+        p.setProject(project);
+        project.addReference("path", p);
+        project.addReference("no-override", p);
+        testReference("testNoInherit", new String[] {"path", "path"},
+                      new boolean[] {true, false}, p);
+        testReference("testNoInherit", new String[] {"path", "path"},
+                      new boolean[] {false, true}, null);
+        testReference("testInherit",
+                      new String[] {"no-override", "no-override"},
+                      new boolean[] {true, false}, p);
+        testReference("testInherit",
+                      new String[] {"no-override", "no-override"},
+                      new boolean[] {false, false}, null);
+    }
+
+    public void testReferenceRename() {
+        Path p = Path.systemClasspath;
+        p.setProject(project);
+        project.addReference("path", p);
+        testReference("testRename", new String[] {"path", "path"},
+                      new boolean[] {true, false}, p);
+        testReference("testRename", new String[] {"path", "path"},
+                      new boolean[] {false, true}, null);
+        testReference("testRename", new String[] {"newpath", "newpath"},
+                      new boolean[] {false, true}, p);
+    }
+
+    public void testInheritPath() {
+        executeTarget("testInheritPath");
+    }
+
+    protected void testReference(String target, String[] keys,
+                                 boolean[] expect, Object value) {
+        ReferenceChecker rc = new ReferenceChecker(keys, expect, value);
+        project.addBuildListener(rc);
+        executeTarget(target);
+        AssertionFailedError ae = rc.getError();
+        if (ae != null) {
+            throw ae;
+        }
+        project.removeBuildListener(rc);
+    }
+
+    public void testLogfilePlacement() {
+        File[] logFiles = new File[] {
+            getProject().resolveFile("test1.log"),
+            getProject().resolveFile("test2.log"),
+            getProject().resolveFile("ant/test3.log"),
+            getProject().resolveFile("ant/test4.log")
+        };
+        for (int i=0; i<logFiles.length; i++) {
+            assertTrue(logFiles[i].getName()+" doesn\'t exist",
+                       !logFiles[i].exists());
+        }
+
+        executeTarget("testLogfilePlacement");
+
+        for (int i=0; i<logFiles.length; i++) {
+            assertTrue(logFiles[i].getName()+" exists",
+                       logFiles[i].exists());
+        }
+    }
+
+    public void testInputHandlerInheritance() {
+        InputHandler ih = new PropertyFileInputHandler();
+        getProject().setInputHandler(ih);
+        InputHandlerChecker ic = new InputHandlerChecker(ih);
+        getProject().addBuildListener(ic);
+        executeTarget("tripleCall");
+        AssertionFailedError ae = ic.getError();
+        if (ae != null) {
+            throw ae;
+        }
+        getProject().removeBuildListener(ic);
+    }
+
+    public void testRefId() {
+        Path testPath = new Path(project);
+        testPath.createPath().setPath(System.getProperty("java.class.path"));
+        PropertyChecker pc =
+            new PropertyChecker("testprop",
+                                new String[] {null,
+                                              testPath.toString()});
+        project.addBuildListener(pc);
+        executeTarget("testRefid");
+        AssertionFailedError ae = pc.getError();
+        if (ae != null) {
+            throw ae;
+        }
+        project.removeBuildListener(pc);
+    }
+
+    public void testUserPropertyWinsInheritAll() {
+        getProject().setUserProperty("test", "7");
+        expectLogContaining("test-property-override-inheritall-start",
+                            "The value of test is 7");
+    }
+
+    public void testUserPropertyWinsNoInheritAll() {
+        getProject().setUserProperty("test", "7");
+        expectLogContaining("test-property-override-no-inheritall-start",
+                            "The value of test is 7");
+    }
+
+    public void testOverrideWinsInheritAll() {
+        expectLogContaining("test-property-override-inheritall-start",
+                            "The value of test is 4");
+    }
+
+    public void testOverrideWinsNoInheritAll() {
+        expectLogContaining("test-property-override-no-inheritall-start",
+                            "The value of test is 4");
+    }
+
+    public void testPropertySet() {
+        executeTarget("test-propertyset");
+        assertTrue(getLog().indexOf("test1 is ${test1}") > -1);
+        assertTrue(getLog().indexOf("test2 is ${test2}") > -1);
+        assertTrue(getLog().indexOf("test1.x is 1") > -1);
+    }
+
+    public void testInfiniteLoopViaDepends() {
+        expectBuildException("infinite-loop-via-depends", "recursive call");
+    }
+
+    public void testMultiSameProperty() {
+        expectLog("multi-same-property", "prop is two");
+    }
+
+    public void testTopLevelTarget() {
+        expectLog("topleveltarget", "Hello world");
+    }
+
+    public void testMultiplePropertyFileChildren() {
+        PropertyChecker pcBar = new PropertyChecker("bar",
+                                                    new String[] {null, "Bar"});
+        PropertyChecker pcFoo = new PropertyChecker("foo",
+                                                    new String[] {null, "Foo"});
+        project.addBuildListener(pcBar);
+        project.addBuildListener(pcFoo);
+        executeTarget("multiple-property-file-children");
+        AssertionFailedError aeBar = pcBar.getError();
+        if (aeBar != null) {
+            throw aeBar;
+        }
+        AssertionFailedError aeFoo = pcFoo.getError();
+        if (aeFoo != null) {
+            throw aeFoo;
+        }
+        project.removeBuildListener(pcBar);
+        project.removeBuildListener(pcFoo);
+    }
+
+    public void testBlankTarget() {
+        expectBuildException("blank-target", "target name must not be empty");
+    }
+
+    public void testMultipleTargets() {
+        expectLog("multiple-targets", "tadadctbdbtc");
+    }
+
+    public void testMultipleTargets2() {
+        expectLog("multiple-targets-2", "dadctb");
+    }
+
+    private class BasedirChecker implements BuildListener {
+        private String[] expectedBasedirs;
+        private int calls = 0;
+        private AssertionFailedError error;
+
+        BasedirChecker(String[] dirs) {
+            expectedBasedirs = dirs;
+        }
+
+        public void buildStarted(BuildEvent event) {}
+        public void buildFinished(BuildEvent event) {}
+        public void targetFinished(BuildEvent event){}
+        public void taskStarted(BuildEvent event) {}
+        public void taskFinished(BuildEvent event) {}
+        public void messageLogged(BuildEvent event) {}
+
+        public void targetStarted(BuildEvent event) {
+            if (event.getTarget().getName().equals("")) {
+                return;
+            }
+            if (error == null) {
+                try {
+                    assertEquals(expectedBasedirs[calls++],
+                                 event.getProject().getBaseDir().getAbsolutePath());
+                } catch (AssertionFailedError e) {
+                    error = e;
+                }
+            }
+        }
+
+        AssertionFailedError getError() {
+            return error;
+        }
+
+    }
+
+    private class ReferenceChecker implements BuildListener {
+        private String[] keys;
+        private boolean[] expectSame;
+        private Object value;
+        private int calls = 0;
+        private AssertionFailedError error;
+
+        ReferenceChecker(String[] keys, boolean[] expectSame, Object value) {
+            this.keys = keys;
+            this.expectSame = expectSame;
+            this.value = value;
+        }
+
+        public void buildStarted(BuildEvent event) {}
+        public void buildFinished(BuildEvent event) {}
+        public void targetFinished(BuildEvent event){}
+        public void taskStarted(BuildEvent event) {}
+        public void taskFinished(BuildEvent event) {}
+        public void messageLogged(BuildEvent event) {}
+
+        public void targetStarted(BuildEvent event) {
+            if (event.getTarget().getName().equals("")) {
+                return;
+            }
+            if (error == null) {
+                try {
+                    String msg =
+                        "Call " + calls + " refid=\'" + keys[calls] + "\'";
+                    if (value == null) {
+                        Object o = event.getProject().getReference(keys[calls]);
+                        if (expectSame[calls++]) {
+                            assertNull(msg, o);
+                        } else {
+                            assertNotNull(msg, o);
+                        }
+                    } else {
+                        // a rather convoluted equals() test
+                        Path expect = (Path) value;
+                        Path received = (Path) event.getProject().getReference(keys[calls]);
+                        boolean shouldBeEqual = expectSame[calls++];
+                        if (received == null) {
+                            assertTrue(msg, !shouldBeEqual);
+                        } else {
+                            String[] l1 = expect.list();
+                            String[] l2 = received.list();
+                            if (l1.length == l2.length) {
+                                for (int i=0; i<l1.length; i++) {
+                                    if (!l1[i].equals(l2[i])) {
+                                        assertTrue(msg, !shouldBeEqual);
+                                    }
+                                }
+                                assertTrue(msg, shouldBeEqual);
+                            } else {
+                                assertTrue(msg, !shouldBeEqual);
+                            }
+                        }
+                    }
+                } catch (AssertionFailedError e) {
+                    error = e;
+                }
+            }
+        }
+
+        AssertionFailedError getError() {
+            return error;
+        }
+
+    }
+
+    private class InputHandlerChecker implements BuildListener {
+        private InputHandler ih;
+        private AssertionFailedError error;
+
+        InputHandlerChecker(InputHandler value) {
+            ih = value;
+        }
+
+        public void buildStarted(BuildEvent event) {
+            check(event);
+        }
+        public void buildFinished(BuildEvent event) {
+            check(event);
+        }
+        public void targetFinished(BuildEvent event) {
+            check(event);
+        }
+        public void taskStarted(BuildEvent event) {
+            check(event);
+        }
+        public void taskFinished(BuildEvent event) {
+            check(event);
+        }
+        public void messageLogged(BuildEvent event) {
+            check(event);
+        }
+
+        public void targetStarted(BuildEvent event) {
+            check(event);
+        }
+
+        private void check(BuildEvent event) {
+            if (error == null) {
+                try {
+                    assertNotNull(event.getProject().getInputHandler());
+                    assertSame(ih, event.getProject().getInputHandler());
+                } catch (AssertionFailedError e) {
+                    error = e;
+                }
+            }
+        }
+
+        AssertionFailedError getError() {
+            return error;
+        }
+
+    }
+
+    private class PropertyChecker implements BuildListener {
+        private String[] expectedValues;
+        private String key;
+        private int calls = 0;
+        private AssertionFailedError error;
+
+        PropertyChecker(String key, String[] values) {
+            this.key = key;
+            this.expectedValues = values;
+        }
+
+        public void buildStarted(BuildEvent event) {}
+        public void buildFinished(BuildEvent event) {}
+        public void targetFinished(BuildEvent event){}
+        public void taskStarted(BuildEvent event) {}
+        public void taskFinished(BuildEvent event) {}
+        public void messageLogged(BuildEvent event) {}
+
+        public void targetStarted(BuildEvent event) {
+            if (event.getTarget().getName().equals("")) {
+                return;
+            }
+            if (calls >= expectedValues.length) {
+                error = new AssertionFailedError("Unexpected invocation of"
+                                                 + " target "
+                                                 + event.getTarget().getName());
+            }
+
+            if (error == null) {
+                try {
+                    assertEquals(expectedValues[calls++],
+                                 event.getProject().getProperty(key));
+                } catch (AssertionFailedError e) {
+                    error = e;
+                }
+            }
+        }
+
+        AssertionFailedError getError() {
+            return error;
+        }
+
+    }
+
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AntlibTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AntlibTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AntlibTest.java	(revision 16139)
@@ -0,0 +1,65 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ */
+public class AntlibTest extends BuildFileTest {
+    public AntlibTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/antlib.xml");
+    }
+
+    public void testAntlibFile() {
+        expectLog("antlib.file", "MyTask called");
+    }
+
+    /**
+     * Confirms that all matching resources will be used, so that you
+     * can collect several antlibs in one Definer call.
+     * @see "http://issues.apache.org/bugzilla/show_bug.cgi?id=24024"
+     */
+    public void testAntlibResource() {
+        expectLog("antlib.resource", "MyTask called-and-then-MyTask2 called");
+    }
+
+    public void testNsCurrent() {
+        expectLog("ns.current", "Echo2 inside a macroHello from x:p");
+    }
+
+    public static class MyTask extends Task {
+        public void execute() {
+            log("MyTask called");
+        }
+    }
+
+    public static class MyTask2 extends Task {
+        public void execute() {
+            log("MyTask2 called");
+        }
+    }
+
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AvailableTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AvailableTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/AvailableTest.java	(revision 16139)
@@ -0,0 +1,205 @@
+/*
+ * Copyright  2000-2002, 2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ * JUnit test for the Available task/condition.
+ */
+public class AvailableTest extends BuildFileTest {
+
+    public AvailableTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/available.xml");
+    }
+
+    // Nothing specified -> Fail
+    public void test1() {
+        expectBuildException("test1", "required argument not specified");
+    }
+
+    // Only property specified -> Fail
+    public void test2() {
+        expectBuildException("test2", "required argument not specified");
+    }
+
+    // Only file specified -> Fail
+    public void test3() {
+        expectBuildException("test3", "required argument not specified");
+    }
+
+    // file doesn't exist -> property 'test' == null
+    public void test4() {
+        executeTarget("test4");
+        assertTrue(project.getProperty("test") == null);
+    }
+
+    // file does exist -> property 'test' == 'true'
+    public void test5() {
+        executeTarget("test5");
+        assertEquals("true", project.getProperty("test"));
+    }
+
+    // resource doesn't exist -> property 'test' == null
+    public void test6() {
+        executeTarget("test6");
+        assertTrue(project.getProperty("test") == null);
+    }
+
+    // resource does exist -> property 'test' == 'true'
+    public void test7() {
+        executeTarget("test7");
+        assertEquals("true", project.getProperty("test"));
+    }
+
+    // class doesn't exist -> property 'test' == null
+    public void test8() {
+        executeTarget("test8");
+        assertTrue(project.getProperty("test") == null);
+    }
+
+    // class does exist -> property 'test' == 'true'
+    public void test9() {
+        executeTarget("test9");
+        assertEquals("true", project.getProperty("test"));
+    }
+
+    // All three specified and all three exist -> true
+    public void test10() {
+        executeTarget("test10");
+        assertEquals("true", project.getProperty("test"));
+    }
+
+    // All three specified but class missing -> null
+    public void test11() {
+        executeTarget("test11");
+        assertNull(project.getProperty("test"));
+    }
+
+    // Specified property-name is "" -> true
+    public void test12() {
+        executeTarget("test12");
+        assertNull(project.getProperty("test"));
+        assertEquals("true", project.getProperty(""));
+    }
+
+    // Specified file is "" -> invalid files do not exist
+    public void test13() {
+        executeTarget("test13");
+        assertNull(project.getProperty("test"));
+    }
+
+    // Specified file is "" actually a directory, so it should pass
+    public void test13b() {
+        executeTarget("test13b");
+        assertEquals("true", project.getProperty("test"));
+    }
+
+    // Specified resource is "" -> can such a thing exist?
+    /*
+     * returns non null IBM JDK 1.3 Linux
+     */
+//    public void test14() {
+//        executeTarget("test14");
+//        assertEquals(project.getProperty("test"), null);
+//    }
+
+    // Specified class is "" -> can not exist
+    public void test15() {
+        executeTarget("test15");
+        assertNull(project.getProperty("test"));
+    }
+
+    // Specified dir is "" -> this is the current directory and should
+    // always exist
+    public void test16() {
+        executeTarget("test16");
+        assertEquals("true", project.getProperty("test"));
+    }
+
+    // Specified dir is "../taskdefs" -> should exist since it's the
+    // location of the buildfile used...
+    public void test17() {
+        executeTarget("test17");
+        assertEquals("true", project.getProperty("test"));
+    }
+
+    // Specified dir is "../this_dir_should_never_exist" -> null
+    public void test18() {
+        executeTarget("test18");
+        assertNull(project.getProperty("test"));
+    }
+
+    // Invalid type specified
+    public void test19() {
+        expectBuildException("test19", "Invalid value for type attribute.");
+    }
+
+    // Core class that exists in system classpath is ignored
+    public void test20() {
+        executeTarget("test20");
+        assertNull(project.getProperty("test"));
+    }
+
+    // Core class that exists in system classpath is ignored, but found in specified classpath
+    public void test21() {
+        executeTarget("test21");
+        assertEquals("true", project.getProperty("test"));
+    }
+
+    // Core class that exists in system classpath is not ignored with ignoresystemclass="false"
+    public void test22() {
+        executeTarget("test22");
+        assertEquals("true", project.getProperty("test"));
+    }
+
+    // Core class that exists in system classpath is not ignored with default ignoresystemclasses value
+    public void test23() {
+        executeTarget("test23");
+        assertEquals("true", project.getProperty("test"));
+    }
+
+    // Class is found in specified classpath
+    public void test24() {
+        executeTarget("test24");
+        assertEquals("true", project.getProperty("test"));
+    }
+
+    // File is not found in specified filepath
+    public void testSearchInPathNotThere() {
+        executeTarget("searchInPathNotThere");
+        assertNull(project.getProperty("test"));
+    }
+
+    // File is not found in specified filepath
+    public void testSearchInPathIsThere() {
+        executeTarget("searchInPathIsThere");
+        assertEquals("true", project.getProperty("test"));
+    }
+
+    // test when file begins with basedir twice
+    public void testDoubleBasedir() {
+        executeTarget("testDoubleBasedir");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/BUnzip2Test.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/BUnzip2Test.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/BUnzip2Test.java	(revision 16139)
@@ -0,0 +1,49 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+import java.io.IOException;
+
+/**
+ */
+public class BUnzip2Test extends BuildFileTest {
+
+    public BUnzip2Test(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/bunzip2.xml");
+        executeTarget("prepare");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testRealTest() throws java.io.IOException {
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        executeTarget("realTest");
+        assertTrue("File content mismatch after bunzip2",
+            fileUtils.contentEquals(project.resolveFile("expected/asf-logo-huge.tar"),
+                                    project.resolveFile("asf-logo-huge.tar")));
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/BZip2Test.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/BZip2Test.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/BZip2Test.java	(revision 16139)
@@ -0,0 +1,58 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+import java.io.IOException;
+
+/**
+ */
+public class BZip2Test extends BuildFileTest {
+
+    public BZip2Test(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/bzip2.xml");
+        executeTarget("prepare");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testRealTest() throws java.io.IOException {
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        executeTarget("realTest");
+        assertTrue("File content mismatch",
+            fileUtils.contentEquals(project.resolveFile("expected/asf-logo-huge.tar.bz2"),
+                                    project.resolveFile("asf-logo-huge.tar.bz2")));
+    }
+
+    public void testDateCheck(){
+        executeTarget("testDateCheck");
+        String log = getLog();
+        assertTrue(
+            "Expecting message ending with 'asf-logo.gif.bz2 is up to date.' but got '" + log + "'",
+            log.endsWith("asf-logo.gif.bz2 is up to date."));
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/BasenameTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/BasenameTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/BasenameTest.java	(revision 16139)
@@ -0,0 +1,83 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class BasenameTest extends BuildFileTest {
+
+    public BasenameTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/basename.xml");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required attribute missing");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "required attribute missing");
+    }
+
+    public void test3() {
+        expectBuildException("test3", "required attribute missing");
+    }
+
+    public void test4() {
+        executeTarget("test4");
+        String checkprop = project.getProperty("file.w.suf");
+        assertEquals("foo.txt", checkprop);
+    }
+
+    public void test5() {
+        executeTarget("test5");
+        String checkprop = project.getProperty("file.wo.suf");
+        assertEquals("foo", checkprop);
+    }
+
+    public void testMultipleDots() {
+        executeTarget("testMultipleDots");
+        String checkprop = project.getProperty("file.wo.suf");
+        assertEquals("foo.bar", checkprop);
+    }
+
+    public void testNoDots() {
+        executeTarget("testNoDots");
+        String checkprop = project.getProperty("file.wo.suf");
+        assertEquals("foo.bar", checkprop);
+    }
+
+    public void testValueEqualsSuffixWithDot() {
+        executeTarget("testValueEqualsSuffixWithDot");
+        String checkprop = project.getProperty("file.wo.suf");
+        assertEquals("", checkprop);
+    }
+
+    public void testValueEqualsSuffixWithoutDot() {
+        executeTarget("testValueEqualsSuffixWithoutDot");
+        String checkprop = project.getProperty("file.wo.suf");
+        assertEquals("", checkprop);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/CVSPassTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/CVSPassTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/CVSPassTest.java	(revision 16139)
@@ -0,0 +1,121 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.*;
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * Tests CVSLogin task.
+ *
+ */
+public class CVSPassTest extends BuildFileTest {
+    private final String EOL = System.getProperty("line.separator");
+    private final String JAKARTA_URL =
+        ":pserver:anoncvs@jakarta.apache.org:/home/cvspublic Ay=0=h<Z";
+    private final String XML_URL =
+        ":pserver:anoncvs@xml.apache.org:/home/cvspublic Ay=0=h<Z";
+    private final String TIGRIS_URL =
+        ":pserver:guest@cvs.tigris.org:/cvs AIbdZ,";
+
+
+    public CVSPassTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/cvspass.xml");
+    }
+
+    public void testNoCVSRoot() {
+        try{
+            executeTarget("test1");
+            fail("BuildException not thrown");
+        }catch(BuildException e){
+            assertEquals("cvsroot is required", e.getMessage());
+        }
+    }
+
+    public void testNoPassword() {
+        try{
+            executeTarget("test2");
+            fail("BuildException not thrown");
+        }catch(BuildException e){
+            assertEquals("password is required", e.getMessage());
+        }
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testPassFile() throws Exception {
+        executeTarget("test3");
+        File f = new File(getProjectDir(), "testpassfile.tmp");
+
+        assertTrue( "Passfile "+f+" not created", f.exists());
+
+        assertEquals(JAKARTA_URL+EOL, readFile(f));
+
+    }
+
+    public void testPassFileDuplicateEntry() throws Exception {
+        executeTarget("test4");
+        File f = new File(getProjectDir(), "testpassfile.tmp");
+
+        assertTrue( "Passfile "+f+" not created", f.exists());
+
+        assertEquals(
+            JAKARTA_URL+ EOL+
+            TIGRIS_URL+ EOL,
+            readFile(f));
+    }
+
+    public void testPassFileMultipleEntry() throws Exception {
+        executeTarget("test5");
+        File f = new File(getProjectDir(), "testpassfile.tmp");
+
+        assertTrue( "Passfile "+f+" not created", f.exists());
+
+        assertEquals(
+            JAKARTA_URL+ EOL+
+            XML_URL+ EOL+
+            TIGRIS_URL+ EOL,
+            readFile(f));
+    }
+
+    private String readFile(File f) throws Exception {
+        BufferedReader reader = null;
+
+        try {
+            reader = new BufferedReader(new FileReader(f));
+
+            StringBuffer buf = new StringBuffer();
+            String line=null;
+            while((line=reader.readLine())!=null){
+                buf.append(line + EOL);
+            }
+            return buf.toString();
+        } finally {
+            if (reader != null) {
+                reader.close();
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/CallTargetTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/CallTargetTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/CallTargetTest.java	(revision 16139)
@@ -0,0 +1,73 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ */
+public class CallTargetTest extends BuildFileTest {
+
+    public CallTargetTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/calltarget.xml");
+    }
+
+    // see bugrep 21724 (references not passing through with antcall)
+    public void testInheritRefFileSet() {
+        expectLogContaining("testinheritreffileset", "calltarget.xml");
+    }
+
+    // see bugrep 21724 (references not passing through with antcall)
+    public void testInheritFilterset() {
+        project.executeTarget("testinheritreffilterset");
+    }
+
+    // see bugrep 11418 (In repeated calls to the same target,
+    // params will not be passed in)
+    public void testMultiCall() {
+        Vector v = new Vector();
+        v.add("call-multi");
+        v.add("call-multi");
+        project.executeTargets(v);
+        assertLogContaining("multi is SETmulti is SET");
+    }
+
+    public void testBlankTarget() {
+        expectBuildException("blank-target", "target name must not be empty");
+    }
+
+    public void testMultipleTargets() {
+        expectLog("multiple-targets", "tadadctbdbtc");
+    }
+
+    public void testMultipleTargets2() {
+        expectLog("multiple-targets-2", "dadctb");
+    }
+
+    public void tearDown() {
+        project.executeTarget("cleanup");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ChecksumTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ChecksumTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ChecksumTest.java	(revision 16139)
@@ -0,0 +1,99 @@
+/*
+ * Copyright  2001,2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+import java.io.IOException;
+import java.io.File;
+
+/**
+ */
+public class ChecksumTest extends BuildFileTest {
+
+    public ChecksumTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/checksum.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testCreateMd5() throws IOException {
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        executeTarget("createMd5");
+        assertTrue(fileUtils.contentEquals(project.resolveFile("expected/asf-logo.gif.md5"),
+                                           project.resolveFile("../asf-logo.gif.MD5")));
+    }
+
+    public void testSetProperty() {
+        executeTarget("setProperty");
+        assertEquals("0541d3df42520911f268abc730f3afe0",
+                     project.getProperty("logo.MD5"));
+        assertTrue(!project.resolveFile("../asf-logo.gif.MD5").exists());
+    }
+
+    public void testVerifyTotal() {
+        executeTarget("verifyTotal");
+        assertEquals("ef8f1477fcc9bf93832c1a74f629c626",
+                     project.getProperty("total"));
+    }
+
+    public void testVerifyChecksumdir() {
+        executeTarget("verifyChecksumdir");
+        assertEquals("ef8f1477fcc9bf93832c1a74f629c626",
+                     project.getProperty("total"));
+        File shouldExist = project.resolveFile("checksum/checksums/foo/zap/Eenie.MD5");
+        File shouldNotExist = project.resolveFile("checksum/foo/zap/Eenie.MD5");
+        assertTrue( "Checksums should be written to " + shouldExist.getAbsolutePath(), shouldExist.exists());
+        assertTrue( "Checksums should not be written to " + shouldNotExist.getAbsolutePath(), !shouldNotExist.exists());
+    }
+
+    public void testVerifyAsTask() {
+        testVerify("verifyAsTask");
+        assertNotNull(project.getProperty("no.logo.MD5"));
+        assertEquals("false", project.getProperty("no.logo.MD5"));
+    }
+
+    public void testVerifyAsCondition() {
+        testVerify("verifyAsCondition");
+        assertNull(project.getProperty("no.logo.MD5"));
+    }
+
+    public void testVerifyFromProperty() {
+        assertNull(getProject().getProperty("verify"));
+        expectPropertySet("verifyFromProperty", "verify", "true");
+    }
+
+    public void testVerifyChecksumdirNoTotal() {
+        executeTarget("verifyChecksumdirNoTotal");
+    }
+    private void testVerify(String target) {
+        assertNull(project.getProperty("logo.MD5"));
+        assertNull(project.getProperty("no.logo.MD5"));
+        executeTarget(target);
+        assertNotNull(project.getProperty("logo.MD5"));
+        assertEquals("true", project.getProperty("logo.MD5"));
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ConcatTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ConcatTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ConcatTest.java	(revision 16139)
@@ -0,0 +1,276 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+
+/**
+ * A test class for the 'concat' task, used to concatenate a series of
+ * files into a single stream.
+ *
+ */
+public class ConcatTest
+    extends BuildFileTest {
+
+    /**
+     * The name of the temporary file.
+     */
+    private static final String tempFile = "concat.tmp";
+
+    /**
+     * The name of the temporary file.
+     */
+    private static final String tempFile2 = "concat.tmp.2";
+
+    /**
+     * Required constructor.
+     */
+    public ConcatTest(String name) {
+        super(name);
+    }
+
+    /**
+     * Test set up, called by the unit test framework prior to each
+     * test.
+     */
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/concat.xml");
+    }
+
+    /**
+     * Test tear down, called by the unit test framework prior to each
+     * test.
+     */
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    /**
+     * Expect an exception when insufficient information is provided.
+     */
+    public void test1() {
+        expectBuildException("test1", "Insufficient information.");
+    }
+
+    /**
+     * Expect an exception when the destination file is invalid.
+     */
+    public void test2() {
+        expectBuildException("test2", "Invalid destination file.");
+    }
+
+    /**
+     * Cats the string 'Hello, World!' to a temporary file.
+     */
+    public void test3() {
+
+        File file = new File(getProjectDir(), tempFile);
+        if (file.exists()) {
+            file.delete();
+        }
+
+        executeTarget("test3");
+
+        assertTrue(file.exists());
+    }
+
+    /**
+     * Cats the file created in test3 three times.
+     */
+    public void test4() {
+        test3();
+
+        File file = new File(getProjectDir(), tempFile);
+        final long origSize = file.length();
+
+        executeTarget("test4");
+
+        File file2 = new File(getProjectDir(), tempFile2);
+        final long newSize = file2.length();
+
+        assertEquals(origSize * 3, newSize);
+    }
+
+    /**
+     * Cats the string 'Hello, World!' to the console.
+     */
+    public void test5() {
+        expectLog("test5", "Hello, World!");
+    }
+
+    public void test6() {
+        String filename = "src/etc/testcases/taskdefs/thisfiledoesnotexist"
+            .replace('/', File.separatorChar);
+        expectLogContaining("test6", filename +" does not exist.");
+    }
+
+    public void testConcatNoNewline() {
+        expectLog("testConcatNoNewline", "ab");
+    }
+
+    public void testConcatNoNewlineEncoding() {
+        expectLog("testConcatNoNewlineEncoding", "ab");
+    }
+
+    public void testPath() {
+        test3();
+
+        File file = new File(getProjectDir(), tempFile);
+        final long origSize = file.length();
+
+        executeTarget("testPath");
+
+        File file2 = new File(getProjectDir(), tempFile2);
+        final long newSize = file2.length();
+
+        assertEquals(origSize, newSize);
+
+    }
+    public void testAppend() {
+        test3();
+
+        File file = new File(getProjectDir(), tempFile);
+        final long origSize = file.length();
+
+        executeTarget("testAppend");
+
+        File file2 = new File(getProjectDir(), tempFile2);
+        final long newSize = file2.length();
+
+        assertEquals(origSize*2, newSize);
+
+    }
+
+    public void testFilter() {
+        executeTarget("testfilter");
+        assertTrue(getLog().indexOf("REPLACED") > -1);
+    }
+
+    public void testNoOverwrite() {
+        executeTarget("testnooverwrite");
+        File file2 = new File(getProjectDir(), tempFile2);
+        long size = file2.length();
+        assertEquals(size, 0);
+    }
+
+    public void testheaderfooter() {
+        test3();
+        expectLog("testheaderfooter", "headerHello, World!footer");
+    }
+
+    public void testfileheader() {
+        test3();
+        expectLog("testfileheader", "Hello, World!Hello, World!");
+    }
+
+    /**
+     * Expect an exception when attempting to cat an file to itself
+     */
+    public void testsame() {
+        expectBuildException("samefile", "output file same as input");
+    }
+
+    /**
+     * Check if filter inline works
+     */
+    public void testfilterinline() {
+        executeTarget("testfilterinline");
+        assertTrue(getLog().indexOf("REPLACED") > -1);
+    }
+
+    /**
+     * Check if multireader works
+     */
+    public void testmultireader() {
+        executeTarget("testmultireader");
+        assertTrue(getLog().indexOf("Bye") > -1);
+        assertTrue(getLog().indexOf("Hello") == -1);
+    }
+    /**
+     * Check if fixlastline works
+     */
+    public void testfixlastline()
+        throws IOException
+    {
+        expectFileContains(
+            "testfixlastline", "concat.line4",
+            "end of line" + System.getProperty("line.separator")
+            + "This has");
+    }
+
+    /**
+     * Check if fixlastline works with eol
+     */
+    public void testfixlastlineeol()
+        throws IOException
+    {
+        expectFileContains(
+            "testfixlastlineeol", "concat.linecr",
+            "end of line\rThis has");
+    }
+
+    // ------------------------------------------------------
+    //   Helper methods - should be in BuildFileTest
+    // -----------------------------------------------------
+
+    private String getFileString(String filename)
+        throws IOException
+    {
+        Reader r = null;
+        try {
+            r = new FileReader(getProject().resolveFile(filename));
+            return  FileUtils.newFileUtils().readFully(r);
+        }
+        finally {
+            try {r.close();} catch (Throwable ignore) {}
+        }
+
+    }
+
+    private String getFileString(String target, String filename)
+        throws IOException
+    {
+        executeTarget(target);
+        return getFileString(filename);
+    }
+
+    private void expectFileContains(
+        String target, String filename, String contains)
+        throws IOException
+    {
+        String content = getFileString(target, filename);
+        assertTrue(
+            "expecting file " + filename + " to contain " +
+            contains +
+            " but got " + content, content.indexOf(contains) > -1);
+    }
+
+    public void testTranscoding() throws IOException {
+        executeTarget("testTranscoding");
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        File f1 = getProject().resolveFile("copy/expected/utf-8");
+        File f2 = getProject().resolveFile("concat.utf8");
+        assertTrue(fileUtils.contentEquals(f1, f2));
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ConditionTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ConditionTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ConditionTest.java	(revision 16139)
@@ -0,0 +1,233 @@
+/*
+ * Copyright  2002, 2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildFileTest;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * @created 13 January 2002
+ */
+public class ConditionTest extends BuildFileTest {
+
+    /**
+     * Constructor for the ConditionTest object
+     *
+     * @param name we dont know
+     */
+    public ConditionTest(String name) {
+        super(name);
+    }
+
+
+    /**
+     * The JUnit setup method
+     */
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/condition.xml");
+    }
+
+
+    /**
+     * The teardown method for JUnit
+     */
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testBasic() {
+       expectPropertySet("basic","basic");
+    }
+
+    public void testConditionIncomplete() {
+        expectSpecificBuildException("condition-incomplete",
+                                     "property attribute has been omitted",
+                                     "The property attribute is required.");
+    }
+
+    public void testConditionEmpty() {
+        expectSpecificBuildException("condition-empty",
+                                     "no conditions",
+                                     "You must nest a condition into <condition>");
+    }
+
+    public void testShortcut() {
+        expectPropertySet("shortcut","shortcut","set");
+    }
+
+    public void testUnset() {
+        expectPropertyUnset("dontset","dontset");
+    }
+
+    public void testSetValue() {
+        expectPropertySet("setvalue","setvalue","woowoo");
+    }
+
+    public void testNegation() {
+        expectPropertySet("negation","negation");
+    }
+
+    public void testNegationFalse() {
+        expectPropertyUnset("negationfalse","negationfalse");
+    }
+
+    public void testNegationIncomplete() {
+        expectSpecificBuildException("negationincomplete",
+                                     "no conditions in <not>",
+                                     "You must nest a condition into <not>");
+    }
+
+    public void testAnd() {
+        expectPropertySet("and","and");
+    }
+
+    public void testAndFails() {
+        expectPropertyUnset("andfails","andfails");
+    }
+
+    public void testAndIncomplete() {
+        expectPropertyUnset("andincomplete","andincomplete");
+    }
+
+    public void testAndempty() {
+        expectPropertySet("andempty","andempty");
+    }
+
+    public void testOr() {
+        expectPropertySet("or","or");
+    }
+
+    public void testOrincomplete() {
+        expectPropertySet("or","or");
+    }
+
+    public void testOrFails() {
+        expectPropertyUnset("orfails","orfails");
+    }
+
+    public void testOrboth() {
+        expectPropertySet("orboth","orboth");
+    }
+
+    public void testFilesmatchIdentical() {
+        expectPropertySet("filesmatch-identical","filesmatch-identical");
+    }
+
+
+    public void testFilesmatchIncomplete() {
+        expectSpecificBuildException("filesmatch-incomplete",
+                                     "Missing file2 attribute",
+                                     "both file1 and file2 are required in filesmatch");
+    }
+
+    public void testFilesmatchOddsizes() {
+        expectPropertyUnset("filesmatch-oddsizes","filesmatch-oddsizes");
+    }
+
+    public void testFilesmatchExistence() {
+        expectPropertyUnset("filesmatch-existence", "filesmatch-existence");
+    }
+
+    public void testFilesmatchDifferent() {
+        expectPropertyUnset("filesmatch-different","filesmatch-different");
+    }
+
+    public void testFilesmatchMatch() {
+        expectPropertySet("filesmatch-match","filesmatch-match");
+    }
+
+    public void testFilesmatchDifferentSizes() {
+        expectPropertyUnset("filesmatch-different-sizes",
+            "filesmatch-different-sizes");
+    }
+
+    public void testFilesmatchDifferentOnemissing() {
+        expectPropertyUnset("filesmatch-different-onemissing",
+            "filesmatch-different-onemissing");
+    }
+
+    public void testContains() {
+        expectPropertySet("contains","contains");
+    }
+
+
+    public void testContainsDoesnt() {
+        expectPropertyUnset("contains-doesnt","contains-doesnt");
+    }
+
+    public void testContainsAnycase() {
+        expectPropertySet("contains-anycase","contains-anycase");
+    }
+
+
+    public void testContainsIncomplete1() {
+        expectSpecificBuildException("contains-incomplete1",
+                    "Missing contains attribute",
+                    "both string and substring are required in contains");
+    }
+
+    public void testContainsIncomplete2() {
+        expectSpecificBuildException("contains-incomplete2",
+                    "Missing contains attribute",
+                    "both string and substring are required in contains");
+    }
+
+    public void testIstrue() {
+        expectPropertySet("istrue","istrue");
+    }
+
+    public void testIstrueNot() {
+        expectPropertyUnset("istrue-not","istrue-not");
+    }
+
+    public void testIstrueFalse() {
+        expectPropertyUnset("istrue-false","istrue-false");
+    }
+
+
+    public void testIstrueIncomplete1() {
+        expectSpecificBuildException("istrue-incomplete",
+                    "Missing attribute",
+                    "Nothing to test for truth");
+    }
+
+    public void testIsfalseTrue() {
+        expectPropertyUnset("isfalse-true","isfalse-true");
+    }
+
+    public void testIsfalseNot() {
+        expectPropertySet("isfalse-not","isfalse-not");
+    }
+
+    public void testIsfalseFalse() {
+        expectPropertySet("isfalse-false","isfalse-false");
+    }
+
+
+    public void testIsfalseIncomplete1() {
+        expectSpecificBuildException("isfalse-incomplete",
+                    "Missing attribute",
+                    "Nothing to test for falsehood");
+    }
+
+    public void testElse() {
+        executeTarget("testElse");
+    }
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/CopyTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/CopyTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/CopyTest.java	(revision 16139)
@@ -0,0 +1,153 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Tests FileSet using the Copy task.
+ *
+ */
+public class CopyTest extends BuildFileTest {
+
+    public CopyTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/copy.xml");
+    }
+
+    public void test1() {
+        executeTarget("test1");
+        File f = new File(getProjectDir(), "copytest1.tmp");
+        if ( !f.exists()) {
+            fail("Copy failed");
+        }
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void test2() {
+        executeTarget("test2");
+        File f = new File(getProjectDir(), "copytest1dir/copy.xml");
+        if ( !f.exists()) {
+            fail("Copy failed");
+        }
+    }
+
+    public void test3() {
+        executeTarget("test3");
+        File file3  = new File(getProjectDir(), "copytest3.tmp");
+        assertTrue(file3.exists());
+        File file3a = new File(getProjectDir(), "copytest3a.tmp");
+        assertTrue(file3a.exists());
+        File file3b = new File(getProjectDir(), "copytest3b.tmp");
+        assertTrue(file3b.exists());
+        File file3c = new File(getProjectDir(), "copytest3c.tmp");
+        assertTrue(file3c.exists());
+
+        //file length checks rely on touch generating a zero byte file
+        if(file3.length()==0) {
+            fail("could not overwrite an existing, older file");
+        }
+        if(file3c.length()!=0) {
+            fail("could not force overwrite an existing, newer file");
+        }
+        if(file3b.length()==0) {
+            fail("unexpectedly overwrote an existing, newer file");
+        }
+
+        //file time checks for java1.2+
+        if (!JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
+            assertTrue(file3a.lastModified()==file3.lastModified());
+            assertTrue(file3c.lastModified()<file3a.lastModified());
+        }
+
+    }
+
+    public void testFilterTest() {
+        executeTarget("filtertest");
+        assertTrue(getOutput().indexOf("loop in tokens") == -1);
+    }
+
+    public void testInfiniteFilter() {
+        executeTarget("infinitetest");
+        assertTrue(getOutput().indexOf("loop in tokens") != -1);
+    }
+
+    public void testFilterSet() throws IOException {
+        executeTarget("testFilterSet");
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        File tmp  = new File(getProjectDir(), "copy.filterset.tmp");
+        File check  = new File(getProjectDir(), "expected/copy.filterset.filtered");
+        assertTrue(tmp.exists());
+        assertTrue(fileUtils.contentEquals(tmp, check));
+    }
+
+    public void testFilterChain() throws IOException {
+        executeTarget("testFilterChain");
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        File tmp  = new File(getProjectDir(), "copy.filterchain.tmp");
+        File check  = new File(getProjectDir(), "expected/copy.filterset.filtered");
+        assertTrue(tmp.exists());
+        assertTrue(fileUtils.contentEquals(tmp, check));
+    }
+
+    public void testSingleFileFileset() {
+        executeTarget("test_single_file_fileset");
+        File file  = new File(getProjectDir(),
+                                        "copytest_single_file_fileset.tmp");
+        assertTrue(file.exists());
+    }
+
+    public void testTranscoding() throws IOException {
+        executeTarget("testTranscoding");
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        File f1 = getProject().resolveFile("copy/expected/utf-8");
+        File f2 = getProject().resolveFile("copytest1.tmp");
+        assertTrue(fileUtils.contentEquals(f1, f2));
+    }
+
+    public void testMissingFileIgnore() {
+        expectLogContaining("testMissingFileIgnore",
+                            "Warning: Could not find file ");
+    }
+
+    public void testMissingFileBail() {
+        expectBuildException("testMissingFileBail", "not-there doesn't exist");
+        assertTrue(getBuildException().getMessage()
+                   .startsWith("Warning: Could not find file "));
+    }
+
+    public void testMissingDirIgnore() {
+        expectLogContaining("testMissingDirIgnore", "Warning: ");
+    }
+
+    public void testMissingDirBail() {
+        expectBuildException("testMissingDirBail", "not-there doesn't exist");
+        assertTrue(getBuildException().getMessage().endsWith(" not found."));
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/CopydirTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/CopydirTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/CopydirTest.java	(revision 16139)
@@ -0,0 +1,68 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class CopydirTest extends BuildFileTest {
+
+    public CopydirTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/copydir.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument not specified");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "required argument not specified");
+    }
+
+    public void test3() {
+        expectBuildException("test3", "required argument not specified");
+    }
+
+    public void test4() {
+        expectLog("test4", "DEPRECATED - The copydir task is deprecated.  Use copy instead.Warning: src == dest");
+    }
+
+    public void test5() {
+        executeTarget("test5");
+        java.io.File f = new java.io.File(getProjectDir(), "../taskdefs.tmp");
+        if (!f.exists() || !f.isDirectory()) {
+            fail("Copy failed");
+        }
+        // We keep this, so we have something to delete in later tests :-)
+    }
+
+    public void test6() {
+        expectBuildException("test6", "target is file");
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/CopyfileTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/CopyfileTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/CopyfileTest.java	(revision 16139)
@@ -0,0 +1,68 @@
+/*
+ * Copyright  2000-2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class CopyfileTest extends BuildFileTest {
+
+    public void test6() {
+        expectBuildException("test6", "target is directory");
+    }
+
+    public CopyfileTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/copyfile.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument not specified");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "required argument not specified");
+    }
+
+    public void test3() {
+        expectBuildException("test3", "required argument not specified");
+    }
+
+    public void test4() {
+        expectLog("test4", "DEPRECATED - The copyfile task is deprecated.  Use copy instead.Warning: src == dest");
+    }
+
+    public void test5() {
+        executeTarget("test5");
+        java.io.File f = new java.io.File(getProjectDir(), "copyfile.tmp");
+        if (f.exists()) {
+            f.delete();
+        } else {
+            fail("Copy failed");
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DefaultExcludesTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DefaultExcludesTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DefaultExcludesTest.java	(revision 16139)
@@ -0,0 +1,110 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.DirectoryScanner;
+
+/**
+ */
+public class DefaultExcludesTest extends BuildFileTest {
+
+    public DefaultExcludesTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/defaultexcludes.xml");
+    }
+
+    public void tearDown() {
+        project.executeTarget("cleanup");
+    }
+
+    // Output the default excludes
+    public void test1() {
+        String[] expected = {
+                          "**/*~",
+                          "**/#*#",
+                          "**/.#*",
+                          "**/%*%",
+                          "**/._*",
+                          "**/CVS",
+                          "**/CVS/**",
+                          "**/.cvsignore",
+                          "**/SCCS",
+                          "**/SCCS/**",
+                          "**/vssver.scc",
+                          "**/.svn",
+                          "**/.svn/**",
+                          "**/.DS_Store"};
+        project.executeTarget("test1");
+        assertEquals("current default excludes", expected, DirectoryScanner.getDefaultExcludes());
+    }
+
+    // adding something to the excludes'
+    public void test2() {
+        String[] expected = {
+                          "**/*~",
+                          "**/#*#",
+                          "**/.#*",
+                          "**/%*%",
+                          "**/._*",
+                          "**/CVS",
+                          "**/CVS/**",
+                          "**/.cvsignore",
+                          "**/SCCS",
+                          "**/SCCS/**",
+                          "**/vssver.scc",
+                          "**/.svn",
+                          "**/.svn/**",
+                          "**/.DS_Store",
+                          "foo"};
+        project.executeTarget("test2");
+        assertEquals("current default excludes", expected, DirectoryScanner.getDefaultExcludes());
+    }
+
+    // removing something from the defaults
+    public void test3() {
+        String[] expected = {
+                          "**/*~",
+                          "**/#*#",
+                          "**/.#*",
+                          "**/%*%",
+                          "**/._*",
+                          //CVS missing
+                          "**/CVS/**",
+                          "**/.cvsignore",
+                          "**/SCCS",
+                          "**/SCCS/**",
+                          "**/vssver.scc",
+                          "**/.svn",
+                          "**/.svn/**",
+                          "**/.DS_Store"};
+        project.executeTarget("test3");
+        assertEquals("current default excludes", expected, DirectoryScanner.getDefaultExcludes());
+    }
+    private void assertEquals(String message, String[] expected, String[] actual) {
+        // check that both arrays have the same size
+        assertEquals(message + " : string array length match", expected.length, actual.length);
+        for (int counter=0; counter <expected.length; counter++) {
+            assertEquals(message + " : " + counter + "th element in array match", expected[counter], actual[counter]);
+        }
+
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DeleteTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DeleteTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DeleteTest.java	(revision 16139)
@@ -0,0 +1,49 @@
+/*
+ * Copyright  2000,2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class DeleteTest extends BuildFileTest {
+
+    public DeleteTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/delete.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument not specified");
+    }
+
+    public void test2() {
+        executeTarget("test2");
+    }
+
+    public void test4() {
+        executeTarget("test4");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DeltreeTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DeltreeTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DeltreeTest.java	(revision 16139)
@@ -0,0 +1,43 @@
+/*
+ * Copyright  2000,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class DeltreeTest extends BuildFileTest {
+
+    public DeltreeTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/deltree.xml");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument not specified");
+    }
+
+    public void test2() {
+        // We try to delete the directory created in CopydirTest
+        executeTarget("test2");
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DemuxOutputTask.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DemuxOutputTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DemuxOutputTask.java	(revision 16139)
@@ -0,0 +1,73 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.BuildFileTest;
+import java.util.Random;
+
+/**
+ * A simple task that prints to System.out and System.err and then catches
+ * the output which it then checks. If the output does not match, an
+ * exception is thrown
+ *
+ * @since 1.5
+ * @created 21 February 2002
+ */
+public class DemuxOutputTask extends Task {
+    private String randomOutValue;
+    private String randomErrValue;
+    private boolean outputReceived = false;
+    private boolean errorReceived = false;
+
+    public void execute() {
+        Random generator = new Random();
+        randomOutValue = "Output Value is " + generator.nextInt();
+        randomErrValue = "Error Value is " + generator.nextInt();
+
+        System.out.println(randomOutValue);
+        System.err.println(randomErrValue);
+        if (!outputReceived) {
+            throw new BuildException("Did not receive output");
+        }
+
+        if (!errorReceived) {
+            throw new BuildException("Did not receive error");
+        }
+    }
+
+    protected void handleOutput(String line) {
+        line = line.trim();
+        if (line.length() != 0 && !line.equals(randomOutValue)) {
+            String message = "Received = [" + line + "], expected = ["
+                + randomOutValue + "]";
+            throw new BuildException(message);
+        }
+        outputReceived = true;
+    }
+
+    protected void handleErrorOutput(String line) {
+        line = line.trim();
+        if (line.length() != 0 && !line.equals(randomErrValue)) {
+            String message = "Received = [" + line + "], expected = ["
+                + randomErrValue + "]";
+            throw new BuildException(message);
+        }
+        errorReceived = true;
+    }
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DependSetTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DependSetTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DependSetTest.java	(revision 16139)
@@ -0,0 +1,64 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * Tests DependSet.
+ *
+ */
+public class DependSetTest extends BuildFileTest {
+
+    public DependSetTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/dependset.xml");
+    }
+
+    public void test1() {
+       expectBuildException("test1","At least one <srcfileset> or <srcfilelist> element must be set");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void test2() {
+       expectBuildException("test2","At least one <targetfileset> or <targetfilelist> element must be set");
+    }
+
+    public void test3() {
+       expectBuildException("test1","At least one <srcfileset> or <srcfilelist> element must be set");
+    }
+
+    public void test4() {
+        executeTarget("test4");
+    }
+
+    public void test5() {
+        executeTarget("test5");
+        java.io.File f = new java.io.File(getProjectDir(), "older.tmp");
+        if (f.exists()) {
+           fail("dependset failed to remove out of date file " + f.toString());
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DirnameTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DirnameTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DirnameTest.java	(revision 16139)
@@ -0,0 +1,66 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class DirnameTest extends BuildFileTest {
+
+    public DirnameTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/dirname.xml");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required attribute missing");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "required attribute missing");
+    }
+
+    public void test3() {
+        expectBuildException("test3", "required attribute missing");
+    }
+
+    public void test4() {
+        executeTarget("test4");
+        String filesep = System.getProperty("file.separator");
+        String expected = filesep + "usr" + filesep + "local";
+        String checkprop = project.getProperty("local.dir");
+        if (!checkprop.equals(expected)) {
+            fail("dirname failed");
+        }
+    }
+
+    public void test5() {
+        executeTarget("test5");
+        String expected = project.getProperty("basedir");
+        String checkprop = project.getProperty("base.dir");
+        if (!checkprop.equals(expected)) {
+            fail("dirname failed");
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DynamicTask.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DynamicTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DynamicTask.java	(revision 16139)
@@ -0,0 +1,45 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.DynamicConfigurator;
+import org.apache.tools.ant.Task;
+
+public class DynamicTask extends Task implements DynamicConfigurator {
+
+    public void execute() {
+    }
+
+    public void setDynamicAttribute(String name, String value) {
+        getProject().setNewProperty(name, value);
+    }
+
+    public Object createDynamicElement(String name) {
+        return new Sub();
+    }
+
+    public class Sub implements DynamicConfigurator {
+        public void setDynamicAttribute(String name, String value) {
+            getProject().setNewProperty(name, value);
+        }
+
+        public Object createDynamicElement(String name) {
+            return null;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DynamicTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DynamicTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/DynamicTest.java	(revision 16139)
@@ -0,0 +1,39 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+
+public class DynamicTest extends BuildFileTest {
+
+    public DynamicTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/dynamictask.xml");
+    }
+
+    public void testSimple() {
+        executeTarget("simple");
+        assertEquals("1", project.getProperty("prop1"));
+        assertEquals("2", project.getProperty("prop2"));
+        assertEquals("3", project.getProperty("prop3"));
+        assertEquals("4", project.getProperty("prop4"));
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/EchoTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/EchoTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/EchoTest.java	(revision 16139)
@@ -0,0 +1,53 @@
+/*
+ * Copyright  2000,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class EchoTest extends BuildFileTest {
+
+    public EchoTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/echo.xml");
+    }
+
+    // Output an empty String
+    public void test1() {
+        expectLog("test1", "");
+    }
+
+    // Output 'OUTPUT OF ECHO'
+    public void test2() {
+        expectLog("test2", "OUTPUT OF ECHO");
+    }
+
+    public void test3() {
+        expectLog("test3", "\n"+
+                              "    This \n"+
+                              "    is\n"+
+                              "    a \n"+
+                              "    multiline\n"+
+                              "    message\n"+
+                              "    ");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ExecTaskTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ExecTaskTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ExecTaskTest.java	(revision 16139)
@@ -0,0 +1,481 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation.
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.util.FileUtils;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.GregorianCalendar;
+
+import junit.framework.ComparisonFailure;
+
+/**
+ * Unit test for the &lt;exec&gt; task.
+ */
+public class ExecTaskTest extends BuildFileTest {
+    private static final String BUILD_PATH = "src/etc/testcases/taskdefs/exec/";
+    private static final String BUILD_FILE = BUILD_PATH + "exec.xml";
+    private final int TIME_TO_WAIT = 1;
+    /** maximum time allowed for the build in milliseconds */
+    private final int MAX_BUILD_TIME = 4000;
+    private final int SECURITY_MARGIN = 2000; // wait 2 second extras
+    // the test failed with 100 ms of margin on cvs.apache.org on August 1st, 2003
+    private File logFile;
+    private MonitoredBuild myBuild = null;
+    volatile private boolean buildFinished = false;
+    public ExecTaskTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject(BUILD_FILE);
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+        if (logFile != null && logFile.exists()) {
+            logFile.delete();
+        }
+    }
+
+    public void testNoRedirect() {
+        executeTarget("no-redirect");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        assertEquals("unexpected log content",
+            getProject().getProperty("ant.file") + " out"
+            + getProject().getProperty("ant.file") + " err", getLog());
+    }
+
+    public void testRedirect1() throws IOException {
+        executeTarget("redirect1");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        String expectedOut = getProject().getProperty("ant.file") + " out\n"
+            + getProject().getProperty("ant.file") + " err\n";
+
+        assertEquals("unexpected output",
+            expectedOut, getFileString("redirect.out"));
+    }
+
+    public void testRedirect2() throws IOException {
+        executeTarget("redirect2");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+
+        assertEquals("unexpected output",
+            getProject().getProperty("ant.file") + " out\n",
+            getFileString("redirect.out"));
+        assertEquals("unexpected error output",
+            getProject().getProperty("ant.file") + " err\n",
+            getFileString("redirect.err"));
+    }
+
+    public void testRedirect3() throws IOException {
+        executeTarget("redirect3");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        assertEquals("unexpected log content",
+            getProject().getProperty("ant.file") + " err", getLog());
+        String expectedOut = getProject().getProperty("ant.file") + " out\n";
+
+        assertEquals("unexpected output",
+            expectedOut, getFileString("redirect.out"));
+        assertPropertyEquals("redirect.out", expectedOut.trim());
+    }
+
+    public void testRedirect4() throws IOException {
+        executeTarget("redirect4");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        String expectedOut = getProject().getProperty("ant.file") + " out\n";
+        String expectedErr = getProject().getProperty("ant.file") + " err\n";
+
+        assertEquals("unexpected output",
+            expectedOut, getFileString("redirect.out"));
+        assertPropertyEquals("redirect.out", expectedOut.trim());
+        assertEquals("unexpected error output",
+            expectedErr, getFileString("redirect.err"));
+        assertPropertyEquals("redirect.err", expectedErr.trim());
+    }
+
+    public void testRedirect5() throws IOException {
+        testRedirect5or6("redirect5");
+    }
+
+    public void testRedirect6() throws IOException {
+        testRedirect5or6("redirect6");
+    }
+
+    public void testRedirect5or6(String target) throws IOException {
+        executeTarget(target);
+        if (getProject().getProperty("wc.can.run") == null) {
+            return;
+        }
+
+        assertEquals("unexpected output", "3", getFileString("redirect.out").trim());
+        assertEquals("property redirect.out", "3",
+            getProject().getProperty("redirect.out").trim());
+        assertNull("unexpected error output", getFileString("redirect.err"));
+        assertPropertyEquals("redirect.err", "");
+    }
+
+    public void testRedirect7() throws IOException {
+        executeTarget("redirect7");
+        if (getProject().getProperty("wc.can.run") == null) {
+            return;
+        }
+
+        assertEquals("unexpected output", "3", getFileString("redirect.out").trim());
+        assertEquals("property redirect.out", "3",
+            getProject().getProperty("redirect.out").trim());
+        assertNull("unexpected error output", getFileString("redirect.err"));
+    }
+
+    public void testRedirector1() {
+        executeTarget("init");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        expectBuildException("redirector1", "cannot have > 1 nested <redirector>s");
+    }
+
+    public void testRedirector2() throws IOException {
+        executeTarget("redirector2");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+
+        assertEquals("unexpected output",
+            getProject().getProperty("ant.file") + " out\n"
+            + getProject().getProperty("ant.file") + " err\n",
+            getFileString("redirector.out"));
+    }
+
+    public void testRedirector3() throws IOException {
+        executeTarget("redirector3");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+
+        assertEquals("unexpected output",
+            getProject().getProperty("ant.file") + " out\n",
+            getFileString("redirector.out"));
+        assertEquals("unexpected error output",
+            getProject().getProperty("ant.file") + " err\n",
+            getFileString("redirector.err"));
+    }
+
+    public void testRedirector4() throws IOException {
+        executeTarget("redirector4");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        String expectedOut = getProject().getProperty("ant.file") + " out\n";
+
+        assertEquals("unexpected log content",
+            getProject().getProperty("ant.file") + " err", getLog());
+        assertEquals("unexpected output", expectedOut,
+            getFileString("redirector.out"));
+        assertPropertyEquals("redirector.out", expectedOut.trim());
+    }
+
+    public void testRedirector5() throws IOException {
+        testRedirector5or6("redirector5");
+    }
+
+    public void testRedirector6() throws IOException {
+        testRedirector5or6("redirector6");
+    }
+
+    private void testRedirector5or6(String target) throws IOException {
+        executeTarget(target);
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        String expectedOut = getProject().getProperty("ant.file") + " out\n";
+        String expectedErr = getProject().getProperty("ant.file") + " err\n";
+
+        assertEquals("unexpected output", expectedOut,
+            getFileString("redirector.out"));
+        assertPropertyEquals("redirector.out", expectedOut.trim());
+        assertEquals("unexpected error output", expectedErr,
+            getFileString("redirector.err"));
+        assertPropertyEquals("redirector.err", expectedErr.trim());
+    }
+
+    public void testRedirector7() throws IOException {
+        executeTarget("redirector7");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        String expectedOut = getProject().getProperty("ant.file") + " out\n";
+        String expectedErr = getProject().getProperty("ant.file") + " ERROR!!!\n";
+
+        assertEquals("unexpected output", expectedOut,
+            getFileString("redirector.out"));
+        assertPropertyEquals("redirector.out", expectedOut.trim());
+        assertEquals("unexpected error output", expectedErr,
+            getFileString("redirector.err"));
+        assertPropertyEquals("redirector.err", expectedErr.trim());
+    }
+
+    public void testRedirector8() throws IOException {
+        executeTarget("redirector8");
+        if (getProject().getProperty("wc.can.run") == null) {
+            return;
+        }
+
+        assertEquals("unexpected output", "3", getFileString("redirector.out").trim());
+        assertEquals("property redirector.out", "3",
+            getProject().getProperty("redirector.out").trim());
+        assertNull("unexpected error output", getFileString("redirector.err"));
+        assertPropertyEquals("redirector.err", "");
+    }
+
+    public void testRedirector9() throws IOException {
+        testRedirector9Thru12("redirector9");
+    }
+
+    public void testRedirector10() throws IOException {
+        testRedirector9Thru12("redirector10");
+    }
+
+    public void testRedirector11() throws IOException {
+        testRedirector9Thru12("redirector11");
+    }
+
+    public void testRedirector12() throws IOException {
+        testRedirector9Thru12("redirector12");
+    }
+
+    private void testRedirector9Thru12(String target) throws IOException {
+        executeTarget(target);
+        if (getProject().getProperty("cat.can.run") == null) {
+            return;
+        }
+        String expectedOut = "blah after blah";
+
+        assertEquals("unexpected output",
+            expectedOut, getFileString("redirector.out").trim());
+        assertPropertyEquals("redirector.out", expectedOut.trim());
+        assertNull("unexpected error output", getFileString("redirector.err"));
+        assertPropertyEquals("redirector.err", "");
+    }
+
+    public void testRedirector13() {
+        executeTarget("redirector13");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        String antfile = getProject().getProperty("ant.file");
+        try {
+            //no point in setting a message
+            assertEquals(antfile + " OUTPUT???" + antfile + " ERROR!!!", getLog());
+        } catch (ComparisonFailure cf) {
+            assertEquals("unexpected log content",
+                antfile + " ERROR!!!" + antfile + " OUTPUT???", getLog());
+        }
+    }
+
+    public void testRedirector14() {
+        executeTarget("redirector14");
+        if (getProject().getProperty("cat.can.run") == null) {
+            return;
+        }
+        assertEquals("unexpected log output", "blah after blah", getLog());
+    }
+
+    public void testRedirector15() throws IOException {
+        executeTarget("redirector15");
+        if (getProject().getProperty("cat.can.run") == null) {
+            return;
+        }
+        assertTrue("error with transcoding",
+            FileUtils.newFileUtils().contentEquals(
+            getProject().resolveFile("expected/utf-8"),
+            getProject().resolveFile("redirector.out")));
+    }
+
+    public void testRedirector16() {
+        executeTarget("redirector16");
+    }
+
+    public void testRedirector17() {
+        executeTarget("redirector17");
+    }
+
+    public void testRedirector18() {
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        expectLog("redirector18", getProject().getProperty("ant.file")
+            + " out" + getProject().getProperty("ant.file") + " err");
+    }
+
+    public void testspawn() {
+        project.executeTarget("init");
+        if (project.getProperty("test.can.run") == null) {
+            return;
+        }
+        myBuild = new MonitoredBuild(new File(BUILD_FILE), "spawn");
+        FileUtils fileutils  = FileUtils.newFileUtils();
+        logFile = fileutils.createTempFile("spawn","log", project.getBaseDir());
+        // this is guaranteed by FileUtils#createTempFile
+        assertTrue("log file not existing", !logFile.exists());
+        // make the spawned process run 4 seconds
+        myBuild.setTimeToWait(TIME_TO_WAIT);
+        myBuild.setLogFile(logFile.getAbsolutePath());
+        myBuild.addBuildListener(new MonitoredBuildListener());
+        myBuild.start();
+        GregorianCalendar startwait = new GregorianCalendar();
+        // this loop runs parallel to the build
+        while (!buildFinished) {
+            try {
+                Thread.sleep(10);
+            } catch (InterruptedException e) {
+                System.out.println("my sleep was interrupted");
+            }
+            GregorianCalendar now = new GregorianCalendar();
+            // security
+            if (now.getTime().getTime() - startwait.getTime().getTime() > MAX_BUILD_TIME) {
+                System.out.println("aborting wait, too long " + (now.getTime().getTime() - startwait.getTime().getTime()) + "milliseconds");
+                break;
+            }
+        }
+        // now wait until the spawned process is finished
+        try {
+            Thread.sleep((TIME_TO_WAIT) * 1000 + SECURITY_MARGIN);
+        } catch (InterruptedException e) {
+            System.out.println("my sleep was interrupted");
+        }
+        // time of the build in milli seconds
+        long elapsed = myBuild.getTimeElapsed();
+        assertTrue("we waited more than the process lasted", TIME_TO_WAIT * 1000
+                + SECURITY_MARGIN > elapsed);
+        logFile = new File(logFile.getAbsolutePath());
+        assertTrue("log file found after spawn", logFile.exists());
+    }
+
+    private static class MonitoredBuild implements Runnable {
+        private Thread worker;
+        private File myBuildFile = null;
+        private String target = null;
+        private Project project = null;
+        private int timeToWait = 0;
+        private String logFile = null;
+        private GregorianCalendar timeStarted = null;
+        private GregorianCalendar timeFinished = null;
+
+        public void setLogFile(String logFile) {
+            this.logFile = logFile;
+            project.setProperty("logFile", logFile);
+        }
+
+        public void setTimeToWait(int timeToWait) {
+            this.timeToWait = timeToWait;
+            project.setProperty("timeToWait", Long.toString(timeToWait));
+        }
+
+        public void addBuildListener(BuildListener bl) {
+            project.addBuildListener(bl);
+        }
+        public MonitoredBuild(File buildFile, String target) {
+            myBuildFile = buildFile;
+            this.target = target;
+            project=new Project();
+            project = new Project();
+            project.init();
+            project.setUserProperty( "ant.file" , myBuildFile.getAbsolutePath() );
+            ProjectHelper.configureProject(project, myBuildFile);
+        }
+        /**
+         *
+         * @return time in millis of the build
+         */
+        public long getTimeElapsed() {
+            return timeFinished.getTime().getTime() - timeStarted.getTime().getTime();
+        }
+        public void start() {
+            worker = new Thread(this, myBuildFile.toString() + "/" + target);
+            worker.start();
+        }
+        public void run() {
+            startProject();
+        }
+        private void startProject() {
+            timeStarted = new GregorianCalendar();
+            project.executeTarget(target);
+            timeFinished = new GregorianCalendar();
+        }
+    }
+    private class MonitoredBuildListener implements BuildListener {
+        public void buildStarted(BuildEvent event) {
+        }
+
+        public void buildFinished(BuildEvent event) {
+        }
+
+        public void targetStarted(BuildEvent event) {
+        }
+
+        public void targetFinished(BuildEvent event) {
+            if (event.getTarget().getName().equals("spawn")) {
+                buildFinished = true;
+            }
+        }
+
+        public void taskStarted(BuildEvent event) {
+        }
+
+        public void taskFinished(BuildEvent event) {
+        }
+
+        public void messageLogged(BuildEvent event) {
+        }
+    }
+
+    //borrowed from TokenFilterTest
+    private String getFileString(String filename) throws IOException {
+        String result = null;
+        FileReader reader = null;
+        try {
+            reader = new FileReader(getProject().resolveFile(filename));
+            result = FileUtils.newFileUtils().readFully(reader);
+        } catch (IOException eyeOhEx) {
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (Throwable ignore) {
+                }
+            }
+        }
+        return result;
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ExecuteJavaTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ExecuteJavaTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ExecuteJavaTest.java	(revision 16139)
@@ -0,0 +1,110 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+import java.io.File;
+
+import junit.framework.TestCase;
+
+/**
+ * Simple testcase for the ExecuteJava class - mostly stolen from
+ * ExecuteWatchdogTest.
+ *
+ */
+public class ExecuteJavaTest extends TestCase {
+
+    private final static int TIME_OUT = 5000;
+
+    private final static int CLOCK_ERROR=200;
+    private final static int TIME_OUT_TEST=TIME_OUT-CLOCK_ERROR;
+
+    private ExecuteJava ej;
+    private Project project;
+
+    public ExecuteJavaTest(String name) {
+        super(name);
+    }
+
+    protected void setUp(){
+        ej = new ExecuteJava();
+        ej.setTimeout(new Long(TIME_OUT));
+        project = new Project();
+        project.setBasedir(".");
+        ej.setClasspath(new Path(project, getTestClassPath()));
+    }
+
+    private Commandline getCommandline(int timetorun) throws Exception {
+        Commandline cmd = new Commandline();
+        cmd.setExecutable(TimeProcess.class.getName());
+        cmd.createArgument().setValue(String.valueOf(timetorun));
+        return cmd;
+    }
+
+    public void testNoTimeOut() throws Exception {
+        Commandline cmd = getCommandline(TIME_OUT/2);
+        ej.setJavaCommand(cmd);
+        ej.execute(project);
+        assertTrue("process should not have been killed", !ej.killedProcess());
+    }
+
+    // test that the watchdog ends the process
+    public void testTimeOut() throws Exception {
+        Commandline cmd = getCommandline(TIME_OUT*2);
+        ej.setJavaCommand(cmd);
+        long now = System.currentTimeMillis();
+        ej.execute(project);
+        long elapsed = System.currentTimeMillis() - now;
+        assertTrue("process should have been killed", ej.killedProcess());
+
+        assertTrue("elapse time of "+elapsed
+                   +" ms is less than timeout value of "+TIME_OUT_TEST+" ms",
+                   elapsed >= TIME_OUT_TEST);
+        assertTrue("elapse time of "+elapsed
+                   +" ms is greater than run value of "+(TIME_OUT*2)+" ms",
+                   elapsed < TIME_OUT*2);
+    }
+
+
+    /**
+     * Dangerous method to obtain the classpath for the test. This is
+     * severely tighted to the build.xml properties.
+     */
+    private static String getTestClassPath(){
+        String classpath = System.getProperty("build.tests");
+        if (classpath == null) {
+            System.err.println("WARNING: 'build.tests' property is not available !");
+            classpath = System.getProperty("java.class.path");
+        }
+
+        // JDK 1.1 needs classes.zip in -classpath argument
+        if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
+            classpath +=   File.pathSeparator
+                + System.getProperty("java.home")
+                + File.separator + "lib"
+                + File.separator + "classes.zip";
+        }
+
+        return classpath;
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ExecuteOnTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ExecuteOnTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ExecuteOnTest.java	(revision 16139)
@@ -0,0 +1,583 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * Unit test for the &lt;apply&gt; task.
+ */
+public class ExecuteOnTest extends BuildFileTest {
+    private static final String BUILD_PATH = "src/etc/testcases/taskdefs/exec/";
+    private static final String BUILD_FILE = BUILD_PATH + "apply.xml";
+    
+    public ExecuteOnTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject(BUILD_FILE);
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testNoRedirect() {
+        executeTarget("no-redirect");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+
+        String log = getLog();
+        File x = getProject().resolveFile("x");
+        File y = getProject().resolveFile("y");
+        File z = getProject().resolveFile("z");
+        int xout = log.indexOf(x + " out");
+        int yout = log.indexOf(y + " out");
+        int zout = log.indexOf(z + " out");
+        int xerr = log.indexOf(x + " err");
+        int yerr = log.indexOf(y + " err");
+        int zerr = log.indexOf(z + " err");
+        assertFalse("xout=" + xout, xout < 0);
+        assertFalse("yout=" + yout, yout < 0);
+        assertFalse("zout=" + zout, zout < 0);
+        assertFalse("xerr=" + xerr, xerr < 0);
+        assertFalse("yerr=" + yerr, yerr < 0);
+        assertFalse("zerr=" + zerr, zerr < 0);
+        assertFalse("yout < xout", yout < xout);
+        assertFalse("zout < yout", zout < yout);
+        assertFalse("yerr < xerr", yerr < xerr);
+        assertFalse("zerr < yerr", zerr < yerr);
+    }
+
+    public void testRedirect1() throws IOException {
+        executeTarget("redirect1");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        String actualOut = getFileString("redirect.out");
+
+        File x = getProject().resolveFile("x");
+        File y = getProject().resolveFile("y");
+        File z = getProject().resolveFile("z");
+        int xout = actualOut.indexOf(x + " out");
+        int yout = actualOut.indexOf(y + " out");
+        int zout = actualOut.indexOf(z + " out");
+        int xerr = actualOut.indexOf(x + " err");
+        int yerr = actualOut.indexOf(y + " err");
+        int zerr = actualOut.indexOf(z + " err");
+        assertFalse("xout=" + xout, xout < 0);
+        assertFalse("yout=" + yout, yout < 0);
+        assertFalse("zout=" + zout, zout < 0);
+        assertFalse("xerr=" + xerr, xerr < 0);
+        assertFalse("yerr=" + yerr, yerr < 0);
+        assertFalse("zerr=" + zerr, zerr < 0);
+        assertFalse("yout < xout", yout < xout);
+        assertFalse("zout < yout", zout < yout);
+        assertFalse("yerr < xerr", yerr < xerr);
+        assertFalse("zerr < yerr", zerr < yerr);
+    }
+
+    public void testRedirect2() throws IOException {
+        executeTarget("redirect2");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        String actualOut = getFileString("redirect.out");
+        String actualErr = getFileString("redirect.err");
+
+        File x = getProject().resolveFile("x");
+        File y = getProject().resolveFile("y");
+        File z = getProject().resolveFile("z");
+        int xout = actualOut.indexOf(x + " out");
+        int yout = actualOut.indexOf(y + " out");
+        int zout = actualOut.indexOf(z + " out");
+        int xerr = actualErr.indexOf(x + " err");
+        int yerr = actualErr.indexOf(y + " err");
+        int zerr = actualErr.indexOf(z + " err");
+        assertFalse("xout=" + xout, xout < 0);
+        assertFalse("yout=" + yout, yout < 0);
+        assertFalse("zout=" + zout, zout < 0);
+        assertFalse("xerr=" + xerr, xerr < 0);
+        assertFalse("yerr=" + yerr, yerr < 0);
+        assertFalse("zerr=" + zerr, zerr < 0);
+        assertFalse("yout < xout", yout < xout);
+        assertFalse("zout < yout", zout < yout);
+        assertFalse("yerr < xerr", yerr < xerr);
+        assertFalse("zerr < yerr", zerr < yerr);
+    }
+
+    public void testRedirect3() throws IOException {
+        executeTarget("redirect3");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        String actualOut = getFileString("redirect.out");
+
+        File x = getProject().resolveFile("x");
+        File y = getProject().resolveFile("y");
+        File z = getProject().resolveFile("z");
+        int xout = actualOut.indexOf(x + " out");
+        int yout = actualOut.indexOf(y + " out");
+        int zout = actualOut.indexOf(z + " out");
+        int xerr = getLog().indexOf(x + " err");
+        int yerr = getLog().indexOf(y + " err");
+        int zerr = getLog().indexOf(z + " err");
+        assertFalse("xout=" + xout, xout < 0);
+        assertFalse("yout=" + yout, yout < 0);
+        assertFalse("zout=" + zout, zout < 0);
+        assertFalse("xerr=" + xerr, xerr < 0);
+        assertFalse("yerr=" + yerr, yerr < 0);
+        assertFalse("zerr=" + zerr, zerr < 0);
+        assertFalse("yout < xout", yout < xout);
+        assertFalse("zout < yout", zout < yout);
+        assertFalse("yerr < xerr", yerr < xerr);
+        assertFalse("zerr < yerr", zerr < yerr);
+
+        String outProperty = getProject().getProperty("redirect.out");
+        int pxout = outProperty.indexOf(x + " out");
+        int pyout = outProperty.indexOf(y + " out");
+        int pzout = outProperty.indexOf(z + " out");
+        assertFalse("pxout=" + pxout, pxout < 0);
+        assertFalse("pyout=" + pyout, pyout < 0);
+        assertFalse("pzout=" + pzout, pzout < 0);
+        assertFalse("pyout < pxout", pyout < pxout);
+        assertFalse("pzout < pyout", pzout < pyout);
+    }
+
+    public void testRedirect4() throws IOException {
+        executeTarget("redirect4");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        String actualOut = getFileString("redirect.out");
+        String actualErr = getFileString("redirect.err");
+
+        File x = getProject().resolveFile("x");
+        File y = getProject().resolveFile("y");
+        File z = getProject().resolveFile("z");
+        int xout = actualOut.indexOf(x + " out");
+        int yout = actualOut.indexOf(y + " out");
+        int zout = actualOut.indexOf(z + " out");
+        int xerr = actualErr.indexOf(x + " err");
+        int yerr = actualErr.indexOf(y + " err");
+        int zerr = actualErr.indexOf(z + " err");
+        assertFalse("xout=" + xout, xout < 0);
+        assertFalse("yout=" + yout, yout < 0);
+        assertFalse("zout=" + zout, zout < 0);
+        assertFalse("xerr=" + xerr, xerr < 0);
+        assertFalse("yerr=" + yerr, yerr < 0);
+        assertFalse("zerr=" + zerr, zerr < 0);
+        assertFalse("yout < xout", yout < xout);
+        assertFalse("zout < yout", zout < yout);
+        assertFalse("yerr < xerr", yerr < xerr);
+        assertFalse("zerr < yerr", zerr < yerr);
+
+        String outProperty = getProject().getProperty("redirect.out");
+        int pxout = outProperty.indexOf(x + " out");
+        int pyout = outProperty.indexOf(y + " out");
+        int pzout = outProperty.indexOf(z + " out");
+        assertFalse("pxout=" + pxout, pxout < 0);
+        assertFalse("pyout=" + pyout, pyout < 0);
+        assertFalse("pzout=" + pzout, pzout < 0);
+        assertFalse("pyout < pxout", pyout < pxout);
+        assertFalse("pzout < pyout", pzout < pyout);
+
+        String errorProperty = getProject().getProperty("redirect.err");
+        int pxerr = errorProperty.indexOf(x + " err");
+        int pyerr = errorProperty.indexOf(y + " err");
+        int pzerr = errorProperty.indexOf(z + " err");
+        assertFalse("pxerr=" + pxerr, pxerr < 0);
+        assertFalse("pyerr=" + pyerr, pyerr < 0);
+        assertFalse("pzerr=" + pzerr, pzerr < 0);
+        assertFalse("pyerr < pxerr", pyerr < pxerr);
+        assertFalse("pzerr < pyerr", pzerr < pyerr);
+    }
+
+    public void testRedirect5() throws IOException {
+        testRedirect5or6("redirect5");
+    }
+
+    public void testRedirect6() throws IOException {
+        testRedirect5or6("redirect6");
+    }
+
+    private void testRedirect5or6(String target) throws IOException {
+        executeTarget(target);
+        if (getProject().getProperty("sed.can.run") == null) {
+            return;
+        }
+
+        assertPropertyEquals("redirect.out", getProject().replaceProperties(
+            "blah y z${line.separator}x blah z${line.separator}x y blah"));
+        assertPropertyEquals("redirect.err", "");
+        assertEquals("unexpected output",
+            "blah y z\nx blah z\nx y blah\n", getFileString("redirect.out"));
+        assertNull("unexpected error output", getFileString("redirect.err"));
+    }
+
+    public void testRedirect7() throws IOException {
+        executeTarget("redirect7");
+        if (getProject().getProperty("sed.can.run") == null) {
+            return;
+        }
+
+        assertPropertyEquals("redirect.out", "blah y z");
+        assertPropertyUnset("redirect.err");
+        assertEquals("unexpected output",
+            "x y blah\n", getFileString("redirect.out"));
+        assertNull("unexpected error output", getFileString("redirect.err"));
+    }
+
+    public void testRedirector1() {
+        executeTarget("init");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        expectBuildException("redirector1", "cannot have > 1 nested <redirector>s");
+    }
+
+    public void testRedirector2() throws IOException {
+        executeTarget("redirector2");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+
+        String actualOut = getFileString("redirector.out");
+
+        File x = getProject().resolveFile("x");
+        File y = getProject().resolveFile("y");
+        File z = getProject().resolveFile("z");
+        int xout = actualOut.indexOf(x + " out");
+        int yout = actualOut.indexOf(y + " out");
+        int zout = actualOut.indexOf(z + " out");
+        int xerr = actualOut.indexOf(x + " err");
+        int yerr = actualOut.indexOf(y + " err");
+        int zerr = actualOut.indexOf(z + " err");
+        assertFalse("xout=" + xout, xout < 0);
+        assertFalse("yout=" + yout, yout < 0);
+        assertFalse("zout=" + zout, zout < 0);
+        assertFalse("xerr=" + xerr, xerr < 0);
+        assertFalse("yerr=" + yerr, yerr < 0);
+        assertFalse("zerr=" + zerr, zerr < 0);
+        assertFalse("yout < xout", yout < xout);
+        assertFalse("zout < yout", zout < yout);
+        assertFalse("yerr < xerr", yerr < xerr);
+        assertFalse("zerr < yerr", zerr < yerr);
+    }
+
+    public void testRedirector3() throws IOException {
+        executeTarget("redirector3");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+
+        String actualOut = getFileString("redirector.out");
+        String actualErr = getFileString("redirector.err");
+
+        File x = getProject().resolveFile("x");
+        File y = getProject().resolveFile("y");
+        File z = getProject().resolveFile("z");
+        int xout = actualOut.indexOf(x + " out");
+        int yout = actualOut.indexOf(y + " out");
+        int zout = actualOut.indexOf(z + " out");
+        int xerr = actualErr.indexOf(x + " err");
+        int yerr = actualErr.indexOf(y + " err");
+        int zerr = actualErr.indexOf(z + " err");
+        assertFalse("xout=" + xout, xout < 0);
+        assertFalse("yout=" + yout, yout < 0);
+        assertFalse("zout=" + zout, zout < 0);
+        assertFalse("xerr=" + xerr, xerr < 0);
+        assertFalse("yerr=" + yerr, yerr < 0);
+        assertFalse("zerr=" + zerr, zerr < 0);
+        assertFalse("yout < xout", yout < xout);
+        assertFalse("zout < yout", zout < yout);
+        assertFalse("yerr < xerr", yerr < xerr);
+        assertFalse("zerr < yerr", zerr < yerr);
+    }
+
+    public void testRedirector4() throws IOException {
+        executeTarget("redirector4");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        String actualOut = getFileString("redirector.out");
+
+        File x = getProject().resolveFile("x");
+        File y = getProject().resolveFile("y");
+        File z = getProject().resolveFile("z");
+        int xout = actualOut.indexOf(x + " out");
+        int yout = actualOut.indexOf(y + " out");
+        int zout = actualOut.indexOf(z + " out");
+        int xerr = getLog().indexOf(x + " err");
+        int yerr = getLog().indexOf(y + " err");
+        int zerr = getLog().indexOf(z + " err");
+        assertFalse("xout=" + xout, xout < 0);
+        assertFalse("yout=" + yout, yout < 0);
+        assertFalse("zout=" + zout, zout < 0);
+        assertFalse("xerr=" + xerr, xerr < 0);
+        assertFalse("yerr=" + yerr, yerr < 0);
+        assertFalse("zerr=" + zerr, zerr < 0);
+        assertFalse("yout < xout", yout < xout);
+        assertFalse("zout < yout", zout < yout);
+        assertFalse("yerr < xerr", yerr < xerr);
+        assertFalse("zerr < yerr", zerr < yerr);
+
+        String outProperty = getProject().getProperty("redirector.out");
+        int pxout = outProperty.indexOf(x + " out");
+        int pyout = outProperty.indexOf(y + " out");
+        int pzout = outProperty.indexOf(z + " out");
+        assertFalse("pxout=" + pxout, pxout < 0);
+        assertFalse("pyout=" + pyout, pyout < 0);
+        assertFalse("pzout=" + pzout, pzout < 0);
+        assertFalse("pyout < pxout", pyout < pxout);
+        assertFalse("pzout < pyout", pzout < pyout);
+    }
+
+    public void testRedirector5() throws IOException {
+        testRedirector5or6("redirector5");
+    }
+
+    public void testRedirector6() throws IOException {
+        testRedirector5or6("redirector6");
+    }
+
+    private void testRedirector5or6(String target) throws IOException {
+        executeTarget(target);
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        String actualOut = getFileString("redirector.out");
+        String actualErr = getFileString("redirector.err");
+
+        File x = getProject().resolveFile("x");
+        File y = getProject().resolveFile("y");
+        File z = getProject().resolveFile("z");
+        int xout = actualOut.indexOf(x + " out");
+        int yout = actualOut.indexOf(y + " out");
+        int zout = actualOut.indexOf(z + " out");
+        int xerr = actualErr.indexOf(x + " err");
+        int yerr = actualErr.indexOf(y + " err");
+        int zerr = actualErr.indexOf(z + " err");
+        assertFalse("xout=" + xout, xout < 0);
+        assertFalse("yout=" + yout, yout < 0);
+        assertFalse("zout=" + zout, zout < 0);
+        assertFalse("xerr=" + xerr, xerr < 0);
+        assertFalse("yerr=" + yerr, yerr < 0);
+        assertFalse("zerr=" + zerr, zerr < 0);
+        assertFalse("yout < xout", yout < xout);
+        assertFalse("zout < yout", zout < yout);
+        assertFalse("yerr < xerr", yerr < xerr);
+        assertFalse("zerr < yerr", zerr < yerr);
+
+        String outProperty = getProject().getProperty("redirector.out");
+        int pxout = outProperty.indexOf(x + " out");
+        int pyout = outProperty.indexOf(y + " out");
+        int pzout = outProperty.indexOf(z + " out");
+        assertFalse("pxout=" + pxout, pxout < 0);
+        assertFalse("pyout=" + pyout, pyout < 0);
+        assertFalse("pzout=" + pzout, pzout < 0);
+        assertFalse("pyout < pxout", pyout < pxout);
+        assertFalse("pzout < pyout", pzout < pyout);
+
+        String errorProperty = getProject().getProperty("redirector.err");
+        int pxerr = errorProperty.indexOf(x + " err");
+        int pyerr = errorProperty.indexOf(y + " err");
+        int pzerr = errorProperty.indexOf(z + " err");
+        assertFalse("pxerr=" + pxerr, pxerr < 0);
+        assertFalse("pyerr=" + pyerr, pyerr < 0);
+        assertFalse("pzerr=" + pzerr, pzerr < 0);
+        assertFalse("pyerr < pxerr", pyerr < pxerr);
+        assertFalse("pzerr < pyerr", pzerr < pyerr);
+    }
+
+    public void testRedirector7() throws IOException {
+        executeTarget("redirector7");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        String actualOut = getFileString("redirector.out");
+        String actualErr = getFileString("redirector.err");
+
+        File x = getProject().resolveFile("x");
+        File y = getProject().resolveFile("y");
+        File z = getProject().resolveFile("z");
+        int xout = actualOut.indexOf(x + " out");
+        int yout = actualOut.indexOf(y + " out");
+        int zout = actualOut.indexOf(z + " out");
+        int xerr = actualErr.indexOf(x + " ERROR!!!");
+        int yerr = actualErr.indexOf(y + " ERROR!!!");
+        int zerr = actualErr.indexOf(z + " ERROR!!!");
+        assertFalse("xout=" + xout, xout < 0);
+        assertFalse("yout=" + yout, yout < 0);
+        assertFalse("zout=" + zout, zout < 0);
+        assertFalse("xerr=" + xerr, xerr < 0);
+        assertFalse("yerr=" + yerr, yerr < 0);
+        assertFalse("zerr=" + zerr, zerr < 0);
+        assertFalse("yout < xout", yout < xout);
+        assertFalse("zout < yout", zout < yout);
+        assertFalse("yerr < xerr", yerr < xerr);
+        assertFalse("zerr < yerr", zerr < yerr);
+
+        String outProperty = getProject().getProperty("redirector.out");
+        int pxout = outProperty.indexOf(x + " out");
+        int pyout = outProperty.indexOf(y + " out");
+        int pzout = outProperty.indexOf(z + " out");
+        assertFalse("pxout=" + pxout, pxout < 0);
+        assertFalse("pyout=" + pyout, pyout < 0);
+        assertFalse("pzout=" + pzout, pzout < 0);
+        assertFalse("pyout < pxout", pyout < pxout);
+        assertFalse("pzout < pyout", pzout < pyout);
+
+        String errorProperty = getProject().getProperty("redirector.err");
+        int pxerr = errorProperty.indexOf(x + " ERROR!!!");
+        int pyerr = errorProperty.indexOf(y + " ERROR!!!");
+        int pzerr = errorProperty.indexOf(z + " ERROR!!!");
+        assertFalse("pxerr=" + pxerr, pxerr < 0);
+        assertFalse("pyerr=" + pyerr, pyerr < 0);
+        assertFalse("pzerr=" + pzerr, pzerr < 0);
+        assertFalse("pyerr < pxerr", pyerr < pxerr);
+        assertFalse("pzerr < pyerr", pzerr < pyerr);
+    }
+
+    public void testRedirector8() throws IOException {
+        executeTarget("redirector8");
+        if (getProject().getProperty("sed.can.run") == null) {
+            return;
+        }
+
+        assertPropertyEquals("redirector.out", getProject().replaceProperties(
+            "blah y z${line.separator}x blah z${line.separator}x y blah"));
+        assertPropertyEquals("redirector.err", "");
+        assertEquals("unexpected output",
+            "blah y z\nx blah z\nx y blah\n", getFileString("redirector.out"));
+        assertNull("unexpected error output", getFileString("redirector.err"));
+    }
+
+    public void testRedirector9() throws IOException {
+        testRedirector9Thru12("redirector9");
+    }
+
+    public void testRedirector10() throws IOException {
+        testRedirector9Thru12("redirector10");
+    }
+
+    public void testRedirector11() throws IOException {
+        testRedirector9Thru12("redirector11");
+    }
+
+    public void testRedirector12() throws IOException {
+        testRedirector9Thru12("redirector12");
+    }
+
+    private void testRedirector9Thru12(String target) throws IOException {
+        executeTarget(target);
+        if (getProject().getProperty("sed.can.run") == null) {
+            return;
+        }
+
+        assertNull("unexpected error output", getFileString("redirector.err"));
+        assertPropertyEquals("redirector.out", getProject().replaceProperties(
+            "blah after y after z${line.separator}x after blah after z"
+            + "${line.separator}x after y after blah"));
+        assertPropertyEquals("redirector.err", "");
+        assertEquals("unexpected output",
+            "blah after y after z\nx after blah after z"
+            + "\nx after y after blah\n", getFileString("redirector.out"));
+    }
+
+    public void testRedirector13() {
+        executeTarget("redirector13");
+        if (getProject().getProperty("test.can.run") == null) {
+            return;
+        }
+        String log = getLog();
+        File x = getProject().resolveFile("x");
+        File y = getProject().resolveFile("y");
+        File z = getProject().resolveFile("z");
+        int xout = log.indexOf(x + " OUTPUT???");
+        int yout = log.indexOf(y + " OUTPUT???");
+        int zout = log.indexOf(z + " OUTPUT???");
+        int xerr = log.indexOf(x + " ERROR!!!");
+        int yerr = log.indexOf(y + " ERROR!!!");
+        int zerr = log.indexOf(z + " ERROR!!!");
+        assertFalse("xout=" + xout, xout < 0);
+        assertFalse("yout=" + yout, yout < 0);
+        assertFalse("zout=" + zout, zout < 0);
+        assertFalse("xerr=" + xerr, xerr < 0);
+        assertFalse("yerr=" + yerr, yerr < 0);
+        assertFalse("zerr=" + zerr, zerr < 0);
+        assertFalse("yout < xout", yout < xout);
+        assertFalse("zout < yout", zout < yout);
+        assertFalse("yerr < xerr", yerr < xerr);
+        assertFalse("zerr < yerr", zerr < yerr);
+    }
+
+    public void testRedirector14() throws IOException {
+        executeTarget("redirector14");
+        if (getProject().getProperty("sed.can.run") == null) {
+            return;
+        }
+
+        assertEquals("unexpected log content",
+            "z after y after blahx after y after blah", getLog());
+
+        assertEquals("unexpected redirector.out content",
+            "x after blah after z\n", getFileString("redirector.out"));
+
+        assertNull("unexpected redirector.err content", getFileString("redirector.err"));
+    }
+
+    public void testIgnoreMissing() {
+        executeTarget("ignoremissing");
+    }
+
+    public void testForce() {
+        executeTarget("force");
+    }
+
+    public void testNoDest() {
+        executeTarget("testNoDest");
+    }
+
+    //borrowed from TokenFilterTest
+    private String getFileString(String filename) throws IOException {
+        String result = null;
+        FileReader reader = null;
+        try {
+            reader = new FileReader(getProject().resolveFile(filename));
+            result = FileUtils.readFully(reader);
+        } finally {
+            if (reader != null) {
+                try {
+                    reader.close();
+                } catch (Throwable ignore) {
+                }
+            }
+        }
+        return result;
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ExecuteWatchdogTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ExecuteWatchdogTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ExecuteWatchdogTest.java	(revision 16139)
@@ -0,0 +1,162 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+import java.net.*;
+import junit.framework.*;
+import java.io.*;
+
+/**
+ * Simple testcase for the ExecuteWatchdog class.
+ *
+ */
+public class ExecuteWatchdogTest extends TestCase {
+
+    private final static int TIME_OUT = 5000;
+
+    private final static String TEST_CLASSPATH = getTestClassPath();
+
+    private final static int CLOCK_ERROR=200;
+    private final static int TIME_OUT_TEST=TIME_OUT-CLOCK_ERROR;
+
+    private ExecuteWatchdog watchdog;
+
+    public ExecuteWatchdogTest(String name) {
+        super(name);
+    }
+
+    protected void setUp(){
+        watchdog = new ExecuteWatchdog(TIME_OUT);
+    }
+
+    /**
+     * Dangerous method to obtain the classpath for the test. This is
+     * severely tighted to the build.xml properties.
+     */
+    private static String getTestClassPath(){
+        String classpath = System.getProperty("build.tests");
+        if (classpath == null) {
+            System.err.println("WARNING: 'build.tests' property is not available !");
+            classpath = System.getProperty("java.class.path");
+        }
+
+        // JDK 1.1 needs classes.zip in -classpath argument
+        if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
+            classpath +=   File.pathSeparator
+                + System.getProperty("java.home")
+                + File.separator + "lib"
+                + File.separator + "classes.zip";
+        }
+
+        return classpath;
+    }
+
+    private Process getProcess(int timetorun) throws Exception {
+        String[] cmdArray = {
+            JavaEnvUtils.getJreExecutable("java"), "-classpath", TEST_CLASSPATH,
+            TimeProcess.class.getName(), String.valueOf(timetorun)
+        };
+        //System.out.println("Testing with classpath: " + System.getProperty("java.class.path"));
+        return Runtime.getRuntime().exec(cmdArray);
+    }
+
+    private String getErrorOutput(Process p) throws Exception {
+        BufferedReader err = new BufferedReader( new InputStreamReader(p.getErrorStream()) );
+        StringBuffer buf = new StringBuffer();
+        String line;
+        while ( (line = err.readLine()) != null){
+            buf.append(line);
+        }
+        return buf.toString();
+    }
+
+    private int waitForEnd(Process p) throws Exception {
+        int retcode = p.waitFor();
+        if (retcode != 0){
+            String err = getErrorOutput(p);
+            if (err.length() > 0){
+                System.err.println("ERROR:");
+                System.err.println(err);
+            }
+        }
+        return retcode;
+    }
+
+    public void testNoTimeOut() throws Exception {
+        Process process = getProcess(TIME_OUT/2);
+        watchdog.start(process);
+        int retCode = waitForEnd(process);
+        assertTrue("process should not have been killed", !watchdog.killedProcess());
+        assertEquals(0, retCode);
+    }
+
+    // test that the watchdog ends the process
+    public void testTimeOut() throws Exception {
+        Process process = getProcess(TIME_OUT*2);
+        long now = System.currentTimeMillis();
+        watchdog.start(process);
+        int retCode = process.waitFor();
+        long elapsed = System.currentTimeMillis() - now;
+        assertTrue("process should have been killed", watchdog.killedProcess());
+                //      assertTrue("return code is invalid: " + retCode, retCode!=0);
+        assertTrue("elapse time of "+elapsed+" ms is less than timeout value of "+TIME_OUT_TEST+" ms", elapsed >= TIME_OUT_TEST);
+        assertTrue("elapse time of "+elapsed+" ms is greater than run value of "+(TIME_OUT*2)+" ms", elapsed < TIME_OUT*2);
+    }
+
+    // test a process that runs and failed
+    public void testFailed() throws Exception {
+        Process process = getProcess(-1); // process should abort
+        watchdog.start(process);
+        int retCode = process.waitFor();
+        assertTrue("process should not have been killed", !watchdog.killedProcess());
+        assertTrue("return code is invalid: " + retCode, retCode!=0);
+    }
+
+    public void testManualStop() throws Exception {
+        final Process process = getProcess(TIME_OUT*2);
+        watchdog.start(process);
+
+        // I assume that starting this takes less than TIME_OUT/2 ms...
+        Thread thread = new Thread(){
+                public void run(){
+                    try {
+                        process.waitFor();
+                    } catch(InterruptedException e){
+                        // not very nice but will do the job
+                        fail("process interrupted in thread");
+                    }
+                }
+        };
+        thread.start();
+
+        // wait for TIME_OUT/2, there should be about TIME_OUT/2 ms remaining before timeout
+        thread.join(TIME_OUT/2);
+
+         // now stop the watchdog.
+        watchdog.stop();
+
+        // wait for the thread to die, should be the end of the process
+        thread.join();
+
+        // process should be dead and well finished
+        assertEquals(0, process.exitValue());
+        assertTrue("process should not have been killed", !watchdog.killedProcess());
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/FailTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/FailTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/FailTest.java	(revision 16139)
@@ -0,0 +1,163 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class FailTest extends BuildFileTest {
+
+    public FailTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/fail.xml");
+    }
+
+    public void test1() {
+        expectBuildExceptionContaining("test1",
+                "it is required to fail :-)",
+                "No message");
+    }
+
+    public void test2() {
+        expectSpecificBuildException("test2",
+            "it is required to fail :-)",
+            "test2");
+    }
+
+    public void testText() {
+        expectSpecificBuildException("testText",
+            "it is required to fail :-)",
+            "testText");
+    }
+
+    public void testIf() {
+        try {
+            executeTarget("testIf");
+        } catch (BuildException be) {
+            fail("foo has not been defined, testIf must not fail");
+        }
+        project.setProperty("foo", "");
+        expectBuildException("testIf", "testIf must fail if foo has been set");
+    }
+
+    public void testUnless() {
+        expectBuildException("testUnless",
+                             "testUnless must fail unless foo has been set");
+        project.setProperty("foo", "");
+        try {
+            executeTarget("testUnless");
+        } catch (BuildException be) {
+            fail("foo has been defined, testUnless must not fail");
+        }
+    }
+
+    /**
+     * see that the different combinations work, and
+     * that the autogenerated text contains information
+     * about which condition was not met
+     */
+    public void testIfAndUnless() {
+        //neither
+        executeTarget("testIfAndUnless");
+        project.setProperty("if", "");
+        expectBuildExceptionContaining("testIfAndUnless",
+                "expect fail on defined(if)",
+                "if=if and unless=unless");
+        project.setProperty("unless", "");
+        //this call should succeed as unless overrides if
+        executeTarget("testIfAndUnless");
+    }
+    /**
+     * see that the different combinations work, and
+     * that the autogenerated text contains information
+     * about which condition was not met
+     */
+    public void testIfAndUnless2() {
+        project.setProperty("unless", "");
+        try {
+            executeTarget("testIfAndUnless");
+        } catch (BuildException be) {
+            fail("defined(if) && !defined(unless); testIfAndUnless must not fail");
+        }
+    }
+
+    public void testNested1() {
+        expectSpecificBuildException("testNested1",
+            "it is required to fail :-)",
+            "condition satisfied");
+    }
+
+    public void testNested2() {
+        try {
+            executeTarget("testNested2");
+        } catch (BuildException be) {
+            fail("condition not satisfied; testNested2 must not fail");
+        }
+    }
+
+    public void testNested3() {
+        expectSpecificBuildException("testNested3",
+            "it is required to fail :-)",
+            "testNested3");
+    }
+
+    public void testNested4() {
+        String specificMessage = "Nested conditions "
+          + "not permitted in conjunction with if/unless attributes";
+
+        char[] c = {'a', 'b', 'c'};
+        StringBuffer target = new StringBuffer("testNested4x");
+
+        for (int i = 0; i < c.length; i++) {
+            target.setCharAt(target.length() - 1, c[i]);
+            expectSpecificBuildException(target.toString(),
+                "it is required to fail :-)", specificMessage);
+        }
+    }
+
+    public void testNested5() {
+        expectSpecificBuildException("testNested5",
+            "it is required to fail :-)",
+            "Only one nested condition is allowed.");
+    }
+
+    public void testNested6() {
+        expectSpecificBuildException("testNested6",
+            "it is required to fail :-)",
+            "testNested6\ntestNested6\ntestNested6");
+    }
+
+    public void testNested7() {
+        String specificMessage = "A single nested condition is required.";
+
+        char[] c = {'a', 'b'};
+        StringBuffer target = new StringBuffer("testNested7x");
+
+        for (int i = 0; i < c.length; i++) {
+            target.setCharAt(target.length() - 1, c[i]);
+            expectSpecificBuildException(target.toString(),
+                "it is required to fail :-)", specificMessage);
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/FilterTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/FilterTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/FilterTest.java	(revision 16139)
@@ -0,0 +1,113 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class FilterTest extends BuildFileTest {
+
+    public FilterTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/filter.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument missing");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "required argument missing");
+    }
+
+    public void test3() {
+        expectBuildException("test3", "required argument missing");
+    }
+
+    public void test4() {
+        executeTarget("test4");
+    }
+
+    public void test5() {
+        executeTarget("test5");
+        assertEquals("2000",
+                     getFilteredFile("5", "filtered.tmp"));
+    }
+
+
+    public void test6() {
+        executeTarget("test6");
+        assertEquals("2000",
+                     getFilteredFile("6", "taskdefs.tmp/filter1.txt"));
+    }
+
+    public void test7() {
+        executeTarget("test7");
+        assertEquals("<%@ include file=\"root/some/include.jsp\"%>",
+                     getFilteredFile("7", "filtered.tmp"));
+    }
+
+    public void test8() {
+        executeTarget("test8");
+        assertEquals("<%@ include file=\"root/some/include.jsp\"%>",
+                     getFilteredFile("8", "taskdefs.tmp/filter2.txt"));
+    }
+
+    public void test9() {
+        executeTarget("test9");
+        assertEquals("included",
+                    getFilteredFile("9", "taskdefs.tmp/filter3.txt"));
+    }
+
+    private String getFilteredFile(String testNumber, String filteredFile) {
+
+        String line = null;
+        File f = new File(getProjectDir(), filteredFile);
+        if (!f.exists()) {
+            fail("filter test"+testNumber+" failed");
+        } else {
+            BufferedReader in = null;
+            try {
+                in = new BufferedReader(new FileReader(f));
+            } catch (FileNotFoundException fnfe) {
+                fail("filter test"+testNumber+" failed, filtered file: " + f.toString() + " not found");
+            }
+            try {
+                line = in.readLine();
+                in.close();
+            } catch (IOException ioe) {
+                fail("filter test"+testNumber+" failed.  IOException while reading filtered file: " + ioe);
+            }
+        }
+        f.delete();
+        return line;
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/FixCrLfTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/FixCrLfTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/FixCrLfTest.java	(revision 16139)
@@ -0,0 +1,216 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.*;
+
+import junit.framework.AssertionFailedError;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ */
+public class FixCrLfTest extends BuildFileTest {
+
+    public FixCrLfTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/fixcrlf/build.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void test1() throws IOException {
+        executeTarget("test1");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk1.java"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk1.java"));
+    }
+
+    public void test2() throws IOException {
+        executeTarget("test2");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk2.java"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk2.java"));
+    }
+
+    public void test3() throws IOException {
+        executeTarget("test3");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk3.java"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk3.java"));
+    }
+
+    public void test4() throws IOException {
+        executeTarget("test4");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk4.java"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk4.java"));
+    }
+
+    public void test5() throws IOException {
+        executeTarget("test5");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk5.java"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk5.java"));
+    }
+
+    public void test6() throws IOException {
+        executeTarget("test6");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk6.java"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk6.java"));
+    }
+
+    public void test7() throws IOException {
+        executeTarget("test7");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk7.java"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk7.java"));
+    }
+
+    public void test8() throws IOException {
+        executeTarget("test8");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk8.java"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk8.java"));
+    }
+
+    public void test9() throws IOException {
+        executeTarget("test9");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Junk9.java"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk9.java"));
+    }
+
+    public void testMacLines() throws IOException {
+        executeTarget("testMacLines");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/Mac2Unix"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/Mac2Unix"));
+    }
+
+    public void testNoOverwrite() throws IOException {
+        executeTarget("test1");
+        File result =
+            new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk1.java");
+        long modTime = result.lastModified();
+
+        /*
+         * Sleep for some time to make sure a newer file would get a
+         * more recent timestamp according to the file system's
+         * granularity (should be > 2s to account for Windows FAT).
+         */
+        try {
+            Thread.currentThread().sleep(5000);
+        } catch (InterruptedException ie) {
+            fail(ie.getMessage());
+        } // end of try-catch
+
+        /*
+         * make sure we get a new Project instance or the target won't get run
+         * a second time.
+         */
+        configureProject("src/etc/testcases/taskdefs/fixcrlf/build.xml");
+
+        executeTarget("test1");
+        result =
+            new File("src/etc/testcases/taskdefs/fixcrlf/result/Junk1.java");
+        assertEquals(modTime, result.lastModified());
+    }
+
+    public void testEncoding() throws IOException {
+        if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
+            // UTF16 is not supported in JDK 1.1
+            return;
+        }
+        executeTarget("testEncoding");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/input.lf.utf16"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/input.crlf.utf16"));
+    }
+
+    public void testLongLines() throws IOException {
+        executeTarget("testLongLines");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/longlines.lf"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/longlines.crlf"));
+    }
+
+    public void testCrCrLfSequenceUnix() throws IOException {
+        executeTarget("testCrCrLfSequence-unix");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/crcrlf.unix"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/crcrlf"));
+    }
+
+    public void testCrCrLfSequenceDos() throws IOException {
+        executeTarget("testCrCrLfSequence-dos");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/crcrlf.dos"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/crcrlf"));
+    }
+
+    public void testCrCrLfSequenceMac() throws IOException {
+        executeTarget("testCrCrLfSequence-mac");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/crcrlf.mac"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/crcrlf"));
+    }
+
+    public void testFixlastDos() throws IOException {
+        executeTarget("testFixlastDos");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/fixlast.dos"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/fixlastfalse.lf"));
+    }
+
+    public void testFixlastFalseMac() throws IOException {
+        executeTarget("testFixlastFalseMac");
+        assertEqualContent(new File("src/etc/testcases/taskdefs/fixcrlf/expected/fixlastfalse.mac"),
+                           new File("src/etc/testcases/taskdefs/fixcrlf/result/fixlastfalse.lf"));
+    }
+
+    /**
+     * Bugzilla Report 20840
+     *
+     * Will fail with an exception if the parent directories do not
+     * get created.
+     */
+    public void testCreateParentDirs() {
+        executeTarget("createParentDirs");
+    }
+
+    public void assertEqualContent(File expect, File result)
+        throws AssertionFailedError, IOException {
+        if (!result.exists()) {
+            fail("Expected file "+result+" doesn\'t exist");
+        }
+
+        InputStream inExpect = null;
+        InputStream inResult = null;
+        try {
+            inExpect = new BufferedInputStream(new FileInputStream(expect));
+            inResult = new BufferedInputStream(new FileInputStream(result));
+
+            int expectedByte = inExpect.read();
+            while (expectedByte != -1) {
+                assertEquals(expectedByte, inResult.read());
+                expectedByte = inExpect.read();
+            }
+            assertEquals("End of file", -1, inResult.read());
+        } finally {
+            if (inResult != null) {
+                inResult.close();
+            }
+            if (inExpect != null) {
+                inExpect.close();
+            }
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/GUnzipTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/GUnzipTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/GUnzipTest.java	(revision 16139)
@@ -0,0 +1,61 @@
+/*
+ * Copyright  2000-2001,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ */
+public class GUnzipTest extends BuildFileTest {
+
+    public GUnzipTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/gunzip.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument missing");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "attribute src invalid");
+    }
+
+    public void testRealTest() throws java.io.IOException {
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        executeTarget("realTest");
+        assertTrue(fileUtils.contentEquals(project.resolveFile("../asf-logo.gif"),
+                                           project.resolveFile("asf-logo.gif")));
+    }
+
+    public void testTestGzipTask() throws java.io.IOException {
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        executeTarget("testGzipTask");
+        assertTrue(fileUtils.contentEquals(project.resolveFile("../asf-logo.gif"),
+                                           project.resolveFile("asf-logo.gif")));
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/GetTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/GetTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/GetTest.java	(revision 16139)
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2000-2001, 2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class GetTest extends BuildFileTest {
+
+    public GetTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/get.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument missing");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "required argument missing");
+    }
+
+    public void test3() {
+        expectBuildException("test3", "required argument missing");
+    }
+
+    public void test4() {
+        expectBuildException("test4", "src invalid");
+    }
+
+    public void test5() {
+        expectBuildException("test5", "dest invalid (or no http-server on local machine)");
+    }
+
+    public void test6() {
+        executeTarget("test6");
+    }
+
+    public void testUseTimestamp() {
+        executeTarget("testUseTimestamp");
+    }
+
+    public void testUseTomorrow() {
+        executeTarget("testUseTomorrow");
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/GzipTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/GzipTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/GzipTest.java	(revision 16139)
@@ -0,0 +1,72 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class GzipTest extends BuildFileTest {
+
+    public GzipTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/gzip.xml");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument missing");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "required argument missing");
+    }
+
+    public void test3() {
+        expectBuildException("test3", "required argument missing");
+    }
+
+    public void test4() {
+        expectBuildException("test4", "zipfile must not point to a directory");
+    }
+
+    public void testGZip(){
+        executeTarget("realTest");
+        String log = getLog();
+        assertTrue("Expecting message starting with 'Building:' but got '"
+            + log + "'", log.startsWith("Building:"));
+        assertTrue("Expecting message ending with 'asf-logo.gif.gz' but got '"
+            + log + "'", log.endsWith("asf-logo.gif.gz"));
+    }
+
+    public void testDateCheck(){
+        executeTarget("testDateCheck");
+        String log = getLog();
+        assertTrue(
+            "Expecting message ending with 'asf-logo.gif.gz is up to date.' but got '" + log + "'",
+            log.endsWith("asf-logo.gif.gz is up to date."));
+    }
+
+    public void tearDown(){
+        executeTarget("cleanup");
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ImportTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ImportTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ImportTest.java	(revision 16139)
@@ -0,0 +1,161 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Project;
+
+/**
+ */
+public class ImportTest extends BuildFileTest {
+
+    public ImportTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+    }
+
+    public void tearDown() {
+    }
+
+    public void testSimpleImport() {
+        configureProject("src/etc/testcases/taskdefs/import/import.xml");
+        assertLogContaining("Before importIn imported topAfter import");
+    }
+
+    public void testUnnamedNesting() {
+        configureProject("src/etc/testcases/taskdefs/import/unnamedImport.xml",
+                         Project.MSG_WARN);
+        String log = getLog();
+        assertTrue("Warnings logged when not expected: " + log,
+                    log.length() == 0);
+    }
+
+    public void testSerial() {
+        configureProject("src/etc/testcases/taskdefs/import/subdir/serial.xml");
+        assertLogContaining("Unnamed2.xmlUnnamed1.xml");
+        String fullLog = getFullLog();
+        String substring = "Skipped already imported file";
+        assertTrue("expecting full log to contain \"" + substring
+            + "\" full log was \"" + fullLog + "\"",
+            fullLog.indexOf(substring) >= 0);
+    }
+
+    // allow this as imported in targets are only tested when a target is run
+    public void testImportInTargetNoEffect() {
+        configureProject("src/etc/testcases/taskdefs/import/subdir/importintarget.xml");
+        expectPropertyUnset("no-import", "foo");
+        assertTrue(null == getProject().getReference("baz"));
+    }
+
+    // deactivate this test as imports within targets are not allowed
+    public void notTestImportInTargetWithEffect() {
+        configureProject("src/etc/testcases/taskdefs/import/subdir/importintarget.xml");
+        expectPropertySet("do-import", "foo", "bar");
+        assertNotNull(getProject().getReference("baz"));
+    }
+
+    public void testImportInTargetNotAllowed() {
+        configureProject(
+            "src/etc/testcases/taskdefs/import/subdir/importintarget.xml");
+        expectBuildExceptionContaining(
+            "do-import", "not a top level task",
+            "import only allowed as a top-level task");
+    }
+
+    public void testImportInSequential() {
+        configureProject(
+            "src/etc/testcases/taskdefs/import/subdir/importinsequential.xml");
+        expectPropertySet("within-imported", "foo", "bar");
+        assertNotNull(getProject().getReference("baz"));
+    }
+
+    public void testImportSameTargets() {
+        try {
+            configureProject(
+                "src/etc/testcases/taskdefs/import/same_target.xml");
+        } catch (BuildException ex) {
+            String message = ex.getMessage();
+            if (message.indexOf("Duplicate target") == -1) {
+                assertTrue("Did not see 'Duplicate target' in '" + message +"'", false);
+            }
+            return;
+        }
+        assertTrue(
+            "Did not see build exception",
+            false);
+    }
+
+    public void testImportError() {
+        try {
+            configureProject(
+                "src/etc/testcases/taskdefs/import/import_bad_import.xml");
+        } catch (BuildException ex) {
+            Location lo = ex.getLocation();
+            assertTrue(
+                "expected location of build exception to be set",
+                (lo != null));
+            assertTrue(
+                "expected location to contain calling file",
+                lo.getFileName().indexOf("import_bad_import.xml") != -1);
+            assertTrue(
+                "expected message of ex to contain called file",
+                ex.getMessage().indexOf("bad.xml") != -1);
+            return;
+        }
+        assertTrue(
+            "Did not see build exception",
+            false);
+    }
+
+    public void testSymlinkedImports() throws Exception {
+        String ln = "/usr/bin/ln";
+        if (!new File(ln).exists()) {
+            ln = "/bin/ln";
+        }
+        if (!new File(ln).exists()) {
+            // Running on Windows or something, so skip it.
+            return;
+        }
+        String symlink = "src/etc/testcases/taskdefs/import/symlinks/d3b";
+        if (Runtime.getRuntime().exec(new String[] {ln, "-s", "d3a", symlink}).waitFor() != 0) {
+            throw new IOException("'" + ln + " -s d3a " + symlink + "' failed");
+        }
+        try {
+            configureProject(
+                "src/etc/testcases/taskdefs/import/symlinks/d1/p1.xml");
+            assertPropertyEquals(
+                "ant.file.p2",
+                new File("src/etc/testcases/taskdefs/import/symlinks/d2/p2.xml")
+                .getAbsolutePath());
+            assertPropertyEquals(
+                "ant.file.p3",
+                new File("src/etc/testcases/taskdefs/import/symlinks/d3b/p3.xml")
+                .getAbsolutePath());
+        } finally {
+            new File(symlink).delete();
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/InitializeClassTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/InitializeClassTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/InitializeClassTest.java	(revision 16139)
@@ -0,0 +1,63 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Test to see if static initializers are invoked the same way
+ * when <java> is invoked in forked and unforked modes.
+ *
+ */
+public class InitializeClassTest extends BuildFileTest {
+
+    public InitializeClassTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/initializeclass.xml");
+    }
+
+    public void testAll() throws IOException {
+        executeTarget("forked");
+        PrintStream ps = System.out;
+        File f1 = new File("src/etc/testcases/taskdefs/forkedout");
+        File f2 = new File("src/etc/testcases/taskdefs/unforkedout");
+        PrintStream newps = new PrintStream(new FileOutputStream(f2));
+        System.setOut(newps);
+        project.executeTarget("unforked");
+        System.setOut(ps);
+        newps.close();
+        FileUtils fu = FileUtils.newFileUtils();
+        assertTrue("Forked - non-forked mismatch", fu.contentEquals(f1, f2));
+    }
+
+    public void tearDown() {
+        File f1 = new File("src/etc/testcases/taskdefs/forkedout");
+        File f2 = new File("src/etc/testcases/taskdefs/unforkedout");
+        f1.delete();
+        f2.delete();
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/InputTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/InputTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/InputTest.java	(revision 16139)
@@ -0,0 +1,79 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.input.PropertyFileInputHandler;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+/**
+ */
+public class InputTest extends BuildFileTest {
+
+    private String targetPostfix = "";
+
+    public InputTest(String name) {
+        super(name);
+        if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
+            targetPostfix = ".1";
+        }
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/input.xml");
+        System.getProperties()
+            .put(PropertyFileInputHandler.FILE_NAME_KEY,
+                 getProject().resolveFile("input.properties")
+                 .getAbsolutePath());
+        getProject().setInputHandler(new PropertyFileInputHandler());
+    }
+
+    public void test1() {
+        executeTarget("test1" + targetPostfix);
+    }
+
+    public void test2() {
+        executeTarget("test2" + targetPostfix);
+    }
+
+    public void test3() {
+        expectSpecificBuildException("test3" + targetPostfix, "invalid input",
+                                     "Found invalid input test for \'"
+                                     + getKey("All data is"
+                                              + " going to be deleted from DB"
+                                              + " continue?")
+                                     + "\'");
+    }
+
+    public void test5() {
+        executeTarget("test5" + targetPostfix);
+    }
+
+    public void test6() {
+        executeTarget("test6" + targetPostfix);
+        assertEquals("scott", project.getProperty("db.user"));
+    }
+
+    private String getKey(String key) {
+        if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
+            key = key.replace(' ', '_');
+        }
+        return key;
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/JarTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/JarTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/JarTest.java	(revision 16139)
@@ -0,0 +1,265 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.taskdefs.condition.Os;
+
+/**
+ */
+public class JarTest extends BuildFileTest {
+
+    private static String tempJar = "tmp.jar";
+    private static String tempDir = "jartmp/";
+    private Reader r1, r2;
+
+    public JarTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/jar.xml");
+    }
+
+    public void tearDown() {
+        if (r1 != null) {
+            try {
+                r1.close();
+            } catch (IOException e) {
+            }
+        }
+        if (r2 != null) {
+            try {
+                r2.close();
+            } catch (IOException e) {
+            }
+        }
+
+        executeTarget("cleanup");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument not specified");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "manifest file does not exist");
+    }
+
+    public void test3() {
+        expectBuildException("test3", "Unrecognized whenempty attribute: format C: /y");
+    }
+
+    public void test4() {
+        executeTarget("test4");
+        File jarFile = new File(getProjectDir(), tempJar);
+        assertTrue(jarFile.exists());
+    }
+
+    public void testNoRecreateWithoutUpdate() {
+        testNoRecreate("test4");
+    }
+
+    public void testNoRecreateWithUpdate() {
+        testNoRecreate("testNoRecreateWithUpdate");
+    }
+
+    private void testNoRecreate(String secondTarget) {
+        executeTarget("test4");
+        File jarFile = new File(getProjectDir(), tempJar);
+        long jarModifiedDate = jarFile.lastModified();
+        try {
+            Thread.currentThread().sleep(2500);
+        } catch (InterruptedException e) {
+        } // end of try-catch
+        executeTarget(secondTarget);
+        assertEquals("jar has not been recreated in " + secondTarget,
+                     jarModifiedDate, jarFile.lastModified());
+    }
+
+    public void testRecreateWithoutUpdateAdditionalFiles() {
+        testRecreate("test4", "testRecreateWithoutUpdateAdditionalFiles");
+    }
+
+    public void testRecreateWithUpdateAdditionalFiles() {
+        testRecreate("test4", "testRecreateWithUpdateAdditionalFiles");
+    }
+
+    public void testRecreateWithoutUpdateNewerFile() {
+        testRecreate("testRecreateNewerFileSetup",
+                     "testRecreateWithoutUpdateNewerFile");
+    }
+
+    public void testRecreateWithUpdateNewerFile() {
+        testRecreate("testRecreateNewerFileSetup",
+                     "testRecreateWithUpdateNewerFile");
+    }
+
+    private void testRecreate(String firstTarget, String secondTarget) {
+        executeTarget(firstTarget);
+        int sleeptime = 2500;
+        if (Os.isFamily("windows")) {
+            sleeptime += 2500;
+        }
+        try {
+            Thread.currentThread().sleep(sleeptime);
+        } catch (InterruptedException e) {
+        } // end of try-catch
+        File jarFile = new File(getProjectDir(), tempJar);
+        long jarModifiedDate = jarFile.lastModified();
+        executeTarget(secondTarget);
+        jarFile = new File(getProjectDir(), tempJar);
+        assertTrue("jar has been recreated in " + secondTarget,
+                   jarModifiedDate < jarFile.lastModified());
+    }
+
+    public void testManifestStaysIntact()
+        throws IOException, ManifestException {
+        executeTarget("testManifestStaysIntact");
+
+        r1 = new FileReader(getProject()
+                            .resolveFile(tempDir + "manifest"));
+        r2 = new FileReader(getProject()
+                            .resolveFile(tempDir + "META-INF/MANIFEST.MF"));
+        Manifest mf1 = new Manifest(r1);
+        Manifest mf2 = new Manifest(r2);
+        assertEquals(mf1, mf2);
+    }
+
+    public void testNoRecreateBasedirExcludesWithUpdate() {
+        testNoRecreate("testNoRecreateBasedirExcludesWithUpdate");
+    }
+
+    public void testNoRecreateBasedirExcludesWithoutUpdate() {
+        testNoRecreate("testNoRecreateBasedirExcludesWithoutUpdate");
+    }
+
+    public void testNoRecreateZipfilesetExcludesWithUpdate() {
+        testNoRecreate("testNoRecreateZipfilesetExcludesWithUpdate");
+    }
+
+    public void testNoRecreateZipfilesetExcludesWithoutUpdate() {
+        testNoRecreate("testNoRecreateZipfilesetExcludesWithoutUpdate");
+    }
+
+    public void testRecreateZipfilesetWithoutUpdateAdditionalFiles() {
+        testRecreate("test4",
+                     "testRecreateZipfilesetWithoutUpdateAdditionalFiles");
+    }
+
+    public void testRecreateZipfilesetWithUpdateAdditionalFiles() {
+        testRecreate("test4",
+                     "testRecreateZipfilesetWithUpdateAdditionalFiles");
+    }
+
+    public void testRecreateZipfilesetWithoutUpdateNewerFile() {
+        testRecreate("testRecreateNewerFileSetup",
+                     "testRecreateZipfilesetWithoutUpdateNewerFile");
+    }
+
+    public void testRecreateZipfilesetWithUpdateNewerFile() {
+        testRecreate("testRecreateNewerFileSetup",
+                     "testRecreateZipfilesetWithUpdateNewerFile");
+    }
+
+    public void testCreateWithEmptyFileset() {
+        executeTarget("testCreateWithEmptyFilesetSetUp");
+        executeTarget("testCreateWithEmptyFileset");
+        executeTarget("testCreateWithEmptyFileset");
+    }
+
+    public void testUpdateIfOnlyManifestHasChanged() {
+        executeTarget("testUpdateIfOnlyManifestHasChanged");
+        File jarXml = getProject().resolveFile(tempDir + "jar.xml");
+        assertTrue(jarXml.exists());
+    }
+
+    // bugzilla report 10262
+    public void testNoDuplicateIndex() throws IOException {
+        ZipFile archive = null;
+        try {
+            executeTarget("testIndexTests");
+            archive = new ZipFile(getProject().resolveFile(tempJar));
+            Enumeration e = archive.entries();
+            int numberOfIndexLists = 0;
+            while (e.hasMoreElements()) {
+                ZipEntry ze = (ZipEntry) e.nextElement();
+                if (ze.getName().equals("META-INF/INDEX.LIST")) {
+                    numberOfIndexLists++;
+                }
+            }
+            assertEquals(1, numberOfIndexLists);
+        } finally {
+            if (archive != null) {
+                archive.close();
+            }
+        }
+    }
+
+    // bugzilla report 16972
+    public void testRootFilesInIndex() throws IOException {
+        ZipFile archive = null;
+        try {
+            executeTarget("testIndexTests");
+            archive = new ZipFile(getProject().resolveFile(tempJar));
+            ZipEntry ze = archive.getEntry("META-INF/INDEX.LIST");
+            InputStream is = archive.getInputStream(ze);
+            BufferedReader r = new BufferedReader(new InputStreamReader(is,
+                                                                        "UTF8"));
+            boolean foundSub = false;
+            boolean foundSubFoo = false;
+            boolean foundFoo = false;
+
+            String line = r.readLine();
+            while (line != null) {
+                if (line.equals("foo")) {
+                    foundFoo = true;
+                } else if (line.equals("sub")) {
+                    foundSub = true;
+                } else if (line.equals("sub/foo")) {
+                    foundSubFoo = true;
+                }
+                line = r.readLine();
+            }
+
+            assertTrue(foundSub);
+            assertTrue(!foundSubFoo);
+            assertTrue(foundFoo);
+        } finally {
+            if (archive != null) {
+                archive.close();
+            }
+        }
+    }
+    public void testManifestOnlyJar() {
+        expectLogContaining("testManifestOnlyJar", "Building MANIFEST-only jar: ");
+        File manifestFile = getProject().resolveFile(tempDir + "META-INF" + File.separator + "MANIFEST.MF");
+        assertTrue(manifestFile.exists());
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/JavaTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/JavaTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/JavaTest.java	(revision 16139)
@@ -0,0 +1,321 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import junit.framework.*;
+import java.io.*;
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.TeeOutputStream;
+
+/**
+ * stress out java task
+ * */
+public class JavaTest extends BuildFileTest {
+
+    private static final int TIME_TO_WAIT = 1;
+    // wait 1 second extra to allow for java to start ...
+    // this time was OK on a Win NT machine and on nagoya
+    private static final int SECURITY_MARGIN = 2000;
+
+    private boolean runFatalTests=false;
+
+    public JavaTest(String name) {
+        super(name);
+    }
+
+    /**
+     * configure the project.
+     * if the property junit.run.fatal.tests is set we run
+     * the fatal tests
+     */
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/java.xml");
+
+        //final String propname="tests-classpath.value";
+        //String testClasspath=System.getProperty(propname);
+        //System.out.println("Test cp="+testClasspath);
+        String propname="tests-classpath.value";
+        String runFatal=System.getProperty("junit.run.fatal.tests");
+        if(runFatal!=null)
+            runFatalTests=true;
+    }
+
+    public void tearDown() {
+        // remove log file from testSpawn
+        project.executeTarget("cleanup");
+    }
+
+    public void testNoJarNoClassname(){
+        expectBuildExceptionContaining("testNoJarNoClassname",
+            "parameter validation",
+            "Classname must not be null.");
+    }
+
+    public void testJarNoFork() {
+        expectBuildExceptionContaining("testJarNoFork",
+            "parameter validation",
+            "Cannot execute a jar in non-forked mode. "
+                + "Please set fork='true'. ");
+    }
+
+    public void testJarAndClassName() {
+        expectBuildException("testJarAndClassName",
+            "Should not be able to set both classname AND jar");
+    }
+
+
+    public void testClassnameAndJar() {
+        expectBuildException("testClassnameAndJar",
+            "Should not be able to set both classname AND jar");
+    }
+
+    public void testRun() {
+        executeTarget("testRun");
+    }
+
+
+
+    /** this test fails but we ignore the return value;
+     *  we verify that failure only matters when failonerror is set
+     */
+    public void testRunFail() {
+        if(runFatalTests) {
+            executeTarget("testRunFail");
+        }
+    }
+
+    public void testRunFailFoe() {
+        if(runFatalTests) {
+            expectBuildExceptionContaining("testRunFailFoe",
+                "java failures being propagated",
+                "Java returned:");
+        }
+}
+
+    public void testRunFailFoeFork() {
+        expectBuildExceptionContaining("testRunFailFoeFork",
+            "java failures being propagated",
+            "Java returned:");
+    }
+
+    public void testExcepting() {
+        expectLogContaining("testExcepting",
+                            "Exception raised inside called program");
+    }
+
+    public void testExceptingFork() {
+        expectLogContaining("testExceptingFork",
+                            "Java Result:");
+    }
+
+    public void testExceptingFoe() {
+        expectBuildExceptionContaining("testExceptingFoe",
+            "passes exception through",
+            "Exception raised inside called program");
+    }
+
+    public void testExceptingFoeFork() {
+        expectBuildExceptionContaining("testExceptingFoeFork",
+            "exceptions turned into error codes",
+            "Java returned:");
+    }
+
+    public void testResultPropertyZero() {
+        executeTarget("testResultPropertyZero");
+        assertEquals("0",project.getProperty("exitcode"));
+    }
+
+    public void testResultPropertyNonZero() {
+        executeTarget("testResultPropertyNonZero");
+        assertEquals("2",project.getProperty("exitcode"));
+    }
+
+    public void testResultPropertyZeroNoFork() {
+        executeTarget("testResultPropertyZeroNoFork");
+        assertEquals("0",project.getProperty("exitcode"));
+    }
+
+    public void testResultPropertyNonZeroNoFork() {
+        executeTarget("testResultPropertyNonZeroNoFork");
+         assertEquals("-1",project.getProperty("exitcode"));
+     }
+
+    public void testRunFailWithFailOnError() {
+        expectBuildExceptionContaining("testRunFailWithFailOnError",
+            "non zero return code",
+            "Java returned:");
+    }
+
+    public void testRunSuccessWithFailOnError() {
+        executeTarget("testRunSuccessWithFailOnError");
+    }
+
+    public void testSpawn() {
+        FileUtils fileutils  = FileUtils.newFileUtils();
+        File logFile = fileutils.createTempFile("spawn","log", project.getBaseDir());
+        // this is guaranteed by FileUtils#createTempFile
+        assertTrue("log file not existing", !logFile.exists());
+        project.setProperty("logFile", logFile.getAbsolutePath());
+        project.setProperty("timeToWait", Long.toString(TIME_TO_WAIT));
+        project.executeTarget("testSpawn");
+        try {
+            Thread.sleep(TIME_TO_WAIT * 1000 + SECURITY_MARGIN);
+        } catch (Exception ex) {
+            System.out.println("my sleep was interrupted");
+        }
+        // let's be nice with the next generation of developers
+        if (!logFile.exists()) {
+            System.out.println("suggestion: increase the constant"
+            + " SECURITY_MARGIN to give more time for java to start.");
+        }
+        assertTrue("log file exists", logFile.exists());
+    }
+
+    public void testRedirect1() {
+        executeTarget("redirect1");
+    }
+
+    public void testRedirect2() {
+        executeTarget("redirect2");
+    }
+
+    public void testRedirect3() {
+        executeTarget("redirect3");
+    }
+
+    public void testRedirector1() {
+        executeTarget("redirector1");
+    }
+
+    public void testRedirector2() {
+        executeTarget("redirector2");
+    }
+
+    /**
+     * entry point class with no dependencies other
+     * than normal JRE runtime
+     */
+    public static class EntryPoint {
+
+    /**
+     * this entry point is used by the java.xml tests to
+     * generate failure strings to handle
+     * argv[0] = exit code (optional)
+     * argv[1] = string to print to System.out (optional)
+     * argv[1] = string to print to System.err (optional)
+     */
+        public static void main(String[] argv) {
+            int exitCode=0;
+            if(argv.length>0) {
+                try {
+                    exitCode=Integer.parseInt(argv[0]);
+                } catch(NumberFormatException nfe) {
+                    exitCode=-1;
+                }
+            }
+            if(argv.length>1) {
+                System.out.println(argv[1]);
+            }
+            if(argv.length>2) {
+                System.err.println(argv[2]);
+            }
+            if(exitCode!=0) {
+                System.exit(exitCode);
+            }
+        }
+    }
+
+    /**
+     * entry point class with no dependencies other
+     * than normal JRE runtime
+     */
+    public static class ExceptingEntryPoint {
+
+        /**
+         * throw a run time exception which does not need
+         * to be in the signature of the entry point
+         */
+        public static void main(String[] argv) {
+            throw new NullPointerException("Exception raised inside called program");
+        }
+    }
+    /**
+     * test class for spawn
+     */
+    public static class SpawnEntryPoint {
+        public static void main(String [] argv) {
+            int sleepTime = 10;
+            String logFile = "spawn.log";
+            if (argv.length >= 1) {
+                sleepTime = Integer.parseInt(argv[0]);
+            }
+            if (argv.length >= 2)
+            {
+                logFile = argv[1];
+            }
+            OutputStreamWriter out = null;
+            try {
+                Thread.sleep(sleepTime * 1000);
+            } catch (InterruptedException ex) {
+                System.out.println("my sleep was interrupted");
+            }
+
+            try {
+                File dest = new File(logFile);
+                FileOutputStream fos = new FileOutputStream(dest);
+                out = new OutputStreamWriter(fos);
+                out.write("bye bye\n");
+            } catch (Exception ex) {}
+            finally {
+                try {out.close();} catch (IOException ioe) {}}
+
+        }
+    }
+
+    /**
+     * entry point class to pipe System.in to the specified stream:
+     * "out", "err", or "both".  If none specified, swallow the input.
+     */
+    public static class PipeEntryPoint {
+
+        /**
+         * pipe input to specified output
+         */
+        public static void main(String[] args) {
+            OutputStream os = null;
+            if (args.length > 0) {
+                if ("out".equalsIgnoreCase(args[0])) {
+                    os = System.out;
+                } else if ("err".equalsIgnoreCase(args[0])) {
+                    os = System.err;
+                } else if ("both".equalsIgnoreCase(args[0])) {
+                    os = new TeeOutputStream(System.out, System.err);
+                }
+            }
+            if (os != null) {
+                Thread t = new Thread(new StreamPumper(System.in, os, true));
+                t.start();
+                try {
+                    t.join();
+                } catch (InterruptedException eyeEx) {
+                }
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/JavacTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/JavacTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/JavacTest.java	(revision 16139)
@@ -0,0 +1,212 @@
+/*
+ * Copyright  2001-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.compilers.CompilerAdapter;
+import org.apache.tools.ant.taskdefs.compilers.CompilerAdapterFactory;
+import org.apache.tools.ant.taskdefs.compilers.DefaultCompilerAdapter;
+import org.apache.tools.ant.taskdefs.compilers.Javac12;
+import org.apache.tools.ant.taskdefs.compilers.Javac13;
+import org.apache.tools.ant.taskdefs.compilers.JavacExternal;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+import junit.framework.TestCase;
+
+/**
+ * Testcase for <javac>.
+ *
+ */
+public class JavacTest extends TestCase {
+
+    private Project project;
+    private Javac javac;
+
+    public JavacTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        project = new Project();
+        project.init();
+        javac = new Javac();
+        javac.setProject(project);
+    }
+
+    /**
+     * Test setting the name of the javac executable.
+     */
+    public void testForkedExecutableName() {
+        assertNull("no fork means no executable", javac.getJavacExecutable());
+
+        project.setProperty("build.compiler", "modern");
+        assertNull("no fork means no executable", javac.getJavacExecutable());
+
+        javac.setFork(true);
+        assertNotNull("normal fork", javac.getJavacExecutable());
+        assertTrue("name should contain \"javac\"",
+                   javac.getJavacExecutable().indexOf("javac") > -1);
+
+        project.setProperty("build.compiler", "extJavac");
+        javac.setFork(false);
+        assertNotNull("fork via property", javac.getJavacExecutable());
+        assertTrue("name should contain \"javac\"",
+                   javac.getJavacExecutable().indexOf("javac") > -1);
+
+        project.setProperty("build.compiler", "whatever");
+        assertNull("no fork and not extJavac means no executable",
+                   javac.getJavacExecutable());
+
+        String myJavac = "Slartibartfast";
+        javac.setFork(true);
+        javac.setExecutable(myJavac);
+        assertEquals(myJavac, javac.getJavacExecutable());
+    }
+
+    /**
+     * Test nested compiler args.
+     */
+    public void testCompilerArg() {
+        String[] args = javac.getCurrentCompilerArgs();
+        assertNotNull(args);
+        assertEquals("no args", 0, args.length);
+
+        Javac.ImplementationSpecificArgument arg = javac.createCompilerArg();
+        String ford = "Ford";
+        String prefect = "Prefect";
+        String testArg = ford + " " + prefect;
+        arg.setValue(testArg);
+        args = javac.getCurrentCompilerArgs();
+        assertEquals("unconditional single arg", 1, args.length);
+        assertEquals(testArg, args[0]);
+
+        arg.setCompiler("jikes");
+        args = javac.getCurrentCompilerArgs();
+        assertNotNull(args);
+        assertEquals("implementation is jikes but build.compiler is null",
+                     0, args.length);
+
+        project.setProperty("build.compiler", "jvc");
+        args = javac.getCurrentCompilerArgs();
+        assertNotNull(args);
+        assertEquals("implementation is jikes but build.compiler is jvc",
+                     0, args.length);
+
+        project.setProperty("build.compiler", "jikes");
+        args = javac.getCurrentCompilerArgs();
+        assertEquals("both are jikes", 1, args.length);
+        assertEquals(testArg, args[0]);
+
+        arg.setLine(testArg);
+        args = javac.getCurrentCompilerArgs();
+        assertEquals("split at space", 2, args.length);
+        assertEquals(ford, args[0]);
+        assertEquals(prefect, args[1]);
+    }
+
+    /**
+     * Test nested compiler args in the fork="true" and
+     * implementation="extJavac" case.
+     */
+    public void testCompilerArgForForkAndExtJavac() {
+        Javac.ImplementationSpecificArgument arg = javac.createCompilerArg();
+        String ford = "Ford";
+        String prefect = "Prefect";
+        String testArg = ford + " " + prefect;
+        arg.setValue(testArg);
+        arg.setCompiler("extJavac");
+        javac.setFork(true);
+        String[] args = javac.getCurrentCompilerArgs();
+        assertEquals("both are forked javac", 1, args.length);
+        assertEquals(testArg, args[0]);
+    }
+
+    /**
+     * Test compiler attribute.
+     */
+    public void testCompilerAttribute() {
+        // check defaults
+        String compiler = javac.getCompiler();
+        assertNotNull(compiler);
+        assertTrue("default value",
+                   "javac1.1".equals(compiler)
+                   || "javac1.2".equals(compiler)
+                   || "javac1.3".equals(compiler)
+                   || "javac1.4".equals(compiler)
+                   || "javac1.5".equals(compiler)
+                   || "classic".equals(compiler));
+
+        javac.setFork(true);
+        assertNotNull(javac.getCompiler());
+        assertEquals("extJavac", javac.getCompiler());
+        assertEquals(compiler, javac.getCompilerVersion());
+
+        // check build.compiler provides defaults
+        javac = new Javac();
+        javac.setProject(project);
+        project.setNewProperty("build.compiler", "jikes");
+        compiler = javac.getCompiler();
+        assertNotNull(compiler);
+        assertEquals("jikes", compiler);
+
+        javac.setFork(true);
+        compiler = javac.getCompiler();
+        assertNotNull(compiler);
+        assertEquals("jikes", compiler);
+
+        // check attribute overrides build.compiler
+        javac.setFork(false);
+        javac.setCompiler("jvc");
+        compiler = javac.getCompiler();
+        assertNotNull(compiler);
+        assertEquals("jvc", compiler);
+
+        javac.setFork(true);
+        compiler = javac.getCompiler();
+        assertNotNull(compiler);
+        assertEquals("jvc", compiler);
+    }
+
+    public void testCompilerAdapter() {
+        if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)
+            || JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_2)
+            || JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_3)) {
+            javac.setCompiler("javac1.1");
+        } else {
+            javac.setCompiler("javac1.4");
+        }
+
+        javac.setDepend(true);
+        CompilerAdapter adapter =
+            CompilerAdapterFactory.getCompiler(javac.getCompiler(), javac);
+
+        if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)
+            || JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_2)
+            || JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_3)) {
+            assertTrue(adapter instanceof Javac12);
+        } else {
+            assertTrue(adapter instanceof Javac13);
+        }
+
+        javac.setFork(true);
+        adapter =
+            CompilerAdapterFactory.getCompiler(javac.getCompiler(), javac);
+        assertTrue(adapter instanceof JavacExternal);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/LengthTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/LengthTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/LengthTest.java	(revision 16139)
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+
+public class LengthTest extends BuildFileTest {
+
+    public LengthTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/length.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testEach() {
+        executeTarget("testEach");
+    }
+
+    public void testEachCondition() {
+        executeTarget("testEachCondition");
+    }
+
+    public void testAll() {
+        executeTarget("testAll");
+    }
+
+    public void testAllCondition() {
+        executeTarget("testAllCondition");
+    }
+
+    public void testFile() {
+        executeTarget("testFile");
+    }
+
+    public void testFileCondition() {
+        executeTarget("testFileCondition");
+    }
+
+    public void testBoth() {
+        executeTarget("testBoth");
+    }
+
+    public void testBothCondition() {
+        executeTarget("testBothCondition");
+    }
+
+    public void testDupes() {
+        executeTarget("testDupes");
+    }
+
+    public void testDupesCondition() {
+        executeTarget("testDupesCondition");
+    }
+
+    public void testString() {
+        executeTarget("testString");
+    }
+
+    public void testStringCondition() {
+        executeTarget("testStringCondition");
+    }
+
+    public void testTrimString() {
+        executeTarget("testTrimString");
+    }
+
+    public void testTrimStringCondition() {
+        executeTarget("testTrimStringCondition");
+    }
+
+    public void testNoTrimString() {
+        executeTarget("testNoTrimString");
+    }
+
+    public void testNoTrimStringCondition() {
+        executeTarget("testNoTrimStringCondition");
+    }
+
+    public void testStringFile() {
+        expectBuildExceptionContaining("testStringFile",
+            "should fail", "incompatible");
+    }
+
+    public void testTrimFile() {
+        expectBuildExceptionContaining("testTrimFile",
+            "should fail", "string length function only");
+    }
+
+    public void testImmutable() {
+        executeTarget("testImmutable");
+    }
+
+    public void testZipFileSet() {
+        executeTarget("testZipFileSet");
+    }
+
+    public void testZipFileSetCondition() {
+        executeTarget("testZipFileSetCondition");
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/LoadFileTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/LoadFileTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/LoadFileTest.java	(revision 16139)
@@ -0,0 +1,157 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import java.io.File;
+
+/**
+ * Test the load file task
+ *
+ * @created 10 December 2001
+ */
+public class LoadFileTest extends BuildFileTest {
+
+    /**
+     * Constructor for the LoadFileTest object
+     *
+     * @param name Description of Parameter
+     */
+    public LoadFileTest(String name) {
+        super(name);
+    }
+
+
+    /**
+     * The JUnit setup method
+     */
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/loadfile.xml");
+    }
+
+
+    /**
+     * The teardown method for JUnit
+     */
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testNoSourcefileDefined() {
+        expectBuildException("testNoSourcefileDefined",
+                "source file not defined");
+    }
+
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testNoPropertyDefined() {
+        expectBuildException("testNoPropertyDefined",
+                "output property not defined");
+    }
+
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testNoSourcefilefound() {
+        expectBuildExceptionContaining("testNoSourcefilefound",
+                "File not found",
+                "Unable to load file");
+    }
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testFailOnError()
+            throws BuildException {
+        expectPropertyUnset("testFailOnError","testFailOnError");
+    }
+
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testLoadAFile()
+            throws BuildException {
+        executeTarget("testLoadAFile");
+        if(project.getProperty("testLoadAFile").indexOf("eh?")<0) {
+            fail("property is not all in the file");
+        }
+    }
+
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testLoadAFileEnc()
+            throws BuildException {
+        executeTarget("testLoadAFileEnc");
+        if(project.getProperty("testLoadAFileEnc")==null) {
+            fail("file load failed");
+        }
+    }
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testEvalProps()
+            throws BuildException {
+        executeTarget("testEvalProps");
+        if(project.getProperty("testEvalProps").indexOf("rain")<0) {
+            fail("property eval broken");
+        }
+    }
+
+    /**
+     * Test FilterChain and FilterReaders
+     */
+    public void testFilterChain()
+            throws BuildException {
+        executeTarget("testFilterChain");
+        if(project.getProperty("testFilterChain").indexOf("World!")<0) {
+            fail("Filter Chain broken");
+        }
+    }
+
+    /**
+     * Test StripJavaComments filterreader functionality.
+     */
+    public final void testStripJavaComments()
+            throws BuildException {
+        executeTarget("testStripJavaComments");
+        final String expected = project.getProperty("expected");
+        final String generated = project.getProperty("testStripJavaComments");
+        assertEquals(expected, generated);
+    }
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testOneLine()
+            throws BuildException {
+            expectPropertySet("testOneLine","testOneLine","1,2,3,4");
+
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/LoadPropertiesTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/LoadPropertiesTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/LoadPropertiesTest.java	(revision 16139)
@@ -0,0 +1,54 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class LoadPropertiesTest extends BuildFileTest {
+
+    public LoadPropertiesTest(String name) {
+        super(name);
+    }
+
+    /**
+     * The JUnit setup method
+     */
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/loadproperties.xml");
+    }
+
+    /**
+     * The teardown method for JUnit
+     */
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testPrefixedProperties() {
+        executeTarget("testPrefixedProperties");
+        String url = project.getProperty("server1.http.url");
+        assertEquals("http://localhost:80", url);
+    }
+
+    public void testPropertiesFromResource() {
+        executeTarget("testPropertiesFromResource");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/MacroDefTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/MacroDefTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/MacroDefTest.java	(revision 16139)
@@ -0,0 +1,137 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ */
+public class MacroDefTest extends BuildFileTest {
+    public MacroDefTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/macrodef.xml");
+    }
+
+    public void testSimple() {
+        expectLog("simple", "Hello World");
+    }
+
+    public void testText() {
+        expectLog("text", "Inner Text");
+    }
+
+    public void testDuplicateAttribute() {
+        expectBuildException(
+            "duplicate.attribute",
+            "the attribute text has already been specified");
+    }
+
+    public void testDuplicateElement() {
+        expectBuildException(
+            "duplicate.element",
+            "the element text has already been specified");
+    }
+
+    public void testUri() {
+        expectLog("uri", "Hello World");
+    }
+
+    public void testNested() {
+        expectLog("nested", "A nested element");
+    }
+
+    public void testDouble() {
+        expectLog(
+            "double",
+            "@{prop} is 'property', value of ${property} is 'A property value'");
+    }
+
+    public void testIgnoreCase() {
+        expectLog(
+            "ignorecase",
+            "a is ab is b");
+    }
+
+    public void testIgnoreElementCase() {
+        expectLog(
+            "ignore-element-case",
+            "nested elementnested element");
+    }
+
+    public void testTextElement() {
+        expectLogContaining(
+            "textelement", "Hello world");
+    }
+
+    public void testTextTrim() {
+        expectLogContaining(
+            "text.trim", "[Hello world]");
+    }
+
+    public void testDuplicateTextName() {
+        expectBuildException(
+            "duplicatetextname",
+            "the name \"text\" is already used as an attribute");
+    }
+    public void testDuplicateTextName2() {
+        expectBuildException(
+            "duplicatetextname2",
+            "the attribute name \"text\" has already been used by the text element");
+    }
+    public void testEscape() {
+        expectLog(
+            "escape",
+            "a@b or a@b is avalue@bvalue");
+    }
+    public void testAttributeDescription() {
+        expectLog(
+            "attribute.description",
+            "description is hello world");
+    }
+    public void testOverrideDefault() {
+        expectLog(
+            "override.default",
+            "value is new");
+    }
+    public void testImplicit() {
+        expectLog(
+            "implicit", "Before implicitIn implicitAfter implicit");
+    }
+    public void testImplicitNotOptional() {
+        expectSpecificBuildException(
+            "implicit.notoptional",
+            "Missing nested elements for implicit element implicit",
+            "Missing nested elements for implicit element implicit");
+    }
+    public void testImplicitOptional() {
+        expectLog(
+            "implicit.optional", "Before implicitAfter implicit");
+    }
+    public void testImplicitExplicit() {
+        expectSpecificBuildException(
+            "implicit.explicit",
+            "Only one element allowed when using implicit elements",
+            "Only one element allowed when using implicit elements");
+    }
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ManifestTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ManifestTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ManifestTest.java	(revision 16139)
@@ -0,0 +1,300 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Vector;
+import java.util.Enumeration;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+
+/**
+ * Testcase for the Manifest class used in the jar task.
+ *
+ */
+public class ManifestTest extends BuildFileTest {
+
+    public static final String EXPANDED_MANIFEST
+        = "src/etc/testcases/taskdefs/manifests/META-INF/MANIFEST.MF";
+
+    public static final String LONG_LINE
+        = "AReallyLongLineToTestLineBreakingInManifests-ACapabilityWhich" +
+          "IsSureToLeadToHundredsOfQuestionsAboutWhyAntMungesManifests" +
+          "OfCourseTheAnswerIsThatIsWhatTheSpecRequiresAndIfAnythingHas" +
+          "AProblemWithThatItIsNotABugInAnt";
+
+    public ManifestTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/manifest.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("clean");
+    }
+
+    /**
+     * Empty manifest - is OK
+     */
+    public void test1() throws ManifestException, IOException {
+        executeTarget("test1");
+        Manifest manifest = getManifest(EXPANDED_MANIFEST);
+        String version = manifest.getManifestVersion();
+        assertEquals("Manifest was not created with correct version - ", "1.0", version);
+    }
+
+    /**
+     * Simple Manifest with version 2.0
+     */
+    public void test2() throws ManifestException, IOException {
+        executeTarget("test2");
+        Manifest manifest = getManifest(EXPANDED_MANIFEST);
+        String version = manifest.getManifestVersion();
+        assertEquals("Manifest was not created with correct version - ", "2.0", version);
+    }
+
+    /**
+     * Malformed manifest - no : on the line
+     */
+    public void test3() {
+        expectBuildExceptionContaining("test3", "Manifest is invalid - no colon on header line",
+                                       "Invalid Manifest");
+    }
+
+    /**
+     * Malformed manifest - starts with continuation line
+     */
+    public void test4() {
+        expectBuildExceptionContaining("test4", "Manifest is invalid - section starts with continuation line",
+                                       "Invalid Manifest");
+   }
+
+    /**
+     * Malformed manifest - Name attribute in main section
+     */
+    public void test5() {
+        executeTarget("test5");
+        String output = getLog();
+        boolean hasWarning = output.indexOf("Manifest warning: \"Name\" attributes should not occur in the main section") != -1;
+        assertEquals("Expected warning about Name in main section", true, hasWarning);
+    }
+
+    /**
+     * New Section not starting with Name attribute.
+     */
+    public void test6() {
+        expectBuildExceptionContaining("test6", "Manifest is invalid - section starts with incorrect attribute",
+                                       "Invalid Manifest");
+        String output = getLog();
+        boolean hasWarning = output.indexOf("Manifest sections should start with a \"Name\" attribute") != -1;
+        assertEquals("Expected warning about section not starting with Name: attribute", true, hasWarning);
+    }
+
+    /**
+     * From attribute is illegal
+     */
+    public void test7() {
+        executeTarget("test7");
+
+        boolean hasWarning = getLog().indexOf("Manifest attributes should not start with \"From\"") != -1;
+        assertEquals("Expected warning about From: attribute", true, hasWarning);
+    }
+
+    /**
+     * Inline manifest - OK
+     */
+    public void test8() throws IOException, ManifestException {
+        executeTarget("test8");
+        Manifest manifest = getManifest(EXPANDED_MANIFEST);
+        Manifest.Section mainSection = manifest.getMainSection();
+        String classpath = mainSection.getAttributeValue("class-path");
+        assertEquals("Class-Path attribute was not set correctly - ", "fubar", classpath);
+
+        Manifest.Section testSection = manifest.getSection("Test");
+        String testAttr = testSection.getAttributeValue("TestAttr");
+        assertEquals("TestAttr attribute was not set correctly - ", "Test", testAttr);
+    }
+
+    /**
+     * Inline manifest - Invalid since has a Name attribute in the section element
+     */
+    public void test9() {
+        expectBuildExceptionContaining("test9", "Construction is invalid - Name attribute should not be used",
+                                       "Specify the section name using the \"name\" attribute of the <section> element");
+    }
+
+    /**
+     * Inline manifest - Invalid attribute without name
+     */
+    public void test10() {
+        expectBuildExceptionContaining("test10", "Attribute has no name",
+                                       "Attributes must have name and value");
+    }
+
+    /**
+     * Inline manifest - Invalid attribute without value
+     */
+    public void test11() {
+        expectBuildExceptionContaining("test11", "Attribute has no value",
+                                       "Attributes must have name and value");
+    }
+
+    /**
+     * Inline manifest - Invalid attribute without value
+     */
+    public void test12() {
+        expectBuildExceptionContaining("test12", "Section with no name",
+                                       "Sections must have a name");
+    }
+
+    /**
+     * Inline manifest - Duplicate attribute
+     */
+    public void test13() {
+        expectBuildExceptionContaining("test13", "Duplicate Attribute",
+                                       "The attribute \"Test\" may not occur more than once in the same section");
+    }
+
+    /**
+     * Inline manifest - OK since classpath entries can be duplicated.
+     */
+    public void test14() throws IOException, ManifestException {
+        executeTarget("test14");
+        Manifest manifest = getManifest(EXPANDED_MANIFEST);
+        Manifest.Section mainSection = manifest.getMainSection();
+        String classpath = mainSection.getAttributeValue("class-path");
+        assertEquals("Class-Path attribute was not set correctly - ",
+            "Test1 Test2 Test3 Test4", classpath);
+    }
+
+    /**
+     * Tets long line wrapping
+     */
+    public void testLongLine() throws IOException, ManifestException {
+        Project project = getProject();
+        project.setUserProperty("test.longline", LONG_LINE);
+        executeTarget("testLongLine");
+
+        Manifest manifest = getManifest(EXPANDED_MANIFEST);
+        Manifest.Section mainSection = manifest.getMainSection();
+        String classpath = mainSection.getAttributeValue("class-path");
+        assertEquals("Class-Path attribute was not set correctly - ",
+            LONG_LINE, classpath);
+    }
+
+    /**
+     * Tests ordering of sections
+     */
+    public void testOrder1() throws IOException, ManifestException {
+        executeTarget("testOrder1");
+
+        Manifest manifest = getManifest(EXPANDED_MANIFEST);
+        Enumeration e = manifest.getSectionNames();
+        String section1 = (String)e.nextElement();
+        String section2 = (String)e.nextElement();
+        assertEquals("First section name unexpected", "Test1", section1);
+        assertEquals("Second section name unexpected", "Test2", section2);
+
+        Manifest.Section section = manifest.getSection("Test1");
+        e = section.getAttributeKeys();
+        String attr1Key = (String)e.nextElement();
+        String attr2Key = (String)e.nextElement();
+        String attr1 = section.getAttribute(attr1Key).getName();
+        String attr2 = section.getAttribute(attr2Key).getName();
+        assertEquals("First attribute name unexpected", "TestAttr1", attr1);
+        assertEquals("Second attribute name unexpected", "TestAttr2", attr2);
+    }
+
+    /**
+     * Tests ordering of sections
+     */
+    public void testOrder2() throws IOException, ManifestException {
+        executeTarget("testOrder2");
+
+        Manifest manifest = getManifest(EXPANDED_MANIFEST);
+        Enumeration e = manifest.getSectionNames();
+        String section1 = (String)e.nextElement();
+        String section2 = (String)e.nextElement();
+        assertEquals("First section name unexpected", "Test2", section1);
+        assertEquals("Second section name unexpected", "Test1", section2);
+
+        Manifest.Section section = manifest.getSection("Test1");
+        e = section.getAttributeKeys();
+        String attr1Key = (String)e.nextElement();
+        String attr2Key = (String)e.nextElement();
+        String attr1 = section.getAttribute(attr1Key).getName();
+        String attr2 = section.getAttribute(attr2Key).getName();
+        assertEquals("First attribute name unexpected", "TestAttr2", attr1);
+        assertEquals("Second attribute name unexpected", "TestAttr1", attr2);
+    }
+
+    /**
+     * file attribute for manifest task is required.
+     */
+    public void testNoFile() {
+        expectBuildException("testNoFile", "file is required");
+    }
+
+    /**
+     * replace changes Manifest-Version from 2.0 to 1.0
+     */
+    public void testReplace() throws IOException, ManifestException {
+        executeTarget("testReplace");
+        Manifest mf = getManifest("src/etc/testcases/taskdefs/mftest.mf");
+        assertNotNull(mf);
+        assertEquals(Manifest.getDefaultManifest(), mf);
+    }
+
+    /**
+     * update keeps the Manifest-Version and adds a new attribute Foo
+     */
+    public void testUpdate() throws IOException, ManifestException {
+        executeTarget("testUpdate");
+        Manifest mf = getManifest("src/etc/testcases/taskdefs/mftest.mf");
+        assertNotNull(mf);
+        assertTrue(!Manifest.getDefaultManifest().equals(mf));
+        String mfAsString = mf.toString();
+        assertNotNull(mfAsString);
+        assertTrue(mfAsString.startsWith("Manifest-Version: 2.0"));
+        assertTrue(mfAsString.indexOf("Foo: Bar") > -1);
+
+        mf = getManifest("src/etc/testcases/taskdefs/mftest2.mf");
+        assertNotNull(mf);
+        mfAsString = mf.toString();
+        assertNotNull(mfAsString);
+        assertEquals(-1, mfAsString.indexOf("Foo: Bar"));
+        assertTrue(mfAsString.indexOf("Foo: Baz") > -1);
+    }
+
+    /**
+     * Reads mftest.mf.
+     */
+    private Manifest getManifest(String filename) throws IOException, ManifestException {
+        FileReader r = new FileReader(filename);
+        try {
+            return new Manifest(r);
+        } finally {
+            r.close();
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/MkdirTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/MkdirTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/MkdirTest.java	(revision 16139)
@@ -0,0 +1,50 @@
+/*
+ * Copyright  2000-2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class MkdirTest extends BuildFileTest {
+
+    public MkdirTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/mkdir.xml");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument missing");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "directory already exists as a file");
+    }
+
+    public void test3() {
+        executeTarget("test3");
+        java.io.File f = new java.io.File(getProjectDir(), "testdir.tmp");
+        if (!f.exists() || !f.isDirectory()) {
+            fail("mkdir failed");
+        } else {
+            f.delete();
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/MoveTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/MoveTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/MoveTest.java	(revision 16139)
@@ -0,0 +1,128 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.FileUtils;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Tests the Move task.
+ *
+ */
+public class MoveTest extends BuildFileTest {
+
+    public MoveTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/move.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testFilterSet() throws IOException {
+        executeTarget("testFilterSet");
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        File tmp  = new File(getProjectDir(), "move.filterset.tmp");
+        File check  = new File(getProjectDir(), "expected/copy.filterset.filtered");
+        assertTrue(tmp.exists());
+        assertTrue(fileUtils.contentEquals(tmp, check));
+    }
+
+    public void testFilterChain() throws IOException {
+        executeTarget("testFilterChain");
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        File tmp  = new File(getProjectDir(), "move.filterchain.tmp");
+        File check  = new File(getProjectDir(), "expected/copy.filterset.filtered");
+        assertTrue(tmp.exists());
+        assertTrue(fileUtils.contentEquals(tmp, check));
+    }
+
+    /** Bugzilla Report 11732 */
+    public void testDirectoryRemoval() throws IOException {
+        executeTarget("testDirectoryRemoval");
+        assertTrue(!getProject().resolveFile("E/B/1").exists());
+        assertTrue(getProject().resolveFile("E/C/2").exists());
+        assertTrue(getProject().resolveFile("E/D/3").exists());
+        assertTrue(getProject().resolveFile("A/B/1").exists());
+        assertTrue(!getProject().resolveFile("A/C/2").exists());
+        assertTrue(!getProject().resolveFile("A/D/3").exists());
+        assertTrue(!getProject().resolveFile("A/C").exists());
+        assertTrue(!getProject().resolveFile("A/D").exists());
+    }
+
+    /** Bugzilla Report 18886 */
+    public void testDirectoryRetaining() throws IOException {
+        executeTarget("testDirectoryRetaining");
+        assertTrue(getProject().resolveFile("E").exists());
+        assertTrue(getProject().resolveFile("E/1").exists());
+        assertTrue(!getProject().resolveFile("A/1").exists());
+        assertTrue(getProject().resolveFile("A").exists());
+    }
+
+    public void testCompleteDirectoryMove() throws IOException {
+        executeTarget("testCompleteDirectoryMove");
+        assertTrue(getProject().resolveFile("E").exists());
+        assertTrue(getProject().resolveFile("E/1").exists());
+        assertTrue(!getProject().resolveFile("A/1").exists());
+        assertTrue(!getProject().resolveFile("A").exists());
+    }
+
+    public void testMoveFileAndFileset() {
+        executeTarget("testMoveFileAndFileset");
+    }
+
+    public void testCompleteDirectoryMoveToExistingDir() {
+        executeTarget("testCompleteDirectoryMoveToExistingDir");
+    }
+
+    public void testCompleteDirectoryMoveFileToFile() {
+        executeTarget("testCompleteDirectoryMoveFileToFile");
+    }
+
+    public void testCompleteDirectoryMoveFileToDir() {
+        executeTarget("testCompleteDirectoryMoveFileToDir");
+    }
+
+    public void testCompleteDirectoryMoveFileAndFileset() {
+        executeTarget("testCompleteDirectoryMoveFileAndFileset");
+    }
+
+    public void testCompleteDirectoryMoveFileToExistingFile() {
+        executeTarget("testCompleteDirectoryMoveFileToExistingFile");
+    }
+
+    public void testCompleteDirectoryMoveFileToExistingDir() {
+        executeTarget("testCompleteDirectoryMoveFileToExistingDir");
+    }
+
+    public void testCompleteDirectoryMoveFileToDirWithExistingFile() {
+        executeTarget("testCompleteDirectoryMoveFileToDirWithExistingFile");
+    }
+
+    public void testCompleteDirectoryMoveFileToDirWithExistingDir() {
+        executeTarget("testCompleteDirectoryMoveFileToDirWithExistingDir");
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/MultiMapTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/MultiMapTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/MultiMapTest.java	(revision 16139)
@@ -0,0 +1,76 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.lang.reflect.Method;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileNameMapper;
+
+
+/**
+ */
+public class MultiMapTest extends BuildFileTest {
+
+    public MultiMapTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/multimap.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testMultiCopy() {
+        executeTarget("multicopy");
+    }
+
+    public void testMultiMove() {
+        executeTarget("multimove");
+    }
+
+    public void testSingleCopy() {
+        executeTarget("singlecopy");
+    }
+
+    public void testSingleMove() {
+        executeTarget("singlemove");
+    }
+
+    public void testCopyWithEmpty() {
+        executeTarget("copywithempty");
+    }
+
+    public void testMoveWithEmpty() {
+        executeTarget("movewithempty");
+    }
+
+    public static class TestMapper implements FileNameMapper {
+        public TestMapper() {}
+        public void setFrom(String from) {}
+        public void setTo(String to) {}
+        public String[] mapFileName(final String source_file_name) {
+            return new String[] {
+                source_file_name, source_file_name+".copy2" };
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ParallelTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ParallelTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ParallelTest.java	(revision 16139)
@@ -0,0 +1,154 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+import java.io.PrintStream;
+import junit.framework.AssertionFailedError;
+import org.apache.tools.ant.BuildException;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.DemuxOutputStream;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ * Test of the parallel TaskContainer
+ *
+ * @created 21 February 2002
+ */
+public class ParallelTest extends BuildFileTest {
+    /** Standard property value for the basic test */
+    public final static String DIRECT_MESSAGE = "direct";
+    /** Standard property value for the basic and fail test */
+    public final static String DELAYED_MESSAGE = "delayed";
+    /** Standard property value for the fail test */
+    public final static String FAILURE_MESSAGE = "failure";
+
+    /** the build fiel associated with this test */
+    public final static String TEST_BUILD_FILE
+         = "src/etc/testcases/taskdefs/parallel.xml";
+
+    /**
+     * Constructor for the ParallelTest object
+     *
+     * @param name name of the test
+     */
+    public ParallelTest(String name) {
+        super(name);
+    }
+
+    /** The JUnit setup method */
+    public void setUp() {
+        configureProject(TEST_BUILD_FILE);
+    }
+
+    /** tests basic operation of the parallel task */
+    public void testBasic() {
+        // should get no output at all
+        Project project = getProject();
+        project.setUserProperty("test.direct", DIRECT_MESSAGE);
+        project.setUserProperty("test.delayed", DELAYED_MESSAGE);
+        expectOutputAndError("testBasic", "", "");
+        String log = getLog();
+        assertEquals("parallel tasks didn't output correct data", log,
+            DIRECT_MESSAGE + DELAYED_MESSAGE);
+
+    }
+
+    /** tests basic operation of the parallel task */
+    public void testThreadCount() {
+        // should get no output at all
+        Project project = getProject();
+        project.setUserProperty("test.direct", DIRECT_MESSAGE);
+        project.setUserProperty("test.delayed", DELAYED_MESSAGE);
+        expectOutputAndError("testThreadCount", "", "");
+        String log = getLog();
+        int pos = 0;
+        while (pos > -1) {
+            pos = countThreads(log, pos);
+        }
+    }
+
+    /**
+     * the test result string should match the regex
+     * <code>^(\|\d+\/(+-)*)+\|$</code> for someting like
+     * <code>|3/++--+-|5/+++++-----|</code>
+     *
+     *@returns -1 no more tests
+     *          # start pos of next test
+     *@throws AssertionFailedException when a constraint is invalid
+     */
+    static int countThreads(String s, int start) {
+        int firstPipe = s.indexOf('|', start);
+        int beginSlash = s.indexOf('/', firstPipe);
+        int lastPipe = s.indexOf('|', beginSlash);
+        if ((firstPipe == -1) || (beginSlash == -1) || (lastPipe == -1)) {
+            return -1;
+        }
+
+        int max = Integer.parseInt(s.substring(firstPipe + 1, beginSlash));
+        int current = 0;
+        int pos = beginSlash + 1;
+        while (pos < lastPipe) {
+            switch (s.charAt(pos++)) {
+                case '+':
+                    current++;
+                    break;
+                case '-':
+                    current--;
+                    break;
+                default:
+                    throw new AssertionFailedError("Only expect '+-' in result count, found "
+                        + s.charAt(--pos) + " at position " + pos);
+            }
+            if (current > max) {
+                throw new AssertionFailedError("Number of executing threads exceeded number allowed: "
+                    + current + " > " + max);
+            }
+        }
+        return lastPipe;
+    }
+
+
+    /** tests the failure of a task within a parallel construction */
+    public void testFail() {
+        // should get no output at all
+        Project project = getProject();
+        project.setUserProperty("test.failure", FAILURE_MESSAGE);
+        project.setUserProperty("test.delayed", DELAYED_MESSAGE);
+        expectBuildExceptionContaining("testFail",
+            "fail task in one parallel branch", FAILURE_MESSAGE);
+    }
+
+    /** tests the demuxing of output streams in a multithreaded situation */
+    public void testDemux() {
+        Project project = getProject();
+        project.addTaskDefinition("demuxtest", DemuxOutputTask.class);
+        PrintStream out = System.out;
+        PrintStream err = System.err;
+        System.setOut(new PrintStream(new DemuxOutputStream(project, false)));
+        System.setErr(new PrintStream(new DemuxOutputStream(project, true)));
+
+        try {
+            project.executeTarget("testDemux");
+        } finally {
+            System.setOut(out);
+            System.setErr(err);
+        }
+    }
+
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/PathConvertTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/PathConvertTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/PathConvertTest.java	(revision 16139)
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * Unit test for the &lt;pathconvert&gt; task.
+ */
+public class PathConvertTest extends BuildFileTest {
+    private static final String BUILD_PATH = "src/etc/testcases/taskdefs/";
+    private static final String BUILD_FILENAME = "pathconvert.xml";
+    private static final String BUILD_FILE = BUILD_PATH + BUILD_FILENAME;
+    
+    public PathConvertTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject(BUILD_FILE);
+    }
+
+    public void testMap() {
+        test("testmap");
+    }
+
+    public void testMapper() {
+        test("testmapper");
+    }
+
+    public void testNoTargetOs() {
+        executeTarget("testnotargetos");
+    }
+
+    private void test(String target) {
+        executeTarget(target);
+        assertPropertyEquals("result", "test#" + BUILD_FILENAME);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/PreSetDefTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/PreSetDefTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/PreSetDefTest.java	(revision 16139)
@@ -0,0 +1,102 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+
+/**
+ */
+public class PreSetDefTest extends BuildFileTest {
+    public PreSetDefTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/presetdef.xml");
+    }
+
+    public void testSimple() {
+        expectLog("simple", "Hello world");
+    }
+
+    public void testText() {
+        expectLog("text", "Inner Text");
+    }
+
+    public void testUri() {
+        expectLog("uri", "Hello world");
+    }
+
+    public void testDefaultTest() {
+        expectLog("defaulttest", "attribute is false");
+    }
+
+    public void testDoubleDefault() {
+        expectLog("doubledefault", "attribute is falseattribute is true");
+    }
+
+    public void testTextOptional() {
+        expectLog("text.optional", "MyTextoverride text");
+    }
+
+    public void testElementOrder() {
+        expectLog("element.order", "Line 1Line 2");
+    }
+
+    public void testElementOrder2() {
+        expectLog("element.order2", "Line 1Line 2Line 3");
+    }
+
+    public void testAntTypeTest() {
+        expectLog("antTypeTest", "");
+    }
+
+    /**
+     * A test class to check default properties
+     */
+    public static class DefaultTest extends Task {
+        boolean isSet = false;
+        boolean attribute = false;
+        public void setAttribute(boolean b) {
+            if (isSet) {
+                throw new BuildException("Attribute Already set");
+            }
+            attribute = b;
+            isSet = true;
+        }
+
+        public void execute() {
+            getProject().log("attribute is " + attribute);
+        }
+    }
+
+    /**
+     * A test class to check presetdef with add and addConfigured and ant-type
+     */
+    public static class AntTypeTest extends Task {
+        public void addFileSet(FileSet fileset) {
+        }
+        public void addConfiguredConfigured(FileSet fileset) {
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ProcessDestroyerTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ProcessDestroyerTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ProcessDestroyerTest.java	(revision 16139)
@@ -0,0 +1,84 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * Created on Feb 19, 2003
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.io.IOException;
+
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+import junit.framework.TestCase;
+
+/**
+ */
+public class ProcessDestroyerTest extends TestCase {
+
+    /**
+     * Constructor for ProcessDestroyerTest.
+     * @param arg0
+     */
+    public ProcessDestroyerTest(String arg0) {
+        super(arg0);
+    }
+
+    public void testProcessDestroyer(){
+        if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)
+            || JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_2)) {
+            return;
+        }
+
+        try {
+            ProcessDestroyer processDestroyer = new ProcessDestroyer();
+            Process process =
+                Runtime.getRuntime().exec(
+                    "java -cp "
+                        + System.getProperty("java.class.path")
+                        + " "
+                        + getClass().getName());
+
+            assertFalse("Not registered as shutdown hook",
+                        processDestroyer.isAddedAsShutdownHook());
+
+            processDestroyer.add(process);
+
+            assertTrue("Registered as shutdown hook",
+                       processDestroyer.isAddedAsShutdownHook());
+            try {
+                process.destroy();
+            } finally {
+                processDestroyer.remove(process);
+            }
+
+            assertFalse("Not registered as shutdown hook",
+                        processDestroyer.isAddedAsShutdownHook());
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+
+    public static void main(String[] args){
+        new ProcessDestroyerTest("testProcessDestroyer").testProcessDestroyer();
+        try{
+            Thread.sleep(60000);
+        }catch(InterruptedException ie){
+            ie.printStackTrace();
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/PropertyTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/PropertyTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/PropertyTest.java	(revision 16139)
@@ -0,0 +1,110 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.net.URL;
+import java.io.File;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ */
+public class PropertyTest extends BuildFileTest {
+
+    public PropertyTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/property.xml");
+    }
+
+    public void test1() {
+        // should get no output at all
+        expectOutputAndError("test1", "", "");
+    }
+
+    public void test2() {
+        expectLog("test2", "testprop1=aa, testprop3=xxyy, testprop4=aazz");
+    }
+
+    public void test3() {
+        try {
+            executeTarget("test3");
+        }
+        catch (BuildException e) {
+            assertEquals("Circular definition not detected - ", true,
+                     e.getMessage().indexOf("was circularly defined") != -1);
+            return;
+        }
+        fail("Did not throw exception on circular exception");
+    }
+
+    public void test4() {
+        expectLog("test4", "http.url is http://localhost:999");
+    }
+
+    public void test5() {
+        String baseDir = getProject().getProperty("basedir");
+        try {
+            String uri = FileUtils.newFileUtils().toURI(
+                baseDir + "/property3.properties");
+            getProject().setNewProperty(
+                "test5.url", uri);
+        } catch (Exception ex) {
+            throw new BuildException(ex);
+        }
+        expectLog("test5", "http.url is http://localhost:999");
+    }
+
+    public void testPrefixSuccess() {
+        executeTarget("prefix.success");
+        assertEquals("80", project.getProperty("server1.http.port"));
+    }
+
+    public void testPrefixFailure() {
+       try {
+            executeTarget("prefix.fail");
+        }
+        catch (BuildException e) {
+            assertEquals("Prefix allowed on non-resource/file load - ", true,
+                     e.getMessage().indexOf("Prefix is only valid") != -1);
+            return;
+        }
+        fail("Did not throw exception on invalid use of prefix");
+    }
+
+    public void testCircularReference() {
+        try {
+            executeTarget("testCircularReference");
+        } catch (BuildException e) {
+            assertEquals("Circular definition not detected - ", true,
+                         e.getMessage().indexOf("was circularly defined")
+                         != -1);
+            return;
+        }
+        fail("Did not throw exception on circular exception");
+    }
+
+    public void testThisIsNotACircularReference() {
+        expectLog("thisIsNotACircularReference", "b is A/A/A");
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/RenameTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/RenameTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/RenameTest.java	(revision 16139)
@@ -0,0 +1,54 @@
+/*
+ * Copyright  2000,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class RenameTest extends BuildFileTest {
+
+    public RenameTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/rename.xml");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument missing");
+    }
+    public void test2() {
+        expectBuildException("test2", "required argument missing");
+    }
+    public void test3() {
+        expectBuildException("test3", "required argument missing");
+    }
+/*
+    public void test4() {
+        expectBuildException("test4", "source and destination the same");
+    }
+    public void test5() {
+        executeTarget("test5");
+    }
+    */
+    public void test6() {
+        executeTarget("test6");
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ReplaceTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ReplaceTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ReplaceTest.java	(revision 16139)
@@ -0,0 +1,107 @@
+/*
+ * Copyright  2000-2001,2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+
+import java.io.*;
+
+import junit.framework.AssertionFailedError;
+
+/**
+ */
+public class ReplaceTest extends BuildFileTest {
+
+    private static final String TEST_PATH = "src/etc/testcases/taskdefs/replace/";
+    public ReplaceTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/replace.xml");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument not specified");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "required argument not specified");
+    }
+
+    public void test3() {
+        expectBuildException("test3", "required argument not specified");
+    }
+
+    public void test4() {
+        expectBuildException("test4", "empty token not allowed");
+    }
+
+    public void test5() {
+        executeTarget("test5");
+    }
+
+    public void test6() {
+        expectBuildException("test6", "required argument not specified");
+    }
+
+    public void test7() {
+        expectBuildException("test7", "empty token not allowed");
+    }
+
+    public void test8() {
+        executeTarget("test8");
+    }
+
+    public void test9() throws IOException{
+        executeTarget("test9");
+        String tmpdir = project.getProperty("tmp.dir");
+        assertEqualContent(new File(tmpdir, "result.txt"),
+                    new File(tmpdir, "output.txt"));
+    }
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+    public void assertEqualContent(File expect, File result)
+        throws AssertionFailedError, IOException {
+        if (!result.exists()) {
+            fail("Expected file "+result+" doesn\'t exist");
+        }
+
+        InputStream inExpect = null;
+        InputStream inResult = null;
+        try {
+            inExpect = new BufferedInputStream(new FileInputStream(expect));
+            inResult = new BufferedInputStream(new FileInputStream(result));
+
+            int expectedByte = inExpect.read();
+            while (expectedByte != -1) {
+                assertEquals(expectedByte, inResult.read());
+                expectedByte = inExpect.read();
+            }
+            assertEquals("End of file", -1, inResult.read());
+        } finally {
+            if (inResult != null) {
+                inResult.close();
+            }
+            if (inExpect != null) {
+                inExpect.close();
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/RmicTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/RmicTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/RmicTest.java	(revision 16139)
@@ -0,0 +1,102 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.Project;
+
+import junit.framework.TestCase;
+
+/**
+ * Testcase for <rmic>.
+ *
+ * @since Ant 1.5
+ */
+public class RmicTest extends TestCase {
+
+    private Project project;
+    private Rmic rmic;
+
+    public RmicTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        project = new Project();
+        project.init();
+        rmic = new Rmic();
+        rmic.setProject(project);
+    }
+
+    /**
+     * Test nested compiler args.
+     */
+    public void testCompilerArg() {
+        String[] args = rmic.getCurrentCompilerArgs();
+        assertNotNull(args);
+        assertEquals("no args", 0, args.length);
+
+        Rmic.ImplementationSpecificArgument arg = rmic.createCompilerArg();
+        String ford = "Ford";
+        String prefect = "Prefect";
+        String testArg = ford + " " + prefect;
+        arg.setValue(testArg);
+        args = rmic.getCurrentCompilerArgs();
+        assertEquals("unconditional single arg", 1, args.length);
+        assertEquals(testArg, args[0]);
+
+        arg.setCompiler("weblogic");
+        args = rmic.getCurrentCompilerArgs();
+        assertNotNull(args);
+        assertEquals("implementation is weblogic but build.rmic is null",
+                     0, args.length);
+
+        project.setProperty("build.rmic", "sun");
+        args = rmic.getCurrentCompilerArgs();
+        assertNotNull(args);
+        assertEquals("implementation is weblogic but build.rmic is sun",
+                     0, args.length);
+
+        project.setProperty("build.rmic", "weblogic");
+        args = rmic.getCurrentCompilerArgs();
+        assertEquals("both are weblogic", 1, args.length);
+        assertEquals(testArg, args[0]);
+    }
+
+    /**
+     * Test compiler attribute.
+     */
+    public void testCompilerAttribute() {
+        // check defaults
+        String compiler = rmic.getCompiler();
+        assertNotNull(compiler);
+        assertTrue("default value",
+                   "sun".equals(compiler) || "kaffe".equals(compiler));
+
+        project.setNewProperty("build.rmic", "weblogic");
+        compiler = rmic.getCompiler();
+        assertNotNull(compiler);
+        assertEquals("weblogic", compiler);
+
+        // check attribute overrides build.compiler
+        rmic.setCompiler("kaffe");
+        compiler = rmic.getCompiler();
+        assertNotNull(compiler);
+        assertEquals("kaffe", compiler);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/SQLExecTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/SQLExecTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/SQLExecTest.java	(revision 16139)
@@ -0,0 +1,236 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.sql.Driver;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.DriverPropertyInfo;
+import java.util.Properties;
+import java.io.File;
+import java.net.URL;
+
+import junit.framework.TestCase;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Simple testcase to test for driver caching.
+ * To test for your own database, you may need to tweak getProperties(int)
+ * and add a couple of keys. see testOracle and testMySQL for an example.
+ *
+ * It would be much better to extend this testcase by using HSQL
+ * as the test db, so that a db is really used.
+ *
+ */
+public class SQLExecTest extends TestCase {
+
+    // some database keys, see #getProperties(int)
+    public final static int NULL = 0;
+    public final static int ORACLE = 1;
+    public final static int MYSQL = 2;
+
+    // keys used in properties.
+    public final static String DRIVER = "driver";
+    public final static String USER = "user";
+    public final static String PASSWORD = "password";
+    public final static String URL = "url";
+    public final static String PATH = "path";
+    public final static String SQL = "sql";
+
+    public SQLExecTest(String s) {
+        super(s);
+    }
+
+    protected void setUp() throws Exception {
+        // make sure the cache is cleared.
+        SQLExec.getLoaderMap().clear();
+    }
+
+   // simple test to ensure that the caching does work...
+    public void testDriverCaching(){
+       SQLExec sql = createTask(getProperties(NULL));
+        assertTrue(!SQLExec.getLoaderMap().containsKey(NULL_DRIVER));
+        try {
+            sql.execute();
+        } catch (BuildException e){
+            assertTrue(e.getException().getMessage().indexOf("No suitable Driver") != -1);
+        }
+        assertTrue(SQLExec.getLoaderMap().containsKey(NULL_DRIVER));
+        assertSame(sql.getLoader(), SQLExec.getLoaderMap().get(NULL_DRIVER));
+        ClassLoader loader1 = sql.getLoader();
+
+        // 2nd run..
+        sql = createTask(getProperties(NULL));
+        // the driver must still be cached.
+        assertTrue(sql.getLoaderMap().containsKey(NULL_DRIVER));
+        try {
+            sql.execute();
+        } catch (BuildException e){
+            assertTrue(e.getException().getMessage().indexOf("No suitable Driver") != -1);
+        }
+        assertTrue(sql.getLoaderMap().containsKey(NULL_DRIVER));
+        assertSame(sql.getLoader(), sql.getLoaderMap().get(NULL_DRIVER));
+        assertSame(loader1, sql.getLoader());
+    }
+
+    public void testNull() throws Exception {
+        doMultipleCalls(1000, NULL, true, true);
+    }
+
+    /*
+    public void testOracle(){
+        doMultipleCalls(1000, ORACLE, true, false);
+    }*/
+
+    /*
+    public void testMySQL(){
+        doMultipleCalls(1000, MYSQL, true, false);
+    }*/
+
+
+    /**
+     * run a sql tasks multiple times.
+     * @param calls number of times to execute the task
+     * @param database the database to execute on.
+     * @param caching should caching be enabled ?
+     * @param catchexception true to catch exception for each call, false if not.
+     */
+    protected void doMultipleCalls(int calls, int database, boolean caching, boolean catchexception){
+        Properties props = getProperties(database);
+        for (int i = 0; i < calls; i++){
+            SQLExec sql = createTask(props);
+            sql.setCaching(caching);
+            try  {
+                sql.execute();
+            } catch (BuildException e){
+                if (!catchexception){
+                    throw e;
+                }
+            }
+        }
+    }
+
+    /**
+     * Create a task from a set of properties
+     * @see #getProperties(int)
+     */
+    protected SQLExec createTask(Properties props){
+        SQLExec sql = new SQLExec();
+        sql.setProject( new Project() );
+        sql.setDriver( props.getProperty(DRIVER) );
+        sql.setUserid( props.getProperty(USER) );
+        sql.setPassword( props.getProperty(PASSWORD) );
+        sql.setUrl( props.getProperty(URL) );
+        sql.createClasspath().setLocation( new File(props.getProperty(PATH)) );
+        sql.addText( props.getProperty(SQL) );
+        return sql;
+    }
+
+    /**
+     * try to find the path from a resource (jar file or directory name)
+     * so that it can be used as a classpath to load the resource.
+     */
+    protected String findResourcePath(String resource){
+        resource = resource.replace('.', '/') + ".class";
+        URL url = getClass().getClassLoader().getResource(resource);
+        if (url == null) {
+            return null;
+        }
+        String u = url.toString();
+        if (u.startsWith("jar:file:")) {
+            int pling = u.indexOf("!");
+            return u.substring("jar:file:".length(), pling);
+        } else if (u.startsWith("file:")) {
+            int tail = u.indexOf(resource);
+            return u.substring("file:".length(), tail);
+        }
+        return null;
+    }
+
+    /**
+     * returns a configuration associated to a specific database.
+     * If you want to test on your specific base, you'd better
+     * tweak this to make it run or add your own database.
+     * The driver lib should be dropped into the system classloader.
+     */
+    protected Properties getProperties(int database){
+        Properties props = null;
+        switch (database){
+            case ORACLE:
+                props = getProperties("oracle.jdbc.driver.OracleDriver", "test", "test", "jdbc:oracle:thin:@127.0.0.1:1521:orcl");
+                break;
+            case MYSQL:
+                props = getProperties("org.gjt.mm.mysql.Driver", "test", "test", "jdbc:mysql://127.0.0.1:3306/test");
+                break;
+            case NULL:
+            default:
+                props = getProperties(NULL_DRIVER, "test", "test", "jdbc:database://hostname:port/name");
+        }
+        // look for the driver path...
+        String path = findResourcePath(props.getProperty(DRIVER));
+        props.put(PATH, path);
+        props.put(SQL, "create table OOME_TEST(X INTEGER NOT NULL);\ndrop table if exists OOME_TEST;");
+        return props;
+    }
+
+    /** helper method to build properties */
+    protected Properties getProperties(String driver, String user, String pwd, String url){
+        Properties props = new Properties();
+        props.put(DRIVER, driver);
+        props.put(USER, user);
+        props.put(PASSWORD, pwd);
+        props.put(URL, url);
+        return props;
+    }
+
+
+//--- NULL JDBC driver just for simple test since there are no db driver
+// available as a default in Ant :)
+
+    public final static String NULL_DRIVER = NullDriver.class.getName();
+
+    public static class NullDriver implements Driver {
+        public Connection connect(String url, Properties info)
+                throws SQLException {
+            return null;
+        }
+
+        public boolean acceptsURL(String url) throws SQLException {
+            return false;
+        }
+
+        public DriverPropertyInfo[] getPropertyInfo(String url, Properties info)
+                throws SQLException {
+            return new DriverPropertyInfo[0];
+        }
+
+        public int getMajorVersion() {
+            return 0;
+        }
+
+        public int getMinorVersion() {
+            return 0;
+        }
+
+        public boolean jdbcCompliant() {
+            return false;
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/SignJarTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/SignJarTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/SignJarTest.java	(revision 16139)
@@ -0,0 +1,69 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Vector;
+import java.util.Enumeration;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+
+/**
+ * Testcase for the Signjar task
+ *
+ */
+public class SignJarTest extends BuildFileTest {
+
+    public static final String EXPANDED_MANIFEST
+        = "src/etc/testcases/taskdefs/manifests/META-INF/MANIFEST.MF";
+
+    public SignJarTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/signjar.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("clean");
+    }
+
+    public void testBasicSigning() {
+        executeTarget("basic");
+    }
+
+    public void testSigFile() {
+        executeTarget("sigfile");
+    }
+
+    public void testMaxMemory() {
+        executeTarget("maxmemory");
+    }
+
+    public void testURLKeystoreFile() {
+        executeTarget("urlKeystoreFile");
+    }
+
+    public void testURLKeystoreHTTP() {
+        executeTarget("urlKeystoreHTTP");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/SleepTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/SleepTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/SleepTest.java	(revision 16139)
@@ -0,0 +1,110 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+/**
+ * @created 01 May 2001
+ */
+public class SleepTest extends BuildFileTest {
+
+
+    private final static String TASKDEFS_DIR = "src/etc/testcases/taskdefs/";
+    private final static boolean TRACE=false;
+	private final static int ERROR_RANGE=1000;
+	
+    public SleepTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject(TASKDEFS_DIR + "sleep.xml");
+    }
+
+    public void test1() {
+       Timer timer=new Timer();
+        executeTarget("test1");
+        timer.stop();
+        if(TRACE) System.out.println(" test1 elapsed time="+timer.time());
+        assertTrue(timer.time()>=0);
+    }
+
+    public void test2() {
+        Timer timer=new Timer();
+        executeTarget("test2");
+        timer.stop();
+        if(TRACE) System.out.println(" test2 elapsed time="+timer.time());
+        assertTrue(timer.time()>=0);
+    }
+
+    public void test3() {
+        Timer timer=new Timer();
+        executeTarget("test3");
+        timer.stop();
+        if(TRACE) System.out.println(" test3 elapsed time="+timer.time());
+        assertTrue(timer.time()>=(2000-ERROR_RANGE));
+    }
+
+    public void test4() {
+        Timer timer=new Timer();
+        executeTarget("test3");
+        timer.stop();
+        if(TRACE) System.out.println(" test4 elapsed time="+timer.time());
+        assertTrue(timer.time()>=(2000-ERROR_RANGE) && timer.time()<60000);
+    }
+
+    public void test5() {
+        expectBuildException("test5",
+            "Negative sleep periods are not supported");
+    }
+
+    public void test6() {
+        Timer timer=new Timer();
+        executeTarget("test6");
+        timer.stop();
+        if(TRACE) System.out.println(" test6 elapsed time="+timer.time());
+        assertTrue(timer.time()<2000);
+    }
+
+
+    /**
+    * inner timer class
+    */
+    private static class Timer {
+        long start=0;
+        long stop=0;
+
+        public Timer() {
+            start();
+        }
+
+        public void start() {
+            start=System.currentTimeMillis();
+        }
+
+        public void stop() {
+            stop=System.currentTimeMillis();
+        }
+
+        public long time() {
+            return stop-start;
+        }
+    }
+
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/StyleTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/StyleTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/StyleTest.java	(revision 16139)
@@ -0,0 +1,137 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.FileWriter;
+
+
+/**
+ * TestCases for <style> / <xslt> task.
+ * @version 2003-08-05
+ */
+public class StyleTest extends BuildFileTest {
+
+    public StyleTest(String s) {
+        super(s);
+    }
+
+    protected void setUp() throws Exception {
+        configureProject("src/etc/testcases/taskdefs/style/build.xml");
+        //executeTarget("setup");
+        //commented out for performance while target is empty
+    }
+
+    protected void tearDown() throws Exception {
+        executeTarget("teardown");
+    }
+
+    public void testStyleIsSet() throws Exception {
+        expectBuildException("testStyleIsSet", "no stylesheet specified");
+    }
+
+    public void testTransferParameterSet() throws Exception {
+        expectFileContains("testTransferParameterSet",  // target
+                           "out/out.xml",               // file
+                           "set='myvalue'");            // exptected string
+    }
+
+    public void testTransferParameterEmpty() throws Exception {
+        expectFileContains("testTransferParameterEmpty",
+                           "out/out.xml",
+                           "empty=''");
+    }
+
+    public void testTransferParameterUnset() throws Exception {
+        expectFileContains("testTransferParameterUnset",
+                           "out/out.xml",
+                           "undefined='${value}'");
+    }
+
+    public void testTransferParameterUnsetWithIf() throws Exception {
+        expectFileContains("testTransferParameterUnsetWithIf",
+                           "out/out.xml",
+                           "undefined='undefined default value'");
+    }
+
+    public void testNewerStylesheet() throws Exception {
+        expectFileContains("testNewerStylesheet",
+                           "out/out.xml",
+                           "new-value");
+    }
+
+
+    public void testDefaultMapper() throws Exception {
+        assertTrue(!getProject().resolveFile("out/data.html").exists());
+        expectFileContains("testDefaultMapper",
+                           "out/data.html",
+                           "set='myvalue'");
+    }
+
+    public void testCustomMapper() throws Exception {
+        assertTrue(!getProject().resolveFile("out/out.xml").exists());
+        expectFileContains("testCustomMapper",
+                           "out/out.xml",
+                           "set='myvalue'");
+    }
+
+    // *************  copied from ConcatTest  *************
+
+    // ------------------------------------------------------
+    //   Helper methods - should be in BuildFileTest
+    // -----------------------------------------------------
+
+    private String getFileString(String filename)
+        throws IOException
+    {
+        Reader r = null;
+        try {
+            r = new FileReader(getProject().resolveFile(filename));
+            return  FileUtils.newFileUtils().readFully(r);
+        }
+        finally {
+            try {r.close();} catch (Throwable ignore) {}
+        }
+
+    }
+
+    private String getFileString(String target, String filename)
+        throws IOException
+    {
+        executeTarget(target);
+        return getFileString(filename);
+    }
+
+    private void expectFileContains(
+        String target, String filename, String contains)
+        throws IOException
+    {
+        String content = getFileString(target, filename);
+        assertTrue(
+            "expecting file " + filename + " to contain " +
+            contains +
+            " but got " + content, content.indexOf(contains) > -1);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/SubAntTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/SubAntTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/SubAntTest.java	(revision 16139)
@@ -0,0 +1,131 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+
+import junit.framework.AssertionFailedError;
+
+import org.apache.tools.ant.BuildEvent;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.input.InputHandler;
+import org.apache.tools.ant.types.Path;
+
+/**
+ */
+public class SubAntTest extends BuildFileTest {
+
+    public SubAntTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/subant.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testnodirs() {
+        project.executeTarget("testnodirs");
+        expectLog("testnodirs", "No sub-builds to iterate on");
+    }
+
+    // target must be specified
+    public void testgenericantfile() {
+        File dir1 = project.resolveFile(".");
+        File dir2 = project.resolveFile("subant/subant-test1");
+        File dir3 = project.resolveFile("subant/subant-test2");
+
+        testBaseDirs("testgenericantfile",
+                     new String[] { dir1.getAbsolutePath(),
+                         dir2.getAbsolutePath(),
+                         dir3.getAbsolutePath()
+
+                     });
+    }
+
+    public void testantfile() {
+        File dir1 = project.resolveFile(".");
+        // basedir of subant/subant-test1/subant.xml is ..
+        // therefore we expect here the subant/subant-test1 subdirectory
+        File dir2 = project.resolveFile("subant/subant-test1");
+        // basedir of subant/subant-test2/subant.xml is ..
+        // therefore we expect here the subant subdirectory
+        File dir3 = project.resolveFile("subant");
+
+        testBaseDirs("testantfile",
+                     new String[] { dir1.getAbsolutePath(),
+                         dir2.getAbsolutePath(),
+                         dir3.getAbsolutePath()
+
+                     });
+
+    }
+
+    protected void testBaseDirs(String target, String[] dirs) {
+        SubAntTest.BasedirChecker bc = new SubAntTest.BasedirChecker(dirs);
+        project.addBuildListener(bc);
+        executeTarget(target);
+        AssertionFailedError ae = bc.getError();
+        if (ae != null) {
+            throw ae;
+        }
+        project.removeBuildListener(bc);
+    }
+
+    private class BasedirChecker implements BuildListener {
+        private String[] expectedBasedirs;
+        private int calls = 0;
+        private AssertionFailedError error;
+
+        BasedirChecker(String[] dirs) {
+            expectedBasedirs = dirs;
+        }
+
+        public void buildStarted(BuildEvent event) {}
+        public void buildFinished(BuildEvent event) {}
+        public void targetFinished(BuildEvent event){}
+        public void taskStarted(BuildEvent event) {}
+        public void taskFinished(BuildEvent event) {}
+        public void messageLogged(BuildEvent event) {}
+
+        public void targetStarted(BuildEvent event) {
+            if (event.getTarget().getName().equals("")) {
+                return;
+            }
+            if (error == null) {
+                try {
+                    assertEquals(expectedBasedirs[calls++],
+                            event.getProject().getBaseDir().getAbsolutePath());
+                } catch (AssertionFailedError e) {
+                    error = e;
+                }
+            }
+        }
+
+        AssertionFailedError getError() {
+            return error;
+        }
+
+    }
+
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TStampTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TStampTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TStampTest.java	(revision 16139)
@@ -0,0 +1,111 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+import java.util.Date;
+import java.text.SimpleDateFormat;
+
+import junit.framework.TestCase;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Location;
+
+/**
+ *
+ */
+public class TStampTest extends TestCase {
+
+    protected Tstamp tstamp;
+    protected Project project;
+    protected Location location;
+
+    public TStampTest(String s) {
+        super(s);
+    }
+
+    protected void setUp() throws Exception {
+        location = new Location("test.xml");
+        project = new Project();
+        tstamp = new Tstamp();
+        tstamp.setLocation(location);
+        tstamp.setProject(project);
+    }
+
+    public void testTimeZone() throws Exception {
+        Tstamp.CustomFormat format = tstamp.createFormat();
+        format.setProperty("today");
+        format.setPattern("HH:mm:ss z");
+        format.setTimezone("GMT");
+        Date date = Calendar.getInstance().getTime();
+        format.execute(project, date, location);
+        String today = project.getProperty("today");
+
+        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss z");
+        sdf.setTimeZone( TimeZone.getTimeZone("GMT") );
+        String expected = sdf.format(date);
+
+        assertEquals(expected, today);
+    }
+
+    /**
+     * verifies that custom props have priority over the
+     * originals
+     * @throws Exception
+     */
+    public void testWriteOrder() throws Exception {
+        Tstamp.CustomFormat format = tstamp.createFormat();
+        format.setProperty("TODAY");
+        format.setPattern("HH:mm:ss z");
+        format.setTimezone("GMT");
+        Date date = Calendar.getInstance().getTime();
+        format.execute(project, date, location);
+        String today = project.getProperty("TODAY");
+
+        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss z");
+        sdf.setTimeZone( TimeZone.getTimeZone("GMT") );
+        String expected = sdf.format(date);
+
+        assertEquals(expected, today);
+
+    }
+
+    /**
+     * verifies that custom props have priority over the
+     * originals
+     * @throws Exception
+     */
+    public void testPrefix() throws Exception {
+        tstamp.setPrefix("prefix");
+        tstamp.execute();
+        String prop= project.getProperty("prefix.DSTAMP");
+        assertNotNull(prop);
+    }
+
+    public void testFormatPrefix() throws Exception {
+	Tstamp.CustomFormat format = tstamp.createFormat();
+        format.setProperty("format");
+        format.setPattern("HH:mm:ss z");
+        format.setTimezone("GMT");
+
+        tstamp.setPrefix("prefix");
+        tstamp.execute();
+        String prop= project.getProperty("prefix.format");
+        assertNotNull(prop);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TarTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TarTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TarTest.java	(revision 16139)
@@ -0,0 +1,115 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class TarTest extends BuildFileTest {
+
+    public TarTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/tar.xml");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument not specified");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "required argument not specified");
+    }
+
+    public void test3() {
+        expectBuildException("test3", "required argument not specified");
+    }
+
+    public void test4() {
+        expectBuildException("test4", "tar cannot include itself");
+    }
+
+    public void test5() {
+        executeTarget("test5");
+        java.io.File f
+            = new java.io.File("src/etc/testcases/taskdefs/test5.tar");
+
+        if (!f.exists()) {
+            fail("Tarring a directory failed");
+        }
+    }
+
+    public void test6() {
+        expectBuildException("test6", "Invalid value specified for longfile attribute.");
+    }
+
+    public void test7() {
+        executeTarget("test7");
+        java.io.File f1
+            = new java.io.File("src/etc/testcases/taskdefs/test7-prefix");
+
+        if (!(f1.exists() && f1.isDirectory())) {
+            fail("The prefix attribute is not working properly.");
+        }
+
+        java.io.File f2
+            = new java.io.File("src/etc/testcases/taskdefs/test7dir");
+
+        if (!(f2.exists() && f2.isDirectory())) {
+            fail("The prefix attribute is not working properly.");
+        }
+    }
+
+    public void test8() {
+        executeTarget("test8");
+        java.io.File f1
+            = new java.io.File("src/etc/testcases/taskdefs/test8.xml");
+        if (! f1.exists()) {
+            fail("The fullpath attribute or the preserveLeadingSlashes attribute does not work propertly");
+        }
+    }
+
+    public void test9() {
+        expectBuildException("test9", "Invalid value specified for compression attribute.");
+    }
+
+    public void test10() {
+        executeTarget("test10");
+        java.io.File f1
+            = new java.io.File("src/etc/testcases/taskdefs/test10.xml");
+        if (! f1.exists()) {
+            fail("The fullpath attribute or the preserveLeadingSlashes attribute does not work propertly");
+        }
+    }
+
+    public void test11() {
+        executeTarget("test11");
+        java.io.File f1
+            = new java.io.File("src/etc/testcases/taskdefs/test11.xml");
+        if (! f1.exists()) {
+            fail("The fullpath attribute or the preserveLeadingSlashes attribute does not work propertly");
+        }
+    }
+
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TaskdefTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TaskdefTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TaskdefTest.java	(revision 16139)
@@ -0,0 +1,81 @@
+/*
+ * Copyright  2000-2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class TaskdefTest extends BuildFileTest {
+
+    public TaskdefTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/taskdef.xml");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument not specified");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "required argument not specified");
+    }
+
+    public void test3() {
+        expectBuildException("test3", "required argument not specified");
+    }
+
+    public void test4() {
+        expectBuildException("test4", "classname specified doesn't exist");
+    }
+
+    public void test5() {
+        expectBuildException("test5", "No public execute() in " + Project.class);
+    }
+
+    public void test5a() {
+        executeTarget("test5a");
+    }
+
+    public void test6() {
+        expectLog("test6", "simpletask: worked");
+    }
+
+    public void test7() {
+        expectLog("test7", "worked");
+    }
+
+    public void testGlobal() {
+        expectLog("testGlobal", "worked");
+    }
+
+    public void testOverride() {
+        executeTarget("testOverride");
+        String log = getLog();
+        assertTrue("override warning sent",
+                   log.indexOf("Trying to override old definition of task copy") > -1);
+        assertTrue("task inside target worked",
+                   log.indexOf("In target") > -1);
+        assertTrue("task inside target worked",
+                   log.indexOf("In TaskContainer") > -1);
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TaskdefsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TaskdefsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TaskdefsTest.java	(revision 16139)
@@ -0,0 +1,33 @@
+/*
+ * Copyright  2000-2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import junit.framework.*;
+import java.io.*;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ *
+ * @deprecated use BuildFileTest instead.
+ */
+public abstract class TaskdefsTest extends BuildFileTest {
+
+    public TaskdefsTest(String name) {
+        super(name);
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TestProcess.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TestProcess.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TestProcess.java	(revision 16139)
@@ -0,0 +1,85 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+/**
+ * Interactive Testcase for Processdestroyer.
+ *
+ */
+public class TestProcess
+    implements Runnable
+{
+    private boolean run = true;
+    private boolean done = false;
+
+    public void shutdown()
+    {
+        if (!done)
+        {
+            System.out.println("shutting down TestProcess");
+            run = false;
+
+            synchronized(this)
+            {
+                while (!done)
+                {
+                    try { wait(); } catch (InterruptedException ie) {}
+                }
+            }
+
+            System.out.println("TestProcess shut down");
+        }
+    }
+
+    public void run()
+    {
+        for (int i = 0; i < 5 && run; i++)
+        {
+            System.out.println(Thread.currentThread().getName());
+
+            try { Thread.sleep(2000); } catch (InterruptedException ie) {}
+        }
+
+        synchronized(this)
+        {
+            done = true;
+            notifyAll();
+        }
+    }
+
+    public Thread getShutdownHook()
+    {
+        return new TestProcessShutdownHook();
+    }
+
+    private class TestProcessShutdownHook
+        extends Thread
+    {
+        public void run()
+        {
+            shutdown();
+        }
+    }
+
+    public static void main(String[] args)
+    {
+        TestProcess tp = new TestProcess();
+        new Thread(tp, "TestProcess thread").start();
+        Runtime.getRuntime().addShutdownHook(tp.getShutdownHook());
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TimeProcess.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TimeProcess.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TimeProcess.java	(revision 16139)
@@ -0,0 +1,33 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+/**
+ * Helper class for ExecuteWatchdogTest and ExecuteJavaTest.
+ *
+ * <p>Used to be an inner class of ExecuteWatchdogTest.
+ *
+ */
+public class TimeProcess {
+    public static void main(String[] args) throws Exception {
+        int time = Integer.parseInt(args[0]);
+        if (time < 1) {
+            throw new IllegalArgumentException("Invalid time: " + time);
+        }
+        Thread.sleep(time);
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TouchTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TouchTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TouchTest.java	(revision 16139)
@@ -0,0 +1,175 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.util.FileUtils;
+
+import java.io.File;
+
+public class TouchTest extends BuildFileTest {
+
+    private static String TOUCH_FILE = "src/etc/testcases/taskdefs/touchtest";
+
+    /** Utilities used for file operations */
+    private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
+
+    public TouchTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/touch.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public long getTargetTime() {
+
+        File file = new File(System.getProperty("root"), TOUCH_FILE);
+        if(!file.exists()) {
+            throw new BuildException("failed to touch file " + file);
+        }
+        return file.lastModified();
+    }
+
+    /**
+     * No real test, simply checks whether the dateformat without
+     * seconds is accepted - by erroring out otherwise.
+     */
+    public void testNoSeconds() {
+        executeTarget("noSeconds");
+        long time = getTargetTime();
+    }
+
+    /**
+     * No real test, simply checks whether the dateformat with
+     * seconds is accepted - by erroring out otherwise.
+     */
+    public void testSeconds() {
+        executeTarget("seconds");
+        long time=getTargetTime();
+    }
+    /**
+     * verify that the millis test sets things up
+     */
+    public void testMillis() {
+        touchFile("testMillis", 662256000000L);
+    }
+
+    /**
+     * verify that the default value defaults to now
+     */
+    public void testNow() {
+        long now=System.currentTimeMillis();
+        executeTarget("testNow");
+        long time = getTargetTime();
+        assertTimesNearlyMatch(time,now,5000);
+    }
+    /**
+     * verify that the millis test sets things up
+     */
+    public void test2000() {
+        touchFile("test2000", 946080000000L);
+    }
+
+    /**
+     * test the file list
+     */
+    public void testFilelist() {
+        touchFile("testFilelist", 662256000000L);
+    }
+
+    /**
+     * test the file set
+     */
+    public void testFileset() {
+        touchFile("testFileset", 946080000000L);
+    }
+
+    /**
+     * test the mapped file set
+     */
+    public void testMappedFileset() {
+        executeTarget("testMappedFileset");
+    }
+
+    /**
+     * test the explicit mapped file set
+     */
+    public void testExplicitMappedFileset() {
+        executeTarget("testExplicitMappedFileset");
+    }
+
+    /**
+     * test the mapped file list
+     */
+    public void testMappedFilelist() {
+        executeTarget("testMappedFilelist");
+    }
+
+    /**
+     * test the pattern attribute
+     */
+    public void testGoodPattern() {
+        executeTarget("testGoodPattern");
+    }
+
+    /**
+     * test the pattern attribute again
+     */
+    public void testBadPattern() {
+        expectBuildExceptionContaining("testBadPattern",
+            "No parsing exception thrown", "Unparseable");
+    }
+
+    /**
+     * run a target to touch the test file; verify the timestamp is as expected
+     * @param targetName
+     * @param timestamp
+     */
+    private void touchFile(String targetName, long timestamp) {
+        executeTarget(targetName);
+        long time = getTargetTime();
+        assertTimesNearlyMatch(timestamp, time);
+    }
+
+    /**
+     * assert that two times are within the current FS granularity;
+     * @param timestamp
+     * @param time
+     */
+    public void assertTimesNearlyMatch(long timestamp,long time) {
+        long granularity= FILE_UTILS.getFileTimestampGranularity();
+        assertTimesNearlyMatch(timestamp, time, granularity);
+    }
+
+    /**
+     * assert that two times are within a specified range
+     * @param timestamp
+     * @param time
+     * @param range
+     */
+    private void assertTimesNearlyMatch(long timestamp, long time, long range) {
+        assertTrue("Time " + timestamp + " is not within " + range + " ms of "
+            + time, (Math.abs(time - timestamp) <= range));
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TypeAdapterTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TypeAdapterTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TypeAdapterTest.java	(revision 16139)
@@ -0,0 +1,154 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.lang.reflect.Method;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TypeAdapter;
+
+
+/**
+ */
+public class TypeAdapterTest extends BuildFileTest {
+
+    public TypeAdapterTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/typeadapter.xml");
+    }
+
+    public void testTaskAdapter() {
+        expectLogContaining("taskadapter", "MyExec called");
+    }
+
+    public void testRunAdapter() {
+        expectLogContaining("runadapter", "MyRunnable called");
+    }
+
+    public void testRunAdapterError() {
+        expectBuildExceptionContaining(
+            "runadaptererror", "xx", "No public run() method in");
+    }
+
+    public void testDelay() {
+        expectLogContaining("delay", "MyTask called");
+    }
+
+    public void testOnErrorReport() {
+        expectLogContaining("onerror.report",
+                            "MyTaskNotPresent cannot be found");
+    }
+
+    public void testOnErrorIgnore() {
+        expectLog("onerror.ignore","");
+    }
+
+    public static class MyTask extends Task {
+        public void execute() {
+            log("MyTask called");
+        }
+    }
+
+    public static class MyExec {
+        private Project project;
+        public void setProject(Project project) {
+            this.project = project;
+        }
+
+        public void execute() {
+            project.log("MyExec called");
+        }
+    }
+
+    public static class MyRunnable {
+        private Project project;
+        public void setProject(Project project) {
+            this.project = project;
+        }
+
+        public void run() {
+            project.log("MyRunnable called");
+        }
+    }
+
+    public static class RunnableAdapter
+        extends Task implements TypeAdapter
+    {
+        private String execMethodName = "run";
+        private Object proxy;
+
+        public Method getExecuteMethod(Class proxyClass) {
+            try {
+                Method execMethod = proxyClass.getMethod(
+                    execMethodName, null);
+                if (!Void.TYPE.equals(execMethod.getReturnType())) {
+                    String message =
+                        "return type of " + execMethodName + "() should be "
+                        + "void but was \"" + execMethod.getReturnType() +
+                        "\" in "
+                        + proxyClass;
+                    log(message, Project.MSG_WARN);
+                }
+                return execMethod;
+            } catch (NoSuchMethodException e) {
+                String message = "No public "+ execMethodName +
+                    "() method in "
+                    + proxyClass;
+                log(message, Project.MSG_ERR);
+                throw new BuildException(message);
+            }
+        }
+        public void checkProxyClass(Class proxyClass) {
+            getExecuteMethod(proxyClass);
+        }
+
+        public void setProxy(Object o) {
+            getExecuteMethod(o.getClass());
+            this.proxy = o;
+        }
+
+        public Object getProxy() {
+            return proxy;
+        }
+
+        public void execute() {
+            getProject().setProjectReference(proxy);
+            Method executeMethod = getExecuteMethod(proxy.getClass());
+            try {
+                executeMethod.invoke(proxy, null);
+            } catch (java.lang.reflect.InvocationTargetException ie) {
+                log("Error in " + proxy.getClass(), Project.MSG_ERR);
+                Throwable t = ie.getTargetException();
+                if (t instanceof BuildException) {
+                    throw ((BuildException) t);
+                } else {
+                    throw new BuildException(t);
+                }
+            } catch (Exception ex) {
+                log("Error in " + proxy.getClass(), Project.MSG_ERR);
+                throw new BuildException(ex);
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TypedefTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TypedefTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/TypedefTest.java	(revision 16139)
@@ -0,0 +1,75 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class TypedefTest extends BuildFileTest {
+
+    public TypedefTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/typedef.xml");
+    }
+
+    public void testEmpty() {
+        expectBuildException("empty", "required argument not specified");
+    }
+
+    public void testNoName() {
+        expectBuildException("noName", "required argument not specified");
+    }
+
+    public void testNoClassname() {
+        expectBuildException("noClassname", "required argument not specified");
+    }
+
+    public void testClassNotFound() {
+        expectBuildException("classNotFound",
+                             "classname specified doesn't exist");
+    }
+
+    public void testGlobal() {
+        expectLog("testGlobal", "");
+        Object ref = project.getReferences().get("global");
+        assertNotNull("ref is not null", ref);
+        assertEquals("org.example.types.TypedefTestType",
+                     ref.getClass().getName());
+    }
+
+    public void testLocal() {
+        expectLog("testLocal", "");
+        Object ref = project.getReferences().get("local");
+        assertNotNull("ref is not null", ref);
+        assertEquals("org.example.types.TypedefTestType",
+                     ref.getClass().getName());
+    }
+
+    /**
+     * test to make sure that one can define a not present
+     * optional type twice and then have a valid definition.
+     */
+    public void testDoubleNotPresent() {
+        expectLogContaining("double-notpresent", "hi");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/UntarTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/UntarTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/UntarTest.java	(revision 16139)
@@ -0,0 +1,91 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ */
+public class UntarTest extends BuildFileTest {
+
+    public UntarTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/untar.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testRealTest() throws java.io.IOException {
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        executeTarget("realTest");
+        assertTrue(fileUtils.contentEquals(project.resolveFile("../asf-logo.gif"),
+                                           project.resolveFile("asf-logo.gif")));
+    }
+
+    public void testRealGzipTest() throws java.io.IOException {
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        executeTarget("realGzipTest");
+        assertTrue(fileUtils.contentEquals(project.resolveFile("../asf-logo.gif"),
+                                           project.resolveFile("asf-logo.gif")));
+    }
+
+    public void testRealBzip2Test() throws java.io.IOException {
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        executeTarget("realBzip2Test");
+        assertTrue(fileUtils.contentEquals(project.resolveFile("../asf-logo.gif"),
+                                           project.resolveFile("asf-logo.gif")));
+    }
+
+    public void testTestTarTask() throws java.io.IOException {
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        executeTarget("testTarTask");
+        assertTrue(fileUtils.contentEquals(project.resolveFile("../asf-logo.gif"),
+                                           project.resolveFile("asf-logo.gif")));
+    }
+
+    public void testTestGzipTarTask() throws java.io.IOException {
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        executeTarget("testGzipTarTask");
+        assertTrue(fileUtils.contentEquals(project.resolveFile("../asf-logo.gif"),
+                                           project.resolveFile("asf-logo.gif")));
+    }
+
+    public void testTestBzip2TarTask() throws java.io.IOException {
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        executeTarget("testBzip2TarTask");
+        assertTrue(fileUtils.contentEquals(project.resolveFile("../asf-logo.gif"),
+                                           project.resolveFile("asf-logo.gif")));
+    }
+
+    public void testSrcDirTest() {
+        expectBuildException("srcDirTest", "Src cannot be a directory.");
+    }
+
+    public void testEncoding() {
+        expectSpecificBuildException("encoding",
+                                     "<untar> overrides setEncoding.",
+                                     "The untar task doesn't support the "
+                                     + "encoding attribute");
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/UnzipTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/UnzipTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/UnzipTest.java	(revision 16139)
@@ -0,0 +1,133 @@
+/*
+ * Copyright  2000-2001,2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ */
+public class UnzipTest extends BuildFileTest {
+
+    public UnzipTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/unzip.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument not specified");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "required argument not specified");
+    }
+
+    public void test3() {
+        expectBuildException("test3", "required argument not specified");
+    }
+
+
+    public void testRealTest() throws java.io.IOException {
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        executeTarget("realTest");
+        assertTrue(fileUtils.contentEquals(project.resolveFile("../asf-logo.gif"),
+                                           project.resolveFile("asf-logo.gif")));
+    }
+
+    public void testTestZipTask() throws java.io.IOException {
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        executeTarget("testZipTask");
+        assertTrue(fileUtils.contentEquals(project.resolveFile("../asf-logo.gif"),
+                                           project.resolveFile("asf-logo.gif")));
+    }
+
+    public void testTestUncompressedZipTask() throws java.io.IOException {
+        FileUtils fileUtils = FileUtils.newFileUtils();
+        executeTarget("testUncompressedZipTask");
+        assertTrue(fileUtils.contentEquals(project.resolveFile("../asf-logo.gif"),
+                                           project.resolveFile("asf-logo.gif")));
+    }
+
+    /*
+     * PR 11100
+     */
+    public void testPatternSetExcludeOnly() {
+        executeTarget("testPatternSetExcludeOnly");
+        assertTrue("1/foo is excluded",
+                   !getProject().resolveFile("unziptestout/1/foo").exists());
+        assertTrue("2/bar is not excluded",
+                   getProject().resolveFile("unziptestout/2/bar").exists());
+    }
+
+    /*
+     * PR 11100
+     */
+    public void testPatternSetIncludeOnly() {
+        executeTarget("testPatternSetIncludeOnly");
+        assertTrue("1/foo is not included",
+                   !getProject().resolveFile("unziptestout/1/foo").exists());
+        assertTrue("2/bar is included",
+                   getProject().resolveFile("unziptestout/2/bar").exists());
+    }
+
+    /*
+     * PR 11100
+     */
+    public void testPatternSetIncludeAndExclude() {
+        executeTarget("testPatternSetIncludeAndExclude");
+        assertTrue("1/foo is not included",
+                   !getProject().resolveFile("unziptestout/1/foo").exists());
+        assertTrue("2/bar is excluded",
+                   !getProject().resolveFile("unziptestout/2/bar").exists());
+    }
+
+    /*
+     * PR 16213
+     */
+    public void testSelfExtractingArchive() {
+        executeTarget("selfExtractingArchive");
+    }
+
+
+    /*
+     * PR 20969
+     */
+    public void testPatternSetSlashOnly() {
+        executeTarget("testPatternSetSlashOnly");
+        assertTrue("1/foo is not included",
+                   !getProject().resolveFile("unziptestout/1/foo").exists());
+        assertTrue("2/bar is included",
+                   getProject().resolveFile("unziptestout/2/bar").exists());
+    }
+
+    /*
+     * PR 10504
+     */
+    public void testEncoding() {
+        executeTarget("encodingTest");
+        assertTrue("foo has been properly named",
+                   getProject().resolveFile("unziptestout/foo").exists());
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/WarTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/WarTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/WarTest.java	(revision 16139)
@@ -0,0 +1,48 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * Testcase for the war task
+ *
+ */
+public class WarTest extends BuildFileTest {
+    public static final String TEST_BUILD_FILE
+        = "src/etc/testcases/taskdefs/war.xml";
+
+    public WarTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject(TEST_BUILD_FILE);
+    }
+
+    public void tearDown() {
+        executeTarget("clean");
+    }
+
+    /**
+     * Test direct dependency removal
+     */
+    public void testLibRefs() {
+        executeTarget("testlibrefs");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/XmlPropertyTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/XmlPropertyTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/XmlPropertyTest.java	(revision 16139)
@@ -0,0 +1,350 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ */
+public class XmlPropertyTest extends BuildFileTest {
+    private static FileUtils fileUtils = FileUtils.newFileUtils();
+
+    public XmlPropertyTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/xmlproperty.xml");
+    }
+
+    public void testProperties() {
+        executeTarget("test");
+        assertEquals("true", getProject().getProperty("root-tag(myattr)"));
+        assertEquals("Text", getProject().getProperty("root-tag.inner-tag"));
+        assertEquals("val",
+                     getProject().getProperty("root-tag.inner-tag(someattr)"));
+        assertEquals("false", getProject().getProperty("root-tag.a2.a3.a4"));
+        assertEquals("CDATA failed",
+            "<test>", getProject().getProperty("root-tag.cdatatag"));
+    }
+
+    public void testDTD() {
+        executeTarget("testdtd");
+        assertEquals("Text", getProject().getProperty("root-tag.inner-tag"));
+    }
+
+    public void testNone () {
+        doTest("testNone", false, false, false, false, false);
+    }
+
+    public void testKeeproot() {
+        doTest("testKeeproot", true, false, false, false, false);
+    }
+
+    public void testCollapse () {
+        doTest("testCollapse", false, true, false, false, false);
+    }
+
+    public void testSemantic () {
+        doTest("testSemantic", false, false, true, false, false);
+    }
+
+    public void testKeeprootCollapse () {
+        doTest("testKeeprootCollapse", true, true, false, false, false);
+    }
+
+    public void testKeeprootSemantic () {
+        doTest("testKeeprootSemantic", true, false, true, false, false);
+    }
+
+    public void testCollapseSemantic () {
+        doTest("testCollapseSemantic", false, true, true, false, false);
+    }
+
+    public void testKeeprootCollapseSemantic () {
+        doTest("testKeeprootCollapseSemantic", true, true, true, false, false);
+    }
+
+    public void testInclude () {
+        doTest("testInclude", false, false, false, true, false);
+    }
+
+    public void testSemanticInclude () {
+        doTest("testSemanticInclude", false, false, true, true, false);
+    }
+
+    public void testSemanticLocal () {
+        doTest("testSemanticInclude", false, false, true, false, true);
+    }
+
+    public void testNeedsCatalog() {
+        executeTarget("testneedscat");
+        assertEquals("true", getProject().getProperty("skinconfig.foo"));
+    }
+
+    /**
+     * Actually run a test, finding all input files (and corresponding
+     * goldfile)
+     */
+    private void doTest(String msg, boolean keepRoot, boolean collapse,
+                        boolean semantic, boolean include, boolean localRoot) {
+        Enumeration iter =
+            getFiles(new File("src/etc/testcases/taskdefs/xmlproperty/inputs"));
+        while (iter.hasMoreElements()) {
+            File inputFile = (File) iter.nextElement();
+            // What's the working directory?  If local, then its the
+            // folder of the input file.  Otherwise, its the "current" dir..
+            File workingDir;
+            if ( localRoot ) {
+                workingDir = fileUtils.getParentFile(inputFile);
+            } else {
+                workingDir = fileUtils.resolveFile(new File("."), ".");
+            }
+
+            try {
+
+                File propertyFile = getGoldfile(inputFile, keepRoot, collapse,
+                                                semantic, include, localRoot);
+                if (!propertyFile.exists()) {
+//                    System.out.println("Skipping as "
+//                                       + propertyFile.getAbsolutePath()
+//                                       + ") doesn't exist.");
+                    continue;
+                }
+
+                //                System.out.println(msg + " (" + propertyFile.getName() + ") in (" + workingDir + ")");
+
+                Project project = new Project();
+
+                XmlProperty xmlproperty = new XmlProperty();
+                xmlproperty.setProject(project);
+                xmlproperty.setFile(inputFile);
+
+                xmlproperty.setKeeproot(keepRoot);
+                xmlproperty.setCollapseAttributes(collapse);
+                xmlproperty.setSemanticAttributes(semantic);
+                xmlproperty.setIncludeSemanticAttribute(include);
+                xmlproperty.setRootDirectory(workingDir);
+
+                // Set a property on the project to make sure that loading
+                // a property with the same name from an xml file will
+                // *not* change it.
+                project.setNewProperty("override.property.test", "foo");
+
+                xmlproperty.execute();
+
+                Properties props = new Properties();
+                props.load(new FileInputStream(propertyFile));
+
+                //printProperties(project.getProperties());
+
+                ensureProperties(msg, inputFile, workingDir, project, props);
+                ensureReferences(msg, inputFile, project.getReferences());
+
+            } catch (IOException ex) {
+                fail(ex.toString());
+            }
+        }
+    }
+
+    /**
+     * Make sure every property loaded from the goldfile was also
+     * read from the XmlProperty.  We could try and test the other way,
+     * but some other properties may get set in the XmlProperty due
+     * to generic Project/Task configuration.
+     */
+    private static void ensureProperties (String msg, File inputFile,
+                                          File workingDir, Project project,
+                                          Properties properties) {
+        Hashtable xmlproperties = project.getProperties();
+        // Every key identified by the Properties must have been loaded.
+        Enumeration propertyKeyEnum = properties.propertyNames();
+        while(propertyKeyEnum.hasMoreElements()){
+            String currentKey = propertyKeyEnum.nextElement().toString();
+            String assertMsg = msg + "-" + inputFile.getName()
+                + " Key=" + currentKey;
+
+            String propertyValue = properties.getProperty(currentKey);
+
+            String xmlValue = (String)xmlproperties.get(currentKey);
+
+            if ( propertyValue.indexOf("ID.") == 0 ) {
+                // The property is an id's thing -- either a property
+                // or a path.  We need to make sure
+                // that the object was created with the given id.
+                // We don't have an adequate way of testing the actual
+                // *value* of the Path object, though...
+                String id = currentKey;
+                Object obj = project.getReferences().get(id);
+
+                if ( obj == null ) {
+                    fail(assertMsg + " Object ID does not exist.");
+                }
+
+                // What is the property supposed to be?
+                propertyValue =
+                    propertyValue.substring(3, propertyValue.length());
+                if (propertyValue.equals("path")) {
+                    if (!(obj instanceof Path)) {
+                        fail(assertMsg + " Path ID is a "
+                             + obj.getClass().getName());
+                    }
+                } else {
+                    assertEquals(assertMsg, propertyValue, obj.toString());
+                }
+
+            } else {
+
+                if (propertyValue.indexOf("FILE.") == 0) {
+                    // The property is the name of a file.  We are testing
+                    // a location attribute, so we need to resolve the given
+                    // file name in the provided folder.
+                    String fileName =
+                        propertyValue.substring(5, propertyValue.length());
+                    File f = new File(workingDir, fileName);
+                    propertyValue = f.getAbsolutePath();
+                }
+
+                assertEquals(assertMsg, propertyValue, xmlValue);
+            }
+
+        }
+    }
+
+    /**
+     * Debugging method to print the properties in the given hashtable
+     */
+    private static void printProperties(Hashtable xmlproperties) {
+        Enumeration keyEnum = xmlproperties.keys();
+        while (keyEnum.hasMoreElements()) {
+            String currentKey = keyEnum.nextElement().toString();
+            System.out.println(currentKey + " = "
+                               + xmlproperties.get(currentKey));
+        }
+    }
+
+    /**
+     * Ensure all references loaded by the project are valid.
+     */
+    private static void ensureReferences (String msg, File inputFile,
+                                          Hashtable references) {
+        Enumeration referenceKeyEnum = references.keys();
+        while(referenceKeyEnum.hasMoreElements()){
+            String currentKey = referenceKeyEnum.nextElement().toString();
+            Object currentValue = references.get(currentKey);
+
+            if (currentValue instanceof Path) {
+            } else if (currentValue instanceof String) {
+            } else {
+                if( ! currentKey.startsWith("ant.") ) {
+                    fail(msg + "-" + inputFile.getName() + " Key="
+                         + currentKey + " is not a recognized type.");
+                }
+            }
+        }
+    }
+
+    /**
+     * Munge the name of the input file to find an appropriate goldfile,
+     * based on hardwired naming conventions.
+     */
+    private static File getGoldfile (File input, boolean keepRoot,
+                                     boolean collapse, boolean semantic,
+                                     boolean include, boolean localRoot) {
+        // Substitute .xml with .properties
+        String baseName = input.getName().toLowerCase();
+        if (baseName.endsWith(".xml")) {
+            baseName = baseName.substring(0, baseName.length() - 4)
+                + ".properties";
+        }
+
+        File dir = fileUtils.getParentFile(fileUtils.getParentFile(input));
+
+        String goldFileFolder = "goldfiles/";
+
+        if (keepRoot) {
+            goldFileFolder += "keeproot-";
+        } else {
+            goldFileFolder += "nokeeproot-";
+        }
+
+        if (semantic) {
+            goldFileFolder += "semantic-";
+            if (include) {
+                goldFileFolder += "include-";
+            }
+        } else {
+            if (collapse) {
+                goldFileFolder += "collapse-";
+            } else {
+                goldFileFolder += "nocollapse-";
+            }
+        }
+
+        return new File(dir, goldFileFolder + baseName);
+    }
+
+    /**
+     * Retrieve a list of xml files in the specified folder
+     * and below.
+     */
+    private static Enumeration getFiles (final File startingDir) {
+        Vector result = new Vector();
+        getFiles(startingDir, result);
+        return result.elements();
+    }
+
+    /**
+     * Collect a list of xml files in the specified folder
+     * and below.
+     */
+    private static void getFiles (final File startingDir, Vector collect) {
+        FileFilter filter = new FileFilter() {
+            public boolean accept (File file) {
+                if (file.isDirectory()) {
+                    return true;
+                } else {
+                    return (file.getPath().indexOf("taskdefs") > 0 &&
+                            file.getPath().toLowerCase().endsWith(".xml") );
+                }
+            }
+        };
+
+        File[] files = startingDir.listFiles(filter);
+        for (int i=0;i<files.length;i++) {
+            File f = files[i];
+            if (!f.isDirectory()) {
+                collect.addElement(f);
+            } else {
+                getFiles(f, collect);
+            }
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/XmlnsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/XmlnsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/XmlnsTest.java	(revision 16139)
@@ -0,0 +1,68 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+/**
+ */
+public class XmlnsTest extends BuildFileTest {
+    public XmlnsTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/xmlns.xml");
+    }
+
+    public void testXmlns() {
+        expectLog("xmlns", "MyTask called");
+    }
+
+    public void testXmlnsFile() {
+        expectLog("xmlns.file", "MyTask called");
+    }
+
+    public void testCore() {
+        expectLog("core", "MyTask called");
+    }
+
+    public void testExcluded() {
+        expectBuildExceptionContaining(
+            "excluded", "excluded uri",
+            "Attempt to use a reserved URI ant:notallowed");
+    }
+
+    public void testOther() {
+        expectLog("other", "a message");
+    }
+
+    public void testNsAttributes() {
+        expectLog("ns.attributes", "hello world");
+    }
+
+    public static class MyTask extends Task {
+        public void execute() {
+            log("MyTask called");
+        }
+    }
+
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ZipTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ZipTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/ZipTest.java	(revision 16139)
@@ -0,0 +1,148 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs;
+import org.apache.tools.ant.BuildFileTest;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+import java.util.Enumeration;
+
+/**
+ */
+public class ZipTest extends BuildFileTest {
+    //instance variable to allow cleanup
+    ZipFile zfPrefixAddsDir = null;
+    public ZipTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/zip.xml");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument not specified");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "required argument not specified");
+    }
+
+    public void test3() {
+        expectBuildException("test3", "zip cannot include itself");
+    }
+
+//    public void test4() {
+//        expectBuildException("test4", "zip cannot include itself");
+//    }
+
+    public void tearDown() {
+        try {
+            if ( zfPrefixAddsDir != null) {
+                zfPrefixAddsDir.close();
+            }
+
+        } catch (IOException e) {
+            //ignored
+        }
+        executeTarget("cleanup");
+    }
+
+    public void test5() {
+        executeTarget("test5");
+    }
+
+
+    public void test6() {
+        executeTarget("test6");
+    }
+
+
+    public void test7() {
+        executeTarget("test7");
+    }
+
+    public void test8() {
+        executeTarget("test8");
+    }
+
+    public void testZipgroupfileset() throws IOException {
+        executeTarget("testZipgroupfileset");
+
+        ZipFile zipFile = new ZipFile(new File(getProjectDir(), "zipgroupfileset.zip"));
+
+        assertTrue(zipFile.getEntry("ant.xml") != null);
+        assertTrue(zipFile.getEntry("optional/jspc.xml") != null);
+        assertTrue(zipFile.getEntry("zip/zipgroupfileset3.zip") != null);
+
+        assertTrue(zipFile.getEntry("test6.mf") == null);
+        assertTrue(zipFile.getEntry("test7.mf") == null);
+
+        zipFile.close();
+    }
+
+    public void testUpdateNotNecessary() {
+        executeTarget("testUpdateNotNecessary");
+        assertEquals(-1, getLog().indexOf("Updating"));
+    }
+
+    public void testUpdateIsNecessary() {
+        expectLogContaining("testUpdateIsNecessary", "Updating");
+    }
+
+    // Bugzilla Report 18403
+    public void testPrefixAddsDir() throws IOException {
+        executeTarget("testPrefixAddsDir");
+        File archive = getProject().resolveFile("test3.zip");
+        zfPrefixAddsDir = new ZipFile(archive);
+        ZipEntry ze = zfPrefixAddsDir.getEntry("test/");
+        assertNotNull("test/ has been added", ze);
+
+    }
+
+    // Bugzilla Report 19449
+    public void testFilesOnlyDoesntCauseRecreate()
+        throws InterruptedException {
+        executeTarget("testFilesOnlyDoesntCauseRecreateSetup");
+        long l = getProject().resolveFile("test3.zip").lastModified();
+        Thread.currentThread().sleep(3000);
+        executeTarget("testFilesOnlyDoesntCauseRecreate");
+        assertEquals(l, getProject().resolveFile("test3.zip").lastModified());
+    }
+
+    // Bugzilla Report 22865
+    public void testEmptySkip() {
+        executeTarget("testEmptySkip");
+        assertTrue("archive should get skipped",
+                   !getProject().resolveFile("test3.zip").exists());
+    }
+    // Bugzilla Report 30365
+    public void testZipEmptyDir() {
+        executeTarget("zipEmptyDir");
+        assertTrue("archive should be created",
+                   getProject().resolveFile("test3.zip").exists());
+    }
+    public void testZipEmptyCreate() {
+        expectLogContaining("zipEmptyCreate", "Note: creating empty");
+        assertTrue("archive should be created",
+                   getProject().resolveFile("test3.zip").exists());
+
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/condition/ContainsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/condition/ContainsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/condition/ContainsTest.java	(revision 16139)
@@ -0,0 +1,42 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import junit.framework.TestCase;
+
+/**
+ * Testcase for the &lt;contains&gt; condition.
+ *
+ */
+public class ContainsTest extends TestCase {
+
+    public ContainsTest(String name) {
+        super(name);
+    }
+
+    public void testCaseSensitive() {
+        Contains con = new Contains();
+        con.setString("abc");
+        con.setSubstring("A");
+        assertTrue(!con.eval());
+
+        con.setCasesensitive(false);
+        assertTrue(con.eval());
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/condition/EqualsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/condition/EqualsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/condition/EqualsTest.java	(revision 16139)
@@ -0,0 +1,55 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.condition;
+
+import junit.framework.TestCase;
+
+/**
+ * Testcase for the &lt;equals&gt; condition.
+ *
+ */
+public class EqualsTest extends TestCase {
+
+    public EqualsTest(String name) {
+        super(name);
+    }
+
+    public void testTrim() {
+        Equals eq = new Equals();
+        eq.setArg1("a");
+        eq.setArg2(" a");
+        assertTrue(!eq.eval());
+
+        eq.setTrim(true);
+        assertTrue(eq.eval());
+
+        eq.setArg2("a\t");
+        assertTrue(eq.eval());
+    }
+
+    public void testCaseSensitive() {
+        Equals eq = new Equals();
+        eq.setArg1("a");
+        eq.setArg2("A");
+        assertTrue(!eq.eval());
+
+        eq.setCasesensitive(false);
+        assertTrue(eq.eval());
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/condition/IsReferenceTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/condition/IsReferenceTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/condition/IsReferenceTest.java	(revision 16139)
@@ -0,0 +1,60 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.condition;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * Testcases for the &lt;isreference&gt; condition.
+ *
+ */
+public class IsReferenceTest extends BuildFileTest {
+
+    public IsReferenceTest(String name) {
+        super(name);
+    }
+
+    /**
+     * The JUnit setup method
+     */
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/conditions/isreference.xml");
+    }
+
+    public void testBasic() {
+       expectPropertySet("basic", "global-path");
+       assertPropertySet("target-path");
+       assertPropertyUnset("undefined");
+    }
+
+    public void testNotEnoughArgs() {
+        expectSpecificBuildException("isreference-incomplete",
+                                     "refid attribute has been omitted",
+                                     "No reference specified for isreference "
+                                     + "condition");
+    }
+
+    public void testType() {
+       expectPropertySet("type", "global-path");
+       assertPropertyUnset("global-path-as-fileset");
+       assertPropertyUnset("global-path-as-foo");
+       assertPropertySet("global-echo");
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/dir1/B.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/dir1/B.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/dir1/B.java	(revision 16139)
@@ -0,0 +1,29 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.dir1;
+
+public class B extends org.apache.tools.ant.taskdefs.dir2.A {
+    static {
+        System.out.println("B CLASS INITIALIZATION");
+        setA(new B());
+    }
+
+    public String toString() {
+        return "I am a B.";
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/dir2/A.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/dir2/A.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/dir2/A.java	(revision 16139)
@@ -0,0 +1,39 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.dir2;
+
+public class A {
+    public static void main(String [] args) {
+        System.out.println("MAIN");
+        System.out.println(a);
+    }
+
+    static A a=new A();
+
+    static {
+        System.out.println("A CLASS INITIALIZATION");
+    }
+
+    protected static void setA(A oa) {
+        a=oa;
+    }
+
+    public String toString() {
+        return "I am a A.";
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/email/EmailAddressTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/email/EmailAddressTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/email/EmailAddressTest.java	(revision 16139)
@@ -0,0 +1,103 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.email;
+
+import junit.framework.TestCase;
+
+/**
+ * @since Ant 1.6
+ */
+public class EmailAddressTest extends TestCase {
+
+    public EmailAddressTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+    }
+
+    public void test1() {
+        expectNameAddress( new EmailAddress("address (name)") );
+    }
+
+    public void test2() {
+        expectNameAddress( new EmailAddress("(name) address") );
+    }
+
+    public void test3() {
+        expectNameAddress( new EmailAddress("name <address>") );
+    }
+
+    public void test4() {
+        expectNameAddress( new EmailAddress("<address> name") );
+    }
+
+    public void test5() {
+        expectNameAddress( new EmailAddress("<address> (name)") );
+    }
+
+    public void test6() {
+        expectNameAddress( new EmailAddress("(name) <address>") );
+    }
+
+    public void test7() {
+        expectNameAddress2( new EmailAddress("address (<name>)") );
+    }
+
+    public void test8() {
+        expectNameAddress2( new EmailAddress("(<name>) address") );
+    }
+
+    public void test9() {
+        expectNameAddress3( new EmailAddress("address") );
+    }
+
+    public void testA() {
+        expectNameAddress3( new EmailAddress("<address>") );
+    }
+
+    public void testB() {
+        expectNameAddress3( new EmailAddress(" <address> ") );
+    }
+
+    public void testC() {
+        expectNameAddress3( new EmailAddress("< address >") );
+    }
+
+    public void testD() {
+        expectNameAddress3( new EmailAddress(" < address > ") );
+    }
+
+    private void expectNameAddress(EmailAddress e) {
+        assertEquals( "name", e.getName() );
+        assertEquals( "address", e.getAddress() );
+    }
+
+    // where the name contains <>
+    private void expectNameAddress2(EmailAddress e) {
+        assertEquals( "<name>", e.getName() );
+        assertEquals( "address", e.getAddress() );
+    }
+
+    // where only an address is supplied
+    private void expectNameAddress3(EmailAddress e) {
+        assertTrue( "Expected null, found <" + e.getName() + ">",
+            e.getName() == null );
+        assertEquals( "address", e.getAddress() );
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/email/EmailTaskTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/email/EmailTaskTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/email/EmailTaskTest.java	(revision 16139)
@@ -0,0 +1,44 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.email;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * TODO : develop these testcases - the email task needs to have attributes allowing
+ * to simulate sending mail and to catch the output in text files or streams
+ */
+public class EmailTaskTest extends BuildFileTest {
+
+    public EmailTaskTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/email/mail.xml");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "SMTP auth only possible with MIME mail");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "SSL only possible with MIME mail");
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/ANTLRTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/ANTLRTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/ANTLRTest.java	(revision 16139)
@@ -0,0 +1,167 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.*;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * If you want to run tests, it is highly recommended
+ * to download ANTLR (www.antlr.org), build the 'antlrall.jar' jar
+ * with  <code>make antlr-all.jar</code> and drop the jar (about 300KB) into
+ * Ant lib.
+ * - Running w/ the default antlr.jar (70KB) does not work (missing class)
+ *
+ * Unless of course you specify the ANTLR classpath in your
+ * system classpath. (see ANTLR install.html)
+ *
+ */
+public class ANTLRTest extends BuildFileTest {
+
+    private final static String TASKDEFS_DIR = "src/etc/testcases/taskdefs/optional/antlr/";
+
+    public ANTLRTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject(TASKDEFS_DIR + "antlr.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "required argument, target, missing");
+    }
+
+    public void test2() {
+        expectBuildException("test2", "Invalid output directory");
+    }
+
+    public void test3() {
+        executeTarget("test3");
+        File outputDirectory = new File(TASKDEFS_DIR + "antlr.tmp");
+        String[] calcFiles = outputDirectory.list(new CalcFileFilter());
+        assertEquals(5, calcFiles.length);
+    }
+
+    public void test4() {
+        executeTarget("test4");
+    }
+
+    public void test5() {
+        // should print "panic: Cannot find importVocab file 'JavaTokenTypes.txt'"
+        // since it needs to run java.g first before java.tree.g
+        expectBuildException("test5", "ANTLR returned: 1");
+    }
+
+    public void test6() {
+        executeTarget("test6");
+    }
+
+    public void test7() {
+        expectBuildException("test7", "Unable to determine generated class");
+    }
+
+    /**
+     * This is a negative test for the super grammar (glib) option.
+     */
+    public void test8() {
+        expectBuildException("test8", "Invalid super grammar file");
+    }
+
+    /**
+     * This is a positive test for the super grammar (glib) option.  ANTLR
+     * will throw an error if everything is not correct.
+     */
+    public void test9() {
+        executeTarget("test9");
+    }
+
+    /**
+     * This test creates an html-ized version of the calculator grammar.
+     * The sanity check is simply whether or not an html file was generated.
+     */
+    public void test10() {
+        executeTarget("test10");
+        File outputDirectory = new File(TASKDEFS_DIR + "antlr.tmp");
+        String[] calcFiles = outputDirectory.list(new HTMLFilter());
+        assertTrue(calcFiles.length > 0);
+    }
+
+    /**
+     * This is just a quick sanity check to run the diagnostic option and
+     * make sure that it doesn't throw any funny exceptions.
+     */
+    public void test11() {
+        executeTarget("test11");
+    }
+
+    /**
+     * This is just a quick sanity check to run the trace option and
+     * make sure that it doesn't throw any funny exceptions.
+     */
+    public void test12() {
+        executeTarget("test12");
+    }
+
+    /**
+     * This is just a quick sanity check to run all the rest of the
+     * trace options (traceLexer, traceParser, and traceTreeWalker) to
+     * make sure that they don't throw any funny exceptions.
+     */
+    public void test13() {
+        executeTarget("test13");
+    }
+
+    public void testNoRecompile() {
+        executeTarget("test9");
+        assertEquals(-1, getFullLog().indexOf("Skipped grammar file."));
+        executeTarget("noRecompile");
+        assertTrue(-1 != getFullLog().indexOf("Skipped grammar file."));
+    }
+
+    public void testNormalRecompile() {
+        executeTarget("test9");
+        assertEquals(-1, getFullLog().indexOf("Skipped grammar file."));
+        executeTarget("normalRecompile");
+        assertEquals(-1, getFullLog().indexOf("Skipped grammar file."));
+    }
+
+    // Bugzilla Report 12961
+    public void testSupergrammarChangeRecompile() {
+        executeTarget("test9");
+        assertEquals(-1, getFullLog().indexOf("Skipped grammar file."));
+        executeTarget("supergrammarChangeRecompile");
+        assertEquals(-1, getFullLog().indexOf("Skipped grammar file."));
+    }
+}
+
+class CalcFileFilter implements FilenameFilter {
+    public boolean accept(File dir, String name) {
+        return name.startsWith("Calc");
+    }
+}
+
+class HTMLFilter implements FilenameFilter {
+    public boolean accept(File dir, String name) {
+        return name.endsWith("html");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/AbstractXSLTLiaisonTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/AbstractXSLTLiaisonTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/AbstractXSLTLiaisonTest.java	(revision 16139)
@@ -0,0 +1,95 @@
+package org.apache.tools.ant.taskdefs.optional;
+
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+import junit.framework.TestCase;
+import org.apache.tools.ant.taskdefs.XSLTLiaison;
+import org.w3c.dom.Document;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.net.URL;
+
+/**
+ * Abtract testcase for XSLTLiaison.
+ * Override createLiaison for each XSLTLiaison.
+ *
+ * <a href="sbailliez@apache.org">Stephane Bailliez</a>
+ */
+public abstract class AbstractXSLTLiaisonTest extends TestCase {
+
+    protected XSLTLiaison liaison;
+
+    protected  AbstractXSLTLiaisonTest(String name){
+        super(name);
+    }
+
+    protected void setUp() throws Exception {
+        liaison = createLiaison();
+    }
+
+    // to override
+    protected abstract XSLTLiaison createLiaison() throws Exception ;
+
+    /** load the file from the caller classloader that loaded this class */
+    protected File getFile(String name) throws FileNotFoundException {
+        URL url = getClass().getResource(name);
+        if (url == null){
+          throw new FileNotFoundException("Unable to load '" + name + "' from classpath");
+        }
+        return new File(url.getFile());
+    }
+
+    /** keep it simple stupid */
+    public void testTransform() throws Exception {
+        File xsl = getFile("/taskdefs/optional/xsltliaison-in.xsl");
+        liaison.setStylesheet(xsl);
+        liaison.addParam("param", "value");
+        File in = getFile("/taskdefs/optional/xsltliaison-in.xml");
+        File out = new File("xsltliaison.tmp");
+        try {
+            liaison.transform(in, out);
+        } finally {
+            out.delete();
+        }
+    }
+
+    public void testEncoding() throws Exception {
+        File xsl = getFile("/taskdefs/optional/xsltliaison-encoding-in.xsl");
+        liaison.setStylesheet(xsl);
+        File in = getFile("/taskdefs/optional/xsltliaison-encoding-in.xml");
+        File out = new File("xsltliaison-encoding.tmp");
+        try {
+            liaison.transform(in, out);
+            Document doc = parseXML(out);
+            assertEquals("root",doc.getDocumentElement().getNodeName());
+            assertEquals("message",doc.getDocumentElement().getFirstChild().getNodeName());
+            assertEquals("\u00E9\u00E0\u00E8\u00EF\u00F9",doc.getDocumentElement().getFirstChild().getFirstChild().getNodeValue());
+        } finally {
+            out.delete();
+        }
+    }
+
+    public Document parseXML(File file) throws Exception {
+        DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
+        DocumentBuilder dbuilder = dbfactory.newDocumentBuilder();
+        return dbuilder.parse(file);
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/BeanShellScriptTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/BeanShellScriptTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/BeanShellScriptTest.java	(revision 16139)
@@ -0,0 +1,43 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * Tests the examples of the &lt;script&gt; task docs.
+ *
+ * @since Ant 1.5.2
+ */
+public class BeanShellScriptTest extends BuildFileTest {
+
+    public BeanShellScriptTest(String name) {
+        super(name);
+    }
+
+    /**
+     * The JUnit setup method
+     */
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/optional/script.xml");
+    }
+
+    public void testCanLoad() {
+        expectLog("useBeanshell", "I'm here");
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/DotnetTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/DotnetTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/DotnetTest.java	(revision 16139)
@@ -0,0 +1,131 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.*;
+import java.util.Properties;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * Tests the Dotnet tasks, based off WsdlToDotnetTest
+ *
+ * @since Ant 1.6
+ */
+public class DotnetTest extends BuildFileTest {
+
+    /**
+     * Description of the Field
+     */
+    private final static String TASKDEFS_DIR = "src/etc/testcases/taskdefs/optional/";
+
+
+    /**
+     * Constructor
+     *
+     * @param name testname
+     */
+    public DotnetTest(String name) {
+        super(name);
+    }
+
+
+    /**
+     * The JUnit setup method
+     */
+    public void setUp() {
+        configureProject(TASKDEFS_DIR + "dotnet.xml");
+    }
+
+
+    /**
+     * The teardown method for JUnit
+     */
+    public void tearDown() {
+        executeTarget("teardown");
+    }
+
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testCSC() throws Exception {
+        executeTarget("testCSC");
+    }
+
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testCSCintrinsicFileset() throws Exception {
+        executeTarget("testCSCintrinsicFileset");
+    }
+
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testCSCdll() throws Exception {
+        executeTarget("testCSCdll");
+    }
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testCscReferences() throws Exception {
+        executeTarget("testCscReferences");
+    }
+
+    /**
+     * test we can assemble
+     */
+    public void testILASM() throws Exception {
+        executeTarget("testILASM");
+    }
+
+    /**
+     * test we can disassemble
+     */
+    public void testILDASM() throws Exception {
+        executeTarget("testILDASM");
+    }
+
+    /**
+     * test we can disassemble
+     */
+    public void testILDASM_empty() throws Exception {
+        expectBuildExceptionContaining("testILDASM_empty",
+                "parameter validation",
+                "invalid");
+    }
+
+    /**
+     * test we can handle jsharp (if found)
+     */
+    public void testJsharp() throws Exception {
+        executeTarget("jsharp");
+    }
+
+    /**
+     * test we can handle jsharp (if found)
+     */
+    public void testResponseFile() throws Exception {
+        executeTarget("testCSCresponseFile");
+    }
+
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/EchoPropertiesTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/EchoPropertiesTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/EchoPropertiesTest.java	(revision 16139)
@@ -0,0 +1,228 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional;
+
+import org.apache.tools.ant.BuildFileTest;
+
+import java.io.IOException;
+import java.io.File;
+import java.io.InputStream;
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.BufferedReader;
+import java.util.Properties;
+
+/**
+ * Tests the EchoProperties task.
+ *
+ * @created   17-Jan-2002
+ * @since     Ant 1.5
+ */
+public class EchoPropertiesTest extends BuildFileTest {
+
+    private final static String TASKDEFS_DIR = "src/etc/testcases/taskdefs/optional/";
+    private static final String GOOD_OUTFILE = "test.properties";
+    private static final String GOOD_OUTFILE_XML = "test.xml";
+    private static final String PREFIX_OUTFILE = "test-prefix.properties";
+    private static final String TEST_VALUE = "isSet";
+    private static final String BAD_OUTFILE = ".";
+
+    public EchoPropertiesTest(String name) {
+        super(name);
+    }
+
+
+    public void setUp() {
+        configureProject(TASKDEFS_DIR + "echoproperties.xml");
+        project.setProperty( "test.property", TEST_VALUE );
+    }
+
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+
+    public void testEchoToLog() {
+        expectLogContaining("testEchoToLog", "test.property="+TEST_VALUE);
+    }
+
+
+    public void testReadBadFile() {
+        expectBuildExceptionContaining( "testReadBadFile",
+            "srcfile is a directory", "srcfile is a directory!" );
+    }
+
+
+    public void testReadBadFileFail() {
+        expectBuildExceptionContaining( "testReadBadFile",
+            "srcfile is a directory", "srcfile is a directory!" );
+    }
+
+
+    public void testReadBadFileNoFail() {
+        expectLog( "testReadBadFileNoFail", "srcfile is a directory!" );
+    }
+
+
+    public void testEchoToBadFile() {
+        expectBuildExceptionContaining( "testEchoToBadFile",
+            "destfile is a directory", "destfile is a directory!" );
+    }
+
+
+    public void testEchoToBadFileFail() {
+        expectBuildExceptionContaining( "testEchoToBadFileFail",
+            "destfile is a directory", "destfile is a directory!" );
+    }
+
+
+    public void testEchoToBadFileNoFail() {
+        expectLog( "testEchoToBadFileNoFail", "destfile is a directory!");
+    }
+
+
+    public void testEchoToGoodFile() throws Exception {
+        executeTarget( "testEchoToGoodFile" );
+        assertGoodFile();
+    }
+
+
+    public void testEchoToGoodFileXml() throws Exception {
+        executeTarget( "testEchoToGoodFileXml" );
+
+        // read in the file
+        File f = createRelativeFile( GOOD_OUTFILE_XML );
+        FileReader fr = new FileReader( f );
+        try {
+            BufferedReader br = new BufferedReader( fr );
+            String read = null;
+            while ( (read = br.readLine()) != null) {
+                if (read.indexOf("<property name=\"test.property\" value=\""+TEST_VALUE+"\"></property>") >= 0) {
+                    // found the property we set - it's good.
+                    return;
+                }
+            }
+            fail( "did not encounter set property in generated file." );
+        } finally {
+            try {
+                fr.close();
+            } catch(IOException e) {}
+        }
+    }
+
+
+    public void testEchoToGoodFileFail() throws Exception {
+        executeTarget( "testEchoToGoodFileFail" );
+        assertGoodFile();
+    }
+
+
+    public void testEchoToGoodFileNoFail() throws Exception {
+        executeTarget( "testEchoToGoodFileNoFail" );
+        assertGoodFile();
+    }
+
+
+    public void testEchoPrefix() throws Exception {
+        testEchoPrefixVarious("testEchoPrefix");
+    }
+
+    public void testEchoPrefixAsPropertyset() throws Exception {
+        testEchoPrefixVarious("testEchoPrefixAsPropertyset");
+    }
+
+    public void testEchoPrefixAsNegatedPropertyset() throws Exception {
+        testEchoPrefixVarious("testEchoPrefixAsNegatedPropertyset");
+    }
+
+    public void testEchoPrefixAsDoublyNegatedPropertyset() throws Exception {
+        testEchoPrefixVarious("testEchoPrefixAsDoublyNegatedPropertyset");
+    }
+
+    private void testEchoPrefixVarious(String target) throws Exception {
+        executeTarget(target);
+        Properties props = loadPropFile(PREFIX_OUTFILE);
+        assertEquals("prefix didn't include 'a.set' property",
+            "true", props.getProperty("a.set"));
+        assertNull("prefix failed to filter out property 'b.set'",
+            props.getProperty("b.set"));
+    }
+
+    protected Properties loadPropFile(String relativeFilename)
+            throws IOException {
+        File f = createRelativeFile( relativeFilename );
+        Properties props=new Properties();
+        InputStream in=null;
+        try  {
+            in=new BufferedInputStream(new FileInputStream(f));
+            props.load(in);
+        } finally {
+            if(in!=null) {
+                try { in.close(); } catch(IOException e) {}
+            }
+        }
+        return props;
+    }
+
+    protected void assertGoodFile() throws Exception {
+        File f = createRelativeFile( GOOD_OUTFILE );
+        assertTrue(
+            "Did not create "+f.getAbsolutePath(),
+            f.exists() );
+        Properties props=loadPropFile(GOOD_OUTFILE);
+        props.list(System.out);
+        assertEquals("test property not found ",
+                     TEST_VALUE, props.getProperty("test.property"));
+/*
+        // read in the file
+        FileReader fr = new FileReader( f );
+        try {
+            BufferedReader br = new BufferedReader( fr );
+            String read = null;
+            while ( (read = br.readLine()) != null)
+            {
+                if (read.indexOf("test.property" + TEST_VALUE) >= 0)
+                {
+                    // found the property we set - it's good.
+                    return;
+                }
+            }
+            fail( "did not encounter set property in generated file." );
+        } finally {
+            try { fr.close(); } catch(IOException e) {}
+        }
+*/
+    }
+
+
+    protected String toAbsolute( String filename ) {
+        return createRelativeFile( filename ).getAbsolutePath();
+    }
+
+
+    protected File createRelativeFile( String filename ) {
+        if (filename.equals( "." )) {
+            return getProjectDir();
+        }
+        // else
+        return new File( getProjectDir(), filename );
+    }
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/JspcTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/JspcTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/JspcTest.java	(revision 16139)
@@ -0,0 +1,230 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.*;
+import java.util.Properties;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.taskdefs.optional.jsp.JspMangler;
+import org.apache.tools.ant.taskdefs.optional.jsp.Jasper41Mangler;
+import org.apache.tools.ant.taskdefs.optional.jsp.JspC;
+import org.apache.tools.ant.taskdefs.optional.jsp.JspNameMangler;
+import org.apache.tools.ant.taskdefs.optional.jsp.compilers.JspCompilerAdapterFactory;
+import org.apache.tools.ant.taskdefs.optional.jsp.compilers.JspCompilerAdapter;
+
+/**
+ * Tests the Jspc task.
+ *
+ * @created 07 March 2002
+ * @since Ant 1.5
+ */
+public class JspcTest extends BuildFileTest {
+    /**
+     * Description of the Field
+     */
+    private File baseDir;
+    /**
+     * Description of the Field
+     */
+    private File outDir;
+
+    /**
+     * Description of the Field
+     */
+    private final static String TASKDEFS_DIR = "src/etc/testcases/taskdefs/optional/";
+
+
+    /**
+     * Constructor for the JspcTest object
+     *
+     * @param name Description of Parameter
+     */
+    public JspcTest(String name) {
+        super(name);
+    }
+
+
+    /**
+     * The JUnit setup method
+     */
+    public void setUp() {
+        configureProject(TASKDEFS_DIR + "jspc.xml");
+        baseDir = new File(TASKDEFS_DIR);
+        outDir = new File(baseDir, "jsp/java");
+    }
+
+
+    /**
+     * The teardown method for JUnit
+     */
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testSimple() throws Exception {
+        executeJspCompile("testSimple", "simple_jsp.java");
+    }
+
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testUriroot() throws Exception {
+        executeJspCompile("testUriroot", "uriroot_jsp.java");
+    }
+
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testXml() throws Exception {
+        executeJspCompile("testXml", "xml_jsp.java");
+    }
+
+
+    /**
+     * try a keyword in a file
+     */
+    public void testKeyword() throws Exception {
+        executeJspCompile("testKeyword", "default_jsp.java");
+    }
+
+
+    /**
+     * what happens to 1nvalid-classname
+     */
+    public void testInvalidClassname() throws Exception {
+        executeJspCompile("testInvalidClassname",
+                "_1nvalid_0002dclassname_jsp.java");
+    }
+
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testNoTld() throws Exception {
+//         expectBuildExceptionContaining("testNoTld",
+//                 "Jasper found an error in a file",
+//                 "Java returned: 9");
+         expectBuildExceptionContaining("testNoTld",
+                 "not found",
+                 "Java returned: 9");
+    }
+
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testNotAJspFile()  throws Exception {
+        executeTarget("testNotAJspFile");
+    }
+
+    /**
+     * webapp test is currently broken, because it picks up
+     * on the missing_tld file, and bails.
+     */
+/*
+    public void testWebapp()  throws Exception {
+        executeTarget("testWebapp");
+    }
+*/
+    /**
+     * run a target then verify the named file gets created
+     *
+     * @param target Description of Parameter
+     * @param javafile Description of Parameter
+     * @exception Exception trouble
+     */
+    protected void executeJspCompile(String target, String javafile)
+        throws Exception {
+        executeTarget(target);
+        assertJavaFileCreated(javafile);
+    }
+
+
+    /**
+     * verify that a named file was created
+     *
+     * @param filename Description of Parameter
+     * @exception Exception trouble
+     */
+    protected void assertJavaFileCreated(String filename)
+        throws Exception {
+        File file = getOutputFile(filename);
+        assertTrue("file " + filename + " not found", file.exists());
+        assertTrue("file " + filename + " is empty", file.length() > 0);
+    }
+
+    /**
+     * Gets the OutputFile attribute of the JspcTest object
+     *
+     * @param subpath Description of Parameter
+     * @return The OutputFile value
+     */
+    protected File getOutputFile(String subpath) {
+        return new File(outDir, subpath);
+    }
+
+    /**
+     * verify that we select the appropriate mangler
+     */
+    public void testJasperNameManglerSelection() {
+        JspCompilerAdapter adapter=
+                JspCompilerAdapterFactory.getCompiler("jasper", null,null);
+        JspMangler mangler=adapter.createMangler();
+        assertTrue(mangler instanceof JspNameMangler);
+        adapter= JspCompilerAdapterFactory.getCompiler("jasper41", null, null);
+        mangler = adapter.createMangler();
+        assertTrue(mangler instanceof Jasper41Mangler);
+    }
+
+    public void testJasper41() {
+        JspMangler mangler = new Jasper41Mangler();
+        //java keywords are not special
+        assertMapped(mangler, "for.jsp", "for_jsp");
+        //underscores go in front of invalid start chars
+        assertMapped(mangler, "0.jsp", "_0_jsp");
+        //underscores at the front get an underscore too
+        assertMapped(mangler, "_.jsp", "___jsp");
+        //non java char at start => underscore then the the _hex value
+        assertMapped(mangler, "-.jsp", "__0002d_jsp");
+        //and paths are stripped
+        char s = File.separatorChar;
+        assertMapped(mangler, "" + s + s + "somewhere" + s + "file" + s + "index.jsp", "index_jsp");
+    }
+
+    /**
+     * assert our mapping rules
+     * @param mangler
+     * @param filename
+     * @param classname
+     */
+    protected void assertMapped(JspMangler mangler, String filename, String classname) {
+        String mappedname = mangler.mapJspToJavaName(new File(filename));
+        assertTrue(filename+" should have mapped to "+classname
+                    +" but instead mapped to "+mappedname,
+                    classname.equals(mappedname));
+    }
+
+
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/PropertyFileTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/PropertyFileTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/PropertyFileTest.java	(revision 16139)
@@ -0,0 +1,227 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import org.apache.tools.ant.BuildFileTest;
+
+import java.util.Properties;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.io.File;
+
+/**
+ *  JUnit testcase that excercises the optional PropertyFile task in ant.
+ *  (this is really more of a functional test so far.., but it's enough to let
+ *   me start refactoring...)
+ *
+ *@created    October 2, 2001
+ */
+
+public class PropertyFileTest extends BuildFileTest {
+
+    public PropertyFileTest(String name) {
+        super(name);
+    }
+
+
+    /**
+     *  The JUnit setup method
+     */
+    public void setUp() throws Exception {
+        destroyTempFiles();
+        initTestPropFile();
+        initBuildPropFile();
+        configureProject(projectFilePath);
+        project.setProperty(valueDoesNotGetOverwrittenPropertyFileKey,valueDoesNotGetOverwrittenPropertyFile);
+    }
+
+
+    /**
+     *  The JUnit tearDown method
+     */
+    public void tearDown() {
+        destroyTempFiles();
+    }
+
+    public void testNonExistingFile() {
+        PropertyFile props = new PropertyFile();
+        props.setProject( getProject() );
+        File file = new File("this-file-does-not-exist.properties");
+        props.setFile(file);
+        assertFalse("Properties file exists before test.", file.exists());
+        props.execute();
+        assertTrue("Properties file does not exist after test.", file.exists());
+        file.delete();
+    }
+
+    /**
+     *  A unit test for JUnit- Excercises the propertyfile tasks ability to
+     *  update properties that are already defined-
+     */
+    public void testUpdatesExistingProperties() throws Exception {
+        Properties beforeUpdate = getTestProperties();
+        assertEquals(FNAME, beforeUpdate.getProperty(FNAME_KEY));
+        assertEquals(LNAME, beforeUpdate.getProperty(LNAME_KEY));
+        assertEquals(EMAIL, beforeUpdate.getProperty(EMAIL_KEY));
+        assertEquals(null, beforeUpdate.getProperty(PHONE_KEY));
+        assertEquals(null, beforeUpdate.getProperty(AGE_KEY));
+        assertEquals(null, beforeUpdate.getProperty(DATE_KEY));
+
+        // ask ant to update the properties...
+        executeTarget("update-existing-properties");
+
+        Properties afterUpdate = getTestProperties();
+        assertEquals(NEW_FNAME, afterUpdate.getProperty(FNAME_KEY));
+        assertEquals(NEW_LNAME, afterUpdate.getProperty(LNAME_KEY));
+        assertEquals(NEW_EMAIL, afterUpdate.getProperty(EMAIL_KEY));
+        assertEquals(NEW_PHONE, afterUpdate.getProperty(PHONE_KEY));
+        assertEquals(NEW_AGE, afterUpdate.getProperty(AGE_KEY));
+        assertEquals(NEW_DATE, afterUpdate.getProperty(DATE_KEY));
+    }
+
+    public void testExerciseDefaultAndIncrement() throws Exception {
+        executeTarget("exercise");
+        assertEquals("3",project.getProperty("int.with.default"));
+        assertEquals("1",project.getProperty("int.without.default"));
+        assertEquals("-->",project.getProperty("string.with.default"));
+        assertEquals(".",project.getProperty("string.without.default"));
+        assertEquals("2002/01/21 12:18", project.getProperty("ethans.birth"));
+        assertEquals("2003/01/21", project.getProperty("first.birthday"));
+        assertEquals("0124", project.getProperty("olderThanAWeek"));
+        assertEquals("37", project.getProperty("existing.prop"));
+        assertEquals("6",project.getProperty("int.without.value"));
+    }
+
+    public void testValueDoesNotGetOverwritten() {
+        // this test shows that the bug report 21505 is fixed
+        executeTarget("bugDemo1");
+        executeTarget("bugDemo2");
+        assertEquals("5", project.getProperty("foo"));
+    }
+/*
+    public void testDirect() throws Exception {
+        PropertyFile pf = new PropertyFile();
+        pf.setProject(project);
+        pf.setFile(new File(testPropsFilePath));
+        PropertyFile.Entry entry = pf.createEntry();
+
+        entry.setKey("date");
+        entry.setValue("123");
+        PropertyFile.Entry.Type type = new PropertyFile.Entry.Type();
+        type.setValue("date");
+        entry.setType(type);
+
+        entry.setPattern("yyyy/MM/dd");
+
+        PropertyFile.Entry.Operation operation = new PropertyFile.Entry.Operation();
+        operation.setValue("+");
+        pf.execute();
+
+        Properties props = getTestProperties();
+        assertEquals("yeehaw", props.getProperty("date"));
+    }
+*/
+
+    private Properties getTestProperties() throws Exception {
+        Properties testProps = new Properties();
+        FileInputStream propsFile = new FileInputStream(testPropsFilePath);
+        testProps.load(propsFile);
+        propsFile.close();
+        return testProps;
+    }
+
+
+    private void initTestPropFile() throws Exception {
+        Properties testProps = new Properties();
+        testProps.put(FNAME_KEY, FNAME);
+        testProps.put(LNAME_KEY, LNAME);
+        testProps.put(EMAIL_KEY, EMAIL);
+        testProps.put("existing.prop", "37");
+
+        FileOutputStream fos = new FileOutputStream(testPropsFilePath);
+        testProps.store(fos, "defaults");
+        fos.close();
+    }
+
+
+    private void initBuildPropFile() throws Exception {
+        Properties buildProps = new Properties();
+        buildProps.put(testPropertyFileKey, testPropertyFile);
+        buildProps.put(FNAME_KEY, NEW_FNAME);
+        buildProps.put(LNAME_KEY, NEW_LNAME);
+        buildProps.put(EMAIL_KEY, NEW_EMAIL);
+        buildProps.put(PHONE_KEY, NEW_PHONE);
+        buildProps.put(AGE_KEY, NEW_AGE);
+        buildProps.put(DATE_KEY, NEW_DATE);
+
+        FileOutputStream fos = new FileOutputStream(buildPropsFilePath);
+        buildProps.store(fos, null);
+        fos.close();
+    }
+
+
+    private void destroyTempFiles() {
+        File tempFile = new File(testPropsFilePath);
+        tempFile.delete();
+        tempFile = null;
+
+        tempFile = new File(buildPropsFilePath);
+        tempFile.delete();
+        tempFile = null;
+
+        tempFile = new File(valueDoesNotGetOverwrittenPropsFilePath);
+        tempFile.delete();
+        tempFile = null;
+    }
+
+
+
+    private static final String
+        projectFilePath     = "src/etc/testcases/taskdefs/optional/propertyfile.xml",
+
+        testPropertyFile    = "propertyfile.test.properties",
+        testPropertyFileKey = "test.propertyfile",
+        testPropsFilePath   = "src/etc/testcases/taskdefs/optional/" + testPropertyFile,
+
+        valueDoesNotGetOverwrittenPropertyFile    = "overwrite.test.properties",
+        valueDoesNotGetOverwrittenPropertyFileKey = "overwrite.test.propertyfile",
+        valueDoesNotGetOverwrittenPropsFilePath   = "src/etc/testcases/taskdefs/optional/" + valueDoesNotGetOverwrittenPropertyFile,
+
+        buildPropsFilePath  = "src/etc/testcases/taskdefs/optional/propertyfile.build.properties",
+
+        FNAME     = "Bruce",
+        NEW_FNAME = "Clark",
+        FNAME_KEY = "firstname",
+
+        LNAME     = "Banner",
+        NEW_LNAME = "Kent",
+        LNAME_KEY = "lastname",
+
+        EMAIL     = "incredible@hulk.com",
+        NEW_EMAIL = "kc@superman.com",
+        EMAIL_KEY = "email",
+
+        NEW_PHONE = "(520) 555-1212",
+        PHONE_KEY = "phone",
+
+        NEW_AGE = "30",
+        AGE_KEY = "age",
+
+        NEW_DATE = "2001/01/01 12:45",
+        DATE_KEY = "date";
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/PvcsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/PvcsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/PvcsTest.java	(revision 16139)
@@ -0,0 +1,58 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.*;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ */
+public class PvcsTest extends BuildFileTest {
+
+    public PvcsTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/optional/pvcs.xml");
+    }
+
+    public void test1() {
+        expectBuildException("test1", "Required argument repository not specified");
+    }
+
+    public void test2() {
+        executeTarget("test2");
+    }
+
+    public void test3() {
+        executeTarget("test3");
+    }
+
+    public void test4() {
+        executeTarget("test4");
+    }
+
+    public void test5() {
+        executeTarget("test5");
+    }
+
+    public void test6() {
+        expectBuildException("test6", "Failed executing: /never/heard/of/a/directory/structure/like/this/pcli lvf -z -aw -pr//ct4serv2/pvcs/monitor /. Exception: /never/heard/of/a/directory/structure/like/this/pcli: not found");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/ReplaceRegExpTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/ReplaceRegExpTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/ReplaceRegExpTest.java	(revision 16139)
@@ -0,0 +1,102 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.util.FileUtils;
+
+import java.util.Properties;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * JUnit Testcase for the optional replaceregexp task.
+ *
+ */
+public class ReplaceRegExpTest extends BuildFileTest {
+    private static final String PROJECT_PATH = "src/etc/testcases/taskdefs/optional";
+    public ReplaceRegExpTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject(PROJECT_PATH + "/replaceregexp.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testReplace() throws IOException {
+        Properties original = new Properties();
+        FileInputStream propsFile = null;
+        try {
+            propsFile = new FileInputStream(PROJECT_PATH + "/replaceregexp.properties");
+            original.load(propsFile);
+        } finally {
+            if (propsFile != null) {
+                propsFile.close();
+                propsFile = null;
+            }
+        }
+
+        assertEquals("Def", original.get("OldAbc"));
+
+        executeTarget("testReplace");
+
+        Properties after = new Properties();
+        try {
+            propsFile = new FileInputStream(PROJECT_PATH + "/test.properties");
+            after.load(propsFile);
+        } finally {
+            if (propsFile != null) {
+                propsFile.close();
+                propsFile = null;
+            }
+        }
+
+        assertNull(after.get("OldAbc"));
+        assertEquals("AbcDef", after.get("NewProp"));
+    }
+    // inspired by bug 22541
+    public void testDirectoryDateDoesNotChange() {
+        executeTarget("touchDirectory");
+        File myFile = new File(PROJECT_PATH + "/" + getProject().getProperty("tmpregexp"));
+        long timeStampBefore = myFile.lastModified();
+        executeTarget("testDirectoryDateDoesNotChange");
+        long timeStampAfter = myFile.lastModified();
+        assertEquals("directory date should not change",
+            timeStampBefore, timeStampAfter);
+    }
+    public void testDontAddNewline1() throws IOException {
+        executeTarget("testDontAddNewline1");
+        assertTrue("Files match",
+                   FileUtils.newFileUtils()
+                   .contentEquals(new File(PROJECT_PATH + "/test.properties"),
+                                  new File(PROJECT_PATH + "/replaceregexp2.result.properties")));
+    }
+
+    public void testDontAddNewline2() throws IOException {
+        executeTarget("testDontAddNewline2");
+        assertTrue("Files match",
+                   FileUtils.newFileUtils()
+                   .contentEquals(new File(PROJECT_PATH + "/test.properties"),
+                                  new File(PROJECT_PATH + "/replaceregexp2.result.properties")));
+    }
+
+}// ReplaceRegExpTest
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/RhinoReferenceTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/RhinoReferenceTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/RhinoReferenceTest.java	(revision 16139)
@@ -0,0 +1,43 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * Tests using an undefined reference.
+ *
+ * @since Ant 1.6
+ */
+public class RhinoReferenceTest extends BuildFileTest {
+
+    public RhinoReferenceTest(String name) {
+        super(name);
+    }
+
+    /**
+     * The JUnit setup method
+     */
+    public void setUp() {
+        configureProject(
+            "src/etc/testcases/taskdefs/optional/script_reference.xml");
+    }
+
+    public void testScript() {
+        executeTarget("script");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/RhinoScriptTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/RhinoScriptTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/RhinoScriptTest.java	(revision 16139)
@@ -0,0 +1,68 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * Tests the examples of the &lt;script&gt; task docs.
+ *
+ * @since Ant 1.5.2
+ */
+public class RhinoScriptTest extends BuildFileTest {
+
+    public RhinoScriptTest(String name) {
+        super(name);
+    }
+
+    /**
+     * The JUnit setup method
+     */
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/optional/script.xml");
+    }
+
+    public void testExample1() {
+        executeTarget("example1");
+        int index = getLog().indexOf("1");
+        assertTrue(index > -1);
+        index = getLog().indexOf("4", index);
+        assertTrue(index > -1);
+        index = getLog().indexOf("9", index);
+        assertTrue(index > -1);
+        index = getLog().indexOf("16", index);
+        assertTrue(index > -1);
+        index = getLog().indexOf("25", index);
+        assertTrue(index > -1);
+        index = getLog().indexOf("36", index);
+        assertTrue(index > -1);
+        index = getLog().indexOf("49", index);
+        assertTrue(index > -1);
+        index = getLog().indexOf("64", index);
+        assertTrue(index > -1);
+        index = getLog().indexOf("81", index);
+        assertTrue(index > -1);
+        index = getLog().indexOf("100", index);
+        assertTrue(index > -1);
+    }
+
+    public void testExample2() {
+        executeTarget("example2");
+        assertTrue(getLog().indexOf("In sub1") > -1);
+        assertTrue(getLog().indexOf("In sub2") > -1);
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/TraXLiaisonTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/TraXLiaisonTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/TraXLiaisonTest.java	(revision 16139)
@@ -0,0 +1,98 @@
+package org.apache.tools.ant.taskdefs.optional;
+
+import org.apache.tools.ant.taskdefs.XSLTLiaison;
+import org.apache.tools.ant.taskdefs.XSLTLogger;
+import org.apache.tools.ant.BuildException;
+
+import java.io.File;
+
+import junit.framework.AssertionFailedError;
+
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/**
+ * TraX XSLTLiaison testcase
+ */
+public class TraXLiaisonTest extends AbstractXSLTLiaisonTest
+    implements XSLTLogger {
+
+    public TraXLiaisonTest(String name){
+        super(name);
+    }
+
+    public void tearDown() {
+        File f = new File("xalan2-redirect-out.tmp");
+        if (f.exists()) {
+            f.delete();
+        }
+    }
+
+    public XSLTLiaison createLiaison() throws Exception {
+        TraXLiaison l = new TraXLiaison();
+        l.setLogger(this);
+        return l;
+    }
+
+    public void testXalan2Redirect() throws Exception {
+        File xsl = getFile("/taskdefs/optional/xalan-redirect-in.xsl");
+        liaison.setStylesheet(xsl);
+        File out = new File("xalan2-redirect-out-dummy.tmp");
+        File in = getFile("/taskdefs/optional/xsltliaison-in.xsl");
+        try {
+            liaison.addParam("xalan-version", "2");
+            liaison.transform(in, out);
+        } finally {
+            out.delete();
+        }
+    }
+
+    public void testMultipleTransform() throws Exception {
+        File xsl = getFile("/taskdefs/optional/xsltliaison-in.xsl");
+        liaison.setStylesheet(xsl);
+        liaison.addParam("param", "value");
+        File in = getFile("/taskdefs/optional/xsltliaison-in.xml");
+        // test for 10 consecutives transform
+        for (int i = 0; i < 50; i++){
+            File out = new File("xsltliaison" + i + ".tmp");
+            try {
+                liaison.transform(in, out);
+            } catch (Exception e){
+                throw new BuildException("failed in transform " + i, e);
+            } finally {
+                out.delete();
+            }
+        }
+    }
+
+    public void testSystemId(){
+        File file = null;
+        if ( File.separatorChar == '\\' ){
+            file = new File("d:\\jdk");
+        } else {
+            file = new File("/user/local/bin");
+        }
+        String systemid = ((TraXLiaison)liaison).getSystemId(file);
+        assertTrue("SystemIDs should start by file:///", systemid.startsWith("file:///"));
+        assertTrue("SystemIDs should not start with file:////", !systemid.startsWith("file:////"));
+    }
+
+    public void log(String message) {
+        throw new AssertionFailedError("Liaison sent message: "+message);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/WsdlToDotnetTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/WsdlToDotnetTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/WsdlToDotnetTest.java	(revision 16139)
@@ -0,0 +1,159 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.*;
+import java.util.Properties;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * Tests the WsdlToDotnet task.
+ *
+ * @since Ant 1.5
+ */
+public class WsdlToDotnetTest extends BuildFileTest {
+
+    /**
+     * Description of the Field
+     */
+    private final static String TASKDEFS_DIR = "src/etc/testcases/taskdefs/optional/";
+
+
+    /**
+     * Constructor
+     *
+     * @param name testname
+     */
+    public WsdlToDotnetTest(String name) {
+        super(name);
+    }
+
+
+    /**
+     * The JUnit setup method
+     */
+    public void setUp() {
+        configureProject(TASKDEFS_DIR + "WsdlToDotnet.xml");
+    }
+
+
+    /**
+     * The teardown method for JUnit
+     */
+    public void tearDown() {
+        executeTarget("teardown");
+    }
+
+
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testNoParams() throws Exception {
+        expectBuildExceptionContaining("testNoParams",
+                "expected failure",
+                "destination file must be specified");
+    }
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testNoSrc() throws Exception {
+        expectBuildExceptionContaining("testNoSrc",
+                "expected failure",
+                "you must specify either a source file or a URL");
+    }
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testDestIsDir() throws Exception {
+        expectBuildExceptionContaining("testDestIsDir",
+                "expected failure",
+                "is a directory");
+    }
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testBothSrc() throws Exception {
+        expectBuildExceptionContaining("testBothSrc",
+                "expected failure",
+                "both a source file and a URL");
+    }
+     /**
+     * A unit test for JUnit
+     */
+    public void testSrcIsDir() throws Exception {
+        expectBuildExceptionContaining("testSrcIsDir",
+                "expected failure",
+                "is a directory");
+    }
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testSrcIsMissing() throws Exception {
+        expectBuildExceptionContaining("testSrcIsMissing",
+                "expected failure",
+                "does not exist");
+    }
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testLocalWsdl() throws Exception {
+        executeTarget("testLocalWsdl");
+    }
+    /**
+     * A unit test for JUnit
+     */
+    public void testLocalWsdlServer() throws Exception {
+        executeTarget("testLocalWsdlServer");
+    }
+     /**
+     * A unit test for JUnit
+     */
+    public void testInvalidExtraOps() throws Exception {
+        expectBuildExceptionContaining("testInvalidExtraOps",
+                "expected failure",
+                "WSDL returned: 1");
+    }
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testLocalWsdlVB() throws Exception {
+        executeTarget("testLocalWsdlVB");
+    }
+    /**
+     * A unit test for JUnit
+     */
+    public void testLocalWsdlServerVB() throws Exception {
+        executeTarget("testLocalWsdlServerVB");
+    }
+     /**
+     * A unit test for JUnit
+     */
+    public void testInvalidExtraOpsVB() throws Exception {
+        expectBuildExceptionContaining("testInvalidExtraOpsVB",
+                "expected failure",
+                "WSDL returned: 1");
+    }
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/XalanLiaisonTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/XalanLiaisonTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/XalanLiaisonTest.java	(revision 16139)
@@ -0,0 +1,56 @@
+package org.apache.tools.ant.taskdefs.optional;
+
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+import org.apache.tools.ant.taskdefs.XSLTLiaison;
+
+import java.io.File;
+
+/**
+ * Xalan Liaison testcase
+ */
+public class XalanLiaisonTest extends AbstractXSLTLiaisonTest {
+    public XalanLiaisonTest(String name){
+        super(name);
+    }
+
+    public void tearDown() {
+        File f = new File("xalan1-redirect-out.tmp");
+        if (f.exists()) {
+            f.delete();
+        }
+    }
+
+    protected XSLTLiaison createLiaison() throws Exception {
+        return new XalanLiaison();
+    }
+
+    public void testXalan1Redirect() throws Exception {
+        File xsl = getFile("/taskdefs/optional/xalan-redirect-in.xsl");
+        liaison.setStylesheet(xsl);
+        File out = new File("xalan1-redirect-out-dummy.tmp");
+        File in = getFile("/taskdefs/optional/xsltliaison-in.xsl");
+        try {
+            liaison.addParam("xalan-version", "1");
+            liaison.transform(in, out);
+        } finally {
+            out.delete();
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/XmlValidateCatalogTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/XmlValidateCatalogTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/XmlValidateCatalogTest.java	(revision 16139)
@@ -0,0 +1,86 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.*;
+import java.util.Properties;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * Tests the XMLValidate optional task with nested external catalogs.
+ *
+ * @see XmlValidateTest
+ * @since Ant 1.6
+ */
+public class XmlValidateCatalogTest extends BuildFileTest {
+
+    /**
+     * where tasks run
+     */
+    private final static String TASKDEFS_DIR = "src/etc/testcases/taskdefs/optional/";
+
+
+    /**
+     * Constructor
+     *
+     * @param name testname
+     */
+    public XmlValidateCatalogTest(String name) {
+        super(name);
+    }
+
+
+    /**
+     * The JUnit setup method
+     */
+    public void setUp() {
+        configureProject(TASKDEFS_DIR + "xmlvalidate.xml");
+    }
+
+
+    /**
+     * The teardown method for JUnit
+     */
+    public void tearDown() {
+
+    }
+
+    /**
+     * catalogfiles fileset should be ignored
+     * if resolver.jar is not present, but will
+     * be used if it is.  either way, test should
+     * work b/c we have a nested dtd with the same
+     * entity
+     */
+    public void testXmlCatalogFiles() {
+        executeTarget("xmlcatalogfiles");
+    }
+
+    /**
+     * Test nested catalogpath.
+     * It should be ignored if resolver.jar is not
+     * present, but will be used if it is.  either
+     * way, test should work b/c we have a nested
+     * dtd with the same entity
+     */
+    public void testXmlCatalogPath() {
+        executeTarget("xmlcatalogpath");
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/XmlValidateTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/XmlValidateTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/XmlValidateTest.java	(revision 16139)
@@ -0,0 +1,188 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * Tests the XMLValidate optional task, by running targets in the test script
+ * <code>src/etc/testcases/taskdefs/optional/xmlvalidate.xml</code>
+ * <p>
+ *
+ * @see XmlValidateCatalogTest
+ * @since Ant 1.5
+ */
+public class XmlValidateTest extends BuildFileTest {
+
+    /**
+     * where tasks run
+     */
+    private final static String TASKDEFS_DIR =
+        "src/etc/testcases/taskdefs/optional/";
+
+    /**
+     * Constructor
+     *
+     * @param name testname
+     */
+    public XmlValidateTest(String name) {
+        super(name);
+    }
+
+    /**
+     * The JUnit setup method
+     */
+    public void setUp() {
+        configureProject(TASKDEFS_DIR + "xmlvalidate.xml");
+    }
+
+    /**
+     * The teardown method for JUnit
+     */
+    public void tearDown() {}
+
+    /**
+     * Basic inline 'dtd' element test.
+     */
+    public void testValidate() throws Exception {
+        executeTarget("testValidate");
+    }
+
+    /**
+     * Test indirect validation.
+     */
+    public void testDeepValidate() throws Exception {
+        executeTarget("testDeepValidate");
+    }
+
+    /**
+     *
+     */
+    public void testXmlCatalog() {
+        executeTarget("xmlcatalog");
+    }
+
+    /**
+     *
+     */
+    public void testXmlCatalogViaRefid() {
+        executeTarget("xmlcatalogViaRefid");
+    }
+
+    /**
+     * Test that the nested dtd element is used when resolver.jar is not
+     * present.  This test should pass either way.
+     */
+    public void testXmlCatalogFiles() {
+        executeTarget("xmlcatalogfiles-override");
+    }
+
+    /**
+     * Test nested catalogpath.
+     * Test that the nested dtd element is used when resolver.jar is not
+     * present.  This test should pass either way.
+     */
+    public void testXmlCatalogPath() {
+        executeTarget("xmlcatalogpath-override");
+    }
+
+    /**
+     * Test nested xmlcatalog definitions
+     */
+    public void testXmlCatalogNested() {
+        executeTarget("xmlcatalognested");
+    }
+
+    /**
+     * Test xml schema validation
+     */
+    public void testXmlSchemaGood() throws BuildException {
+        try {
+            executeTarget("testSchemaGood");
+        } catch (BuildException e) {
+            if (e
+                .getMessage()
+                .endsWith(" doesn't recognize feature http://apache.org/xml/features/validation/schema")
+                || e.getMessage().endsWith(
+                    " doesn't support feature http://apache.org/xml/features/validation/schema")) {
+                System.err.println(" skipped, parser doesn't support schema");
+            } else {
+                throw e;
+            }
+        }
+    }
+    /**
+     * Test xml schema validation
+     */
+    public void testXmlSchemaBad() {
+        try {
+            executeTarget("testSchemaBad");
+            fail("Should throw BuildException because 'Bad Schema Validation'");
+
+            expectBuildExceptionContaining(
+                "testSchemaBad",
+                "Bad Schema Validation",
+                "not a valid XML document");
+        } catch (BuildException e) {
+            if (e
+                .getMessage()
+                .endsWith(" doesn't recognize feature http://apache.org/xml/features/validation/schema")
+                || e.getMessage().endsWith(
+                    " doesn't support feature http://apache.org/xml/features/validation/schema")) {
+                System.err.println(" skipped, parser doesn't support schema");
+            } else {
+                assertTrue(
+                    e.getMessage().indexOf("not a valid XML document") > -1);
+            }
+        }
+    }
+
+    /**
+     * iso-2022-jp.xml is valid but wouldn't get recognized on systems
+     * with a different native encoding.
+     *
+     * Bug 11279
+     */
+    public void testIso2022Jp() {
+        executeTarget("testIso2022Jp");
+    }
+
+    /**
+     * utf-8.xml is invalid as it contains non-UTF-8 characters, but
+     * would pass on systems with a native iso-8859-1 (or similar)
+     * encoding.
+     *
+     * Bug 11279
+     */
+    public void testUtf8() {
+        expectBuildException("testUtf8", "invalid characters in file");
+    }
+
+    // Tests property element, using XML schema properties as an example.
+
+    public void testPropertySchemaForValidXML() {
+        executeTarget("testProperty.validXML");
+    }
+
+    public void testPropertySchemaForInvalidXML() {
+        expectBuildException(
+            "testProperty.invalidXML",
+            "XML file does not satisfy schema.");
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/XslpLiaisonTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/XslpLiaisonTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/XslpLiaisonTest.java	(revision 16139)
@@ -0,0 +1,33 @@
+package org.apache.tools.ant.taskdefs.optional;
+
+import org.apache.tools.ant.taskdefs.XSLTLiaison;
+
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/**
+ * XSLP Liaison testcase
+ */
+public class XslpLiaisonTest extends AbstractXSLTLiaisonTest {
+    public XslpLiaisonTest(String name){
+        super(name);
+    }
+
+    protected XSLTLiaison createLiaison() throws Exception {
+        return new XslpLiaison();
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/XsltTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/XsltTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/XsltTest.java	(revision 16139)
@@ -0,0 +1,89 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * Tests the style/xslt task.
+ *
+ * @since Ant 1.5
+ */
+public class XsltTest extends BuildFileTest {
+
+    /**
+     * where tasks run
+     */
+    private final static String TASKDEFS_DIR = "src/etc/testcases/taskdefs/optional/";
+
+
+    /**
+     * Constructor
+     *
+     * @param name testname
+     */
+    public XsltTest(String name) {
+        super(name);
+    }
+
+
+    /**
+     * The JUnit setup method
+     */
+    public void setUp() {
+        configureProject(TASKDEFS_DIR + "xslt.xml");
+    }
+
+
+    /**
+     * The teardown method for JUnit
+     */
+    public void tearDown() {
+        executeTarget("teardown");
+    }
+
+
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testCatchNoDtd() throws Exception {
+        expectBuildExceptionContaining("testCatchNoDtd",
+                                       "expected failure",
+                                       "Fatal error during transformation");
+    }
+
+    /**
+     * A unit test for JUnit
+     */
+    public void testCatalog() throws Exception {
+         executeTarget("testCatalog");
+    }
+
+    public void testOutputProperty() throws Exception {
+      executeTarget("testOutputProperty");
+    }
+
+    public void testFactory() throws Exception {
+        executeTarget("testFactory");
+    }
+
+    public void testAttribute() throws Exception {
+        executeTarget("testAttribute");
+    }
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/depend/DependTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/depend/DependTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/depend/DependTest.java	(revision 16139)
@@ -0,0 +1,163 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.depend;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.DirectoryScanner;
+
+/**
+ * Testcase for the Depend optional task.
+ *
+ */
+public class DependTest extends BuildFileTest {
+    public static final String RESULT_FILESET = "result";
+
+    public static final String TEST_BUILD_FILE
+        = "src/etc/testcases/taskdefs/optional/depend/depend.xml";
+
+    public DependTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject(TEST_BUILD_FILE);
+    }
+
+    public void tearDown() {
+        executeTarget("clean");
+    }
+
+    /**
+     * Test direct dependency removal
+     */
+    public void testDirect() {
+        executeTarget("testdirect");
+        Hashtable files = getResultFiles();
+        assertEquals("Depend did not leave correct number of files", 3,
+            files.size());
+        assertTrue("Result did not contain A.class",
+            files.containsKey("A.class"));
+        assertTrue("Result did not contain D.class",
+            files.containsKey("D.class"));
+    }
+
+    /**
+     * Test dependency traversal (closure)
+     */
+    public void testClosure() {
+        executeTarget("testclosure");
+        Hashtable files = getResultFiles();
+        assertTrue("Depend did not leave correct number of files", 
+            files.size() <= 2);
+        assertTrue("Result did not contain D.class",
+            files.containsKey("D.class"));
+    }
+
+    /**
+     * Test that inner class dependencies trigger deletion of the outer class
+     */
+    public void testInner() {
+        executeTarget("testinner");
+        assertEquals("Depend did not leave correct number of files", 0,
+            getResultFiles().size());
+    }
+
+    /**
+     * Test that multi-leve inner class dependencies trigger deletion of
+     * the outer class
+     */
+    public void testInnerInner() {
+        executeTarget("testinnerinner");
+        assertEquals("Depend did not leave correct number of files", 0,
+            getResultFiles().size());
+    }
+
+    /**
+     * Test that an exception is thrown when there is no source
+     */
+    public void testNoSource() {
+        expectBuildExceptionContaining("testnosource",
+            "No source specified", "srcdir attribute must be set");
+    }
+
+    /**
+     * Test that an exception is thrown when the source attribute is empty
+     */
+    public void testEmptySource() {
+        expectBuildExceptionContaining("testemptysource",
+            "No source specified", "srcdir attribute must be non-empty");
+    }
+
+    /**
+     * Read the result fileset into a Hashtable
+     *
+     * @return a Hashtable containing the names of the files in the result
+     * fileset
+     */
+    private Hashtable getResultFiles() {
+        FileSet resultFileSet = (FileSet) project.getReference(RESULT_FILESET);
+        DirectoryScanner scanner = resultFileSet.getDirectoryScanner(project);
+        String[] scannedFiles = scanner.getIncludedFiles();
+        Hashtable files = new Hashtable();
+        for (int i = 0; i < scannedFiles.length; ++i) {
+            files.put(scannedFiles[i], scannedFiles[i]);
+        }
+        return files;
+    }
+
+
+    /**
+     * Test mutual dependency between inner and outer do not cause both to be
+     * deleted
+     */
+    public void testInnerClosure() {
+        executeTarget("testinnerclosure");
+        assertEquals("Depend did not leave correct number of files", 4,
+            getResultFiles().size());
+    }
+
+    /**
+     * Test the operation of the cache
+     */
+    public void testCache() {
+        executeTarget("testcache");
+    }
+
+    /**
+     * Test the detection and warning of non public classes
+     */
+    public void testNonPublic() {
+        executeTarget("testnonpublic");
+        String log = getLog();
+        assertTrue("Expected warning about APrivate",
+            log.indexOf("The class APrivate in file") != -1);
+        assertTrue("but has not been deleted because its source file "
+            + "could not be determined",
+            log.indexOf("The class APrivate in file") != -1);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/i18n/TranslateTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/i18n/TranslateTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/i18n/TranslateTest.java	(revision 16139)
@@ -0,0 +1,95 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.i18n;
+
+import org.apache.tools.ant.BuildFileTest;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * Tests the Translate task.
+ *
+ * @since     Ant 1.6
+ */
+public class TranslateTest extends BuildFileTest {
+    static private final int BUF_SIZE = 32768;
+
+    private final static String TASKDEFS_DIR = "src/etc/testcases/taskdefs/optional/i18n/translate";
+
+    public TranslateTest(String name) {
+        super(name);
+    }
+
+
+    public void setUp() {
+        configureProject(TASKDEFS_DIR + "/translate.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void test1() {
+        executeTarget("test1");
+        assertTrue("translation of "+ TASKDEFS_DIR + "/input/template.txt",compareFiles(TASKDEFS_DIR+"/expected/de/template.txt",TASKDEFS_DIR+"/output/de/template.txt"));
+    }
+    private boolean compareFiles(String name1, String name2) {
+        File file1 = new File(name1);
+        File file2 = new File(name2);
+
+        try {
+            if (!file1.exists() || !file2.exists()) {
+                System.out.println("One or both files do not exist:" + name1 + ", " + name2);
+                return false;
+            }
+
+            if (file1.length() != file2.length()) {
+                System.out.println("File size mismatch:" + name1 + "(" + file1.length() + "), " +
+                                   name2  + "(" + file2.length() + ")");
+                return false;
+            }
+
+            // byte - byte compare
+            byte[] buffer1 = new byte[BUF_SIZE];
+            byte[] buffer2 = new byte[BUF_SIZE];
+
+            FileInputStream fis1 = new FileInputStream(file1);
+            FileInputStream fis2 = new FileInputStream(file2);
+            int index = 0;
+            int read = 0;
+            while ((read = fis1.read(buffer1)) != -1) {
+                fis2.read(buffer2);
+                for (int i = 0; i < read; ++i, ++index) {
+                    if (buffer1[i] != buffer2[i]) {
+                        System.out.println("Bytes mismatch:" + name1 + ", " + name2 +
+                                           " at byte " + index);
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+        catch (IOException e) {
+            System.out.println("IOException comparing files: " + name1 + ", " + name2);
+            return false;
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/image/ImageTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/image/ImageTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/image/ImageTest.java	(revision 16139)
@@ -0,0 +1,115 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.image;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.taskdefs.condition.Os;
+
+import java.io.IOException;
+import java.io.File;
+import java.io.InputStream;
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.io.FileReader;
+import java.io.BufferedReader;
+import java.util.Properties;
+
+/**
+ * Tests the Image task.
+ *
+ * @since     Ant 1.5
+ */
+public class ImageTest extends BuildFileTest {
+
+    private final static String TASKDEFS_DIR = "src/etc/testcases/taskdefs/optional/image/";
+    private final static String LARGEIMAGE = "largeimage.jpg";
+
+    public ImageTest(String name) {
+        super(name);
+    }
+
+
+    public void setUp() {
+        configureProject(TASKDEFS_DIR + "image.xml");
+    }
+
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void testEchoToLog() {
+        expectLogContaining("testEchoToLog", "Processing File");
+    }
+
+    public void testSimpleScale(){
+      expectLogContaining("testSimpleScale", "Processing File");
+      File f = createRelativeFile( "/dest/" + LARGEIMAGE  );
+          assertTrue(
+              "Did not create "+f.getAbsolutePath(),
+        f.exists() );
+
+    }
+
+    public void testOverwriteTrue() {
+      expectLogContaining("testSimpleScale", "Processing File");
+      File f = createRelativeFile( "/dest/" + LARGEIMAGE  );
+      long lastModified = f.lastModified();
+       if (Os.isFamily("dos")) {
+           try {
+            Thread.sleep(2000);
+           }
+           catch (InterruptedException e) {}
+       }
+      expectLogContaining("testOverwriteTrue", "Processing File");
+      f = createRelativeFile( "/dest/" + LARGEIMAGE  );
+      long overwrittenLastModified = f.lastModified();
+      assertTrue("File was not overwritten.",lastModified < overwrittenLastModified);
+    }
+
+    public void testOverwriteFalse() {
+      expectLogContaining("testSimpleScale", "Processing File");
+      File f = createRelativeFile( "/dest/" + LARGEIMAGE  );
+      long lastModified = f.lastModified();
+      expectLogContaining("testOverwriteFalse", "Processing File");
+      f = createRelativeFile( "/dest/" + LARGEIMAGE  );
+      long overwrittenLastModified = f.lastModified();
+      assertTrue("File was overwritten.",lastModified == overwrittenLastModified);
+    }
+
+
+    public void off_testFailOnError() {
+      try {
+        expectLogContaining("testFailOnError", "Unable to process image stream");
+      }
+      catch (RuntimeException re){
+        assertTrue("Run time exception should say 'Unable to process image stream'. :" + re.toString(),re.toString().indexOf("Unable to process image stream") > -1);
+      }
+    }
+
+
+
+  protected File createRelativeFile( String filename ) {
+        if (filename.equals( "." )) {
+            return getProjectDir();
+        }
+        // else
+        return new File( getProjectDir(), filename );
+    }
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/jdepend/JDependTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/jdepend/JDependTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/jdepend/JDependTest.java	(revision 16139)
@@ -0,0 +1,117 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.jdepend;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.DirectoryScanner;
+
+/**
+ * Testcase for the JDepend optional task.
+ *
+ */
+public class JDependTest extends BuildFileTest {
+    public static final String RESULT_FILESET = "result";
+
+    public JDependTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject(
+            "src/etc/testcases/taskdefs/optional/jdepend/jdepend.xml");
+    }
+
+    /**
+     * Test simple
+     */
+    public void testSimple() {
+        expectOutputContaining(
+            "simple", "Package: org.apache.tools.ant.util.facade");
+    }
+
+    /**
+     * Test xml
+     */
+    public void testXml() {
+        expectOutputContaining(
+            "xml", "<DependsUpon>");
+    }
+
+    /**
+     * Test fork
+     * - forked output goes to log
+     */
+    public void testFork() {
+        expectLogContaining(
+            "fork", "Package: org.apache.tools.ant.util.facade");
+    }
+
+    /**
+     * Test fork xml
+     */
+    public void testForkXml() {
+        expectLogContaining(
+            "fork-xml", "<DependsUpon>");
+    }
+
+    /**
+     * Test timeout
+     */
+    public void testTimeout() {
+        expectLogContaining(
+            "fork-timeout", "JDepend FAILED - Timed out");
+    }
+
+
+    /**
+     * Test timeout without timing out
+     */
+    public void testTimeoutNot() {
+        expectLogContaining(
+            "fork-timeout-not", "Package: org.apache.tools.ant.util.facade");
+    }
+
+    /**
+     * Assert that the given substring is in the output messages
+     */
+
+    protected void assertOutputContaining(String substring) {
+        String realOutput = getOutput();
+        assertTrue("expecting output to contain \"" + substring + "\" output was \""
+                   + realOutput + "\"",
+                   realOutput.indexOf(substring) >= 0);
+    }
+
+    /**
+     * Assert that the given message has been outputted
+     */
+    protected void expectOutputContaining(String target, String substring) {
+        executeTarget(target);
+        assertOutputContaining(substring);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/junit/JUnitClassLoaderTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/junit/JUnitClassLoaderTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/junit/JUnitClassLoaderTest.java	(revision 16139)
@@ -0,0 +1,37 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import junit.framework.TestCase;
+
+/**
+ * Test to ensure that the classloader loading JUnit testcase
+ * is also the context classloader.
+ *
+ */
+public class JUnitClassLoaderTest extends TestCase {
+
+    public JUnitClassLoaderTest(String s) {
+        super(s);
+    }
+
+    public void testContextClassLoader(){
+        ClassLoader context = Thread.currentThread().getContextClassLoader();
+        ClassLoader caller = getClass().getClassLoader();
+        assertSame(context, caller);
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/junit/JUnitReportTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/junit/JUnitReportTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/junit/JUnitReportTest.java	(revision 16139)
@@ -0,0 +1,56 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.File;
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * Small testcase for the junitreporttask.
+ * First test added to reproduce an fault, still a lot to improve
+ *
+ */
+public class JUnitReportTest extends BuildFileTest {
+
+    public JUnitReportTest(String name){
+        super(name);
+    }
+
+    protected void setUp() {
+        configureProject("src/etc/testcases/taskdefs/optional/junitreport.xml");
+    }
+
+    protected void tearDown() {
+        executeTarget("clean");
+    }
+
+    /**
+     * Verifies that no empty junit-noframes.html is generated when frames
+     * output is selected via the default.
+     * Needs reports1 task from junitreport.xml.
+     */
+    public void testNoFileJUnitNoFrames() {
+        executeTarget("reports1");
+        if (new File("src/etc/testcases/taskdefs/optional/junitreport/test/html/junit-noframes.html").exists())
+        {
+            fail("No file junit-noframes.html expected");
+        }
+    }
+
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunnerTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunnerTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/junit/JUnitTestRunnerTest.java	(revision 16139)
@@ -0,0 +1,152 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import java.io.*;
+import junit.framework.*;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Small testcase for the runner, tests are very very very basics.
+ * They must be enhanced with time.
+ *
+ */
+public class JUnitTestRunnerTest extends TestCase {
+
+    // mandatory constructor
+    public JUnitTestRunnerTest(String name){
+        super(name);
+    }
+
+    // check that having no suite generates no errors
+    public void testNoSuite(){
+        TestRunner runner = createRunner(NoSuiteTestCase.class);
+        runner.run();
+        assertEquals(runner.getFormatter().getError(), JUnitTestRunner.SUCCESS, runner.getRetCode());
+    }
+
+    // check that a suite generates no errors
+    public void testSuite(){
+        TestRunner runner = createRunner(SuiteTestCase.class);
+        runner.run();
+        assertEquals(runner.getFormatter().getError(), JUnitTestRunner.SUCCESS, runner.getRetCode());
+    }
+
+    // check that an invalid suite generates an error.
+    public void testInvalidSuite(){
+        TestRunner runner = createRunner(InvalidSuiteTestCase.class);
+        runner.run();
+        String error = runner.getFormatter().getError();
+        assertEquals(error, JUnitTestRunner.ERRORS, runner.getRetCode());
+        assertTrue(error, error.indexOf("thrown on purpose") != -1);
+    }
+
+    // check that something which is not a testcase generates no errors
+    // at first even though this is incorrect.
+    public void testNoTestCase(){
+        TestRunner runner = createRunner(NoTestCase.class);
+        runner.run();
+        assertEquals(runner.getFormatter().getError(), JUnitTestRunner.FAILURES, runner.getRetCode());
+    }
+
+    // check that an exception in the constructor is noticed
+    public void testInvalidTestCase(){
+        TestRunner runner = createRunner(InvalidTestCase.class);
+        runner.run();
+        String error = runner.getFormatter().getError();
+        assertEquals(error, JUnitTestRunner.FAILURES, runner.getRetCode());
+        //@fixme as of now does not report the original stacktrace.
+        //assertTrue(error, error.indexOf("thrown on purpose") != -1);
+    }
+
+    protected TestRunner createRunner(Class clazz){
+        return new TestRunner(new JUnitTest(clazz.getName()), true, true, true);
+    }
+
+    // the test runner that wrap the dummy formatter that interests us
+    private final static class TestRunner extends JUnitTestRunner {
+        private ResultFormatter formatter = new ResultFormatter();
+        TestRunner(JUnitTest test, boolean haltonerror, boolean filtertrace, boolean haltonfailure){
+            super(test, haltonerror, filtertrace,  haltonfailure, TestRunner.class.getClassLoader());
+            // use the classloader that loaded this class otherwise
+            // it will not be able to run inner classes if this test
+            // is ran in non-forked mode.
+            addFormatter(formatter);
+        }
+        ResultFormatter getFormatter(){
+            return formatter;
+        }
+    }
+
+    // dummy formatter just to catch the error
+    private final static class ResultFormatter implements JUnitResultFormatter {
+        private Throwable error;
+        public void setSystemOutput(String output){}
+        public void setSystemError(String output){}
+        public void startTestSuite(JUnitTest suite) throws BuildException{}
+        public void endTestSuite(JUnitTest suite) throws BuildException{}
+        public void setOutput(java.io.OutputStream out){}
+        public void startTest(Test t) {}
+        public void endTest(Test test) {}
+        public void addFailure(Test test, Throwable t) { }
+        public void addFailure(Test test, AssertionFailedError t) { }
+        public void addError(Test test, Throwable t) {
+            error = t;
+        }
+        String getError(){
+            if (error == null){
+                return "";
+            }
+            StringWriter sw = new StringWriter();
+            error.printStackTrace(new PrintWriter(sw));
+            return sw.toString();
+        }
+    }
+
+    public static class NoTestCase {
+    }
+
+    public static class InvalidTestCase extends TestCase {
+        public InvalidTestCase(String name){
+            super(name);
+            throw new NullPointerException("thrown on purpose");
+        }
+    }
+
+    public static class NoSuiteTestCase extends TestCase {
+        public NoSuiteTestCase(String name){ super(name); }
+        public void testA(){}
+    }
+
+    public static class SuiteTestCase extends NoSuiteTestCase {
+        public SuiteTestCase(String name){ super(name); }
+        public static Test suite(){
+            return new TestSuite(SuiteTestCase.class);
+        }
+    }
+
+    public static class InvalidSuiteTestCase extends NoSuiteTestCase {
+        public InvalidSuiteTestCase(String name){ super(name); }
+        public static Test suite(){
+            throw new NullPointerException("thrown on purpose");
+        }
+    }
+    public static void main(String[] args){
+        junit.textui.TestRunner.run(JUnitTestRunnerTest.class);
+    }
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelperTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelperTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/junit/JUnitVersionHelperTest.java	(revision 16139)
@@ -0,0 +1,80 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.junit;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestResult;
+
+/**
+ */
+public class JUnitVersionHelperTest extends TestCase {
+
+    public JUnitVersionHelperTest(String name) {
+        super(name);
+    }
+
+    public void testMyOwnName() {
+        assertEquals("testMyOwnName",
+                     JUnitVersionHelper.getTestCaseName(this));
+    }
+
+    public void testNonTestCaseName() {
+        assertEquals("I'm a foo",
+                     JUnitVersionHelper.getTestCaseName(new Foo1()));
+    }
+
+    public void testNoStringReturn() {
+        assertEquals("unknown",
+                     JUnitVersionHelper.getTestCaseName(new Foo2()));
+    }
+
+    public void testNoGetName() {
+        assertEquals("unknown",
+                     JUnitVersionHelper.getTestCaseName(new Foo3()));
+    }
+
+    public void testNameNotGetName() {
+        assertEquals("I'm a foo, too",
+                     JUnitVersionHelper.getTestCaseName(new Foo4()));
+    }
+
+    public void testNull() {
+        assertEquals("unknown", JUnitVersionHelper.getTestCaseName(null));
+    }
+
+    public static class Foo implements Test {
+        public int countTestCases() {return 0;}
+        public void run(TestResult result) {}
+    }
+
+    public static class Foo1 extends Foo {
+        public String getName() {return "I'm a foo";}
+    }
+
+    public static class Foo2 extends Foo {
+        public int getName() {return 1;}
+    }
+
+    public static class Foo3 extends Foo {
+    }
+
+    public static class Foo4 extends Foo {
+        public String name() {return "I'm a foo, too";}
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/metamata/MAuditParserTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/metamata/MAuditParserTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/metamata/MAuditParserTest.java	(revision 16139)
@@ -0,0 +1,69 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.metamata;
+
+import java.io.File;
+
+import junit.framework.TestCase;
+
+import org.apache.tools.ant.util.StringUtils;
+
+/**
+ * Test for the Audit parser.
+ *
+ */
+public class MAuditParserTest extends TestCase {
+
+    private MAuditParser parser;
+
+    public MAuditParserTest(String s) {
+        super(s);
+    }
+
+    protected void setUp() {
+        parser = new MAuditParser();
+    }
+
+    public void testViolation() {
+        String line = "file:\\WebGain\\QA\\examples\\auditexamples\\Vector.java:55: Array declarators (\"[]\") should be placed with their component types and not after field/method declarations (5.27).";
+        // the replace is done to simulate a platform dependant separator since
+        // the parser may do some magic with the file separator
+        line = StringUtils.replace(line, "\\", File.separator);
+        MAuditParser.Violation violation = parser.parseLine(line);
+        assertEquals("\\WebGain\\QA\\examples\\auditexamples\\Vector.java",
+                StringUtils.replace(violation.file, File.separator, "\\"));
+        assertEquals("55", violation.line);
+        assertEquals("Array declarators (\"[]\") should be placed with their component types and not after field/method declarations (5.27).", violation.error);
+    }
+
+    public void testNonViolation(){
+        String line = "Audit completed with 36 violations.";
+        Object violation = parser.parseLine(line);
+        assertNull(violation);
+    }
+
+    public void testFilePathInViolation(){
+        String line = "file:\\WebGain\\QA\\examples\\auditexamples\\Hashtable.java:302: Loop variable defined at file:\\WebGain\\QA\\examples\\auditexamples\\Hashtable.java:300 is being modified (5.16).";
+        line = StringUtils.replace(line, "\\", File.separator);
+        MAuditParser.Violation violation = parser.parseLine(line);
+        assertEquals("\\WebGain\\QA\\examples\\auditexamples\\Hashtable.java",
+                StringUtils.replace(violation.file, File.separator, "\\"));
+        assertEquals("302", violation.line);
+        assertEquals("Loop variable defined at Hashtable.java:300 is being modified (5.16).", violation.error);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/net/FTPTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/net/FTPTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/net/FTPTest.java	(revision 16139)
@@ -0,0 +1,583 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.net;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.taskdefs.optional.net.FTP;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.taskdefs.condition.Os;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.apache.commons.net.ftp.FTPClient;
+
+public class FTPTest extends BuildFileTest{
+    // keep track of what operating systems are supported here.
+    private boolean supportsSymlinks = Os.isFamily("unix")
+        && !JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1);
+
+    private FTPClient ftp;
+    private boolean connectionSucceeded = true;
+    private boolean loginSuceeded = true;
+    private String tmpDir = null;
+    private String remoteTmpDir = null;
+    private String ftpFileSep = null;
+    private myFTP myFTPTask = new myFTP();
+
+    public FTPTest(String name) {
+        super(name);
+    }
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/optional/net/ftp.xml");
+        getProject().executeTarget("setup");
+        tmpDir = getProject().getProperty("tmp.dir");
+        ftp = new FTPClient();
+        ftpFileSep = getProject().getProperty("ftp.filesep");
+        myFTPTask.setSeparator(ftpFileSep);
+        myFTPTask.setProject(getProject());
+        remoteTmpDir = myFTPTask.resolveFile(tmpDir);
+        String remoteHost = getProject().getProperty("ftp.host");
+        int port = Integer.parseInt(getProject().getProperty("ftp.port"));
+        String remoteUser = getProject().getProperty("ftp.user");
+        String password = getProject().getProperty("ftp.password");
+        try {
+            ftp.connect(remoteHost, port);
+        } catch (Exception ex) {
+            connectionSucceeded = false;
+            loginSuceeded = false;
+            System.out.println("could not connect to host " + remoteHost + " on port " + port);
+        }
+        if (connectionSucceeded) {
+            try {
+                ftp.login(remoteUser, password);
+            } catch (IOException ioe) {
+                loginSuceeded = false;
+                System.out.println("could not log on to " + remoteHost + " as user " + remoteUser);
+            }
+        }
+    }
+
+    public void tearDown() {
+        try {
+            ftp.disconnect();
+        } catch (IOException ioe) {
+            // do nothing
+        }
+        getProject().executeTarget("cleanup");
+    }
+    private boolean changeRemoteDir(String remoteDir) {
+        boolean result = true;
+        try {
+            ftp.cwd(remoteDir);
+        }
+        catch (Exception ex) {
+            System.out.println("could not change directory to " + remoteTmpDir);
+            result = false;
+        }
+        return result;
+    }
+    public void test1() {
+        if (loginSuceeded) {
+            if (changeRemoteDir(remoteTmpDir))  {
+                FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+                ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+                ds.setIncludes(new String[] {"alpha"});
+                ds.scan();
+                compareFiles(ds, new String[] {} ,new String[] {"alpha"});
+            }
+        }
+    }
+
+    public void test2() {
+        if (loginSuceeded) {
+            if (changeRemoteDir(remoteTmpDir)) {
+                FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+                ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+                ds.setIncludes(new String[] {"alpha/"});
+                ds.scan();
+                compareFiles(ds, new String[] {"alpha/beta/beta.xml",
+                                               "alpha/beta/gamma/gamma.xml"},
+                    new String[] {"alpha", "alpha/beta", "alpha/beta/gamma"});
+            }
+        }
+    }
+
+    public void test3() {
+        if (loginSuceeded) {
+            if (changeRemoteDir(remoteTmpDir)) {
+                FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+                ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+                ds.scan();
+                compareFiles(ds, new String[] {"alpha/beta/beta.xml",
+                                               "alpha/beta/gamma/gamma.xml"},
+                    new String[] {"alpha", "alpha/beta",
+                                  "alpha/beta/gamma"});
+            }
+        }
+    }
+
+    public void testFullPathMatchesCaseSensitive() {
+        if (loginSuceeded) {
+            if (changeRemoteDir(remoteTmpDir)) {
+                FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+                ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+                ds.setIncludes(new String[] {"alpha/beta/gamma/GAMMA.XML"});
+                ds.scan();
+                compareFiles(ds, new String[] {}, new String[] {});
+            }
+        }
+    }
+
+    public void testFullPathMatchesCaseInsensitive() {
+        if (loginSuceeded) {
+            if (changeRemoteDir(remoteTmpDir)) {
+                FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+                ds.setCaseSensitive(false);
+                ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+                ds.setIncludes(new String[] {"alpha/beta/gamma/GAMMA.XML"});
+                ds.scan();
+                compareFiles(ds, new String[] {"alpha/beta/gamma/gamma.xml"},
+                    new String[] {});
+            }
+        }
+    }
+
+    public void test2ButCaseInsensitive() {
+        if (loginSuceeded) {
+            if (changeRemoteDir(remoteTmpDir)) {
+                FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+                ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+                ds.setIncludes(new String[] {"ALPHA/"});
+                ds.setCaseSensitive(false);
+                ds.scan();
+                compareFiles(ds, new String[] {"alpha/beta/beta.xml",
+                                               "alpha/beta/gamma/gamma.xml"},
+                    new String[] {"alpha", "alpha/beta", "alpha/beta/gamma"});
+            }
+        }
+    }
+    public void test2bisButCaseInsensitive() {
+        if (loginSuceeded) {
+            if (changeRemoteDir(remoteTmpDir)) {
+                FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+                ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+                ds.setIncludes(new String[] {"alpha/BETA/gamma/"});
+                ds.setCaseSensitive(false);
+                ds.scan();
+                compareFiles(ds, new String[] {"alpha/beta/gamma/gamma.xml"},
+                    new String[] {"alpha/beta/gamma"});
+            }
+        }
+    }
+    public void testGetWithSelector() {
+        expectLogContaining("ftp-get-with-selector",
+            "selectors are not supported in remote filesets");
+        FileSet fsDestination = (FileSet) getProject().getReference("fileset-destination-without-selector");
+        DirectoryScanner dsDestination = fsDestination.getDirectoryScanner(getProject());
+        dsDestination.scan();
+        String [] sortedDestinationDirectories = dsDestination.getIncludedDirectories();
+        String [] sortedDestinationFiles = dsDestination.getIncludedFiles();
+        for (int counter = 0; counter < sortedDestinationDirectories.length; counter++) {
+            sortedDestinationDirectories[counter] =
+                sortedDestinationDirectories[counter].replace(File.separatorChar, '/');
+        }
+        for (int counter = 0; counter < sortedDestinationFiles.length; counter++) {
+            sortedDestinationFiles[counter] =
+                sortedDestinationFiles[counter].replace(File.separatorChar, '/');
+        }
+        FileSet fsSource =  (FileSet) getProject().getReference("fileset-source-without-selector");
+        DirectoryScanner dsSource = fsSource.getDirectoryScanner(getProject());
+        dsSource.scan();
+        compareFiles(dsSource, sortedDestinationFiles, sortedDestinationDirectories);
+    }
+    public void testGetFollowSymlinksTrue() {
+        if (!supportsSymlinks) {
+            return;
+        }
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        getProject().executeTarget("ftp-get-directory-symbolic-link");
+        FileSet fsDestination = (FileSet) getProject().getReference("fileset-destination-without-selector");
+        DirectoryScanner dsDestination = fsDestination.getDirectoryScanner(getProject());
+        dsDestination.scan();
+        compareFiles(dsDestination, new String[] {"alpha/beta/gamma/gamma.xml"},
+            new String[] {"alpha", "alpha/beta", "alpha/beta/gamma"});
+    }
+    public void testGetFollowSymlinksFalse() {
+        if (!supportsSymlinks) {
+            return;
+        }
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        getProject().executeTarget("ftp-get-directory-no-symbolic-link");
+        FileSet fsDestination = (FileSet) getProject().getReference("fileset-destination-without-selector");
+        DirectoryScanner dsDestination = fsDestination.getDirectoryScanner(getProject());
+        dsDestination.scan();
+        compareFiles(dsDestination, new String[] {},
+            new String[] {});
+    }
+    public void testAllowSymlinks() {
+        if (!supportsSymlinks) {
+            return;
+        }
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        getProject().executeTarget("symlink-setup");
+        FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/beta/gamma/"});
+        ds.setFollowSymlinks(true);
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/gamma/gamma.xml"},
+                     new String[] {"alpha/beta/gamma"});
+    }
+
+    public void testProhibitSymlinks() {
+        if (!supportsSymlinks) {
+            return;
+        }
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        getProject().executeTarget("symlink-setup");
+        FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/beta/gamma/"});
+        ds.setFollowSymlinks(false);
+        ds.scan();
+        compareFiles(ds, new String[] {}, new String[] {});
+    }
+    public void testFileSymlink() {
+        if (!supportsSymlinks) {
+            return;
+        }
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        getProject().executeTarget("symlink-file-setup");
+        FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/beta/gamma/"});
+        ds.setFollowSymlinks(true);
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/gamma/gamma.xml"},
+                     new String[] {"alpha/beta/gamma"});
+    }
+    // father and child pattern test
+    public void testOrderOfIncludePatternsIrrelevant() {
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        String [] expectedFiles = {"alpha/beta/beta.xml",
+                                   "alpha/beta/gamma/gamma.xml"};
+        String [] expectedDirectories = {"alpha/beta", "alpha/beta/gamma" };
+        FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/be?a/**", "alpha/beta/gamma/"});
+        ds.scan();
+        compareFiles(ds, expectedFiles, expectedDirectories);
+        // redo the test, but the 2 include patterns are inverted
+        ds = myFTPTask.newScanner(ftp);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/beta/gamma/", "alpha/be?a/**"});
+        ds.scan();
+        compareFiles(ds, expectedFiles, expectedDirectories);
+    }
+
+    public void testPatternsDifferInCaseScanningSensitive() {
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/", "ALPHA/"});
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/beta.xml",
+                                       "alpha/beta/gamma/gamma.xml"},
+                     new String[] {"alpha", "alpha/beta", "alpha/beta/gamma"});
+    }
+
+    public void testPatternsDifferInCaseScanningInsensitive() {
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/", "ALPHA/"});
+        ds.setCaseSensitive(false);
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/beta.xml",
+                                       "alpha/beta/gamma/gamma.xml"},
+                     new String[] {"alpha", "alpha/beta", "alpha/beta/gamma"});
+    }
+
+    public void testFullpathDiffersInCaseScanningSensitive() {
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {
+            "alpha/beta/gamma/gamma.xml",
+            "alpha/beta/gamma/GAMMA.XML"
+        });
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/gamma/gamma.xml"},
+                     new String[] {});
+    }
+
+    public void testFullpathDiffersInCaseScanningInsensitive() {
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {
+            "alpha/beta/gamma/gamma.xml",
+            "alpha/beta/gamma/GAMMA.XML"
+        });
+        ds.setCaseSensitive(false);
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/gamma/gamma.xml"},
+                     new String[] {});
+    }
+
+    public void testParentDiffersInCaseScanningSensitive() {
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/", "ALPHA/beta/"});
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/beta.xml",
+                                       "alpha/beta/gamma/gamma.xml"},
+                     new String[] {"alpha", "alpha/beta", "alpha/beta/gamma"});
+    }
+
+    public void testParentDiffersInCaseScanningInsensitive() {
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {"alpha/", "ALPHA/beta/"});
+        ds.setCaseSensitive(false);
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/beta.xml",
+                                       "alpha/beta/gamma/gamma.xml"},
+                     new String[] {"alpha", "alpha/beta", "alpha/beta/gamma"});
+    }
+
+    public void testExcludeOneFile() {
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {
+            "**/*.xml"
+        });
+        ds.setExcludes(new String[] {
+            "alpha/beta/b*xml"
+        });
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/gamma/gamma.xml"},
+                     new String[] {});
+    }
+    public void testExcludeHasPrecedence() {
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {
+            "alpha/**"
+        });
+        ds.setExcludes(new String[] {
+            "alpha/**"
+        });
+        ds.scan();
+        compareFiles(ds, new String[] {},
+                     new String[] {});
+
+    }
+    public void testAlternateIncludeExclude() {
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setIncludes(new String[] {
+            "alpha/**",
+            "alpha/beta/gamma/**"
+        });
+        ds.setExcludes(new String[] {
+            "alpha/beta/**"
+        });
+        ds.scan();
+        compareFiles(ds, new String[] {},
+                     new String[] {"alpha"});
+
+    }
+    public void testAlternateExcludeInclude() {
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setExcludes(new String[] {
+            "alpha/**",
+            "alpha/beta/gamma/**"
+        });
+        ds.setIncludes(new String[] {
+            "alpha/beta/**"
+        });
+        ds.scan();
+        compareFiles(ds, new String[] {},
+                     new String[] {});
+
+    }
+    /**
+     * Test inspired by Bug#1415.
+     */
+    public void testChildrenOfExcludedDirectory() {
+        if (!loginSuceeded) {
+            return;
+        }
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        getProject().executeTarget("children-of-excluded-dir-setup");
+        FTP.FTPDirectoryScanner ds = myFTPTask.newScanner(ftp);
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setExcludes(new String[] {"alpha/**"});
+        ds.scan();
+        compareFiles(ds, new String[] {"delta/delta.xml"},
+                    new String[] {"delta"});
+
+        ds = myFTPTask.newScanner(ftp);
+        if (!changeRemoteDir(remoteTmpDir)) {
+            return;
+        }
+        ds.setBasedir(new File(getProject().getBaseDir(), "tmp"));
+        ds.setExcludes(new String[] {"alpha"});
+        ds.scan();
+        compareFiles(ds, new String[] {"alpha/beta/beta.xml",
+                                       "alpha/beta/gamma/gamma.xml",
+                                        "delta/delta.xml"},
+                     new String[] {"alpha/beta", "alpha/beta/gamma", "delta"});
+
+    }
+    /**
+     *  this test is inspired by a user reporting that deletions of directories with the ftp task do not work
+     */
+    public void testFTPDelete() {
+        getProject().executeTarget("ftp-delete");
+    }
+    private void compareFiles(DirectoryScanner ds, String[] expectedFiles,
+                              String[] expectedDirectories) {
+        String includedFiles[] = ds.getIncludedFiles();
+        String includedDirectories[] = ds.getIncludedDirectories();
+        assertEquals("file present: ", expectedFiles.length,
+                     includedFiles.length);
+        assertEquals("directories present: ", expectedDirectories.length,
+                     includedDirectories.length);
+
+        for (int counter=0; counter < includedFiles.length; counter++) {
+            includedFiles[counter] = includedFiles[counter].replace(File.separatorChar, '/');
+        }
+        Arrays.sort(includedFiles);
+        for (int counter=0; counter < includedDirectories.length; counter++) {
+            includedDirectories[counter] = includedDirectories[counter]
+                            .replace(File.separatorChar, '/');
+        }
+        Arrays.sort(includedDirectories);
+        for (int counter=0; counter < includedFiles.length; counter++) {
+            assertEquals(expectedFiles[counter], includedFiles[counter]);
+        }
+        for (int counter=0; counter < includedDirectories.length; counter++) {
+            assertEquals(expectedDirectories[counter], includedDirectories[counter]);
+            counter++;
+        }
+    }
+    private static class myFTP extends FTP {
+        public FTP.FTPDirectoryScanner newScanner(FTPClient client) {
+            return new FTP.FTPDirectoryScanner(client);
+        }
+        // provide public visibility
+        public String resolveFile(String file) {
+            return super.resolveFile(file);
+        }
+
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/perforce/P4ChangeTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/perforce/P4ChangeTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/perforce/P4ChangeTest.java	(revision 16139)
@@ -0,0 +1,51 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.perforce;
+
+import junit.framework.TestCase;
+import org.apache.oro.text.perl.Perl5Util;
+
+/**
+ * Basic testcase to ensure that backslashing is OK.
+ */
+public class P4ChangeTest extends TestCase {
+
+    protected P4Change p4change;
+
+    public P4ChangeTest(String s) {
+        super(s);
+    }
+
+    protected void setUp() throws Exception {
+        p4change = new P4Change();
+    }
+
+    public void testBackslash(){
+        String input = "comment with a / inside";
+        String output = p4change.backslash(input);
+        assertEquals("comment with a \\/ inside", output);
+    }
+
+    public void testSubstitute(){
+        Perl5Util util = new Perl5Util();
+        String tosubstitute = "xx<here>xx";
+        String input = p4change.backslash("/a/b/c/");
+        String output = util.substitute("s/<here>/" + input + "/", tosubstitute);
+        assertEquals("xx/a/b/c/xx", output);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/perforce/p4integrate.xml
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/perforce/p4integrate.xml	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/perforce/p4integrate.xml	(revision 16139)
@@ -0,0 +1,15 @@
+<!-- author Antoine Levy-Lambert levylambert@tiscali-dsl.de -->
+<!-- this test demonstrates p4integrate -->
+<project name="integrate" default= "testintegrate">
+  <property name="second_client" value="levyant_cygwin_test"/>
+  <property name="second_client_root" value="C:\dev\test"/>
+  <property name="depot_file_spec" value="//depot/foobar"/>
+  <property name="depot_file_spec_2" value="//depot/hello"/>
+  <target name="testintegrate">
+    <p4change client="${second_client}"/>
+    <property name="change1" value="${p4.change}" />
+    <p4integrate client="${second_client}" change="${p4.change}" fromfile="${depot_file_spec}" tofile="${depot_file_spec_2}" forceintegrate="true" />
+    <p4resolve  client="${second_client}" view="${depot_file_spec_2}" resolvemode="theirs"/>
+    <p4submit client="${second_client}" change="${p4.change}"/>
+  </target>
+</project>
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/script/ScriptDefTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/script/ScriptDefTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/script/ScriptDefTest.java	(revision 16139)
@@ -0,0 +1,117 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.script;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileSet;
+import java.io.File;
+
+/**
+ * Tests the examples of the &lt;scriptdef&gt; task.
+ *
+ * @since Ant 1.6
+ */
+public class ScriptDefTest extends BuildFileTest {
+
+    public ScriptDefTest(String name) {
+        super(name);
+    }
+
+    /**
+     * The JUnit setup method
+     */
+    public void setUp() {
+        configureProject("src/etc/testcases/taskdefs/optional/script/scriptdef.xml");
+    }
+
+    public void testSimple() {
+        executeTarget("simple");
+        // get the fileset and its basedir
+        Project project = getProject();
+        FileSet fileset = (FileSet) project.getReference("testfileset");
+        File baseDir = fileset.getDir(project);
+        String log = getLog();
+        assertTrue("Expecting attribute value printed",
+            log.indexOf("Attribute attr1 = test") != -1);
+
+        assertTrue("Expecting nested element value printed",
+            log.indexOf("Fileset basedir = " + baseDir.getAbsolutePath()) != -1);
+    }
+
+    public void testNoLang() {
+        expectBuildExceptionContaining("nolang",
+            "Absence of language attribute not detected",
+            "requires a language attribute");
+    }
+
+    public void testNoName() {
+        expectBuildExceptionContaining("noname",
+            "Absence of name attribute not detected",
+            "scriptdef requires a name attribute");
+    }
+
+    public void testNestedByClassName() {
+        executeTarget("nestedbyclassname");
+        // get the fileset and its basedir
+        Project project = getProject();
+        FileSet fileset = (FileSet) project.getReference("testfileset");
+        File baseDir = fileset.getDir(project);
+        String log = getLog();
+        assertTrue("Expecting attribute value to be printed",
+            log.indexOf("Attribute attr1 = test") != -1);
+
+        assertTrue("Expecting nested element value to be printed",
+            log.indexOf("Fileset basedir = " + baseDir.getAbsolutePath()) != -1);
+    }
+
+    public void testNoElement() {
+        expectOutput("noelement", "Attribute attr1 = test");
+    }
+
+    public void testException() {
+        expectBuildExceptionContaining("exception",
+            "Should have thrown an exception in the script",
+            "TypeError");
+    }
+
+    public void testDoubleDef() {
+        executeTarget("doubledef");
+        String log = getLog();
+        assertTrue("Task1 did not execute",
+            log.indexOf("Task1") != -1);
+        assertTrue("Task2 did not execute",
+            log.indexOf("Task2") != -1);
+    }
+
+    public void testDoubleAttribute() {
+        expectBuildExceptionContaining("doubleAttributeDef",
+            "Should have detected duplicate attribute definition",
+            "attr1 attribute more than once");
+    }
+
+    public void testProperty() {
+        executeTarget("property");
+        // get the fileset and its basedir
+        Project project = getProject();
+        String log = getLog();
+        assertTrue("Expecting property in attribute value replaced",
+            log.indexOf("Attribute value = test") != -1);
+    }
+
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/sitraka/ClassFileTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/sitraka/ClassFileTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/sitraka/ClassFileTest.java	(revision 16139)
@@ -0,0 +1,82 @@
+/*
+ * Copyright  2001-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sitraka;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import junit.framework.TestCase;
+import org.apache.tools.ant.util.JavaEnvUtils;
+import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.ClassFile;
+import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.MethodInfo;
+
+/**
+ * Nothing special about this testcase...
+ *
+ */
+public class ClassFileTest extends TestCase {
+    public ClassFileTest(String s) {
+        super(s);
+    }
+
+    public void testVector() throws IOException {
+        String classname = ClassTest.class.getName().replace('.', '/') + ".class";
+        InputStream is = getClass().getClassLoader().getResourceAsStream(classname);
+		assertNotNull("Unable to find resource " + classname + "in caller classloader");
+        ClassFile clazzfile = new ClassFile(is);
+        assertEquals("ClassTest", clazzfile.getName());
+        assertEquals("ClassFileTest.java", clazzfile.getSourceFile());
+        MethodInfo[] methods = clazzfile.getMethods();
+        assertEquals(3, methods.length);
+        assertHasMethod("void <init>()", 1, methods);
+        assertHasMethod("void testTwoLines()", 2, methods);
+        assertHasMethod("void testOneLine()", 3, methods);
+    }
+
+    protected void assertHasMethod(String methodsig, int line, MethodInfo[] methods) {
+        boolean found = false;
+        for (int i = 0; i < methods.length; i++) {
+            MethodInfo method = methods[i];
+            if (methodsig.equals(method.getFullSignature())) {
+
+                assertTrue(methodsig, method.getNumberOfLines() >= line);
+                return;
+            }
+        }
+        fail("Could not find method " + methodsig);
+    }
+}
+
+class ClassTest {
+
+    // 2 lines
+    public ClassTest() {
+    }
+
+    // 2 lines
+    public void testTwoLines() {
+        System.out.println("This is 1 line");
+    }
+
+    // 1 line
+    public void testOneLine() {
+        try {
+            throw new Exception();
+        } catch (Exception e) {
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/sitraka/ClassPathLoaderTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/sitraka/ClassPathLoaderTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/sitraka/ClassPathLoaderTest.java	(revision 16139)
@@ -0,0 +1,44 @@
+/*
+ * Copyright  2001,2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sitraka;
+
+import java.util.Hashtable;
+import java.io.File;
+
+import junit.framework.TestCase;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.taskdefs.optional.sitraka.bytecode.ClassPathLoader;
+
+/**
+ * Minimal testing for the classpath loader..
+ *
+ */
+public class ClassPathLoaderTest extends TestCase {
+    public ClassPathLoaderTest(String s) {
+        super(s);
+    }
+
+    public void testgetClasses() throws Exception {
+        // good performance test...load all classes in rt.jar
+        Path p = new Path(null);
+        p.addJavaRuntime();
+        ClassPathLoader cl = new ClassPathLoader(p.toString());
+        Hashtable map = cl.getClasses();
+        assertTrue(map.size() > 0);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/sitraka/XMLReportTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/sitraka/XMLReportTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/sitraka/XMLReportTest.java	(revision 16139)
@@ -0,0 +1,103 @@
+/*
+ * Copyright  2001,2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sitraka;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.OutputStream;
+import java.net.URL;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import junit.framework.TestCase;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileUtils;
+import org.w3c.dom.Document;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * Ensure that reference classpath feature is working fine...
+ */
+public class XMLReportTest extends TestCase {
+    /** helper for some File/URL connversions */
+    private static FileUtils fileUtils = FileUtils.newFileUtils();
+
+    public XMLReportTest(String s) {
+        super(s);
+    }
+
+    protected File getFile(String name) throws FileNotFoundException {
+        URL url = getClass().getResource(name);
+        if (url == null) {
+            throw new FileNotFoundException("Unable to load '" + name + "' from classpath");
+        }
+        return new File(fileUtils.fromURI(url.toString()));
+    }
+
+    public void testCreateDocument() throws Exception {
+        // this is a sample from running Ant include data for java.* only
+        File reportFile = getFile("/taskdefs/optional/sitraka/covreport-test.xml");
+        XMLReport report = new XMLReport(reportFile);
+        ReportFilters filters = new ReportFilters();
+        ReportFilters.Include incl = new ReportFilters.Include();
+        incl.setClass("java.util.Vector");
+        incl.setMethod("set*");
+        filters.addInclude(incl);
+        report.setReportFilters(filters);
+        Path p = new Path(null);
+        p.addJavaRuntime();
+        Document doc = report.createDocument(p.list());
+
+        Node snapshot = doc.getDocumentElement();
+        assertEquals("snapshot", snapshot.getNodeName());
+
+        // there is only java.util
+        NodeList packages = doc.getElementsByTagName("package");
+        assertEquals(1, packages.getLength());
+        assertEquals("java.util", packages.item(0).getAttributes().getNamedItem("name").getNodeValue());
+
+        // there is only Vector
+        NodeList classes = doc.getElementsByTagName("class");
+        assertEquals(1, classes.getLength());
+        assertEquals("Vector", classes.item(0).getAttributes().getNamedItem("name").getNodeValue());
+
+        // there are 3 set* methods
+        // set(int, Object)
+        // setSize(int)
+        // setElementAt(Object, int)
+        NodeList methods = doc.getElementsByTagName("method");
+        assertEquals(3, methods.getLength());
+
+        //dump(doc, System.out);
+    }
+
+    /**
+     *  might be useful to spit out the document
+     * it's a nightmare to navigate in a DOM structure in a debugger.
+     */
+    protected void dump(Document doc, OutputStream out) throws Exception {
+        TransformerFactory tfactory = TransformerFactory.newInstance();
+        Transformer transformer = tfactory.newTransformer();
+        transformer.setOutputProperty(OutputKeys.INDENT, "no");
+        transformer.setOutputProperty(OutputKeys.METHOD, "xml");
+        transformer.transform(new DOMSource(doc), new StreamResult(out));
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/sos/SOSTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/sos/SOSTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/sos/SOSTest.java	(revision 16139)
@@ -0,0 +1,336 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.sos;
+
+import java.io.File;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ *  Testcase to ensure that command line generation and required attributes are
+ *  correct.
+ *
+ */
+public class SOSTest extends BuildFileTest {
+
+    private Project project;
+    private Commandline commandline;
+
+    private static final String VSS_SERVER_PATH = "\\\\server\\vss\\srcsafe.ini";
+    private static final String VSS_PROJECT_PATH = "/SourceRoot/Project";
+    private static final String DS_VSS_PROJECT_PATH = "$/SourceRoot/Project";
+    private static final String SOS_SERVER_PATH = "192.168.0.1:8888";
+    private static final String SOS_USERNAME = "ant";
+    private static final String SOS_PASSWORD = "rocks";
+    private static final String LOCAL_PATH = "testdir";
+    private static final String SRC_FILE = "Class1.java";
+    private static final String SRC_LABEL = "label1";
+    private static final String SRC_COMMENT = "I fixed a bug";
+    private static final String SOS_HOME = "/home/user/.sos";
+    private static final String VERSION = "007";
+
+    /**
+     *  Constructor for the SOSTest object
+     *
+     * @param  s  Test name
+     */
+    public SOSTest(String s) {
+        super(s);
+    }
+
+    /**
+     *  The JUnit setup method
+     *
+     * @throws  Exception
+     */
+    protected void setUp()
+        throws Exception {
+        project = new Project();
+        project.setBasedir(".");
+    }
+
+    /**
+     *  The teardown method for JUnit
+     *
+     * @throws  Exception
+     */
+    protected void tearDown()
+        throws Exception {
+        File file = new File(project.getBaseDir(), LOCAL_PATH);
+        if (file.exists()) {
+            file.delete();
+        }
+    }
+
+    /**  Test SOSGetFile flags & commandline generation  */
+    public void testGetFileFlags() {
+        String[] sTestCmdLine = {"soscmd", "-command", "GetFile", "-file",
+                SRC_FILE, "-revision", "007", "-server", SOS_SERVER_PATH, "-name",
+                SOS_USERNAME, "-password", SOS_PASSWORD, "-database", VSS_SERVER_PATH,
+                "-project", DS_VSS_PROJECT_PATH, "-verbose", "-nocompress",
+                "-nocache", "-workdir", project.getBaseDir().getAbsolutePath()
+                 + File.separator + LOCAL_PATH};
+
+        // Set up a SOSGet task
+        SOSGet sosGet = new SOSGet();
+        sosGet.setProject(project);
+        sosGet.setVssServerPath(VSS_SERVER_PATH);
+        sosGet.setSosServerPath(SOS_SERVER_PATH);
+        sosGet.setProjectPath(VSS_PROJECT_PATH);
+        sosGet.setFile(SRC_FILE);
+        sosGet.setUsername(SOS_USERNAME);
+        sosGet.setPassword(SOS_PASSWORD);
+        sosGet.setVersion(VERSION);
+        sosGet.setLocalPath(new Path(project, LOCAL_PATH));
+        sosGet.setNoCache(true);
+        sosGet.setNoCompress(true);
+        sosGet.setVerbose(true);
+        sosGet.setRecursive(true);
+
+        commandline = sosGet.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**  Test SOSGetProject flags & commandline generation  */
+    public void testGetProjectFlags() {
+        String[] sTestCmdLine = {"soscmd", "-command", "GetProject", "-recursive",
+                "-label", SRC_LABEL, "-server", SOS_SERVER_PATH, "-name", SOS_USERNAME,
+                "-password", "", "-database", VSS_SERVER_PATH, "-project",
+                DS_VSS_PROJECT_PATH, "", "", "-soshome", SOS_HOME, "-workdir",
+                project.getBaseDir().getAbsolutePath()};
+
+        // Set up a SOSGet task
+        SOSGet sosGet = new SOSGet();
+        sosGet.setProject(project);
+        sosGet.setVssServerPath(VSS_SERVER_PATH);
+        sosGet.setSosServerPath(SOS_SERVER_PATH);
+        sosGet.setProjectPath(DS_VSS_PROJECT_PATH);
+        sosGet.setLabel(SRC_LABEL);
+        sosGet.setUsername(SOS_USERNAME);
+        sosGet.setSosHome(SOS_HOME);
+        sosGet.setNoCache(true);
+        sosGet.setNoCompress(false);
+        sosGet.setVerbose(false);
+        sosGet.setRecursive(true);
+
+        commandline = sosGet.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**  Tests SOSGet required attributes.  */
+    public void testGetExceptions() {
+        configureProject("src/etc/testcases/taskdefs/optional/sos/sos.xml");
+        expectSpecificBuildException("sosget.1", "some cause", "sosserverpath attribute must be set!");
+        expectSpecificBuildException("sosget.2", "some cause", "username attribute must be set!");
+        expectSpecificBuildException("sosget.3", "some cause", "vssserverpath attribute must be set!");
+        expectSpecificBuildException("sosget.4", "some cause", "projectpath attribute must be set!");
+    }
+
+    /**  Test CheckInFile option flags  */
+    public void testCheckinFileFlags() {
+        String[] sTestCmdLine = {"soscmd", "-command", "CheckInFile", "-file",
+                SRC_FILE, "-server", SOS_SERVER_PATH, "-name", SOS_USERNAME,
+                "-password", SOS_PASSWORD, "-database", VSS_SERVER_PATH, "-project",
+                DS_VSS_PROJECT_PATH, "-verbose", "-nocompress", "-nocache",
+                "-workdir", project.getBaseDir().getAbsolutePath() + File.separator
+                 + LOCAL_PATH, "-log", SRC_COMMENT};
+
+        // Set up a SOSCheckin task
+        SOSCheckin sosCheckin = new SOSCheckin();
+        sosCheckin.setProject(project);
+        sosCheckin.setVssServerPath(VSS_SERVER_PATH);
+        sosCheckin.setSosServerPath(SOS_SERVER_PATH);
+        sosCheckin.setProjectPath(VSS_PROJECT_PATH);
+        sosCheckin.setFile(SRC_FILE);
+        sosCheckin.setComment(SRC_COMMENT);
+        sosCheckin.setUsername(SOS_USERNAME);
+        sosCheckin.setPassword(SOS_PASSWORD);
+        sosCheckin.setLocalPath(new Path(project, LOCAL_PATH));
+        sosCheckin.setNoCache(true);
+        sosCheckin.setNoCompress(true);
+        sosCheckin.setVerbose(true);
+        sosCheckin.setRecursive(true);
+
+        commandline = sosCheckin.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**  Test CheckInProject option flags  */
+    public void testCheckinProjectFlags() {
+        String[] sTestCmdLine = {"soscmd", "-command", "CheckInProject",
+                "-recursive", "-server", SOS_SERVER_PATH, "-name", SOS_USERNAME,
+                "-password", "", "-database", VSS_SERVER_PATH, "-project",
+                DS_VSS_PROJECT_PATH, "", "", "-soshome", SOS_HOME, "-workdir",
+                project.getBaseDir().getAbsolutePath(), "-log", SRC_COMMENT,};
+
+        // Set up a SOSCheckin task
+        SOSCheckin sosCheckin = new SOSCheckin();
+        sosCheckin.setProject(project);
+        sosCheckin.setVssServerPath(VSS_SERVER_PATH);
+        sosCheckin.setSosServerPath(SOS_SERVER_PATH);
+        sosCheckin.setProjectPath(DS_VSS_PROJECT_PATH);
+        sosCheckin.setComment(SRC_COMMENT);
+        sosCheckin.setUsername(SOS_USERNAME);
+        sosCheckin.setSosHome(SOS_HOME);
+        sosCheckin.setNoCache(true);
+        sosCheckin.setNoCompress(false);
+        sosCheckin.setVerbose(false);
+        sosCheckin.setRecursive(true);
+
+        commandline = sosCheckin.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**  Test SOSCheckIn required attributes.  */
+    public void testCheckinExceptions() {
+        configureProject("src/etc/testcases/taskdefs/optional/sos/sos.xml");
+        expectSpecificBuildException("soscheckin.1", "some cause", "sosserverpath attribute must be set!");
+        expectSpecificBuildException("soscheckin.2", "some cause", "username attribute must be set!");
+        expectSpecificBuildException("soscheckin.3", "some cause", "vssserverpath attribute must be set!");
+        expectSpecificBuildException("soscheckin.4", "some cause", "projectpath attribute must be set!");
+    }
+
+    /**  Test CheckOutFile option flags  */
+    public void testCheckoutFileFlags() {
+        String[] sTestCmdLine = {"soscmd", "-command", "CheckOutFile", "-file",
+                SRC_FILE, "-server", SOS_SERVER_PATH, "-name", SOS_USERNAME,
+                "-password", SOS_PASSWORD, "-database", VSS_SERVER_PATH, "-project",
+                DS_VSS_PROJECT_PATH, "-verbose", "-nocompress", "-nocache",
+                "-workdir", project.getBaseDir().getAbsolutePath()
+                 + File.separator + LOCAL_PATH};
+
+        // Set up a SOSCheckout task
+        SOSCheckout sosCheckout = new SOSCheckout();
+        sosCheckout.setProject(project);
+        sosCheckout.setVssServerPath(VSS_SERVER_PATH);
+        sosCheckout.setSosServerPath(SOS_SERVER_PATH);
+        sosCheckout.setProjectPath(DS_VSS_PROJECT_PATH);
+        sosCheckout.setFile(SRC_FILE);
+        sosCheckout.setUsername(SOS_USERNAME);
+        sosCheckout.setPassword(SOS_PASSWORD);
+        sosCheckout.setLocalPath(new Path(project, LOCAL_PATH));
+        sosCheckout.setNoCache(true);
+        sosCheckout.setNoCompress(true);
+        sosCheckout.setVerbose(true);
+        sosCheckout.setRecursive(true);
+
+        commandline = sosCheckout.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**  Test CheckOutProject option flags  */
+    public void testCheckoutProjectFlags() {
+        String[] sTestCmdLine = {"soscmd", "-command", "CheckOutProject",
+                "-recursive", "-server", SOS_SERVER_PATH, "-name", SOS_USERNAME,
+                "-password", "", "-database", VSS_SERVER_PATH, "-project",
+                DS_VSS_PROJECT_PATH, "", "", "-soshome", SOS_HOME, "-workdir",
+                project.getBaseDir().getAbsolutePath()};
+
+        // Set up a sosCheckout task
+        SOSCheckout sosCheckout = new SOSCheckout();
+        sosCheckout.setProject(project);
+        sosCheckout.setVssServerPath(VSS_SERVER_PATH);
+        sosCheckout.setSosServerPath(SOS_SERVER_PATH);
+        sosCheckout.setProjectPath(VSS_PROJECT_PATH);
+        sosCheckout.setUsername(SOS_USERNAME);
+        sosCheckout.setSosHome(SOS_HOME);
+        sosCheckout.setNoCache(true);
+        sosCheckout.setNoCompress(false);
+        sosCheckout.setVerbose(false);
+        sosCheckout.setRecursive(true);
+
+        commandline = sosCheckout.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**  Test SOSCheckout required attributes.  */
+    public void testCheckoutExceptions() {
+        configureProject("src/etc/testcases/taskdefs/optional/sos/sos.xml");
+        expectSpecificBuildException("soscheckout.1", "some cause", "sosserverpath attribute must be set!");
+        expectSpecificBuildException("soscheckout.2", "some cause", "username attribute must be set!");
+        expectSpecificBuildException("soscheckout.3", "some cause", "vssserverpath attribute must be set!");
+        expectSpecificBuildException("soscheckout.4", "some cause", "projectpath attribute must be set!");
+    }
+
+    /**  Test Label option flags  */
+    public void testLabelFlags() {
+        String[] sTestCmdLine = {"soscmd", "-command", "AddLabel", "-server",
+                SOS_SERVER_PATH, "-name", SOS_USERNAME, "-password", "", "-database",
+                VSS_SERVER_PATH, "-project", DS_VSS_PROJECT_PATH, "-label",
+                SRC_LABEL, "-verbose", "-log", SRC_COMMENT};
+
+        // Set up a sosCheckout task
+        SOSLabel sosLabel = new SOSLabel();
+        sosLabel.setVssServerPath(VSS_SERVER_PATH);
+        sosLabel.setSosServerPath(SOS_SERVER_PATH);
+        sosLabel.setProjectPath(DS_VSS_PROJECT_PATH);
+        sosLabel.setUsername(SOS_USERNAME);
+        sosLabel.setSosHome(SOS_HOME);
+        sosLabel.setComment(SRC_COMMENT);
+        sosLabel.setLabel(SRC_LABEL);
+        sosLabel.setNoCache(true);
+        sosLabel.setNoCompress(false);
+        sosLabel.setVerbose(true);
+
+        commandline = sosLabel.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**  Test SOSLabel required attributes.  */
+    public void testLabelExceptions() {
+        configureProject("src/etc/testcases/taskdefs/optional/sos/sos.xml");
+        expectSpecificBuildException("soslabel.1", "some cause", "sosserverpath attribute must be set!");
+        expectSpecificBuildException("soslabel.2", "some cause", "username attribute must be set!");
+        expectSpecificBuildException("soslabel.3", "some cause", "vssserverpath attribute must be set!");
+        expectSpecificBuildException("soslabel.4", "some cause", "projectpath attribute must be set!");
+        expectSpecificBuildException("soslabel.5", "some cause", "label attribute must be set!");
+    }
+
+    /**
+     *  Iterate through the generated command line comparing it to reference
+     *  one.
+     *
+     * @param  sTestCmdLine       The reference command line;
+     * @param  sGeneratedCmdLine  The generated command line;
+     */
+    private void checkCommandLines(String[] sTestCmdLine, String[] sGeneratedCmdLine) {
+        int length = sTestCmdLine.length;
+        for (int i = 0; i < length; i++) {
+            try {
+                assertEquals("arg # " + String.valueOf(i),
+                        sTestCmdLine[i],
+                        sGeneratedCmdLine[i]);
+            } catch (ArrayIndexOutOfBoundsException aioob) {
+                fail("missing arg " + sTestCmdLine[i]);
+            }
+        }
+        if (sGeneratedCmdLine.length > sTestCmdLine.length) {
+            // We have extra elements
+            fail("extra args");
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/splash/SplashScreenTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/splash/SplashScreenTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/splash/SplashScreenTest.java	(revision 16139)
@@ -0,0 +1,55 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.splash;
+
+import org.apache.tools.ant.Project;
+
+/**
+ * This is an "interactive" test, it passes if the splash screen
+ * disappears after the "finished" but before the "exiting" message.
+ *
+ * This even isn't a JUnit test case.
+ *
+ * @since Ant 1.5.2
+ */
+public class SplashScreenTest {
+
+    public static void main(String[] args) {
+        Project p = new Project();
+        SplashTask t = new SplashTask();
+        t.setProject(p);
+        t.execute();
+
+        // give it some time to display
+        try {
+            Thread.currentThread().sleep(2000);
+        } catch (InterruptedException e) {
+        } // end of try-catch
+
+        p.fireBuildFinished(null);
+        System.err.println("finished");
+
+        try {
+            Thread.currentThread().sleep(2000);
+        } catch (InterruptedException e) {
+        } // end of try-catch
+        System.err.println("exiting");
+        System.exit(0);
+    }
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/ssh/ScpTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/ssh/ScpTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/ssh/ScpTest.java	(revision 16139)
@@ -0,0 +1,165 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.taskdefs.optional.ssh;
+
+import junit.framework.TestCase;
+
+import java.io.*;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.condition.FilesMatch;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.selectors.FilenameSelector;
+
+/**
+ * This is a unit test for the Scp task in Ant.  It must be
+ * configured with command line options in order for it to work.
+ * Here are the options:
+ *
+ * scp.tmp              This is a local path to a temporary
+ *                      directory for this task to use.
+ * scp.host             This is the remote location of the form:
+ *                      "user:password@host:/path/to/directory"
+ * scp.port             The port of the listening SSH service.
+ *                      Defaults to 22.  (optional)
+ * scp.known.hosts      The file containing the public keys of known
+ *                      hosts.  Must be a SSH2 version file, but
+ *                      supports RSA and DSA keys. If it is not present
+ *                      this task setTrust() to true.  (optional)
+ */
+public class ScpTest extends TestCase {
+
+    private File tempDir = new File( System.getProperty("scp.tmp") );
+    private String sshHostUri = System.getProperty("scp.host");
+    private int port = Integer.parseInt( System.getProperty( "scp.port", "22" ) );
+    private String knownHosts = System.getProperty("scp.known.hosts");
+
+    private List cleanUpList = new ArrayList();
+
+    public ScpTest(String testname) {
+        super(testname);
+    }
+
+    protected void setUp() {
+        cleanUpList.clear();
+    }
+
+    protected void tearDown() {
+        for( Iterator i = cleanUpList.iterator(); i.hasNext(); ) {
+            File file = (File) i.next();
+            file.delete();
+        }
+    }
+
+    public void testSingleFileUploadAndDownload() throws IOException {
+        File uploadFile = createTemporaryFile();
+
+        Scp scpTask = createTask();
+        scpTask.setFile( uploadFile.getPath() );
+        scpTask.setTodir( sshHostUri );
+        scpTask.execute();
+
+        File testFile = new File( tempDir.getPath() + File.separator +
+                "download-testSingleFileUploadAndDownload.test" );
+        addCleanup( testFile );
+        assertTrue( "Assert that the testFile does not exist.",
+                !testFile.exists() );
+
+        scpTask.setFile( sshHostUri + "/" + uploadFile.getName() );
+        scpTask.setTodir( testFile.getPath() );
+        scpTask.execute();
+
+        assertTrue( "Assert that the testFile exists.", testFile.exists() );
+        compareFiles( uploadFile, testFile );
+    }
+
+    public void testMultiUploadAndDownload() throws IOException {
+        List uploadList = new ArrayList();
+        for( int i = 0; i < 5; i++ ) {
+            uploadList.add( createTemporaryFile() );
+        }
+
+        Scp scp = createTask();
+        FilenameSelector selector = new FilenameSelector();
+        selector.setName( "scp*" );
+        FileSet fileset = new FileSet();
+        fileset.setDir( tempDir );
+        fileset.addFilename( selector );
+        scp.addFileset( fileset );
+        scp.setTodir( sshHostUri );
+        scp.execute();
+
+        File multi = new File( tempDir, "multi" );
+        multi.mkdir();
+        addCleanup( multi );
+
+        Scp scp2 = createTask();
+        scp2.setFile( sshHostUri + "/scp*" );
+        scp2.setTodir( multi.getPath() );
+        scp2.execute();
+
+        FilesMatch match = new FilesMatch();
+        for( Iterator i = uploadList.iterator(); i.hasNext(); ) {
+            File f = (File)i.next();
+            match.setFile1( f );
+            File f2 = new File( multi, f.getName() );
+            match.setFile2( f2 );
+            assertTrue("Assert file '" + f.getPath() + "' and file '" +
+                    f2.getPath() + "'", match.eval() );
+        }
+    }
+
+    public void addCleanup( File file ) {
+        cleanUpList.add( file );
+    }
+
+    private void compareFiles(File src, File dest) {
+        FilesMatch match = new FilesMatch();
+        match.setFile1( src );
+        match.setFile2( dest );
+
+        assertTrue( "Assert files are equal.", match.eval() );
+    }
+
+    private File createTemporaryFile() throws IOException {
+        File uploadFile;
+        uploadFile = File.createTempFile( "scp", "test", tempDir );
+        FileWriter writer = new FileWriter( uploadFile );
+        writer.write("Can you hear me now?\n");
+        writer.close();
+        addCleanup( uploadFile );
+        return uploadFile;
+    }
+
+    private Scp createTask() {
+        Scp scp = new Scp();
+        Project p = new Project();
+        p.init();
+        scp.setProject( p );
+        if( knownHosts != null ) {
+            scp.setKnownhosts( knownHosts );
+        } else {
+            scp.setTrust( true );
+        }
+        scp.setPort( port );
+        return scp;
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/unix/SymlinkTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/unix/SymlinkTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/unix/SymlinkTest.java	(revision 16139)
@@ -0,0 +1,189 @@
+/*
+ * Copyright  2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+/*
+ * Since the initial version of this file was deveolped on the clock on
+ * an NSF grant I should say the following boilerplate:
+ *
+ * This material is based upon work supported by the National Science
+ * Foundaton under Grant No. EIA-0196404. Any opinions, findings, and
+ * conclusions or recommendations expressed in this material are those
+ * of the author and do not necessarily reflect the views of the
+ * National Science Foundation.
+ */
+
+package org.apache.tools.ant.taskdefs.optional.unix;
+
+import org.apache.tools.ant.taskdefs.condition.Os;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+
+/**
+ * Test cases for the Symlink task. Link creation, link deletion, recording
+ * of links in multiple directories, and restoration of links recorded are
+ * all tested. A separate test for the utility method Symlink.deleteSymlink
+ * is not included because action="delete" only prints a message and calls
+ * Symlink.deleteSymlink, making a separate test redundant.
+ *
+ */
+
+public class SymlinkTest extends BuildFileTest {
+
+    private Project p;
+    private boolean supportsSymlinks = Os.isFamily("unix");
+
+    public SymlinkTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        if (supportsSymlinks) {
+            configureProject("src/etc/testcases/taskdefs/optional/unix/symlink.xml");
+            executeTarget("setup");
+        }
+    }
+
+
+    public void testSingle() {
+        if (supportsSymlinks) {
+            executeTarget("test-single");
+            p = getProject();
+            assertNotNull("Failed to create file",
+                          p.getProperty("test.single.file.created"));
+            assertNotNull("Failed to create link",
+                          p.getProperty("test.single.link.created"));
+        }
+    }
+
+    public void testDelete() {
+        if (supportsSymlinks) {
+            executeTarget("test-delete");
+            p = getProject();
+            String linkDeleted = p.getProperty("test.delete.link.still.there");
+            assertNotNull("Actual file deleted by symlink",
+                          p.getProperty("test.delete.file.still.there"));
+            if (linkDeleted != null) {
+                fail(linkDeleted);
+            }
+        }
+    }
+
+    public void testRecord() {
+        if (supportsSymlinks) {
+            executeTarget("test-record");
+            p = getProject();
+
+            assertNotNull("Failed to create dir1",
+                          p.getProperty("test.record.dir1.created"));
+
+            assertNotNull("Failed to create dir2",
+                          p.getProperty("test.record.dir2.created"));
+
+            assertNotNull("Failed to create file1",
+                          p.getProperty("test.record.file1.created"));
+
+            assertNotNull("Failed to create file2",
+                          p.getProperty("test.record.file2.created"));
+
+            assertNotNull("Failed to create fileA",
+                          p.getProperty("test.record.fileA.created"));
+
+            assertNotNull("Failed to create fileB",
+                          p.getProperty("test.record.fileB.created"));
+
+            assertNotNull("Failed to create fileC",
+                          p.getProperty("test.record.fileC.created"));
+
+            assertNotNull("Failed to create link1",
+                          p.getProperty("test.record.link1.created"));
+
+            assertNotNull("Failed to create link2",
+                          p.getProperty("test.record.link2.created"));
+
+            assertNotNull("Failed to create link3",
+                          p.getProperty("test.record.link3.created"));
+
+            assertNotNull("Failed to create dirlink",
+                          p.getProperty("test.record.dirlink.created"));
+
+            assertNotNull("Failed to create dirlink2",
+                          p.getProperty("test.record.dirlink2.created"));
+
+            assertNotNull("Couldn't record links in dir1",
+                          p.getProperty("test.record.dir1.recorded"));
+
+            assertNotNull("Couldn't record links in dir2",
+                          p.getProperty("test.record.dir2.recorded"));
+
+            String dir3rec = p.getProperty("test.record.dir3.recorded");
+
+            if (dir3rec != null) {
+                fail(dir3rec);
+            }
+
+        }
+    }
+
+    public void testRecreate() {
+        if (supportsSymlinks) {
+            executeTarget("test-recreate");
+            p = getProject();
+            String link1Rem = p.getProperty("test.recreate.link1.not.removed");
+            String link2Rem = p.getProperty("test.recreate.link2.not.removed");
+            String link3Rem = p.getProperty("test.recreate.link3.not.removed");
+            String dirlinkRem = p.getProperty("test.recreate.dirlink.not.removed");
+            if (link1Rem != null) {
+                fail(link1Rem);
+            }
+            if (link2Rem != null) {
+                fail(link2Rem);
+            }
+            if (link3Rem != null) {
+                fail(link3Rem);
+            }
+            if (dirlinkRem != null) {
+                fail(dirlinkRem);
+            }
+            assertNotNull("Failed to recreate link1",
+                          p.getProperty("test.recreate.link1.recreated"));
+            assertNotNull("Failed to recreate link2",
+                          p.getProperty("test.recreate.link2.recreated"));
+            assertNotNull("Failed to recreate link3",
+                          p.getProperty("test.recreate.link3.recreated"));
+            assertNotNull("Failed to recreate dirlink",
+                          p.getProperty("test.recreate.dirlink.recreated"));
+            
+            String doubleRecreate = p.getProperty("test.recreate.dirlink2.recreated.twice");
+
+            if (doubleRecreate != null) {
+                fail(doubleRecreate);
+            }
+
+            assertNotNull("Failed to alter dirlink3",
+                          p.getProperty("test.recreate.dirlink3.was.altered"));
+
+        }
+    }
+
+    public void tearDown() {
+        if (supportsSymlinks) {
+            executeTarget("teardown");
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/vss/MSVSSTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/vss/MSVSSTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/taskdefs/optional/vss/MSVSSTest.java	(revision 16139)
@@ -0,0 +1,452 @@
+/*
+ * Copyright  2003-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.taskdefs.optional.vss;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.TimeZone;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Location;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Tstamp;
+import org.apache.tools.ant.types.Commandline;
+import org.apache.tools.ant.types.Path;
+
+/**
+ *  Testcase to ensure that command line generation and required attributes are correct.
+ *
+ */
+public class MSVSSTest extends BuildFileTest implements MSVSSConstants {
+
+    private Commandline commandline;
+
+    private static final String VSS_SERVER_PATH = "\\\\server\\vss\\srcsafe.ini";
+    private static final String VSS_PROJECT_PATH = "/SourceRoot/Project";
+    private static final String DS_VSS_PROJECT_PATH = "$/SourceRoot/Project";
+    private static final String VSS_USERNAME = "ant";
+    private static final String VSS_PASSWORD = "rocks";
+    private static final String LOCAL_PATH = "testdir";
+    private static final String SRC_FILE = "Class1.java";
+    private static final String SRC_LABEL = "label1";
+    private static final String LONG_LABEL = "123456789012345678901234567890";
+    private static final String SRC_COMMENT = "I fixed a bug";
+    private static final String VERSION = "007";
+    private static final String DATE = "00-00-00";
+    private static final String DATE2 = "01-01-01";
+    private static final String OUTPUT = "output.log";
+    private static final String SS_DIR = "c:/winnt".replace('/', File.separatorChar);
+
+    /**
+     *  Constructor for the MSVSSTest object
+     *
+     * @param  s  Test name
+     */
+    public MSVSSTest(String s) {
+        super(s);
+    }
+
+    /**
+     *  The JUnit setup method
+     *
+     * @throws  Exception
+     */
+    protected void setUp()
+        throws Exception {
+        project = new Project();
+        project.setBasedir(".");
+    }
+
+    /**
+     *  The teardown method for JUnit
+     *
+     * @throws  Exception
+     */
+    protected void tearDown()
+        throws Exception {
+        File file = new File(project.getBaseDir(), LOCAL_PATH);
+        if (file.exists()) {
+            file.delete();
+        }
+    }
+
+    /**  Tests VSSGet commandline generation.  */
+    public void testGetCommandLine() {
+        String[] sTestCmdLine = {MSVSS.SS_EXE, MSVSS.COMMAND_GET, DS_VSS_PROJECT_PATH,
+                MSVSS.FLAG_OVERRIDE_WORKING_DIR + project.getBaseDir().getAbsolutePath()
+                 + File.separator + LOCAL_PATH, MSVSS.FLAG_AUTORESPONSE_DEF,
+                MSVSS.FLAG_RECURSION, MSVSS.FLAG_VERSION + VERSION, MSVSS.FLAG_LOGIN
+                 + VSS_USERNAME + "," + VSS_PASSWORD, FLAG_FILETIME_UPDATED, FLAG_SKIP_WRITABLE};
+
+        // Set up a VSSGet task
+        MSVSSGET vssGet = new MSVSSGET();
+        vssGet.setProject(project);
+        vssGet.setRecursive(true);
+        vssGet.setLocalpath(new Path(project, LOCAL_PATH));
+        vssGet.setLogin(VSS_USERNAME + "," + VSS_PASSWORD);
+        vssGet.setVersion(VERSION);
+        vssGet.setQuiet(false);
+        vssGet.setDate(DATE);
+        vssGet.setLabel(SRC_LABEL);
+        vssGet.setVsspath(VSS_PROJECT_PATH);
+        MSVSS.CurrentModUpdated cmu = new MSVSS.CurrentModUpdated();
+        cmu.setValue(TIME_UPDATED);
+        vssGet.setFileTimeStamp(cmu);
+        MSVSS.WritableFiles wf = new MSVSS.WritableFiles();
+        wf.setValue(WRITABLE_SKIP);
+        vssGet.setWritableFiles(wf);
+
+        commandline = vssGet.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**  Tests VSSGet required attributes.  */
+    public void testGetExceptions() {
+        configureProject("src/etc/testcases/taskdefs/optional/vss/vss.xml");
+        expectSpecificBuildException("vssget.1", "some cause", "vsspath attribute must be set!");
+    }
+
+    /**  Tests Label commandline generation.  */
+    public void testLabelCommandLine1() {
+        String[] sTestCmdLine = {MSVSS.SS_EXE, MSVSS.COMMAND_LABEL, DS_VSS_PROJECT_PATH,
+                MSVSS.FLAG_COMMENT + SRC_COMMENT, MSVSS.FLAG_AUTORESPONSE_YES,
+                MSVSS.FLAG_LABEL + SRC_LABEL, MSVSS.FLAG_VERSION + VERSION, MSVSS.FLAG_LOGIN
+                 + VSS_USERNAME + "," + VSS_PASSWORD};
+
+        // Set up a VSSLabel task
+        MSVSSLABEL vssLabel = new MSVSSLABEL();
+        vssLabel.setProject(project);
+        vssLabel.setComment(SRC_COMMENT);
+        vssLabel.setLogin(VSS_USERNAME + "," + VSS_PASSWORD);
+        vssLabel.setVersion(VERSION);
+        vssLabel.setAutoresponse("Y");
+        vssLabel.setLabel(SRC_LABEL);
+        vssLabel.setVsspath(VSS_PROJECT_PATH);
+
+        commandline = vssLabel.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**  Tests Label commandline generation with a label of more than 31 chars.  */
+    public void testLabelCommandLine2() {
+        String[] sTestCmdLine = {MSVSS.SS_EXE, MSVSS.COMMAND_LABEL, DS_VSS_PROJECT_PATH,
+                MSVSS.FLAG_COMMENT + SRC_COMMENT, MSVSS.FLAG_AUTORESPONSE_DEF,
+                MSVSS.FLAG_LABEL + LONG_LABEL,
+                MSVSS.FLAG_LOGIN + VSS_USERNAME + "," + VSS_PASSWORD};
+
+        // Set up a VSSLabel task
+        MSVSSLABEL vssLabel = new MSVSSLABEL();
+        vssLabel.setProject(project);
+        vssLabel.setComment(SRC_COMMENT);
+        vssLabel.setLogin(VSS_USERNAME + "," + VSS_PASSWORD);
+        vssLabel.setLabel(LONG_LABEL + "blahblah");
+        vssLabel.setVsspath(VSS_PROJECT_PATH);
+
+        commandline = vssLabel.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**
+     * Test VSSLabel required attributes.
+     */
+    public void testLabelExceptions() {
+        configureProject("src/etc/testcases/taskdefs/optional/vss/vss.xml");
+        expectSpecificBuildException("vsslabel.1", "some cause", "vsspath attribute must be set!");
+        expectSpecificBuildException("vsslabel.2", "some cause", "label attribute must be set!");
+    }
+
+    /**  Tests VSSHistory commandline generation with from label.  */
+    public void testHistoryCommandLine1() {
+        String[] sTestCmdLine = {MSVSS.SS_EXE, MSVSS.COMMAND_HISTORY, DS_VSS_PROJECT_PATH,
+                MSVSS.FLAG_AUTORESPONSE_DEF, MSVSS.FLAG_VERSION_LABEL + LONG_LABEL
+                 + MSVSS.VALUE_FROMLABEL + SRC_LABEL, MSVSS.FLAG_LOGIN + VSS_USERNAME
+                 + "," + VSS_PASSWORD, MSVSS.FLAG_OUTPUT + project.getBaseDir().getAbsolutePath()
+                 + File.separator + OUTPUT};
+
+        // Set up a VSSHistory task
+        MSVSSHISTORY vssHistory = new MSVSSHISTORY();
+        vssHistory.setProject(project);
+
+        vssHistory.setLogin(VSS_USERNAME + "," + VSS_PASSWORD);
+
+        vssHistory.setFromLabel(SRC_LABEL);
+        vssHistory.setToLabel(LONG_LABEL + "blahblah");
+        vssHistory.setVsspath(VSS_PROJECT_PATH);
+        vssHistory.setRecursive(false);
+        vssHistory.setOutput(new File(project.getBaseDir().getAbsolutePath(), OUTPUT));
+
+        commandline = vssHistory.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**  Tests VSSHistory commandline generation with from date.  */
+    public void testHistoryCommandLine2() {
+        String[] sTestCmdLine = {MSVSS.SS_EXE, MSVSS.COMMAND_HISTORY, DS_VSS_PROJECT_PATH,
+                MSVSS.FLAG_AUTORESPONSE_DEF, MSVSS.FLAG_VERSION_DATE + DATE + MSVSS.VALUE_FROMDATE
+                + DATE2, MSVSS.FLAG_RECURSION,  MSVSS.FLAG_LOGIN + VSS_USERNAME + "," + VSS_PASSWORD};
+
+        // Set up a VSSHistory task
+        MSVSSHISTORY vssHistory = new MSVSSHISTORY();
+        vssHistory.setProject(project);
+        vssHistory.setLogin(VSS_USERNAME + "," + VSS_PASSWORD);
+        vssHistory.setFromDate(DATE2);
+        vssHistory.setToDate(DATE);
+        vssHistory.setVsspath(VSS_PROJECT_PATH);
+        vssHistory.setRecursive(true);
+
+        commandline = vssHistory.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**  Tests VSSHistory commandline generation with date calculation.  */
+    public void testHistoryCommandLine3() {
+        // Set up a Timestamp
+        Tstamp tstamp = new Tstamp();
+        Location location = new Location("src/etc/testcases/taskdefs/optional/vss/vss.xml");
+        tstamp.setLocation(location);
+        tstamp.setProject(project);
+        Tstamp.CustomFormat format = tstamp.createFormat();
+        format.setProperty("today");
+        format.setPattern("HH:mm:ss z");
+        format.setTimezone("GMT");
+        Date date = Calendar.getInstance().getTime();
+        format.execute(project, date, location);
+        String today = project.getProperty("today");
+
+        // Get today's date
+        SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss z");
+        sdf.setTimeZone( TimeZone.getTimeZone("GMT") );
+        String expected = sdf.format(date);
+
+        // Set up a VSSHistory task
+        MSVSSHISTORY vssHistory = new MSVSSHISTORY();
+        vssHistory.setProject(project);
+        vssHistory.setLogin(VSS_USERNAME);
+        vssHistory.setToDate(today);
+        vssHistory.setVsspath(VSS_PROJECT_PATH);
+
+        String[] sTestCmdLine = {MSVSS.SS_EXE, MSVSS.COMMAND_HISTORY, DS_VSS_PROJECT_PATH,
+        MSVSS.FLAG_AUTORESPONSE_DEF, MSVSS.FLAG_VERSION_DATE + expected, MSVSS.FLAG_LOGIN + VSS_USERNAME};
+
+        commandline = vssHistory.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**
+     * Tests VSSHistory required attributes.
+     */
+    public void testHistoryExceptions() {
+        configureProject("src/etc/testcases/taskdefs/optional/vss/vss.xml");
+        expectSpecificBuildException("vsshistory.1", "some cause", "vsspath attribute must be set!");
+    }
+
+    /**  Tests CheckIn commandline generation.  */
+    public void testCheckinCommandLine() {
+        String[] sTestCmdLine = {MSVSS.SS_EXE, MSVSS.COMMAND_CHECKIN, DS_VSS_PROJECT_PATH,
+                MSVSS.FLAG_AUTORESPONSE_NO, MSVSS.FLAG_WRITABLE, MSVSS.FLAG_LOGIN + VSS_USERNAME,
+                MSVSS.FLAG_COMMENT + SRC_COMMENT};
+
+        // Set up a VSSCheckIn task
+        MSVSSCHECKIN vssCheckin = new MSVSSCHECKIN();
+        vssCheckin.setProject(project);
+        vssCheckin.setComment(SRC_COMMENT);
+        vssCheckin.setLogin(VSS_USERNAME);
+        vssCheckin.setAutoresponse("N");
+        vssCheckin.setVsspath(VSS_PROJECT_PATH);
+        vssCheckin.setWritable(true);
+
+        commandline = vssCheckin.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**
+     * Test VSSCheckIn required attributes.
+     */
+    public void testCheckinExceptions() {
+        configureProject("src/etc/testcases/taskdefs/optional/vss/vss.xml");
+        expectSpecificBuildException("vsscheckin.1", "some cause", "vsspath attribute must be set!");
+    }
+
+    /**  Tests CheckOut commandline generation.  */
+    public void testCheckoutCommandLine() {
+        String[] sTestCmdLine = {SS_DIR + File.separator + MSVSS.SS_EXE, MSVSS.COMMAND_CHECKOUT,
+                DS_VSS_PROJECT_PATH, MSVSS.FLAG_AUTORESPONSE_DEF, MSVSS.FLAG_RECURSION,
+                MSVSS.FLAG_VERSION_DATE + DATE, MSVSS.FLAG_LOGIN + VSS_USERNAME,
+                FLAG_FILETIME_MODIFIED, FLAG_NO_GET};
+
+        // Set up a VSSCheckOut task
+        MSVSSCHECKOUT vssCheckout = new MSVSSCHECKOUT();
+        vssCheckout.setProject(project);
+        vssCheckout.setLogin(VSS_USERNAME);
+        vssCheckout.setVsspath(DS_VSS_PROJECT_PATH);
+        vssCheckout.setRecursive(true);
+        vssCheckout.setDate(DATE);
+        vssCheckout.setLabel(SRC_LABEL);
+        vssCheckout.setSsdir(SS_DIR);
+        MSVSS.CurrentModUpdated cmu = new MSVSS.CurrentModUpdated();
+        cmu.setValue(TIME_MODIFIED);
+        vssCheckout.setFileTimeStamp(cmu);
+        vssCheckout.setGetLocalCopy(false);
+
+        commandline = vssCheckout.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**
+     * Test VSSCheckout required attributes.
+     */
+    public void testCheckoutExceptions() {
+        configureProject("src/etc/testcases/taskdefs/optional/vss/vss.xml");
+        expectSpecificBuildException("vsscheckout.1", "some cause", "vsspath attribute must be set!");
+        expectSpecificBuildException("vsscheckout.2", "some cause", "blah is not a legal value for this attribute");
+    }
+
+    /**  Tests Add commandline generation.  */
+    public void testAddCommandLine() {
+        String[] sTestCmdLine = {SS_DIR + File.separator + MSVSS.SS_EXE, MSVSS.COMMAND_ADD,
+                project.getBaseDir().getAbsolutePath() + File.separator + LOCAL_PATH,
+                MSVSS.FLAG_AUTORESPONSE_DEF, MSVSS.FLAG_RECURSION,
+                MSVSS.FLAG_LOGIN + VSS_USERNAME + "," + VSS_PASSWORD, MSVSS.FLAG_COMMENT + "-"};
+
+        // Set up a VSSAdd task
+        MSVSSADD vssAdd = new MSVSSADD();
+        vssAdd.setProject(project);
+        vssAdd.setLogin(VSS_USERNAME + "," + VSS_PASSWORD);
+        vssAdd.setVsspath(DS_VSS_PROJECT_PATH);
+        vssAdd.setRecursive(true);
+        vssAdd.setSsdir(SS_DIR);
+        vssAdd.setWritable(false);
+        vssAdd.setLocalpath(new Path(project, LOCAL_PATH));
+
+        commandline = vssAdd.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**
+     * Test VSSAdd required attributes.
+     */
+    public void testAddExceptions() {
+        configureProject("src/etc/testcases/taskdefs/optional/vss/vss.xml");
+        expectSpecificBuildException("vssadd.1", "some cause", "localPath attribute must be set!");
+    }
+
+    /**  Tests CP commandline generation.  */
+    public void testCpCommandLine() {
+        String[] sTestCmdLine = {MSVSS.SS_EXE, MSVSS.COMMAND_CP,
+                DS_VSS_PROJECT_PATH, MSVSS.FLAG_AUTORESPONSE_DEF, MSVSS.FLAG_LOGIN +
+                VSS_USERNAME};
+
+        // Set up a VSSCp task
+        MSVSSCP vssCp = new MSVSSCP();
+        vssCp.setProject(project);
+        vssCp.setLogin(VSS_USERNAME);
+        vssCp.setVsspath(DS_VSS_PROJECT_PATH);
+
+        commandline = vssCp.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**
+     * Test VSSCP required attributes.
+     */
+    public void testCpExceptions() {
+        configureProject("src/etc/testcases/taskdefs/optional/vss/vss.xml");
+        expectSpecificBuildException("vsscp.1", "some cause", "vsspath attribute must be set!");
+    }
+
+    /**  Tests Create commandline generation.  */
+    public void testCreateCommandLine() {
+        String[] sTestCmdLine = { MSVSS.SS_EXE, MSVSS.COMMAND_CREATE,
+                DS_VSS_PROJECT_PATH, MSVSS.FLAG_COMMENT + SRC_COMMENT, MSVSS.FLAG_AUTORESPONSE_NO,
+                MSVSS.FLAG_QUIET, MSVSS.FLAG_LOGIN + VSS_USERNAME};
+
+        // Set up a VSSCreate task
+        MSVSSCREATE vssCreate = new MSVSSCREATE();
+        vssCreate.setProject(project);
+        vssCreate.setComment(SRC_COMMENT);
+        vssCreate.setLogin(VSS_USERNAME);
+        vssCreate.setVsspath(DS_VSS_PROJECT_PATH);
+        vssCreate.setFailOnError(true);
+        vssCreate.setAutoresponse("N");
+        vssCreate.setQuiet(true);
+
+        commandline = vssCreate.buildCmdLine();
+
+        checkCommandLines(sTestCmdLine, commandline.getCommandline());
+    }
+
+    /**
+     * Test VSSCreate required attributes.
+     */
+    public void testCreateExceptions() {
+        configureProject("src/etc/testcases/taskdefs/optional/vss/vss.xml");
+        expectSpecificBuildException("vsscreate.1", "some cause", "vsspath attribute must be set!");
+    }
+
+    /**
+     * Iterate through the generated command line comparing it to reference one.
+     * @param sTestCmdLine          The reference command line;
+     * @param sGeneratedCmdLine     The generated command line;
+     */
+    private void checkCommandLines(String[] sTestCmdLine, String[] sGeneratedCmdLine) {
+        int testLength = sTestCmdLine.length;
+        int genLength = sGeneratedCmdLine.length;
+
+        int genIndex = 0;
+        int testIndex = 0;
+
+        while (testIndex < testLength) {
+            try {
+                if (sGeneratedCmdLine[genIndex] == "") {
+                    genIndex++;
+                    continue;
+                }
+                assertEquals("arg # " + testIndex,
+                        sTestCmdLine[testIndex],
+                        sGeneratedCmdLine[genIndex]);
+                testIndex++;
+                genIndex++;
+            } catch (ArrayIndexOutOfBoundsException aioob) {
+                fail("missing arg " + sTestCmdLine[testIndex]);
+            }
+        }
+
+        // Count the number of empty strings
+        int cnt = 0;
+        for (int i = 0; i < genLength; i++) {
+            if (sGeneratedCmdLine[i] == "") {
+                cnt++;
+            }
+        }
+        if (genLength - cnt > sTestCmdLine.length) {
+            // We have extra elements
+            fail("extra args");
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/AbstractFileSetTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/AbstractFileSetTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/AbstractFileSetTest.java	(revision 16139)
@@ -0,0 +1,245 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+import java.io.File;
+
+/**
+ * Base class for FileSetTest and DirSetTest.
+ *
+ * <p>This doesn't actually test much, mainly reference handling.
+ *
+ */
+
+public abstract class AbstractFileSetTest extends TestCase {
+
+    private Project project;
+
+    public AbstractFileSetTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        project = new Project();
+        project.setBasedir(".");
+    }
+
+    protected abstract AbstractFileSet getInstance();
+
+    protected final Project getProject() {
+        return project;
+    }
+
+    public final void testEmptyElementIfIsReference() {
+        AbstractFileSet f = getInstance();
+        f.setIncludes("**/*.java");
+        try {
+            f.setRefid(new Reference("dummyref"));
+            fail("Can add reference to "
+                 + f.getDataTypeName()
+                 + " with elements from setIncludes");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute "
+                         + "when using refid", be.getMessage());
+        }
+
+        f = getInstance();
+        f.createPatternSet();
+        try {
+            f.setRefid(new Reference("dummyref"));
+            fail("Can add reference to "
+                 + f.getDataTypeName()
+                 + " with nested patternset element.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify nested elements when "
+                         + "using refid", be.getMessage());
+        }
+
+        f = getInstance();
+        f.createInclude();
+        try {
+            f.setRefid(new Reference("dummyref"));
+            fail("Can add reference to "
+                 + f.getDataTypeName()
+                 + " with nested include element.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute "
+                         + "when using refid", be.getMessage());
+        }
+
+        f = getInstance();
+        f.setRefid(new Reference("dummyref"));
+        try {
+            f.setIncludes("**/*.java");
+            fail("Can set includes in "
+                 + f.getDataTypeName()
+                 + " that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute "
+                         + "when using refid", be.getMessage());
+        }
+        try {
+            f.setIncludesfile(new File("/a"));
+            fail("Can set includesfile in "
+                 + f.getDataTypeName()
+                 + " that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute "
+                         + "when using refid", be.getMessage());
+        }
+        try {
+            f.setExcludes("**/*.java");
+            fail("Can set excludes in "
+                 + f.getDataTypeName()
+                 + " that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute "
+                         + "when using refid", be.getMessage());
+        }
+        try {
+            f.setExcludesfile(new File("/a"));
+            fail("Can set excludesfile in "
+                 + f.getDataTypeName()
+                 + " that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute "
+                         + "when using refid", be.getMessage());
+        }
+        try {
+            f.setDir(project.resolveFile("."));
+            fail("Can set dir in "
+                 + f.getDataTypeName()
+                 + " that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute "
+                         + "when using refid", be.getMessage());
+        }
+        try {
+            f.createInclude();
+            fail("Can add nested include in "
+                 + f.getDataTypeName()
+                 + " that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify nested elements when using "
+                         + "refid", be.getMessage());
+        }
+        try {
+            f.createExclude();
+            fail("Can add nested exclude in "
+                 + f.getDataTypeName()
+                 + " that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify nested elements when using "
+                         + "refid", be.getMessage());
+        }
+        try {
+            f.createIncludesFile();
+            fail("Can add nested includesfile in "
+                 + f.getDataTypeName()
+                 + " that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify nested elements when using "
+                         + "refid", be.getMessage());
+        }
+        try {
+            f.createExcludesFile();
+            fail("Can add nested excludesfile in "
+                 + f.getDataTypeName()
+                 + " that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify nested elements when using "
+                         + "refid", be.getMessage());
+        }
+        try {
+            f.createPatternSet();
+            fail("Can add nested patternset in "
+                 + f.getDataTypeName()
+                 + " that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify nested elements when using "
+                         + "refid", be.getMessage());
+        }
+    }
+
+    public void testCircularReferenceCheck() {
+        AbstractFileSet f = getInstance();
+        project.addReference("dummy", f);
+        f.setRefid(new Reference("dummy"));
+        try {
+            f.getDir(project);
+            fail("Can make " + f.getDataTypeName()
+                 + " a Reference to itself.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        }
+        try {
+            f.getDirectoryScanner(project);
+            fail("Can make " + f.getDataTypeName()
+                 + " a Reference to itself.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        }
+
+        // dummy1 --> dummy2 --> dummy3 --> dummy1
+        AbstractFileSet f1 = getInstance();
+        project.addReference("dummy1", f1);
+        f1.setRefid(new Reference("dummy2"));
+        AbstractFileSet f2 = getInstance();
+        project.addReference("dummy2", f2);
+        f2.setRefid(new Reference("dummy3"));
+        AbstractFileSet f3 = getInstance();
+        project.addReference("dummy3", f3);
+        f3.setRefid(new Reference("dummy1"));
+        try {
+            f1.getDir(project);
+            fail("Can make circular reference.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        }
+        try {
+            f1.getDirectoryScanner(project);
+            fail("Can make circular reference.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        }
+
+        // dummy1 --> dummy2 --> dummy3
+        // (which has the Project's basedir as root).
+        f1 = getInstance();
+        project.addReference("dummy1", f1);
+        f1.setRefid(new Reference("dummy2"));
+        f2 = getInstance();
+        project.addReference("dummy2", f2);
+        f2.setRefid(new Reference("dummy3"));
+        f3 = getInstance();
+        project.addReference("dummy3", f3);
+        f3.setDir(project.resolveFile("."));
+        File dir = f1.getDir(project);
+        assertEquals("Dir is basedir", dir, project.getBaseDir());
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/AddTypeTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/AddTypeTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/AddTypeTest.java	(revision 16139)
@@ -0,0 +1,160 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+
+public class AddTypeTest extends BuildFileTest {
+
+    public AddTypeTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/types/addtype.xml");
+    }
+
+    public void testAddPath() {
+        executeTarget("addpath");
+    }
+
+    public void testAddCondition() {
+        executeTarget("addcondition");
+    }
+
+    public void testAddFilter() {
+        executeTarget("addfilter");
+    }
+
+    public void testAddSelector() {
+        executeTarget("addselector");
+    }
+
+    public void testNestedA() {
+        expectLogContaining("nested.a", "add A called");
+    }
+
+    public void testNestedB() {
+        expectLogContaining("nested.b", "add B called");
+    }
+
+    public void testNestedC() {
+        expectLogContaining("nested.c", "add C called");
+    }
+
+    public void testNestedAB() {
+        expectBuildExceptionContaining(
+            "nested.ab", "Should have got ambiguous", "ambiguous");
+    }
+
+    public void testConditionType() {
+        expectLogContaining("condition.type", "beforeafter");
+    }
+
+    public void testConditionTask() {
+        expectLogContaining("condition.task", "My Condition execution");
+    }
+    public void testConditionConditionType() {
+        expectLogContaining("condition.condition.type", "My Condition eval");
+    }
+    public void testConditionConditionTask() {
+        expectBuildExceptionContaining(
+            "condition.condition.task", "task masking condition",
+            "doesn't support the nested");
+    }
+
+    public void testAddConfigured() {
+        expectLogContaining(
+            "myaddconfigured", "value is Value Setexecute: value is Value Set");
+    }
+
+    public void testNamespace() {
+        executeTarget("namespacetest");
+    }
+
+    // The following will be used as types and tasks
+
+    public static interface A {}
+    public static interface B {}
+    public static interface C extends A {}
+    public static interface AB extends A, B {}
+
+    public static class AImpl implements A{}
+    public static class BImpl implements B{}
+    public static class CImpl implements C{}
+    public static class ABImpl implements AB{}
+
+    public static class NestedContainer
+        extends Task
+    {
+        public void add(A el) {
+            log("add A called");
+        }
+        public void add(B el) {
+            log("add B called");
+        }
+        public void add(C el) {
+            log("add C called");
+        }
+    }
+
+    public static class MyCondition
+        implements Condition
+    {
+        Project project;
+        public void setProject(Project project) {
+            this.project = project;
+        }
+        public boolean eval() {
+            project.log("My Condition eval");
+            return true;
+        }
+        public void execute() {
+            project.log("My Condition execution");
+        }
+    }
+
+    public static class MyValue
+    {
+        private String text = "NOT SET YET";
+        public void addText(String text) {
+            this.text = text;
+        }
+        public String toString() {
+            return text;
+        }
+    }
+
+    public static class MyAddConfigured
+        extends Task
+    {
+        MyValue value;
+        public void addConfigured(MyValue value) {
+            log("value is " + value);
+            this.value = value;
+        }
+        public void execute() {
+            log("execute: value is " + value);
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/AssertionsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/AssertionsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/AssertionsTest.java	(revision 16139)
@@ -0,0 +1,95 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildFileTest;
+
+/**
+ * test assertion handling
+ */
+public class AssertionsTest extends BuildFileTest {
+
+    public AssertionsTest(String name) {
+        super(name);
+    }
+
+    protected void setUp() throws Exception {
+        configureProject("src/etc/testcases/types/assertions.xml");
+    }
+
+    protected void tearDown() throws Exception {
+        executeTarget("teardown");
+    }
+
+    /**
+     * runs a test and expects an assertion thrown in forked code
+     * @param target
+     */
+    protected void expectAssertion(String target) {
+        expectBuildExceptionContaining(target,
+                "assertion not thrown in "+target,
+                "Java returned: 1");
+    }
+
+    public void testClassname() {
+        expectAssertion("test-classname");
+    }
+
+    public void testPackage() {
+        expectAssertion("test-package");
+    }
+
+    public void testEmptyAssertions() {
+        executeTarget("test-empty-assertions");
+    }
+
+    public void testDisable() {
+        executeTarget("test-disable");
+    }
+
+    public void testOverride() {
+        expectAssertion("test-override");
+    }
+
+    public void testOverride2() {
+        executeTarget("test-override2");
+    }
+    public void testReferences() {
+        expectAssertion("test-references");
+    }
+
+    public void testMultipleAssertions() {
+        expectBuildExceptionContaining("test-multiple-assertions",
+                "multiple assertions rejected",
+                "Only one assertion declaration is allowed");
+    }
+
+    public void testReferenceAbuse() {
+        expectBuildExceptionContaining("test-reference-abuse",
+                "reference abuse rejected",
+                "You must not specify more than one attribute when using refid");
+    }
+
+    public void testNofork() {
+        expectLogContaining("test-nofork",
+                "Assertion statements are currently ignored in non-forked mode");
+    }
+
+
+}
+
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/CommandlineJavaTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/CommandlineJavaTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/CommandlineJavaTest.java	(revision 16139)
@@ -0,0 +1,175 @@
+/*
+ * Copyright  2000-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+import java.io.File;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.ant.CommandlineJava
+ *
+ */
+public class CommandlineJavaTest extends TestCase {
+
+    public CommandlineJavaTest(String name) {
+        super(name);
+    }
+
+    private Project project;
+
+    public void setUp() {
+        project = new Project();
+        project.setBasedir(".");
+        project.setProperty("build.sysclasspath", "ignore");
+    }
+
+    public void testGetCommandline() throws CloneNotSupportedException {
+        CommandlineJava c = new CommandlineJava();
+        c.createArgument().setValue("org.apache.tools.ant.CommandlineJavaTest");
+        c.setClassname("junit.textui.TestRunner");
+        c.createVmArgument().setValue("-Djava.compiler=NONE");
+        String[] s = c.getCommandline();
+        assertEquals("no classpath", 4, s.length);
+        /*
+         * After changing CommandlineJava to search for the java
+         * executable, I don't know, how to tests the value returned
+         * here without using the same logic as applied in the class
+         * itself.
+         *
+         * assertTrue("no classpath", "java", s[0]);
+         */
+        assertEquals("no classpath", "-Djava.compiler=NONE", s[1]);
+        assertEquals("no classpath", "junit.textui.TestRunner", s[2]);
+        assertEquals("no classpath",
+                     "org.apache.tools.ant.CommandlineJavaTest", s[3]);
+        try {
+            CommandlineJava c2 = (CommandlineJava) c.clone();
+        } catch (NullPointerException ex) {
+            fail("cloning should work without classpath specified");
+        }
+
+        c.createClasspath(project).setLocation(project.resolveFile("build.xml"));
+        c.createClasspath(project).setLocation(project.resolveFile(
+            System.getProperty("ant.home")+"/lib/ant.jar"));
+        s = c.getCommandline();
+        assertEquals("with classpath", 6, s.length);
+        //        assertEquals("with classpath", "java", s[0]);
+        assertEquals("with classpath", "-Djava.compiler=NONE", s[1]);
+        assertEquals("with classpath", "-classpath", s[2]);
+        assertTrue("build.xml contained",
+               s[3].indexOf("build.xml"+java.io.File.pathSeparator) >= 0);
+        assertTrue("ant.jar contained", s[3].endsWith("ant.jar"));
+        assertEquals("with classpath", "junit.textui.TestRunner", s[4]);
+        assertEquals("with classpath",
+                     "org.apache.tools.ant.CommandlineJavaTest", s[5]);
+    }
+
+    public void testJarOption() throws Exception {
+        CommandlineJava c = new CommandlineJava();
+        c.createArgument().setValue("arg1");
+        c.setJar("myfile.jar");
+        c.createVmArgument().setValue("-classic");
+        c.createVmArgument().setValue("-Dx=y");
+        String[] s = c.getCommandline();
+        assertEquals("-classic", s[1]);
+        assertEquals("-Dx=y", s[2]);
+        assertEquals("-jar", s[3]);
+        assertEquals("myfile.jar", s[4]);
+        assertEquals("arg1", s[5]);
+    }
+
+    public void testSysproperties() {
+        String currentClasspath = System.getProperty("java.class.path");
+        assertNotNull(currentClasspath);
+        assertNull(System.getProperty("key"));
+        CommandlineJava c = new CommandlineJava();
+        Environment.Variable v = new Environment.Variable();
+        v.setKey("key");
+        v.setValue("value");
+        c.addSysproperty(v);
+
+        project.setProperty("key2", "value2");
+        PropertySet ps = new PropertySet();
+        ps.setProject(project);
+        ps.appendName("key2");
+        c.addSyspropertyset(ps);
+
+        try {
+            c.setSystemProperties();
+            String newClasspath = System.getProperty("java.class.path");
+            assertNotNull(newClasspath);
+            assertEquals(currentClasspath, newClasspath);
+            assertNotNull(System.getProperty("key"));
+            assertEquals("value", System.getProperty("key"));
+            assertTrue(System.getProperties().containsKey("java.class.path"));
+            assertNotNull(System.getProperty("key2"));
+            assertEquals("value2", System.getProperty("key2"));
+        } finally {
+            c.restoreSystemProperties();
+        }
+        assertNull(System.getProperty("key"));
+        assertNull(System.getProperty("key2"));
+    }
+
+    public void testAssertions() throws CloneNotSupportedException {
+        if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_2)
+            || JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_3)) {
+            return;
+        }
+
+        CommandlineJava c = new CommandlineJava();
+        c.createArgument().setValue("org.apache.tools.ant.CommandlineJavaTest");
+        c.setClassname("junit.textui.TestRunner");
+        c.createVmArgument().setValue("-Djava.compiler=NONE");
+        Assertions a = new Assertions();
+        a.setProject(project);
+        Assertions.EnabledAssertion ea = new Assertions.EnabledAssertion();
+        ea.setClass("junit.textui.TestRunner");
+        a.addEnable(ea);
+        c.setAssertions(a);
+
+        String[] expected = new String[] {
+            null,
+            "-Djava.compiler=NONE",
+            "-ea:junit.textui.TestRunner",
+            "junit.textui.TestRunner",
+            "org.apache.tools.ant.CommandlineJavaTest",
+        };
+            
+        // only the second iteration would pass because of PR 27218
+        for (int i = 0; i < 3; i++) {
+            String[] s = c.getCommandline();
+            assertEquals(expected.length, s.length);
+            for (int j = 1; j < expected.length; j++) {
+                assertEquals(expected[j], s[j]);
+            }
+        }
+        CommandlineJava c2 = (CommandlineJava) c.clone();
+        String[] s = c2.getCommandline();
+        assertEquals(expected.length, s.length);
+        for (int j = 1; j < expected.length; j++) {
+            assertEquals(expected[j], s[j]);
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/CommandlineTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/CommandlineTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/CommandlineTest.java	(revision 16139)
@@ -0,0 +1,145 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+import java.io.File;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.ant.CommandLine
+ *
+ */
+public class CommandlineTest extends TestCase {
+
+    public CommandlineTest(String name) {
+        super(name);
+    }
+
+    public void testTokenizer() {
+        String[] s = Commandline.translateCommandline("1 2 3");
+        assertEquals("Simple case", 3, s.length);
+        for (int i=0; i<3; i++) {
+            assertEquals(""+(i+1), s[i]);
+        }
+
+        s = Commandline.translateCommandline("");
+        assertEquals("empty string", 0, s.length);
+
+        s = Commandline.translateCommandline(null);
+        assertEquals("null", 0, s.length);
+
+        s = Commandline.translateCommandline("1 \'2\' 3");
+        assertEquals("Simple case with single quotes", 3, s.length);
+        assertEquals("Single quotes have been stripped", "2", s[1]);
+
+        s = Commandline.translateCommandline("1 \"2\" 3");
+        assertEquals("Simple case with double quotes", 3, s.length);
+        assertEquals("Double quotes have been stripped", "2", s[1]);
+
+        s = Commandline.translateCommandline("1 \"2 3\" 4");
+        assertEquals("Case with double quotes and whitespace", 3, s.length);
+        assertEquals("Double quotes stripped, space included", "2 3", s[1]);
+
+        s = Commandline.translateCommandline("1 \"2\'3\" 4");
+        assertEquals("Case with double quotes around single quote", 3, s.length);
+        assertEquals("Double quotes stripped, single quote included", "2\'3",
+                     s[1]);
+
+        s = Commandline.translateCommandline("1 \'2 3\' 4");
+        assertEquals("Case with single quotes and whitespace", 3, s.length);
+        assertEquals("Single quotes stripped, space included", "2 3", s[1]);
+
+        s = Commandline.translateCommandline("1 \'2\"3\' 4");
+        assertEquals("Case with single quotes around double quote", 3, s.length);
+        assertEquals("Single quotes stripped, double quote included", "2\"3",
+                     s[1]);
+
+        // \ doesn't have a special meaning anymore - this is different from
+        // what the Unix sh does but causes a lot of problems on DOS
+        // based platforms otherwise
+        s = Commandline.translateCommandline("1 2\\ 3 4");
+        assertEquals("case with quoted whitespace", 4, s.length);
+        assertEquals("backslash included", "2\\", s[1]);
+
+        // "" should become a single empty argument, same for ''
+        // PR 5906
+        s = Commandline.translateCommandline("\"\" a");
+        assertEquals("Doublequoted null arg prepend", 2, s.length);
+        assertEquals("Doublequoted null arg prepend", "", s[0]);
+        assertEquals("Doublequoted null arg prepend", "a", s[1]);
+        s = Commandline.translateCommandline("a \"\"");
+        assertEquals("Doublequoted null arg append", 2, s.length);
+        assertEquals("Doublequoted null arg append", "a", s[0]);
+        assertEquals("Doublequoted null arg append", "", s[1]);
+        s = Commandline.translateCommandline("\"\"");
+        assertEquals("Doublequoted null arg", 1, s.length);
+        assertEquals("Doublequoted null arg", "", s[0]);
+
+        s = Commandline.translateCommandline("\'\' a");
+        assertEquals("Singlequoted null arg prepend", 2, s.length);
+        assertEquals("Singlequoted null arg prepend", "", s[0]);
+        assertEquals("Singlequoted null arg prepend", "a", s[1]);
+        s = Commandline.translateCommandline("a \'\'");
+        assertEquals("Singlequoted null arg append", 2, s.length);
+        assertEquals("Singlequoted null arg append", "a", s[0]);
+        assertEquals("Singlequoted null arg append", "", s[1]);
+        s = Commandline.translateCommandline("\'\'");
+        assertEquals("Singlequoted null arg", 1, s.length);
+        assertEquals("Singlequoted null arg", "", s[0]);
+
+        // now to the expected failures
+
+        try {
+            s = Commandline.translateCommandline("a \'b c");
+            fail("unbalanced single quotes undetected");
+        } catch (BuildException be) {
+            assertEquals("unbalanced quotes in a \'b c", be.getMessage());
+        }
+
+        try {
+            s = Commandline.translateCommandline("a \"b c");
+            fail("unbalanced double quotes undetected");
+        } catch (BuildException be) {
+            assertEquals("unbalanced quotes in a \"b c", be.getMessage());
+        }
+    }
+
+    public void testToString() {
+        assertEquals("", Commandline.toString(new String[0]));
+        assertEquals("", Commandline.toString(null));
+        assertEquals("1 2 3", Commandline.toString(new String[] {"1", "2", "3"}));
+        assertEquals("1 \"2 3\"", Commandline.toString(new String[] {"1", "2 3"}));
+        assertEquals("1 \"2\'3\"", Commandline.toString(new String[] {"1", "2\'3"}));
+        assertEquals("1 \'2\"3\'", Commandline.toString(new String[] {"1", "2\"3"}));
+    }
+
+    public void testAwkCommand(){
+        Commandline c = new Commandline();
+        c.setExecutable("awk");
+        c.createArgument().setValue("'NR == 2 { print $NF }'");
+        String[] s = c.getCommandline();
+        assertNotNull(s);
+        assertEquals(2, s.length);
+        assertEquals("awk", s[0]);
+        assertEquals("'NR == 2 { print $NF }'", s[1]);
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/DescriptionTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/DescriptionTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/DescriptionTest.java	(revision 16139)
@@ -0,0 +1,64 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildFileTest;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+import java.io.*;
+
+/**
+ * FilterSet testing
+ *
+ */
+public class DescriptionTest extends BuildFileTest {
+
+    public DescriptionTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+    }
+
+    public void tearDown() {
+    }
+
+    public void test1() {
+        configureProject("src/etc/testcases/types/description1.xml");
+        assertEquals("Single description failed", "Test Project Description", project.getDescription());
+    }
+
+    public void test2() {
+        configureProject("src/etc/testcases/types/description2.xml");
+        assertEquals("Multi line description failed", "Multi Line\nProject Description", project.getDescription());
+    }
+
+    public void test3() {
+        configureProject("src/etc/testcases/types/description3.xml");
+        assertEquals("Multi instance description failed", "Multi Instance Project Description", project.getDescription());
+    }
+
+    public void test4() {
+        configureProject("src/etc/testcases/types/description4.xml");
+        assertEquals("Multi instance nested description failed", "Multi Instance Nested Project Description", project.getDescription());
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/DirSetTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/DirSetTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/DirSetTest.java	(revision 16139)
@@ -0,0 +1,62 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.ant.types.DirSet.
+ *
+ */
+public class DirSetTest extends AbstractFileSetTest {
+
+    public DirSetTest(String name) {
+        super(name);
+    }
+
+    protected AbstractFileSet getInstance() {
+        return new DirSet();
+    }
+
+    public void testFileSetIsNoDirSet() {
+        DirSet ds = (DirSet) getInstance();
+        ds.setProject(getProject());
+        FileSet fs = new FileSet();
+        fs.setProject(getProject());
+        getProject().addReference("dummy", fs);
+        ds.setRefid(new Reference("dummy"));
+        try {
+            ds.getDir(getProject());
+            fail("DirSet created from FileSet reference");
+        } catch (BuildException e) {
+            assertEquals("dummy doesn\'t denote a DirSet", e.getMessage());
+        }
+
+        ds = (DirSet) getInstance();
+        ds.setProject(getProject());
+        getProject().addReference("dummy2", ds);
+        fs.setRefid(new Reference("dummy2"));
+        try {
+            fs.getDir(getProject());
+            fail("FileSet created from DirSet reference");
+        } catch (BuildException e) {
+            assertEquals("dummy2 doesn\'t denote a FileSet", e.getMessage());
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/EnumeratedAttributeTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/EnumeratedAttributeTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/EnumeratedAttributeTest.java	(revision 16139)
@@ -0,0 +1,86 @@
+/*
+ * Copyright  2000-2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.ant.EnumeratedAttribute.
+ *
+ */
+
+public class EnumeratedAttributeTest extends TestCase {
+
+    private static String[] expected = {"a", "b", "c"};
+
+    public EnumeratedAttributeTest(String name) {
+        super(name);
+    }
+
+    public void testContains() {
+        EnumeratedAttribute t1 = new TestNormal();
+        for (int i=0; i<expected.length; i++) {
+            assertTrue(expected[i]+" is in TestNormal",
+                   t1.containsValue(expected[i]));
+            assertTrue(expected[i].toUpperCase()+" is in TestNormal",
+                   !t1.containsValue(expected[i].toUpperCase()));
+        }
+        assertTrue("TestNormal doesn\'t have \"d\" attribute",
+               !t1.containsValue("d"));
+        assertTrue("TestNull doesn\'t have \"d\" attribute and doesn\'t die",
+               !(new TestNull()).containsValue("d"));
+    }
+
+    public void testExceptions() {
+        EnumeratedAttribute t1 = new TestNormal();
+        for (int i=0; i<expected.length; i++) {
+            try {
+                t1.setValue(expected[i]);
+            } catch (BuildException be) {
+                fail("unexpected exception for value "+expected[i]);
+            }
+        }
+        try {
+            t1.setValue("d");
+            fail("expected exception for value \"d\"");
+        } catch (BuildException be) {
+        }
+        try {
+            (new TestNull()).setValue("d");
+            fail("expected exception for value \"d\" in TestNull");
+        } catch (BuildException be) {
+        } catch (Throwable other) {
+            fail("unexpected death of TestNull: "+other.getMessage());
+        }
+    }
+
+    public static class TestNormal extends EnumeratedAttribute {
+        public String[] getValues() {
+            return expected;
+        }
+    }
+
+    public static class TestNull extends EnumeratedAttribute {
+        public String[] getValues() {
+            return null;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/FileListTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/FileListTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/FileListTest.java	(revision 16139)
@@ -0,0 +1,146 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+import java.io.File;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.ant.types.FileList.
+ *
+ * <p>This doesn't actually test much, mainly reference handling.
+ * Adapted from FileSetTest.</p>
+ *
+ */
+
+public class FileListTest extends TestCase {
+
+    private Project project;
+
+    public FileListTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        project = new Project();
+        project.setBasedir(".");
+    }
+
+    public void testEmptyElementIfIsReference() {
+        FileList f = new FileList();
+        f.setDir(project.resolveFile("."));
+        try {
+            f.setRefid(new Reference("dummyref"));
+            fail("Can add reference to FileList with directory attribute set.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+
+        f = new FileList();
+        f.setFiles("foo.xml,c/d/bar.xml");
+        try {
+            f.setRefid(new Reference("dummyref"));
+            fail("Can add reference to FileList with file attribute set.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+
+        f = new FileList();
+        f.setRefid(new Reference("dummyref"));
+        try {
+            f.setFiles("a/b/foo.java");
+            fail("Can set files in FileList that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+        try {
+            f.setDir(project.resolveFile("."));
+            fail("Can set dir in FileList that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+    }
+
+    public void testCircularReferenceCheck() {
+        FileList f = new FileList();
+        project.addReference("dummy", f);
+        f.setRefid(new Reference("dummy"));
+        try {
+            f.getDir(project);
+            fail("Can make FileList a Reference to itself.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        }
+        try {
+            f.getFiles(project);
+            fail("Can make FileList a Reference to itself.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        }
+
+        // dummy1 --> dummy2 --> dummy3 --> dummy1
+        FileList f1 = new FileList();
+        project.addReference("dummy1", f1);
+        f1.setRefid(new Reference("dummy2"));
+        FileList f2 = new FileList();
+        project.addReference("dummy2", f2);
+        f2.setRefid(new Reference("dummy3"));
+        FileList f3 = new FileList();
+        project.addReference("dummy3", f3);
+        f3.setRefid(new Reference("dummy1"));
+        try {
+            f1.getDir(project);
+            fail("Can make circular reference.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        }
+        try {
+            f1.getFiles(project);
+            fail("Can make circular reference.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        }
+
+        // dummy1 --> dummy2 --> dummy3
+        // (which has the Project's basedir as root).
+        f1 = new FileList();
+        project.addReference("dummy1", f1);
+        f1.setRefid(new Reference("dummy2"));
+        f2 = new FileList();
+        project.addReference("dummy2", f2);
+        f2.setRefid(new Reference("dummy3"));
+        f3 = new FileList();
+        project.addReference("dummy3", f3);
+        f3.setDir(project.resolveFile("."));
+        File dir = f1.getDir(project);
+        assertEquals("Dir is basedir", dir, project.getBaseDir());
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/FileSetTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/FileSetTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/FileSetTest.java	(revision 16139)
@@ -0,0 +1,45 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+import java.io.File;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.ant.types.FileSet.
+ *
+ * <p>This doesn't actually test much, mainly reference handling.
+ *
+ */
+
+public class FileSetTest extends AbstractFileSetTest {
+
+    public FileSetTest(String name) {
+        super(name);
+    }
+
+    protected AbstractFileSet getInstance() {
+        return new FileSet();
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/FilterSetTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/FilterSetTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/FilterSetTest.java	(revision 16139)
@@ -0,0 +1,185 @@
+/*
+ * Copyright  2001-2002, 2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildFileTest;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+import java.io.*;
+import java.util.Hashtable;
+
+/**
+ * FilterSet testing
+ *
+ */
+public class FilterSetTest extends BuildFileTest {
+
+    static private final int BUF_SIZE = 32768;
+
+    public FilterSetTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/types/filterset.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("cleanup");
+    }
+
+    public void test1() {
+        executeTarget("test1");
+        assertTrue("Filterset 1 failed", compareFiles("src/etc/testcases/types/gold/filterset1.txt",
+                                                      "src/etc/testcases/types/dest1.txt"));
+    }
+
+    public void test2() {
+        executeTarget("test2");
+        assertTrue("Filterset 2 failed", compareFiles("src/etc/testcases/types/gold/filterset2.txt",
+                                                      "src/etc/testcases/types/dest2.txt"));
+    }
+
+    public void test3() {
+        executeTarget("test3");
+        assertTrue("Filterset 3 failed", compareFiles("src/etc/testcases/types/gold/filterset3.txt",
+                                                      "src/etc/testcases/types/dest3.txt"));
+    }
+
+    /**
+     * This will test the recursive FilterSet.  Which means that if
+     * the filter value @test@ contains another filter value, it will
+     * actually resolve.
+     */
+    public void testRecursive() {
+        String result = "it works line";
+        String line="@test@ line";
+        FilterSet fs = new FilterSet();
+        fs.addFilter("test", "@test1@");
+        fs.addFilter("test1","@test2@");
+        fs.addFilter("test2", "it works");
+        fs.setBeginToken("@");
+        fs.setEndToken("@");
+        assertEquals(result, fs.replaceTokens(line));
+    }
+
+    /**
+     * Test to see what happens when the resolving occurs in an
+     * infinite loop.
+     */
+    public void testInfinite() {
+        String result = "@test@ line testvalue";
+        String line = "@test@ line @test3@";
+        FilterSet fs = new FilterSet();
+        fs.addFilter("test", "@test1@");
+        fs.addFilter("test1","@test2@");
+        fs.addFilter("test2", "@test@");
+        fs.addFilter("test3", "testvalue");
+        fs.setBeginToken("@");
+        fs.setEndToken("@");
+        assertEquals(result, fs.replaceTokens(line));
+    }
+
+    /**
+     * Test to see what happens when the resolving occurs in
+     * what would be an infinite loop, but with recursion disabled.
+     */
+    public void testRecursionDisabled() {
+        String result = "@test1@ line testvalue";
+        String line = "@test@ line @test2@";
+        FilterSet fs = new FilterSet();
+        fs.addFilter("test", "@test1@");
+        fs.addFilter("test1","@test@");
+        fs.addFilter("test2", "testvalue");
+        fs.setBeginToken("@");
+        fs.setEndToken("@");
+        fs.setRecurse(false);
+        assertEquals(result, fs.replaceTokens(line));
+    }
+
+    public void testNestedFilterSets() {
+        executeTarget("test-nested-filtersets");
+
+        FilterSet fs = (FilterSet) getProject().getReference("1");
+        Hashtable filters = fs.getFilterHash();
+        assertEquals(1, filters.size());
+        assertEquals("value1", filters.get("token1"));
+
+        fs = (FilterSet) getProject().getReference("2");
+        filters = fs.getFilterHash();
+        assertEquals(2, filters.size());
+        assertEquals("1111", filters.get("aaaa"));
+        assertEquals("2222", filters.get("bbbb"));
+
+        fs = (FilterSet) getProject().getReference("3");
+        filters = fs.getFilterHash();
+        assertEquals(1, filters.size());
+        assertEquals("value4", filters.get("token4"));
+
+        fs = (FilterSet) getProject().getReference("5");
+        filters = fs.getFilterHash();
+        assertEquals(1, filters.size());
+        assertEquals("value1", filters.get("token1"));
+    }
+
+    private boolean compareFiles(String name1, String name2) {
+        File file1 = new File(System.getProperty("root"), name1);
+        File file2 = new File(System.getProperty("root"), name2);
+
+        try {
+            if (!file1.exists() || !file2.exists()) {
+                System.out.println("One or both files do not exist:" + name1 + ", " + name2);
+                return false;
+            }
+
+            if (file1.length() != file2.length()) {
+                System.out.println("File size mismatch:" + name1 + "(" + file1.length() + "), " +
+                                   name2  + "(" + file2.length() + ")");
+                return false;
+            }
+
+            // byte - byte compare
+            byte[] buffer1 = new byte[BUF_SIZE];
+            byte[] buffer2 = new byte[BUF_SIZE];
+
+            FileInputStream fis1 = new FileInputStream(file1);
+            FileInputStream fis2 = new FileInputStream(file2);
+            int index = 0;
+            int read = 0;
+            while ((read = fis1.read(buffer1)) != -1) {
+                fis2.read(buffer2);
+                for (int i = 0; i < read; ++i, ++index) {
+                    if (buffer1[i] != buffer2[i]) {
+                        System.out.println("Bytes mismatch:" + name1 + ", " + name2 +
+                                           " at byte " + index);
+                        return false;
+                    }
+                }
+            }
+            return true;
+        }
+        catch (IOException e) {
+            System.out.println("IOException comparing files: " + name1 + ", " + name2);
+            return false;
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/FlexIntegerTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/FlexIntegerTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/FlexIntegerTest.java	(revision 16139)
@@ -0,0 +1,72 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+
+public class FlexIntegerTest extends BuildFileTest {
+
+    public FlexIntegerTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/types/flexinteger.xml");
+    }
+
+    public void testFlexInteger() {
+        executeTarget("test");
+        assertEquals(project.getProperty("flexint.value1"), "10");
+        assertEquals(project.getProperty("flexint.value2"), "8");
+    }
+
+    // This class acts as a custom Ant task also
+    // and uses these variables/methods in that mode
+    private Project taskProject;
+    String propName;
+    private FlexInteger value;
+
+    /**
+     * To make taskdef happy
+     */
+    public FlexIntegerTest() {
+        super("FlexIntegerTest");
+    }
+
+    public void setPropName(String propName) {
+        this.propName = propName;
+    }
+
+    public void setValue(FlexInteger value) {
+        this.value = value;
+    }
+
+    public void setProject(Project project) {
+        taskProject = project;
+    }
+
+    public void execute() {
+        if (propName == null || value == null) {
+            throw new BuildException("name and value required");
+        }
+
+        taskProject.setNewProperty(propName, value.toString());
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/MapperTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/MapperTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/MapperTest.java	(revision 16139)
@@ -0,0 +1,243 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.*;
+import org.apache.tools.ant.BuildFileTest;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+import java.io.File;
+import java.util.List;
+import java.util.Arrays;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.ant.types.Mapper.
+ *
+ */
+
+public class MapperTest extends TestCase {
+
+    private Project project;
+
+    public MapperTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        project = new Project();
+        project.setBasedir(".");
+    }
+
+    public void testEmptyElementIfIsReference() {
+        Mapper m = new Mapper(project);
+        m.setFrom("*.java");
+        try {
+            m.setRefid(new Reference("dummyref"));
+            fail("Can add reference to Mapper with from attribute set");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+
+        m = new Mapper(project);
+        m.setRefid(new Reference("dummyref"));
+        try {
+            m.setFrom("*.java");
+            fail("Can set from in Mapper that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+
+        m = new Mapper(project);
+        m.setRefid(new Reference("dummyref"));
+        try {
+            m.setTo("*.java");
+            fail("Can set to in Mapper that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+        try {
+            Mapper.MapperType mt = new Mapper.MapperType();
+            mt.setValue("glob");
+            m.setType(mt);
+            fail("Can set type in Mapper that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+    }
+
+    public void testCircularReferenceCheck() {
+        Mapper m = new Mapper(project);
+        project.addReference("dummy", m);
+        m.setRefid(new Reference("dummy"));
+        try {
+            m.getImplementation();
+            fail("Can make Mapper a Reference to itself.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        }
+
+        // dummy1 --> dummy2 --> dummy3 --> dummy1
+        Mapper m1 = new Mapper(project);
+        project.addReference("dummy1", m1);
+        m1.setRefid(new Reference("dummy2"));
+        Mapper m2 = new Mapper(project);
+        project.addReference("dummy2", m2);
+        m2.setRefid(new Reference("dummy3"));
+        Mapper m3 = new Mapper(project);
+        project.addReference("dummy3", m3);
+        m3.setRefid(new Reference("dummy1"));
+        try {
+            m1.getImplementation();
+            fail("Can make circular reference.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        }
+
+        // dummy1 --> dummy2 --> dummy3
+        // (which holds a glob mapper from "*.java" to "*.class"
+        m1 = new Mapper(project);
+        project.addReference("dummy1", m1);
+        m1.setRefid(new Reference("dummy2"));
+        m2 = new Mapper(project);
+        project.addReference("dummy2", m2);
+        m2.setRefid(new Reference("dummy3"));
+        m3 = new Mapper(project);
+        project.addReference("dummy3", m3);
+        Mapper.MapperType mt = new Mapper.MapperType();
+        mt.setValue("glob");
+        m3.setType(mt);
+        m3.setFrom("*.java");
+        m3.setTo("*.class");
+        FileNameMapper fmm = m1.getImplementation();
+        assertTrue("should be glob", fmm instanceof GlobPatternMapper);
+        String[] result = fmm.mapFileName("a.java");
+        assertEquals("a.java should match", 1, result.length);
+        assertEquals("a.class", result[0]);
+    }
+
+    public void testNested() {
+        Mapper mapper1 = new Mapper(project);
+        Mapper.MapperType mt = new Mapper.MapperType();
+        mt.setValue("glob");
+        mapper1.setType(mt);
+        mapper1.setFrom("from*");
+        mapper1.setTo("to*");
+
+        //mix element types
+        FileNameMapper mapper2 = new FlatFileNameMapper();
+        FileNameMapper mapper3 = new MergingMapper();
+        mapper3.setTo("mergefile");
+
+        Mapper container = new Mapper(project);
+        container.addConfiguredMapper(mapper1);
+        container.add(mapper2);
+        container.add(mapper3);
+
+        FileNameMapper fileNameMapper = container.getImplementation();
+        String[] targets = fileNameMapper.mapFileName("fromfilename");
+        assertNotNull("no filenames mapped", targets);
+        assertEquals("wrong number of filenames mapped", 3, targets.length);
+        List list = Arrays.asList(targets);
+        assertTrue("cannot find expected target \"tofilename\"",
+            list.contains("tofilename"));
+        assertTrue("cannot find expected target \"fromfilename\"",
+            list.contains("fromfilename"));
+        assertTrue("cannot find expected target \"mergefile\"",
+            list.contains("mergefile"));
+    }
+
+    public void testChained() {
+
+        // a --> b --> c --- def
+        //               \-- ghi
+
+        FileNameMapper mapperAB = new GlobPatternMapper();
+        mapperAB.setFrom("a");
+        mapperAB.setTo("b");
+
+        FileNameMapper mapperBC = new GlobPatternMapper();
+        mapperBC.setFrom("b");
+        mapperBC.setTo("c");
+
+        //implicit composite
+        Mapper mapperCX = new Mapper(project);
+
+        FileNameMapper mapperDEF = new GlobPatternMapper();
+        mapperDEF.setFrom("c");
+        mapperDEF.setTo("def");
+
+        FileNameMapper mapperGHI = new GlobPatternMapper();
+        mapperGHI.setFrom("c");
+        mapperGHI.setTo("ghi");
+
+        mapperCX.add(mapperDEF);
+        mapperCX.add(mapperGHI);
+
+        Mapper chained = new Mapper(project);
+        chained.setClassname(ChainedMapper.class.getName());
+        chained.add(mapperAB);
+        chained.add(mapperBC);
+        chained.addConfiguredMapper(mapperCX);
+
+        FileNameMapper fileNameMapper = chained.getImplementation();
+        String[] targets = fileNameMapper.mapFileName("a");
+        assertNotNull("no filenames mapped", targets);
+        assertEquals("wrong number of filenames mapped", 2, targets.length);
+        List list = Arrays.asList(targets);
+        assertTrue("cannot find expected target \"def\"", list.contains("def"));
+        assertTrue("cannot find expected target \"ghi\"", list.contains("ghi"));
+    }
+
+    public void testCopyTaskWithTwoFilesets() {
+        TaskdefForCopyTest t = new TaskdefForCopyTest("test1");
+        try {
+            t.setUp();
+            t.test1();
+        } finally {
+            t.tearDown();
+        }
+    }
+
+    private class TaskdefForCopyTest extends BuildFileTest {
+        TaskdefForCopyTest(String name) {
+            super(name);
+        }
+
+        public void setUp() {
+            configureProject("src/etc/testcases/types/mapper.xml");
+        }
+
+        public void tearDown() {
+            executeTarget("cleanup");
+        }
+
+        public void test1() {
+            executeTarget("test1");
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/PathTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/PathTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/PathTest.java	(revision 16139)
@@ -0,0 +1,548 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.condition.Os;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+import java.io.File;
+import java.util.Locale;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.ant.types.Path
+ *
+ */
+
+public class PathTest extends TestCase {
+
+    public static boolean isUnixStyle = File.pathSeparatorChar == ':';
+    public static boolean isNetWare = Os.isFamily("netware");
+
+    private Project project;
+
+    public PathTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        project = new Project();
+        project.setBasedir(".");
+    }
+
+    // actually tests constructor as well as setPath
+    public void testConstructorUnixStyle() {
+        Path p = new Path(project, "/a:/b");
+        String[] l = p.list();
+        assertEquals("two items, Unix style", 2, l.length);
+        if (isUnixStyle) {
+            assertEquals("/a", l[0]);
+            assertEquals("/b", l[1]);
+        } else if (isNetWare) {
+            assertEquals("\\a", l[0]);
+            assertEquals("\\b", l[1]);
+        } else {
+            assertEquals(":\\a", l[0].substring(1));
+            assertEquals(":\\b", l[1].substring(1));
+        }
+    }
+
+    public void testRelativePathUnixStyle() {
+        project.setBasedir("src/etc");
+        Path p = new Path(project, "..:testcases");
+        String[] l = p.list();
+        assertEquals("two items, Unix style", 2, l.length);
+        if (isUnixStyle) {
+           assertTrue("test resolved relative to src/etc",
+                 l[0].endsWith("/src"));
+           assertTrue("test resolved relative to src/etc",
+                 l[1].endsWith("/src/etc/testcases"));
+        } else if (isNetWare) {
+           assertTrue("test resolved relative to src/etc",
+                 l[0].endsWith("\\src"));
+           assertTrue("test resolved relative to src/etc",
+                 l[1].endsWith("\\src\\etc\\testcases"));
+        } else {
+           assertTrue("test resolved relative to src/etc",
+                 l[0].endsWith("\\src"));
+           assertTrue("test resolved relative to src/etc",
+                 l[1].endsWith("\\src\\etc\\testcases"));
+        }
+    }
+
+    public void testConstructorWindowsStyle() {
+        Path p = new Path(project, "\\a;\\b");
+        String[] l = p.list();
+        assertEquals("two items, DOS style", 2, l.length);
+        if (isUnixStyle) {
+            assertEquals("/a", l[0]);
+            assertEquals("/b", l[1]);
+        } else if (isNetWare) {
+            assertEquals("\\a", l[0]);
+            assertEquals("\\b", l[1]);
+        } else {
+            assertEquals(":\\a", l[0].substring(1));
+            assertEquals(":\\b", l[1].substring(1));
+        }
+
+        p = new Path(project, "c:\\test");
+        l = p.list();
+        if (isUnixStyle) {
+            assertEquals("no drives on Unix", 2, l.length);
+            assertTrue("c resolved relative to project\'s basedir",
+                   l[0].endsWith("/c"));
+            assertEquals("/test", l[1]);
+        } else if (isNetWare) {
+            assertEquals("volumes on NetWare", 1, l.length);
+            assertEquals("c:\\test", l[0].toLowerCase(Locale.US));
+        } else {
+            assertEquals("drives on DOS", 1, l.length);
+            assertEquals("c:\\test", l[0].toLowerCase(Locale.US));
+        }
+
+        p = new Path(project, "c:\\test;d:\\programs");
+        l = p.list();
+        if (isUnixStyle) {
+            assertEquals("no drives on Unix", 4, l.length);
+            assertTrue("c resolved relative to project\'s basedir",
+                   l[0].endsWith("/c"));
+            assertEquals("/test", l[1]);
+            assertTrue("d resolved relative to project\'s basedir",
+                   l[2].endsWith("/d"));
+            assertEquals("/programs", l[3]);
+        } else if (isNetWare) {
+            assertEquals("volumes on NetWare", 2, l.length);
+            assertEquals("c:\\test", l[0].toLowerCase(Locale.US));
+            assertEquals("d:\\programs", l[1].toLowerCase(Locale.US));
+        } else {
+            assertEquals("drives on DOS", 2, l.length);
+            assertEquals("c:\\test", l[0].toLowerCase(Locale.US));
+            assertEquals("d:\\programs", l[1].toLowerCase(Locale.US));
+        }
+
+        p = new Path(project, "c:/test");
+        l = p.list();
+        if (isUnixStyle) {
+            assertEquals("no drives on Unix", 2, l.length);
+            assertTrue("c resolved relative to project\'s basedir",
+                   l[0].endsWith("/c"));
+            assertEquals("/test", l[1]);
+        } else if (isNetWare) {
+            assertEquals("volumes on NetWare", 1, l.length);
+            assertEquals("c:\\test", l[0].toLowerCase(Locale.US));
+        } else {
+            assertEquals("drives on DOS", 1, l.length);
+            assertEquals("c:\\test", l[0].toLowerCase(Locale.US));
+        }
+
+        p = new Path(project, "c:/test;d:/programs");
+        l = p.list();
+        if (isUnixStyle) {
+            assertEquals("no drives on Unix", 4, l.length);
+            assertTrue("c resolved relative to project\'s basedir",
+                   l[0].endsWith("/c"));
+            assertEquals("/test", l[1]);
+            assertTrue("d resolved relative to project\'s basedir",
+                   l[2].endsWith("/d"));
+            assertEquals("/programs", l[3]);
+        } else if (isNetWare) {
+            assertEquals("volumes on NetWare", 2, l.length);
+            assertEquals("c:\\test", l[0].toLowerCase(Locale.US));
+            assertEquals("d:\\programs", l[1].toLowerCase(Locale.US));
+        } else {
+            assertEquals("drives on DOS", 2, l.length);
+            assertEquals("c:\\test", l[0].toLowerCase(Locale.US));
+            assertEquals("d:\\programs", l[1].toLowerCase(Locale.US));
+        }
+    }
+
+    public void testConstructorNetWareStyle() {
+        // try a netware-volume length path, see how it is handled
+        Path p = new Path(project, "sys:\\test");
+        String[] l = p.list();
+        if (isUnixStyle) {
+            assertEquals("no drives on Unix", 2, l.length);
+            assertTrue("sys resolved relative to project\'s basedir",
+                   l[0].endsWith("/sys"));
+            assertEquals("/test", l[1]);
+        } else if (isNetWare) {
+            assertEquals("sys:\\test", l[0].toLowerCase(Locale.US));
+            assertEquals("volumes on NetWare", 1, l.length);
+        } else {
+            assertEquals("no multiple character-length volumes on Windows", 2, l.length);
+            assertTrue("sys resolved relative to project\'s basedir",
+                   l[0].endsWith("\\sys"));
+            assertTrue("test resolved relative to project\'s basedir",
+                   l[1].endsWith("\\test"));
+        }
+
+        // try a multi-part netware-volume length path, see how it is handled
+        p = new Path(project, "sys:\\test;dev:\\temp");
+        l = p.list();
+        if (isUnixStyle) {
+            assertEquals("no drives on Unix", 4, l.length);
+            assertTrue("sys resolved relative to project\'s basedir",
+                   l[0].endsWith("/sys"));
+            assertEquals("/test", l[1]);
+            assertTrue("dev resolved relative to project\'s basedir",
+                   l[2].endsWith("/dev"));
+            assertEquals("/temp", l[3]);
+        } else if (isNetWare) {
+            assertEquals("volumes on NetWare", 2, l.length);
+            assertEquals("sys:\\test", l[0].toLowerCase(Locale.US));
+            assertEquals("dev:\\temp", l[1].toLowerCase(Locale.US));
+        } else {
+            assertEquals("no multiple character-length volumes on Windows", 4, l.length);
+            assertTrue("sys resolved relative to project\'s basedir",
+                   l[0].endsWith("\\sys"));
+            assertTrue("test resolved relative to project\'s basedir",
+                   l[1].endsWith("\\test"));
+            assertTrue("dev resolved relative to project\'s basedir",
+                   l[2].endsWith("\\dev"));
+            assertTrue("temp resolved relative to project\'s basedir",
+                   l[3].endsWith("\\temp"));
+        }
+
+        // try a netware-volume length path w/forward slash, see how it is handled
+        p = new Path(project, "sys:/test");
+        l = p.list();
+        if (isUnixStyle) {
+            assertEquals("no drives on Unix", 2, l.length);
+            assertTrue("sys resolved relative to project\'s basedir",
+                   l[0].endsWith("/sys"));
+            assertEquals("/test", l[1]);
+        } else if (isNetWare) {
+            assertEquals("volumes on NetWare", 1, l.length);
+            assertEquals("sys:\\test", l[0].toLowerCase(Locale.US));
+        } else {
+            assertEquals("no multiple character-length volumes on Windows", 2, l.length);
+            assertTrue("sys resolved relative to project\'s basedir",
+                   l[0].endsWith("\\sys"));
+            assertTrue("test resolved relative to project\'s basedir",
+                   l[1].endsWith("\\test"));
+        }
+
+        // try a multi-part netware-volume length path w/forward slash, see how it is handled
+        p = new Path(project, "sys:/test;dev:/temp");
+        l = p.list();
+        if (isUnixStyle) {
+            assertEquals("no drives on Unix", 4, l.length);
+            assertTrue("sys resolved relative to project\'s basedir",
+                   l[0].endsWith("/sys"));
+            assertEquals("/test", l[1]);
+            assertTrue("dev resolved relative to project\'s basedir",
+                   l[2].endsWith("/dev"));
+            assertEquals("/temp", l[3]);
+        } else if (isNetWare) {
+            assertEquals("volumes on NetWare", 2, l.length);
+            assertEquals("sys:\\test", l[0].toLowerCase(Locale.US));
+            assertEquals("dev:\\temp", l[1].toLowerCase(Locale.US));
+        } else {
+            assertEquals("no multiple character-length volumes on Windows", 4, l.length);
+            assertTrue("sys resolved relative to project\'s basedir",
+                   l[0].endsWith("\\sys"));
+            assertTrue("test resolved relative to project\'s basedir",
+                   l[1].endsWith("\\test"));
+            assertTrue("dev resolved relative to project\'s basedir",
+                   l[2].endsWith("\\dev"));
+            assertTrue("temp resolved relative to project\'s basedir",
+                   l[3].endsWith("\\temp"));
+         }
+
+        // try a multi-part netware-volume length path with UNIX
+        // separator (this testcase if from an actual bug that was
+        // found, in AvailableTest, which uses PathTokenizer)
+        p = new Path(project,
+                     "SYS:\\JAVA/lib/rt.jar:SYS:\\JAVA/lib/classes.zip");
+        l = p.list();
+        if (isUnixStyle) {
+            assertEquals("no drives on Unix", 3, l.length);
+            assertTrue("sys resolved relative to project\'s basedir",
+                   l[0].endsWith("/SYS"));
+            assertEquals("/JAVA/lib/rt.jar", l[1]);
+            assertEquals("/JAVA/lib/classes.zip", l[2]);
+        } else if (isNetWare) {
+            assertEquals("volumes on NetWare", 2, l.length);
+            assertEquals("sys:\\java\\lib\\rt.jar", l[0].toLowerCase(Locale.US));
+            assertEquals("sys:\\java\\lib\\classes.zip", l[1].toLowerCase(Locale.US));
+        } else {
+            assertEquals("no multiple character-length volumes on Windows", 3, l.length);
+            assertTrue("sys resolved relative to project\'s basedir",
+                   l[0].endsWith("\\SYS"));
+            assertTrue("java/lib/rt.jar resolved relative to project\'s basedir",
+                   l[1].endsWith("\\JAVA\\lib\\rt.jar"));
+            assertTrue("java/lib/classes.zip resolved relative to project\'s basedir",
+                   l[2].endsWith("\\JAVA\\lib\\classes.zip"));
+        }
+    }
+
+    public void testConstructorMixedStyle() {
+        Path p = new Path(project, "\\a;\\b:/c");
+        String[] l = p.list();
+        assertEquals("three items, mixed style", 3, l.length);
+        if (isUnixStyle) {
+            assertEquals("/a", l[0]);
+            assertEquals("/b", l[1]);
+            assertEquals("/c", l[2]);
+        } else if (isNetWare) {
+            assertEquals("\\a", l[0]);
+            assertEquals("\\b", l[1]);
+            assertEquals("\\c", l[2]);
+        } else {
+            assertEquals(":\\a", l[0].substring(1));
+            assertEquals(":\\b", l[1].substring(1));
+            assertEquals(":\\c", l[2].substring(1));
+        }
+    }
+
+    public void testSetLocation() {
+        Path p = new Path(project);
+        p.setLocation(new File(File.separatorChar+"a"));
+        String[] l = p.list();
+        if (isUnixStyle) {
+            assertEquals(1, l.length);
+            assertEquals("/a", l[0]);
+        } else if (isNetWare) {
+            assertEquals(1, l.length);
+            assertEquals("\\a", l[0]);
+        } else {
+            assertEquals(1, l.length);
+            assertEquals(":\\a", l[0].substring(1));
+        }
+    }
+
+    public void testAppending() {
+        Path p = new Path(project, "/a:/b");
+        String[] l = p.list();
+        assertEquals("2 after construction", 2, l.length);
+        p.setLocation(new File("/c"));
+        l = p.list();
+        assertEquals("3 after setLocation", 3, l.length);
+        p.setPath("\\d;\\e");
+        l = p.list();
+        assertEquals("5 after setPath", 5, l.length);
+        p.append(new Path(project, "\\f"));
+        l = p.list();
+        assertEquals("6 after append", 6, l.length);
+        p.createPath().setLocation(new File("/g"));
+        l = p.list();
+        assertEquals("7 after append", 7, l.length);
+    }
+
+    public void testEmpyPath() {
+        Path p = new Path(project, "");
+        String[] l = p.list();
+        assertEquals("0 after construction", 0, l.length);
+        p.setPath("");
+        l = p.list();
+        assertEquals("0 after setPath", 0, l.length);
+        p.append(new Path(project));
+        l = p.list();
+        assertEquals("0 after append", 0, l.length);
+        p.createPath();
+        l = p.list();
+        assertEquals("0 after append", 0, l.length);
+    }
+
+    public void testUnique() {
+        Path p = new Path(project, "/a:/a");
+        String[] l = p.list();
+        assertEquals("1 after construction", 1, l.length);
+        p.setLocation(new File(File.separatorChar+"a"));
+        l = p.list();
+        assertEquals("1 after setLocation", 1, l.length);
+        p.setPath("\\a;/a");
+        l = p.list();
+        assertEquals("1 after setPath", 1, l.length);
+        p.append(new Path(project, "/a;\\a:\\a"));
+        l = p.list();
+        assertEquals("1 after append", 1, l.length);
+        p.createPath().setPath("\\a:/a");
+        l = p.list();
+        assertEquals("1 after append", 1, l.length);
+    }
+
+    public void testEmptyElementIfIsReference() {
+        Path p = new Path(project, "/a:/a");
+        try {
+            p.setRefid(new Reference("dummyref"));
+            fail("Can add reference to Path with elements from constructor");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+
+        p = new Path(project);
+        p.setLocation(new File("/a"));
+        try {
+            p.setRefid(new Reference("dummyref"));
+            fail("Can add reference to Path with elements from setLocation");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+
+        Path another = new Path(project, "/a:/a");
+        project.addReference("dummyref", another);
+        p = new Path(project);
+        p.setRefid(new Reference("dummyref"));
+        try {
+            p.setLocation(new File("/a"));
+            fail("Can set location in Path that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+
+        try {
+            p.setPath("/a;\\a");
+            fail("Can set path in Path that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+
+        try {
+            p.createPath();
+            fail("Can create nested Path in Path that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify nested elements when using refid",
+                         be.getMessage());
+        }
+
+        try {
+            p.createPathElement();
+            fail("Can create nested PathElement in Path that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify nested elements when using refid",
+                         be.getMessage());
+        }
+
+        try {
+            p.addFileset(new FileSet());
+            fail("Can add nested FileSet in Path that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify nested elements when using refid",
+                         be.getMessage());
+        }
+
+        try {
+            p.addFilelist(new FileList());
+            fail("Can add nested FileList in Path that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify nested elements when using refid",
+                         be.getMessage());
+        }
+
+        try {
+            p.addDirset(new DirSet());
+            fail("Can add nested Dirset in Path that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify nested elements when using refid",
+                         be.getMessage());
+        }
+    }
+
+    public void testCircularReferenceCheck() {
+        Path p = new Path(project);
+        project.addReference("dummy", p);
+        p.setRefid(new Reference("dummy"));
+        try {
+            p.list();
+            fail("Can make Path a Reference to itself.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        }
+
+        // dummy1 --> dummy2 --> dummy3 --> dummy1
+        Path p1 = new Path(project);
+        project.addReference("dummy1", p1);
+        Path p2 = p1.createPath();
+        project.addReference("dummy2", p2);
+        Path p3 = p2.createPath();
+        project.addReference("dummy3", p3);
+        p3.setRefid(new Reference("dummy1"));
+        try {
+            p1.list();
+            fail("Can make circular reference.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        }
+
+        // dummy1 --> dummy2 --> dummy3 (with Path "/a")
+        p1 = new Path(project);
+        project.addReference("dummy1", p1);
+        p2 = p1.createPath();
+        project.addReference("dummy2", p2);
+        p3 = p2.createPath();
+        project.addReference("dummy3", p3);
+        p3.setLocation(new File("/a"));
+        String[] l = p1.list();
+        assertEquals("One element buried deep inside a nested path structure",
+                     1, l.length);
+        if (isUnixStyle) {
+            assertEquals("/a", l[0]);
+        } else if (isNetWare) {
+            assertEquals("\\a", l[0]);
+        } else {
+            assertEquals(":\\a", l[0].substring(1));
+        }
+    }
+
+    public void testFileList() {
+        Path p = new Path(project);
+        FileList f = new FileList();
+        f.setProject(project);
+        f.setDir(project.resolveFile("."));
+        f.setFiles("build.xml");
+        p.addFilelist(f);
+        String[] l = p.list();
+        assertEquals(1, l.length);
+        assertEquals(project.resolveFile("build.xml").getAbsolutePath(), l[0]);
+    }
+
+    public void testFileSet() {
+        Path p = new Path(project);
+        FileSet f = new FileSet();
+        f.setProject(project);
+        f.setDir(project.resolveFile("."));
+        f.setIncludes("build.xml");
+        p.addFileset(f);
+        String[] l = p.list();
+        assertEquals(1, l.length);
+        assertEquals(project.resolveFile("build.xml").getAbsolutePath(), l[0]);
+    }
+
+    public void testDirSet() {
+        Path p = new Path(project);
+        DirSet d = new DirSet();
+        d.setProject(project);
+        d.setDir(project.resolveFile("."));
+        d.setIncludes("build");
+        p.addDirset(d);
+        String[] l = p.list();
+        assertEquals(1, l.length);
+        assertEquals(project.resolveFile("build").getAbsolutePath(), l[0]);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/PatternSetTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/PatternSetTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/PatternSetTest.java	(revision 16139)
@@ -0,0 +1,203 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+import java.io.File;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.ant.types.PatternSet.
+ *
+ * <p>This doesn't actually test much, mainly reference handling.</p>
+ *
+ */
+
+public class PatternSetTest extends TestCase {
+
+    private Project project;
+
+    public PatternSetTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        project = new Project();
+        project.setBasedir(".");
+    }
+
+    public void testEmptyElementIfIsReference() {
+        PatternSet p = new PatternSet();
+        p.setIncludes("**/*.java");
+        try {
+            p.setRefid(new Reference("dummyref"));
+            fail("Can add reference to PatternSet with elements from setIncludes");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+
+        p = new PatternSet();
+        p.setRefid(new Reference("dummyref"));
+        try {
+            p.setIncludes("**/*.java");
+            fail("Can set includes in PatternSet that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+
+        p = new PatternSet();
+        p.setRefid(new Reference("dummyref"));
+        try {
+            p.setIncludesfile(new File("/a"));
+            fail("Can set includesfile in PatternSet that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+        try {
+            p.setExcludes("**/*.java");
+            fail("Can set excludes in PatternSet that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+        try {
+            p.setExcludesfile(new File("/a"));
+            fail("Can set excludesfile in PatternSet that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one attribute when using refid",
+                         be.getMessage());
+        }
+        try {
+            p.createInclude();
+            fail("Can add nested include in PatternSet that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify nested elements when using refid",
+                         be.getMessage());
+        }
+        try {
+            p.createExclude();
+            fail("Can add nested exclude in PatternSet that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify nested elements when using refid",
+                         be.getMessage());
+        }
+        try {
+            p.createIncludesFile();
+            fail("Can add nested includesfile in PatternSet that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify nested elements when using refid",
+                         be.getMessage());
+        }
+        try {
+            p.createExcludesFile();
+            fail("Can add nested excludesfile in PatternSet that is a reference.");
+        } catch (BuildException be) {
+            assertEquals("You must not specify nested elements when using refid",
+                         be.getMessage());
+        }
+    }
+
+    public void testCircularReferenceCheck() {
+        PatternSet p = new PatternSet();
+        project.addReference("dummy", p);
+        p.setRefid(new Reference("dummy"));
+        try {
+            p.getIncludePatterns(project);
+            fail("Can make PatternSet a Reference to itself.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        }
+        try {
+            p.getExcludePatterns(project);
+            fail("Can make PatternSet a Reference to itself.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        }
+
+        // dummy1 --> dummy2 --> dummy3 --> dummy1
+        PatternSet p1 = new PatternSet();
+        project.addReference("dummy1", p1);
+        p1.setRefid(new Reference("dummy2"));
+        PatternSet p2 = new PatternSet();
+        project.addReference("dummy2", p2);
+        p2.setRefid(new Reference("dummy3"));
+        PatternSet p3 = new PatternSet();
+        project.addReference("dummy3", p3);
+        p3.setRefid(new Reference("dummy1"));
+        try {
+            p1.getIncludePatterns(project);
+            fail("Can make circular reference.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        }
+        try {
+            p1.getExcludePatterns(project);
+            fail("Can make circular reference.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        }
+
+        // dummy1 --> dummy2 --> dummy3
+        // (which holds patterns "include" and "exclude")
+        p1 = new PatternSet();
+        project.addReference("dummy1", p1);
+        p1.setRefid(new Reference("dummy2"));
+        p2 = new PatternSet();
+        project.addReference("dummy2", p2);
+        p2.setRefid(new Reference("dummy3"));
+        p3 = new PatternSet();
+        project.addReference("dummy3", p3);
+        p3.setIncludes("include");
+        p3.createExclude().setName("exclude");
+        String[] i = p1.getIncludePatterns(project);
+        assertEquals("One include pattern buried deep inside a nested patternset structure",
+                     1, i.length);
+        assertEquals("include", i[0]);
+        i = p3.getExcludePatterns(project);
+        assertEquals("One exclude pattern buried deep inside a nested patternset structure",
+                     1, i.length);
+        assertEquals("exclude", i[0]);
+    }
+
+    public void testNestedPatternset() {
+        PatternSet p = new PatternSet();
+        p.setIncludes("**/*.java");
+
+        PatternSet nested = new PatternSet();
+        nested.setExcludes("**/*.class");
+
+        p.addConfiguredPatternset(nested);
+
+        String[] excludes = p.getExcludePatterns(project);
+        String[] includes = p.getIncludePatterns(project);
+
+        assertEquals("Includes","**/*.java", includes[0]);
+        assertEquals("Excludes","**/*.class", excludes[0]);
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/PermissionsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/PermissionsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/PermissionsTest.java	(revision 16139)
@@ -0,0 +1,155 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import junit.framework.TestCase;
+
+import org.apache.tools.ant.ExitException;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.ant.types.Permissions.
+ *
+ */
+public class PermissionsTest extends TestCase {
+
+    Permissions perms;
+
+    public PermissionsTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        perms = new Permissions();
+        Permissions.Permission perm = new Permissions.Permission();
+        // Grant extra permissions to read and write the user.* properties and read to the
+        // java.home property
+        perm.setActions("read, write");
+        perm.setName("user.*");
+        perm.setClass("java.util.PropertyPermission");
+        perms.addConfiguredGrant(perm);
+
+        perm = new Permissions.Permission();
+        perm.setActions("read");
+        perm.setName("java.home");
+        perm.setClass("java.util.PropertyPermission");
+        perms.addConfiguredGrant(perm);
+
+        perm = new Permissions.Permission();
+        perm.setActions("read");
+        perm.setName("file.encoding");
+        perm.setClass("java.util.PropertyPermission");
+        perms.addConfiguredGrant(perm);
+
+        // Revoke permission to write user.home (granted above via user.*), still able to read though.
+        // and the default granted permission to read os.name.
+        perm = new Permissions.Permission();
+        perm.setActions("write");
+        perm.setName("user.home");
+        perm.setClass("java.util.PropertyPermission");
+        perms.addConfiguredRevoke(perm);
+
+        perm = new Permissions.Permission();
+        perm.setActions("read");
+        perm.setName("os.*");
+        perm.setClass("java.util.PropertyPermission");
+        perms.addConfiguredRevoke(perm);
+    }
+
+    /** Tests a permission that is granted per default. */
+    public void testDefaultGranted() {
+        perms.setSecurityManager();
+        try {
+            String s = System.getProperty("line.separator");
+        } finally {
+            perms.restoreSecurityManager();
+        }
+    }
+
+    /** Tests a permission that has been granted later via wildcard. */
+    public void testGranted() {
+        perms.setSecurityManager();
+        try {
+            String s = System.getProperty("user.name");
+            System.setProperty("user.name", s);
+        } finally {
+            perms.restoreSecurityManager();
+        }
+    }
+
+    /** Tests a permission that has been granted and revoked later. */
+    public void testGrantedAndRevoked() {
+        perms.setSecurityManager();
+        try {
+            String s = System.getProperty("user.home");
+            System.setProperty("user.home", s);
+            fail("Could perform an action that should have been forbidden.");
+        } catch (SecurityException e){
+            // Was expected, test passes
+        } finally {
+            perms.restoreSecurityManager();
+        }
+    }
+
+    /** Tests a permission that is granted as per default but revoked later via wildcard. */
+    public void testDefaultRevoked() {
+        perms.setSecurityManager();
+        try {
+            System.getProperty("os.name");
+            fail("Could perform an action that should have been forbidden.");
+        } catch (SecurityException e){
+            // Was expected, test passes
+        } finally {
+            perms.restoreSecurityManager();
+        }
+    }
+    /** Tests a permission that has not been granted or revoked. */
+    public void testOther() {
+        String ls = System.getProperty("line.separator");
+        perms.setSecurityManager();
+        try {
+            String s = System.setProperty("line.separator",ls);
+            fail("Could perform an action that should have been forbidden.");
+        } catch (SecurityException e){
+            // Was expected, test passes
+        } finally {
+            perms.restoreSecurityManager();
+        }
+    }
+
+    /** Tests an exit condition. */
+    public void testExit() {
+        perms.setSecurityManager();
+        try {
+            System.out.println("If this is the last line on standard out the testExit f.a.i.l.e.d");
+            System.exit(3);
+            fail("Totaly impossible that this fail is ever executed. Please let me know if it is!");
+        } catch (ExitException e) {
+            if (e.getStatus() != 3) {
+                fail("Received wrong exit status in Exit Exception.");
+            }
+            System.out.println("testExit successfull.");
+        } finally {
+            perms.restoreSecurityManager();
+        }
+    }
+
+
+    public void tearDown() {
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/PolyTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/PolyTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/PolyTest.java	(revision 16139)
@@ -0,0 +1,68 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+
+public class PolyTest extends BuildFileTest {
+
+    public PolyTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/types/poly.xml");
+    }
+
+    public void testFileSet() {
+        expectLogContaining("fileset", "types.FileSet");
+    }
+
+    public void testFileSetAntType() {
+        expectLogContaining("fileset-ant-type", "types.PolyTest$MyFileSet");
+    }
+
+    public void testPath() {
+        expectLogContaining("path", "types.Path");
+    }
+
+    public void testPathAntType() {
+        expectLogContaining("path-ant-type", "types.PolyTest$MyPath");
+    }
+
+    public static class MyFileSet extends FileSet {}
+
+    public static class MyPath extends Path {
+        public MyPath(Project project) {
+            super(project);
+        }
+    }
+
+    public static class MyTask extends Task {
+        public void addPath(Path path) {
+            log("class of path is " + path.getClass());
+        }
+        public void addFileset(FileSet fileset) {
+            log("class of fileset is " + fileset.getClass());
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/PropertySetTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/PropertySetTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/PropertySetTest.java	(revision 16139)
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+
+public class PropertySetTest extends BuildFileTest {
+
+    public PropertySetTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/types/propertyset.xml");
+    }
+
+    public void testReferenceToTwoReferences() {
+        executeTarget("reference-to-two-references");
+    }
+
+    public void testNestedMapped() {
+        executeTarget("nested-mapped");
+    }
+
+    public void testNestedMappedMapped() {
+        executeTarget("nested-mapped-mapped");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/RedirectorElementTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/RedirectorElementTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/RedirectorElementTest.java	(revision 16139)
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2004-2005 The Apache Software Foundation.
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildFileTest;
+
+public class RedirectorElementTest extends BuildFileTest {
+
+    public RedirectorElementTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        configureProject("src/etc/testcases/types/redirector.xml", Project.MSG_VERBOSE);
+    }
+
+    public void test1() {
+        executeTarget("test1");
+        assertTrue((getProject().getReference("test1")
+            instanceof RedirectorElement));
+    }
+
+    public void test2() {
+        expectBuildException("test2", "You must not specify more than one "
+            + "attribute when using refid");
+    }
+
+    public void test3() {
+        expectBuildException("test3", "You must not specify nested elements "
+            + "when using refid");
+    }
+
+    public void test4() {
+        executeTarget("test4");
+    }
+
+    public void testRefid() {
+        executeTarget("testRefid");
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/XMLCatalogBuildFileTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/XMLCatalogBuildFileTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/XMLCatalogBuildFileTest.java	(revision 16139)
@@ -0,0 +1,100 @@
+/*
+ * Copyright  2001-2002,2004-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildFileTest;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+/**
+ * BuildFileTest testcases for org.apache.tools.ant.types.XMLCatalog
+ *
+ * @see org.apache.tools.ant.types.XMLCatalogTest
+ *
+ */
+public class XMLCatalogBuildFileTest extends BuildFileTest {
+
+    public XMLCatalogBuildFileTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+    }
+
+    public void tearDown() {
+    }
+
+    //
+    // Ensure that an external entity resolves as expected with NO
+    // XMLCatalog involvement:
+    //
+    // Transform an XML file that refers to the entity into a text
+    // file, stuff result into property: val1
+    //
+    public void testEntityNoCatalog() {
+        configureProject("src/etc/testcases/types/xmlcatalog.xml");
+        expectPropertySet("testentitynocatalog", "val1",
+                          "A stitch in time saves nine");
+    }
+
+    //
+    // Ensure that an external entity resolves as expected Using an
+    // XMLCatalog:
+    //
+    // Transform an XML file that refers to the entity into a text
+    // file, entity is listed in the XMLCatalog pointing to a
+    // different file.  Stuff result into property: val2
+    //
+    public void testEntityWithCatalog() {
+        configureProject("src/etc/testcases/types/xmlcatalog.xml");
+        expectPropertySet("testentitywithcatalog", "val2",
+                          "No news is good news");
+    }
+
+    //
+    // Ensure that an external entity resolves as expected with NO
+    // XMLCatalog involvement:
+    //
+    // Transform an XML file that contains a reference to a _second_ XML file
+    // via the document() function.  The _second_ XML file refers to an entity.
+    // Stuff result into the property: val3
+    //
+    public void testDocumentNoCatalog() {
+        configureProject("src/etc/testcases/types/xmlcatalog.xml");
+        expectPropertySet("testdocumentnocatalog", "val3",
+                          "A stitch in time saves nine");
+    }
+
+    //
+    // Ensure that an external entity resolves as expected Using an
+    // XMLCatalog:
+    //
+    // Transform an XML file that contains a reference to a _second_ XML file
+    // via the document() function.  The _second_ XML file refers to an entity.
+    // The entity is listed in the XMLCatalog pointing to a different file.
+    // Stuff result into the property: val4
+    //
+    public void testDocumentWithCatalog() {
+        configureProject("src/etc/testcases/types/xmlcatalog.xml");
+        expectPropertySet("testdocumentwithcatalog", "val4",
+                          "No news is good news");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/XMLCatalogTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/XMLCatalogTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/XMLCatalogTest.java	(revision 16139)
@@ -0,0 +1,415 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.JAXPUtils;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import javax.xml.transform.Source;
+import javax.xml.transform.sax.SAXSource;
+import javax.xml.transform.TransformerException;
+
+/**
+ * JUnit testcases for org.apache.tools.ant.types.XMLCatalog
+ *
+ */
+public class XMLCatalogTest extends TestCase {
+
+    private Project project;
+    private XMLCatalog catalog;
+
+    private XMLCatalog newCatalog() {
+        XMLCatalog cat = new XMLCatalog();
+        cat.setProject(project);
+        return cat;
+    }
+
+    private String toURLString(File file) throws MalformedURLException {
+        return JAXPUtils.getSystemId(file);
+    }
+
+    public XMLCatalogTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        project = new Project();
+        project.setBasedir(".");
+
+        // This causes XMLCatalog to print out detailed logging
+        // messages for debugging
+        //
+        // DefaultLogger logger = new DefaultLogger();
+        // logger.setMessageOutputLevel(Project.MSG_DEBUG);
+        // logger.setOutputPrintStream(System.out);
+        // logger.setErrorPrintStream(System.err);
+        // project.addBuildListener(logger);
+
+        catalog = newCatalog();
+    }
+
+   public void tearDown() {
+      project = null;
+      catalog = null;
+   }
+
+   public void testEmptyCatalog() {
+       try {
+           InputSource result = catalog.resolveEntity("PUBLIC ID ONE",
+                                                      "i/dont/exist.dtd");
+           assertNull("Empty catalog should return null", result);
+       } catch (Exception e) {
+           fail("resolveEntity() failed!" + e.toString());
+       }
+
+       try {
+           Source result = catalog.resolve("i/dont/exist.dtd", null);
+           String expected = toURLString(new File(project.getBaseDir() +
+                                                  "/i/dont/exist.dtd"));
+           //
+           // These shenanigans are necessary b/c Norm Walsh's resolver
+           // has a different idea of how file URLs are created on windoze
+           // ie file://c:/foo instead of file:///c:/foo
+           //
+           String resultStr = new URL(((SAXSource)result).getInputSource().getSystemId()).getFile();
+           assertTrue("Empty catalog should return input",
+                      expected.endsWith(resultStr));
+       } catch (Exception e) {
+           fail("resolve() failed!" + e.toString());
+       }
+   }
+
+    public void testNonExistentEntry() {
+
+        ResourceLocation dtd = new ResourceLocation();
+        dtd.setPublicId("PUBLIC ID ONE");
+        dtd.setLocation("i/dont/exist.dtd");
+
+        try {
+            InputSource result = catalog.resolveEntity("PUBLIC ID ONE",
+                                                       "i/dont/exist.dtd");
+            assertNull("Nonexistent Catalog entry should not be returned", result);
+        } catch (Exception e) {
+            fail("resolveEntity() failed!" + e.toString());
+        }
+
+        try {
+            Source result = catalog.resolve("i/dont/exist.dtd", null);
+            String expected = toURLString(new File(project.getBaseDir().toURL() +
+                                                   "/i/dont/exist.dtd"));
+            String resultStr = new URL(((SAXSource)result).getInputSource().getSystemId()).getFile();
+            assertTrue("Nonexistent Catalog entry return input",
+                       expected.endsWith(resultStr));
+        } catch (Exception e) {
+            fail("resolve() failed!" + e.toString());
+        }
+    }
+
+    public void testEmptyElementIfIsReference() {
+        ResourceLocation dtd = new ResourceLocation();
+        dtd.setPublicId("PUBLIC ID ONE");
+        dtd.setLocation("i/dont/exist.dtd");
+        catalog.addDTD(dtd);
+        project.addReference("catalog", catalog);
+
+        try {
+            catalog.setRefid(new Reference("dummyref"));
+            fail("Can add reference to nonexistent XMLCatalog");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one "
+                         + "attribute when using refid", be.getMessage());
+        }
+
+        XMLCatalog catalog2 = newCatalog();
+        catalog2.setRefid(new Reference("catalog"));
+
+        try {
+            catalog2.addConfiguredXMLCatalog(catalog);
+            fail("Can add nested XMLCatalog to XMLCatalog that is a reference");
+        } catch (BuildException be) {
+            assertEquals("You must not specify nested elements when using refid",
+                         be.getMessage());
+        }
+    }
+
+    public void testCircularReferenceCheck() {
+
+        // catalog <--> catalog
+        project.addReference("catalog", catalog);
+        catalog.setRefid(new Reference("catalog"));
+
+        try {
+            InputSource result = catalog.resolveEntity("PUBLIC ID ONE",
+                                                       "i/dont/exist.dtd");
+            fail("Can make XMLCatalog a Reference to itself.");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        } catch (Exception e) {
+            fail("resolveEntity() failed!" + e.toString());
+        }
+
+        // catalog1 --> catalog2 --> catalog3 --> catalog1
+        XMLCatalog catalog1 = newCatalog();
+        project.addReference("catalog1", catalog1);
+        XMLCatalog catalog2 = newCatalog();
+        project.addReference("catalog2", catalog2);
+        XMLCatalog catalog3 = newCatalog();
+        project.addReference("catalog3", catalog3);
+
+        catalog3.setRefid(new Reference("catalog1"));
+        catalog2.setRefid(new Reference("catalog3"));
+        catalog1.setRefid(new Reference("catalog2"));
+
+        try {
+            InputSource result = catalog1.resolveEntity("PUBLIC ID ONE",
+                                                        "i/dont/exist.dtd");
+            fail("Can make circular reference");
+        } catch (BuildException be) {
+            assertEquals("This data type contains a circular reference.",
+                         be.getMessage());
+        } catch (Exception e) {
+            fail("resolveEntity() failed!" + e.toString());
+        }
+    }
+    // inspired by Bugzilla Report 23913
+    // a problem used to happen under Windows when the location of the DTD was given as an absolute path
+    // possibly with a mixture of file separators
+    public void testAbsolutePath() {
+        ResourceLocation dtd = new ResourceLocation();
+        dtd.setPublicId("-//stevo//DTD doc 1.0//EN");
+
+        String sysid = System.getProperty("user.dir") + File.separator + "src/etc/testcases/taskdefs/optional/xml/doc.dtd";
+        dtd.setLocation(sysid);
+        catalog.addDTD(dtd);
+        File dtdFile = project.resolveFile(sysid);
+
+        try {
+            InputSource result = catalog.resolveEntity("-//stevo//DTD doc 1.0//EN",
+                                                       "nap:chemical+brothers");
+            assertNotNull(result);
+            assertEquals(toURLString(dtdFile),
+                         result.getSystemId());
+        } catch (Exception e) {
+            fail("resolveEntity() failed!" + e.toString());
+        }
+
+    }
+
+    public void testSimpleEntry() {
+
+        ResourceLocation dtd = new ResourceLocation();
+        dtd.setPublicId("-//stevo//DTD doc 1.0//EN");
+        String sysid = "src/etc/testcases/taskdefs/optional/xml/doc.dtd";
+        dtd.setLocation(sysid);
+        catalog.addDTD(dtd);
+        File dtdFile = project.resolveFile(sysid);
+
+        try {
+            InputSource result = catalog.resolveEntity("-//stevo//DTD doc 1.0//EN",
+                                                       "nap:chemical+brothers");
+            assertNotNull(result);
+            assertEquals(toURLString(dtdFile),
+                         result.getSystemId());
+        } catch (Exception e) {
+            fail("resolveEntity() failed!" + e.toString());
+        }
+    }
+
+    public void testEntryReference() {
+
+        String publicId = "-//stevo//DTD doc 1.0//EN";
+        String sysid = "src/etc/testcases/taskdefs/optional/xml/doc.dtd";
+
+        // catalog2 --> catalog1 --> catalog
+        ResourceLocation dtd = new ResourceLocation();
+        dtd.setPublicId(publicId);
+        dtd.setLocation(sysid);
+        catalog.addDTD(dtd);
+        File dtdFile = project.resolveFile(sysid);
+
+        String uri = "http://foo.com/bar/blah.xml";
+        String uriLoc = "src/etc/testcases/taskdefs/optional/xml/about.xml";
+
+        ResourceLocation entity = new ResourceLocation();
+        entity.setPublicId(uri);
+        entity.setLocation(uriLoc);
+        catalog.addEntity(entity);
+        File xmlFile = project.resolveFile(uriLoc);
+
+        project.addReference("catalog", catalog);
+
+        XMLCatalog catalog1 = newCatalog();
+        project.addReference("catalog1", catalog1);
+        XMLCatalog catalog2 = newCatalog();
+        project.addReference("catalog2", catalog1);
+
+        catalog1.setRefid(new Reference("catalog"));
+        catalog2.setRefid(new Reference("catalog1"));
+
+        try {
+            InputSource result = catalog2.resolveEntity(publicId,
+                                                        "nap:chemical+brothers");
+
+            assertNotNull(result);
+            assertEquals(toURLString(dtdFile),
+                         result.getSystemId());
+        } catch (Exception e) {
+            fail("resolveEntity() failed!" + e.toString());
+        }
+
+        try {
+            Source result = catalog.resolve(uri, null);
+            assertNotNull(result);
+            assertEquals(toURLString(xmlFile),
+                         result.getSystemId());
+        } catch (Exception e) {
+            fail("resolve() failed!" + e.toString());
+        }
+    }
+
+    public void testNestedCatalog() {
+
+        String publicId = "-//stevo//DTD doc 1.0//EN";
+        String dtdLoc = "src/etc/testcases/taskdefs/optional/xml/doc.dtd";
+
+        ResourceLocation dtd = new ResourceLocation();
+        dtd.setPublicId(publicId);
+        dtd.setLocation(dtdLoc);
+        catalog.addDTD(dtd);
+        File dtdFile = project.resolveFile(dtdLoc);
+
+        String uri = "http://foo.com/bar/blah.xml";
+        String uriLoc = "src/etc/testcases/taskdefs/optional/xml/about.xml";
+
+        ResourceLocation entity = new ResourceLocation();
+        entity.setPublicId(uri);
+        entity.setLocation(uriLoc);
+        catalog.addEntity(entity);
+        File xmlFile = project.resolveFile(uriLoc);
+
+        XMLCatalog catalog1 = newCatalog();
+        catalog1.addConfiguredXMLCatalog(catalog);
+
+        try {
+            InputSource result = catalog1.resolveEntity(publicId,
+                                                        "nap:chemical+brothers");
+            assertNotNull(result);
+            assertEquals(toURLString(dtdFile),
+                         result.getSystemId());
+        } catch (Exception e) {
+            fail("resolveEntity() failed!" + e.toString());
+        }
+
+        try {
+            Source result = catalog.resolve(uri, null);
+            assertNotNull(result);
+            assertEquals(toURLString(xmlFile),
+                         result.getSystemId());
+        } catch (Exception e) {
+            fail("resolve() failed!" + e.toString());
+        }
+
+    }
+
+    public void testResolverBase() {
+
+        String uri = "http://foo.com/bar/blah.xml";
+        String uriLoc = "etc/testcases/taskdefs/optional/xml/about.xml";
+        String base = null;
+        try {
+            base = toURLString(project.getBaseDir()) + "/src/";
+        } catch (MalformedURLException ex) {
+            fail (ex.toString());
+        }
+
+        ResourceLocation entity = new ResourceLocation();
+        entity.setPublicId(uri);
+        entity.setLocation(uriLoc);
+        catalog.addEntity(entity);
+        File xmlFile = project.resolveFile("src/" + uriLoc);
+
+        try {
+            Source result = catalog.resolve(uri, base);
+            assertNotNull(result);
+            assertEquals(toURLString(xmlFile),
+                         result.getSystemId());
+        } catch (Exception e) {
+            fail("resolve() failed!" + e.toString());
+        }
+    }
+
+    public void testClasspath() {
+
+
+        String publicId = "-//stevo//DTD doc 1.0//EN";
+        String dtdLoc = "testcases/taskdefs/optional/xml/doc.dtd";
+        String path1 = project.getBaseDir().toString() + "/src/etc";
+
+        ResourceLocation dtd = new ResourceLocation();
+        dtd.setPublicId(publicId);
+        dtd.setLocation(dtdLoc);
+        catalog.addDTD(dtd);
+        File dtdFile = project.resolveFile("src/etc/" + dtdLoc);
+
+        String uri = "http://foo.com/bar/blah.xml";
+        String uriLoc = "etc/testcases/taskdefs/optional/xml/about.xml";
+        String path2 = project.getBaseDir().toString() + "/src";
+
+        ResourceLocation entity = new ResourceLocation();
+        entity.setPublicId(uri);
+        entity.setLocation(uriLoc);
+        catalog.addEntity(entity);
+        File xmlFile = project.resolveFile("src/" + uriLoc);
+
+        Path aPath = new Path(project, path1);
+        aPath.append(new Path(project, path2));
+        catalog.setClasspath(aPath);
+
+        try {
+            InputSource result = catalog.resolveEntity(publicId,
+                                                       "nap:chemical+brothers");
+            assertNotNull(result);
+            String resultStr = new URL(result.getSystemId()).getFile();
+            assertTrue(toURLString(dtdFile).endsWith(resultStr));
+        } catch (Exception e) {
+            fail("resolveEntity() failed!" + e.toString());
+        }
+
+        try {
+            Source result = catalog.resolve(uri, null);
+            assertNotNull(result);
+            String resultStr = new URL(result.getSystemId()).getFile();
+            assertTrue(toURLString(xmlFile).endsWith(resultStr));
+        } catch (Exception e) {
+            fail("resolve() failed!" + e.toString());
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/ZipFileSetTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/ZipFileSetTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/ZipFileSetTest.java	(revision 16139)
@@ -0,0 +1,118 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+import java.io.File;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.ant.types.ZipFileSet.
+ *
+ * <p>This doesn't actually test much, mainly reference handling.
+ *
+ */
+
+public class ZipFileSetTest extends AbstractFileSetTest {
+
+    public ZipFileSetTest(String name) {
+        super(name);
+    }
+
+    protected AbstractFileSet getInstance() {
+        return new ZipFileSet();
+    }
+    public final void testAttributes() {
+        ZipFileSet f = (ZipFileSet)getInstance();
+        //check that dir and src are incompatible
+        f.setSrc(new File("example.zip"));
+        try {
+            f.setDir(new File("examples"));
+            fail("can add dir to "
+                    + f.getDataTypeName()
+                    + " when a src is already present");
+        } catch (BuildException be) {
+            assertEquals("Cannot set both dir and src attributes",be.getMessage());
+        }
+        f = (ZipFileSet)getInstance();
+        //check that dir and src are incompatible
+        f.setDir(new File("examples"));
+        try {
+            f.setSrc(new File("example.zip"));
+            fail("can add src to "
+                    + f.getDataTypeName()
+                    + " when a dir is already present");
+        } catch (BuildException be) {
+            assertEquals("Cannot set both dir and src attributes",be.getMessage());
+        }
+        //check that fullpath and prefix are incompatible
+        f = (ZipFileSet)getInstance();
+        f.setSrc(new File("example.zip"));
+        f.setPrefix("/examples");
+        try {
+            f.setFullpath("/doc/manual/index.html");
+            fail("Can add fullpath to "
+                    + f.getDataTypeName()
+                    + " when a prefix is already present");
+        } catch (BuildException be) {
+            assertEquals("Cannot set both fullpath and prefix attributes", be.getMessage());
+        }
+        f = (ZipFileSet)getInstance();
+        f.setSrc(new File("example.zip"));
+        f.setFullpath("/doc/manual/index.html");
+        try {
+            f.setPrefix("/examples");
+            fail("Can add prefix to "
+                    + f.getDataTypeName()
+                    + " when a fullpath is already present");
+        } catch (BuildException be) {
+            assertEquals("Cannot set both fullpath and prefix attributes", be.getMessage());
+        }
+        // check that reference zipfilesets cannot have specific attributes
+        f = (ZipFileSet)getInstance();
+        f.setRefid(new Reference("test"));
+        try {
+            f.setSrc(new File("example.zip"));
+            fail("Can add src to "
+                    + f.getDataTypeName()
+                    + " when a refid is already present");
+        } catch (BuildException be) {
+            assertEquals("You must not specify more than one "
+            + "attribute when using refid", be.getMessage());
+        }
+        // check that a reference zipfileset gets the same attributes as the original
+        f = (ZipFileSet)getInstance();
+        f.setSrc(new File("example.zip"));
+        f.setPrefix("/examples");
+        f.setFileMode("600");
+        f.setDirMode("530");
+        getProject().addReference("test",f);
+        ZipFileSet zid=(ZipFileSet)getInstance();
+        zid.setRefid(new Reference("test"));
+        assertTrue("src attribute copied by copy constructor",zid.getSrc(getProject()).equals(f.getSrc(getProject())));
+        assertTrue("prefix attribute copied by copy constructor",f.getPrefix(getProject()).equals(zid.getPrefix(getProject())));
+        assertTrue("file mode attribute copied by copy constructor",f.getFileMode(getProject())==zid.getFileMode(getProject()));
+        assertTrue("dir mode attribute copied by copy constructor",f.getDirMode(getProject())==zid.getDirMode(getProject()));
+      }
+
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/optional/depend/ClassFileSetTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/optional/depend/ClassFileSetTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/optional/depend/ClassFileSetTest.java	(revision 16139)
@@ -0,0 +1,175 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.optional.depend;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Date;
+import java.util.Vector;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.DirectoryScanner;
+
+/**
+ * Testcase for the Classfileset optional type.
+ *
+ */
+public class ClassFileSetTest extends BuildFileTest {
+    public static final String RESULT_FILESET = "result";
+
+    public ClassFileSetTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        // share the setup for testing the depend task
+        configureProject("src/etc/testcases/taskdefs/optional/depend/depend.xml");
+    }
+
+    public void tearDown() {
+        executeTarget("clean");
+    }
+
+    /**
+     * Test basic classfileset
+     */
+    public void testBasicSet() {
+        Project p = getProject();
+        executeTarget("testbasicset");
+        FileSet resultFileSet = (FileSet)p.getReference(RESULT_FILESET);
+        DirectoryScanner scanner = resultFileSet.getDirectoryScanner(p);
+        String[] scannedFiles = scanner.getIncludedFiles();
+        Hashtable files = new Hashtable();
+        for (int i = 0; i < scannedFiles.length; ++i) {
+            files.put(scannedFiles[i], scannedFiles[i]);
+        }
+        assertEquals("Classfileset did not pick up expected number of "
+            + "class files", 4, files.size());
+        assertTrue("Result did not contain A.class",
+            files.containsKey("A.class"));
+        assertTrue("Result did not contain B.class",
+            files.containsKey("B.class"));
+        assertTrue("Result did not contain C.class",
+            files.containsKey("C.class"));
+        assertTrue("Result did not contain D.class",
+            files.containsKey("D.class"));
+    }
+
+    /**
+     * Test small classfileset
+     */
+    public void testSmallSet() {
+        Project p = getProject();
+        executeTarget("testsmallset");
+        FileSet resultFileSet = (FileSet)p.getReference(RESULT_FILESET);
+        DirectoryScanner scanner = resultFileSet.getDirectoryScanner(p);
+        String[] scannedFiles = scanner.getIncludedFiles();
+        Hashtable files = new Hashtable();
+        for (int i = 0; i < scannedFiles.length; ++i) {
+            files.put(scannedFiles[i], scannedFiles[i]);
+        }
+        assertEquals("Classfileset did not pick up expected number of "
+            + "class files", 2, files.size());
+        assertTrue("Result did not contain B.class",
+            files.containsKey("B.class"));
+        assertTrue("Result did not contain C.class",
+            files.containsKey("C.class"));
+    }
+
+    /**
+     * Test combo classfileset
+     */
+    public void testComboSet() {
+        Project p = getProject();
+        executeTarget("testcomboset");
+        FileSet resultFileSet = (FileSet)p.getReference(RESULT_FILESET);
+        DirectoryScanner scanner = resultFileSet.getDirectoryScanner(p);
+        String[] scannedFiles = scanner.getIncludedFiles();
+        Hashtable files = new Hashtable();
+        for (int i = 0; i < scannedFiles.length; ++i) {
+            files.put(scannedFiles[i], scannedFiles[i]);
+        }
+        assertEquals("Classfileset did not pick up expected number of "
+            + "class files", 1, files.size());
+        assertTrue("Result did not contain C.class",
+            files.containsKey("C.class"));
+    }
+
+    /**
+     * Test that you can pass a classfileset by reference to a fileset.
+     */
+    public void testByReference() {
+        executeTarget("testbyreference");
+    }
+
+    /**
+     * Test that classes included in a method "System.out.println(MyClass.class)" are included.
+     */
+    public void testMethodParam() {
+        Project p = getProject();
+        executeTarget("testmethodparam");
+        FileSet resultFileSet = (FileSet)p.getReference(RESULT_FILESET);
+        DirectoryScanner scanner = resultFileSet.getDirectoryScanner(p);
+        String[] scannedFiles = scanner.getIncludedFiles();
+        Hashtable files = new Hashtable();
+        for (int i = 0; i < scannedFiles.length; ++i) {
+            files.put(scannedFiles[i], scannedFiles[i]);
+        }
+        assertEquals("Classfileset did not pick up expected number of "
+            + "class files", 5, files.size());
+        assertTrue("Result did not contain A.class",
+            files.containsKey("A.class"));
+        assertTrue("Result did not contain B.class",
+            files.containsKey("B.class"));
+        assertTrue("Result did not contain C.class",
+            files.containsKey("C.class"));
+        assertTrue("Result did not contain D.class",
+            files.containsKey("D.class"));
+        assertTrue("Result did not contain E.class",
+            files.containsKey("E.class"));
+    }
+
+    /**
+     * Test that classes included in a method "System.out.println(Outer.Inner.class)" are included.
+     */
+    public void testMethodParamInner() {
+        Project p = getProject();
+        executeTarget("testmethodparaminner");
+        FileSet resultFileSet = (FileSet)p.getReference(RESULT_FILESET);
+        DirectoryScanner scanner = resultFileSet.getDirectoryScanner(p);
+        String[] scannedFiles = scanner.getIncludedFiles();
+        Hashtable files = new Hashtable();
+        for (int i = 0; i < scannedFiles.length; ++i) {
+            files.put(scannedFiles[i], scannedFiles[i]);
+        }
+        assertEquals("Classfileset did not pick up expected number of "
+            + "class files", 4, files.size());
+        assertTrue("Result did not contain test" + File.separator + "Outer$Inner.class",
+            files.containsKey("test" + File.separator + "Outer$Inner.class"));
+        assertTrue("Result did not contain test" + File.separator + "Outer.class",
+            files.containsKey("test" + File.separator + "Outer.class"));
+        assertTrue("Result did not contain test" + File.separator + "ContainsOnlyInner.class",
+            files.containsKey("test" + File.separator + "ContainsOnlyInner.class"));
+        assertTrue("Result did not contain test" + File.separator + "ContainsOnlyInner.class",
+            files.containsKey("test" + File.separator + "MethodParam.class"));
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/BaseSelectorTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/BaseSelectorTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/BaseSelectorTest.java	(revision 16139)
@@ -0,0 +1,289 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildFileTest;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+import java.io.File;
+
+/**
+ * Base test case for Selectors. Provides a shared test as well as
+ * a test bed for selecting on, and a helper method for determining
+ * whether selections are correct.
+ *
+ */
+public abstract class BaseSelectorTest extends TestCase {
+
+    private Project project;
+    private TaskdefForMakingBed tbed = null;
+    protected String basedirname = "src/etc/testcases/types";
+    protected String beddirname = basedirname + "/selectortest";
+    protected String mirrordirname = basedirname + "/selectortest2";
+    protected File basedir = new File(basedirname);
+    protected File beddir = new File(beddirname);
+    protected File mirrordir = new File(mirrordirname);
+    protected String[] filenames = {".","asf-logo.gif.md5","asf-logo.gif.bz2",
+            "asf-logo.gif.gz","copy.filterset.filtered","zip/asf-logo.gif.zip",
+            "tar/asf-logo.gif.tar","tar/asf-logo-huge.tar.gz",
+            "tar/gz/asf-logo.gif.tar.gz","tar/bz2/asf-logo.gif.tar.bz2",
+            "tar/bz2/asf-logo-huge.tar.bz2","tar/bz2"};
+    protected File[] files = new File[filenames.length];
+    protected File[] mirrorfiles = new File[filenames.length];
+
+    public BaseSelectorTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        project = new Project();
+        project.init();
+        project.setBaseDir(basedir);
+        for (int x = 0; x < files.length; x++) {
+            files[x] = new File(beddir,filenames[x]);
+            mirrorfiles[x] = new File(mirrordir,filenames[x]);
+        }
+    }
+
+    /**
+     * Override this in child classes to return a specific Selector
+     */
+    public abstract BaseSelector getInstance();
+
+
+    /**
+     * Return a preconfigured selector (with a set reference to
+     * project instance).
+     * @return the selector
+     */
+    public BaseSelector getSelector() {
+        BaseSelector selector = getInstance();
+        selector.setProject( getProject() );
+        return selector;
+    }
+
+
+    public Project getProject() {
+        return project;
+    }
+
+    /**
+     * This is a test that all Selectors derived from BaseSelector can
+     * use. It calls the setError() method and checks to ensure that a
+     * BuildException is thrown as a result.
+     */
+    public void testRespondsToError() {
+        BaseSelector s = getInstance();
+        if (s == null) {
+            return;
+        }
+        s.setError("test error");
+        try {
+            s.isSelected(beddir,filenames[0],files[0]);
+            fail("Cannot cause BuildException when setError() is called");
+        } catch (BuildException be) {
+            assertEquals("test error",
+                         be.getMessage());
+        }
+    }
+
+
+    /**
+     * This is a helper method that takes a selector and calls its
+     * isSelected() method on each file in the testbed. It returns
+     * a string of "T"s amd "F"s
+     */
+    public String selectionString(FileSelector selector) {
+        return selectionString(beddir,files,selector);
+    }
+
+    /**
+     * This is a helper method that takes a selector and calls its
+     * isSelected() method on each file in the mirror testbed. This
+     * variation is used for dependency checks and to get around the
+     * limitations in the touch task when running JDK 1.1. It returns
+     * a string of "T"s amd "F"s.
+     */
+    public String mirrorSelectionString(FileSelector selector) {
+        return selectionString(mirrordir,mirrorfiles,selector);
+    }
+
+    /**
+     * Worker method for the two convenience methods above. Applies a
+     * selector on a set of files passed in and returns a string of
+     * "T"s amd "F"s from applying the selector to each file.
+     */
+    public String selectionString(File basedir, File[] files, FileSelector selector) {
+        StringBuffer buf = new StringBuffer();
+        for (int x = 0; x < files.length; x++) {
+            if (selector.isSelected(basedir,filenames[x],files[x])) {
+                buf.append('T');
+            }
+            else {
+                buf.append('F');
+            }
+        }
+        return buf.toString();
+    }
+
+    /**
+     * Does the selection test for a given selector and prints the
+     * filenames of the differing files (selected but shouldn't,
+     * not selected but should).
+     * @param selector  The selector to test
+     * @param expected  The expected result
+     */
+    public void performTests(FileSelector selector, String expected) {
+        String result = selectionString(selector);
+        String diff = diff(expected, result);
+        String resolved = resolve(diff);
+        assertEquals("Differing files: " + resolved, result, expected);
+    }
+
+    /**
+     *  Checks which files are selected and shouldn't be or which
+     *  are not selected but should.
+     *  @param expected    String containing 'F's and 'T's
+     *  @param result      String containing 'F's and 'T's
+     *  @return Difference as String containing '-' (equal) and
+     *          'X' (difference).
+     */
+    public String diff(String expected, String result) {
+        int length1 = expected.length();
+        int length2 = result.length();
+        int min = (length1 > length2) ? length2 : length1;
+        StringBuffer sb = new StringBuffer();
+        for (int i=0; i<min; i++) {
+            sb.append(
+                  (expected.charAt(i) == result.charAt(i))
+                ? "-"
+                : "X"
+            );
+        }
+        return sb.toString();
+    }
+
+
+    /**
+     * Resolves a diff-String (@see diff()) against the (inherited) filenames-
+     * and files arrays.
+     * @param filelist    Diff-String
+     * @return String containing the filenames for all differing files,
+     *         separated with semicolons ';'
+     */
+    public String resolve(String filelist) {
+        StringBuffer sb = new StringBuffer();
+        int min = (filenames.length > filelist.length())
+                ? filelist.length()
+                : filenames.length;
+        for (int i=0; i<min; i++) {
+            if ('X'==filelist.charAt(i)) {
+                sb.append(filenames[i]);
+                sb.append(";");
+            }
+        }
+        return sb.toString();
+    }
+
+
+    /**
+     * <p>Creates a testbed. We avoid the dreaded "test" word so that we
+     * don't falsely identify this as a test to be run. The actual
+     * setting up of the testbed is done in the
+     * <code>src/etc/testcases/types/selectors.xml</code> build file.</p>
+     *
+     * <p>Note that the right way to call this is within a try block,
+     * with a finally clause that calls cleanupBed(). You place tests of
+     * the isSelected() method within the try block.</p>
+     */
+    protected void makeBed() {
+        tbed = new TaskdefForMakingBed("setupfiles");
+        tbed.setUp();
+        tbed.makeTestbed();
+    }
+
+    /**
+     * Cleans up the testbed by calling a target in the
+     * <code>src/etc/testcases/types/selectors.xml</code> file.
+     */
+    protected void cleanupBed() {
+        if (tbed != null) {
+            tbed.tearDown();
+            tbed = null;
+        }
+    }
+
+
+    /**
+     * <p>Creates a mirror of the testbed for use in dependency checks.</p>
+     *
+     * <p>Note that the right way to call this is within a try block,
+     * with a finally clause that calls cleanupMirror(). You place tests of
+     * the isSelected() method within the try block.</p>
+     */
+    protected void makeMirror() {
+        tbed = new TaskdefForMakingBed("mirrorfiles");
+        tbed.setUp();
+        tbed.makeMirror();
+    }
+
+    /**
+     * Cleans up the mirror testbed by calling a target in the
+     * <code>src/etc/testcases/types/selectors.xml</code> file.
+     */
+    protected void cleanupMirror() {
+        if (tbed != null) {
+            tbed.deleteMirror();
+            tbed = null;
+        }
+    }
+
+    private class TaskdefForMakingBed extends BuildFileTest {
+
+        TaskdefForMakingBed(String name) {
+            super(name);
+        }
+
+        public void setUp() {
+            configureProject("src/etc/testcases/types/selectors.xml");
+        }
+
+        public void tearDown() {
+            executeTarget("cleanup");
+        }
+
+        public void makeTestbed() {
+            executeTarget("setupfiles");
+        }
+
+        public void makeMirror() {
+            executeTarget("mirrorfiles");
+        }
+
+        public void deleteMirror() {
+            executeTarget("cleanup.mirrorfiles");
+        }
+    }
+
+
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/ContainsRegexpTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/ContainsRegexpTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/ContainsRegexpTest.java	(revision 16139)
@@ -0,0 +1,85 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildFileTest;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+import java.io.File;
+
+
+public class ContainsRegexpTest extends TestCase {
+
+    private Project project;
+
+    public ContainsRegexpTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        project = new Project();
+        project.setBasedir(".");
+    }
+
+    public void testContainsRegexp() {
+        TaskdefForRegexpTest MyTask =
+            new TaskdefForRegexpTest("containsregexp");
+        try {
+            MyTask.setUp();
+            MyTask.test();
+        } finally {
+            MyTask.tearDown();
+        }
+    }
+
+    private class TaskdefForRegexpTest extends BuildFileTest {
+        TaskdefForRegexpTest(String name) {
+            super(name);
+        }
+
+        public void setUp() {
+            configureProject("src/etc/testcases/types/selectors.xml");
+        }
+
+        public void tearDown() {
+            executeTarget("cleanupregexp");
+        }
+
+        public void test() {
+            File dir = null;
+            File[] files = null;
+            int filecount;
+
+            executeTarget("containsregexp");
+	
+            dir = new File(getProjectDir() + "/regexpseltestdest/");
+            files = dir.listFiles();
+            filecount = files.length;
+	
+            if (filecount != 1)
+                assertEquals("ContainsRegexp test should have copied 1 file",
+                             1, files.length);
+	
+        }
+    }
+}
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/ContainsSelectorTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/ContainsSelectorTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/ContainsSelectorTest.java	(revision 16139)
@@ -0,0 +1,127 @@
+/*
+ * Copyright  2002-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.*;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.types.Parameter;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+/**
+ * Tests Contains Selectors.
+ *
+ */
+public class ContainsSelectorTest extends BaseSelectorTest {
+
+    private Project project;
+
+    public ContainsSelectorTest(String name) {
+        super(name);
+    }
+
+    /**
+     * Factory method from base class. This is overriden in child
+     * classes to return a specific Selector class.
+     */
+    public BaseSelector getInstance() {
+        return new ContainsSelector();
+    }
+
+    /**
+     * Test the code that validates the selector.
+     */
+    public void testValidate() {
+        ContainsSelector s = (ContainsSelector)getInstance();
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("ContainsSelector did not check for required field 'text'");
+        } catch (BuildException be1) {
+            assertEquals("The text attribute is required", be1.getMessage());
+        }
+
+        s = (ContainsSelector)getInstance();
+        Parameter param = new Parameter();
+        param.setName("garbage in");
+        param.setValue("garbage out");
+        Parameter[] params = {param};
+        s.setParameters(params);
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("ContainsSelector did not check for valid parameter element");
+        } catch (BuildException be2) {
+            assertEquals("Invalid parameter garbage in", be2.getMessage());
+        }
+
+    }
+
+    /**
+     * Tests to make sure that the selector is selecting files correctly.
+     */
+    public void testSelectionBehaviour() {
+        ContainsSelector s;
+        String results;
+
+        try {
+            makeBed();
+
+            s = (ContainsSelector)getInstance();
+            s.setText("no such string in test files");
+            results = selectionString(s);
+            assertEquals("TFFFFFFFFFFT", results);
+
+            s = (ContainsSelector)getInstance();
+            s.setText("Apache Ant");
+            results = selectionString(s);
+            assertEquals("TFFFTFFFFFFT", results);
+
+            s = (ContainsSelector)getInstance();
+            s.setText("apache ant");
+            s.setCasesensitive(true);
+            results = selectionString(s);
+            assertEquals("TFFFFFFFFFFT", results);
+
+            s = (ContainsSelector)getInstance();
+            s.setText("apache ant");
+            s.setCasesensitive(false);
+            results = selectionString(s);
+            assertEquals("TFFFTFFFFFFT", results);
+
+            s = (ContainsSelector)getInstance();
+            s.setText("ApacheAnt");
+            s.setIgnorewhitespace(true);
+            results = selectionString(s);
+            assertEquals("TFFFTFFFFFFT", results);
+
+            s = (ContainsSelector)getInstance();
+            s.setText("A p a c h e    A n t");
+            s.setIgnorewhitespace(true);
+            results = selectionString(s);
+            assertEquals("TFFFTFFFFFFT", results);
+
+        }
+        finally {
+            cleanupBed();
+        }
+
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/DateSelectorTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/DateSelectorTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/DateSelectorTest.java	(revision 16139)
@@ -0,0 +1,244 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+import java.text.SimpleDateFormat;
+import java.text.ParsePosition;
+import java.util.Date;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+/**
+ * Tests Date Selectors.
+ *
+ */
+public class DateSelectorTest extends BaseSelectorTest {
+
+    private Project project;
+
+    public DateSelectorTest(String name) {
+        super(name);
+    }
+
+    /**
+     * Factory method from base class. This is overriden in child
+     * classes to return a specific Selector class.
+     */
+    public BaseSelector getInstance() {
+        return new DateSelector();
+    }
+
+    /**
+     * Test the code that validates the selector.
+     */
+    public void testValidate() {
+        DateSelector s = (DateSelector)getInstance();
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("DateSelector did not check for required fields");
+        } catch (BuildException be1) {
+            assertEquals("You must provide a datetime or the number of "
+                    + "milliseconds.", be1.getMessage());
+        }
+
+        s = (DateSelector)getInstance();
+        s.setDatetime("01/01/1969 01:01 AM");
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("DateSelector did not check for Datetime being in the "
+                    + "allowable range");
+        } catch (BuildException be2) {
+            assertEquals("Date of 01/01/1969 01:01 AM results in negative "
+                    + "milliseconds value relative to epoch (January 1, "
+                    + "1970, 00:00:00 GMT).", be2.getMessage());
+        }
+
+        s = (DateSelector)getInstance();
+        s.setDatetime("this is not a date");
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("DateSelector did not check for Datetime being in a "
+                    + "valid format");
+        } catch (BuildException be3) {
+            assertEquals("Date of this is not a date"
+                        + " Cannot be parsed correctly. It should be in"
+                        + " MM/DD/YYYY HH:MM AM_PM format.", be3.getMessage());
+        }
+
+        s = (DateSelector)getInstance();
+        Parameter param = new Parameter();
+        param.setName("garbage in");
+        param.setValue("garbage out");
+        Parameter[] params = new Parameter[1];
+        params[0] = param;
+        s.setParameters(params);
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("DateSelector did not check for valid parameter element");
+        } catch (BuildException be4) {
+            assertEquals("Invalid parameter garbage in", be4.getMessage());
+        }
+
+        s = (DateSelector)getInstance();
+        param = new Parameter();
+        param.setName("millis");
+        param.setValue("garbage out");
+        params[0] = param;
+        s.setParameters(params);
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("DateSelector did not check for valid millis parameter");
+        } catch (BuildException be5) {
+            assertEquals("Invalid millisecond setting garbage out",
+                    be5.getMessage());
+        }
+
+        s = (DateSelector)getInstance();
+        param = new Parameter();
+        param.setName("granularity");
+        param.setValue("garbage out");
+        params[0] = param;
+        s.setParameters(params);
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("DateSelector did not check for valid granularity parameter");
+        } catch (BuildException be6) {
+            assertEquals("Invalid granularity setting garbage out",
+                    be6.getMessage());
+        }
+
+    }
+
+    /**
+     * Tests to make sure that the selector is selecting files correctly.
+     */
+    public void testSelectionBehaviour() {
+        DateSelector s;
+        String results;
+
+        DateSelector.TimeComparisons before = new
+                DateSelector.TimeComparisons();
+        before.setValue("before");
+        DateSelector.TimeComparisons equal = new
+                DateSelector.TimeComparisons();
+        equal.setValue("equal");
+        DateSelector.TimeComparisons after = new
+                DateSelector.TimeComparisons();
+        after.setValue("after");
+
+        try {
+            makeBed();
+
+            s = (DateSelector)getInstance();
+            s.setDatetime("10/10/1999 1:45 PM");
+            s.setWhen(before);
+            results = selectionString(s);
+            assertEquals("TFFFFFFFFFFT", results);
+
+            s = (DateSelector)getInstance();
+            s.setDatetime("10/10/1999 1:45 PM");
+            s.setWhen(before);
+            s.setCheckdirs(true);
+            results = selectionString(s);
+            assertEquals("FFFFFFFFFFFF", results);
+
+            s = (DateSelector)getInstance();
+            s.setDatetime("10/10/1999 1:45 PM");
+            s.setWhen(after);
+            results = selectionString(s);
+            assertEquals("TTTTTTTTTTTT", results);
+
+            if (!JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
+                s = (DateSelector)getInstance();
+                s.setDatetime("11/21/2001 4:54 AM");
+                s.setWhen(before);
+                results = selectionString(s);
+                assertEquals("TFTFFFFFFFFT", results);
+
+                s = (DateSelector)getInstance();
+                s.setDatetime("11/21/2001 4:55 AM");
+                SimpleDateFormat formatter = new SimpleDateFormat();
+                Date d = formatter.parse("11/21/2001 4:55 AM",new ParsePosition(0));
+
+                long milliseconds = s.getMillis();
+                s.setWhen(equal);
+                results = selectionString(s);
+                assertEquals("TTFFTFFFTTTT", results);
+
+                s = (DateSelector)getInstance();
+                s.setMillis(milliseconds);
+                s.setWhen(equal);
+                results = selectionString(s);
+                assertEquals("TTFFTFFFTTTT", results);
+
+                s = (DateSelector)getInstance();
+                s.setDatetime("11/21/2001 4:56 AM");
+                s.setWhen(after);
+                results = selectionString(s);
+                assertEquals("TFFTFTTTFFFT", results);
+
+                s = (DateSelector)getInstance();
+                Parameter param1 = new Parameter();
+                Parameter param2 = new Parameter();
+                param1.setName("datetime");
+                param1.setValue("11/21/2001 4:56 AM");
+                param2.setName("when");
+                param2.setValue("after");
+                Parameter[] params = {param1,param2};
+                s.setParameters(params);
+                results = selectionString(s);
+                assertEquals("TFFTFTTTFFFT", results);
+            }
+            try {
+                makeMirror();
+
+                s = (DateSelector)getInstance();
+                long testtime = mirrorfiles[5].lastModified();
+                s.setMillis(testtime);
+                s.setWhen(after);
+                s.setGranularity(2);
+                results = mirrorSelectionString(s);
+                assertEquals("TFFFFTTTTTTT", results);
+
+                s = (DateSelector)getInstance();
+                testtime = mirrorfiles[6].lastModified();
+                s.setMillis(testtime);
+                s.setWhen(before);
+                s.setGranularity(2);
+                results = mirrorSelectionString(s);
+                assertEquals("TTTTTTTFFFFT", results);
+            }
+            finally {
+                cleanupMirror();
+            }
+
+        }
+        finally {
+            cleanupBed();
+        }
+
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/DependSelectorTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/DependSelectorTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/DependSelectorTest.java	(revision 16139)
@@ -0,0 +1,180 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.IdentityMapper;
+import org.apache.tools.ant.util.GlobPatternMapper;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+import java.io.File;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+/**
+ * Tests Depend Selectors
+ *
+ */
+public class DependSelectorTest extends BaseSelectorTest {
+
+    private Project project;
+
+    public DependSelectorTest(String name) {
+        super(name);
+    }
+
+    /**
+     * Factory method from base class. This is overriden in child
+     * classes to return a specific Selector class.
+     */
+    public BaseSelector getInstance() {
+        return new DependSelector();
+    }
+
+    /**
+     * Test the code that validates the selector.
+     */
+    public void testValidate() {
+        DependSelector s = (DependSelector)getInstance();
+        try {
+            s.createMapper();
+            s.createMapper();
+            fail("DependSelector allowed more than one nested mapper.");
+        } catch (BuildException be1) {
+            assertEquals("Cannot define more than one mapper",
+                    be1.getMessage());
+        }
+
+        s = (DependSelector)getInstance();
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("DependSelector did not check for required fields");
+        } catch (BuildException be2) {
+            assertEquals("The targetdir attribute is required.",
+                    be2.getMessage());
+        }
+
+    }
+
+    /**
+     * Tests to make sure that the selector is selecting files correctly.
+     */
+    public void testSelectionBehaviour() {
+        DependSelector s;
+        String results;
+        File subdir;
+        Mapper m;
+        Mapper.MapperType identity = new Mapper.MapperType();
+        identity.setValue("identity");
+        Mapper.MapperType glob = new Mapper.MapperType();
+        glob.setValue("glob");
+        Mapper.MapperType merge = new Mapper.MapperType();
+        merge.setValue("merge");
+
+        try {
+            makeBed();
+
+            s = (DependSelector)getInstance();
+            s.setTargetdir(beddir);
+            results = selectionString(s);
+            assertEquals("FFFFFFFFFFFF", results);
+
+            s = (DependSelector)getInstance();
+            s.setTargetdir(beddir);
+            m = s.createMapper();
+            m.setType(identity);
+            results = selectionString(s);
+            assertEquals("FFFFFFFFFFFF", results);
+
+            if (!JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
+                s = (DependSelector)getInstance();
+                s.setTargetdir(beddir);
+                m = s.createMapper();
+                m.setType(merge);
+                m.setTo("asf-logo.gif.gz");
+                results = selectionString(s);
+                assertEquals("TFFFFTTTFFF", results.substring(0,11));
+
+                s = (DependSelector)getInstance();
+                s.setTargetdir(beddir);
+                m = s.createMapper();
+                m.setType(merge);
+                m.setTo("asf-logo.gif.bz2");
+                results = selectionString(s);
+                assertEquals("TTFTTTTTTTTT", results);
+
+                // Test for path relative to project base directory
+                s = (DependSelector)getInstance();
+                subdir = new File("selectortest/tar/bz2");
+                s.setTargetdir(subdir);
+                m = s.createMapper();
+                m.setType(glob);
+                m.setFrom("*.bz2");
+                m.setTo("*.tar.bz2");
+                results = selectionString(s);
+                assertEquals("FFTFFFFFFTTF", results);
+            }
+
+            s = (DependSelector)getInstance();
+            subdir = new File(beddir,"tar/bz2");
+            s.setTargetdir(subdir);
+            m = s.createMapper();
+            m.setType(glob);
+            m.setFrom("*.bz2");
+            m.setTo("*.tar.bz2");
+            results = selectionString(s);
+            assertEquals("FFFFFFFFFTTF", results);
+
+            try {
+                makeMirror();
+
+                s = (DependSelector)getInstance();
+                File testdir = getProject().resolveFile("selectortest2");
+                s.setTargetdir(testdir);
+                results = selectionString(s);
+                assertEquals("FFFTTFFFFFFF", results);
+
+                s = (DependSelector)getInstance();
+                testdir = getProject().resolveFile("selectortest2/tar/bz2");
+                s.setTargetdir(testdir);
+                m = s.createMapper();
+                m.setType(glob);
+                m.setFrom("*.bz2");
+                m.setTo("*.tar.bz2");
+                results = mirrorSelectionString(s);
+                assertEquals("FFFFFFFFFTTF", results);
+                results = selectionString(s);
+                assertEquals("FFFFFFFFFTTF", results);
+            }
+            finally {
+                cleanupMirror();
+            }
+
+        }
+        finally {
+            cleanupBed();
+        }
+
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/DepthSelectorTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/DepthSelectorTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/DepthSelectorTest.java	(revision 16139)
@@ -0,0 +1,162 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.*;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.types.Parameter;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+/**
+ * Tests Depth Selectors
+ *
+ */
+public class DepthSelectorTest extends BaseSelectorTest {
+
+    private Project project;
+
+    public DepthSelectorTest(String name) {
+        super(name);
+    }
+
+    /**
+     * Factory method from base class. This is overriden in child
+     * classes to return a specific Selector class.
+     */
+    public BaseSelector getInstance() {
+        return new DepthSelector();
+    }
+
+    /**
+     * Test the code that validates the selector.
+     */
+    public void testValidate() {
+        DepthSelector s = (DepthSelector)getInstance();
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("DepthSelector did not check for required fields");
+        } catch (BuildException be1) {
+            assertEquals("You must set at least one of the min or the " +
+                    "max levels.", be1.getMessage());
+        }
+
+        s = (DepthSelector)getInstance();
+        s.setMin(5);
+        s.setMax(2);
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("DepthSelector did not check for maximum being higher "
+                    + "than minimum");
+        } catch (BuildException be2) {
+            assertEquals("The maximum depth is lower than the minimum.",
+                    be2.getMessage());
+        }
+
+        s = (DepthSelector)getInstance();
+        Parameter param = new Parameter();
+        param.setName("garbage in");
+        param.setValue("garbage out");
+        Parameter[] params = new Parameter[1];
+        params[0] = param;
+        s.setParameters(params);
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("DepthSelector did not check for valid parameter element");
+        } catch (BuildException be3) {
+            assertEquals("Invalid parameter garbage in", be3.getMessage());
+        }
+
+        s = (DepthSelector)getInstance();
+        param = new Parameter();
+        param.setName("min");
+        param.setValue("garbage out");
+        params[0] = param;
+        s.setParameters(params);
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("DepthSelector accepted bad minimum as parameter");
+        } catch (BuildException be4) {
+            assertEquals("Invalid minimum value garbage out",
+                    be4.getMessage());
+        }
+
+        s = (DepthSelector)getInstance();
+        param = new Parameter();
+        param.setName("max");
+        param.setValue("garbage out");
+        params[0] = param;
+        s.setParameters(params);
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("DepthSelector accepted bad maximum as parameter");
+        } catch (BuildException be5) {
+            assertEquals("Invalid maximum value garbage out",
+                    be5.getMessage());
+        }
+
+    }
+
+    /**
+     * Tests to make sure that the selector is selecting files correctly.
+     */
+    public void testSelectionBehaviour() {
+        DepthSelector s;
+        String results;
+
+        try {
+            makeBed();
+
+            s = (DepthSelector)getInstance();
+            s.setMin(20);
+            s.setMax(25);
+            results = selectionString(s);
+            assertEquals("FFFFFFFFFFFF", results);
+
+            s = (DepthSelector)getInstance();
+            s.setMin(0);
+            results = selectionString(s);
+            assertEquals("TTTTTTTTTTTT", results);
+
+            s = (DepthSelector)getInstance();
+            s.setMin(1);
+            results = selectionString(s);
+            assertEquals("FFFFFTTTTTTT", results);
+
+            s = (DepthSelector)getInstance();
+            s.setMax(0);
+            results = selectionString(s);
+            assertEquals("TTTTTFFFFFFF", results);
+
+            s = (DepthSelector)getInstance();
+            s.setMin(1);
+            s.setMax(1);
+            results = selectionString(s);
+            assertEquals("FFFFFTTTFFFT", results);
+
+        }
+        finally {
+            cleanupBed();
+        }
+
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/FilenameSelectorTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/FilenameSelectorTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/FilenameSelectorTest.java	(revision 16139)
@@ -0,0 +1,129 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.*;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.types.Parameter;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+/**
+ * Tests Filename Selectors
+ *
+ */
+public class FilenameSelectorTest extends BaseSelectorTest {
+
+    private Project project;
+
+    public FilenameSelectorTest(String name) {
+        super(name);
+    }
+
+    /**
+     * Factory method from base class. This is overriden in child
+     * classes to return a specific Selector class.
+     */
+    public BaseSelector getInstance() {
+        return new FilenameSelector();
+    }
+
+    /**
+     * Test the code that validates the selector.
+     */
+    public void testValidate() {
+        FilenameSelector s = (FilenameSelector)getInstance();
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("FilenameSelector did not check for required fields");
+        } catch (BuildException be1) {
+            assertEquals("The name attribute is required", be1.getMessage());
+        }
+
+        s = (FilenameSelector)getInstance();
+        Parameter param = new Parameter();
+        param.setName("garbage in");
+        param.setValue("garbage out");
+        Parameter[] params = {param};
+        s.setParameters(params);
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("FilenameSelector did not check for valid parameter element");
+        } catch (BuildException be2) {
+            assertEquals("Invalid parameter garbage in", be2.getMessage());
+        }
+
+    }
+
+    /**
+     * Tests to make sure that the selector is selecting files correctly.
+     */
+    public void testSelectionBehaviour() {
+        FilenameSelector s;
+        String results;
+
+        try {
+            makeBed();
+
+            s = (FilenameSelector)getInstance();
+            s.setName("no match possible");
+            results = selectionString(s);
+            assertEquals("FFFFFFFFFFFF", results);
+
+            s = (FilenameSelector)getInstance();
+            s.setName("*.gz");
+            results = selectionString(s);
+            // This is turned off temporarily. There appears to be a bug
+            // in SelectorUtils.matchPattern() where it is recursive on
+            // Windows even if no ** is in pattern.
+            //assertEquals("FFFTFFFFFFFF", results); // Unix
+            // vs
+            //assertEquals("FFFTFFFFTFFF", results); // Windows
+
+            s = (FilenameSelector)getInstance();
+            s.setName("**/*.gz");
+            s.setNegate(true);
+            results = selectionString(s);
+            assertEquals("TTTFTTTFFTTT", results);
+
+            s = (FilenameSelector)getInstance();
+            s.setName("**/*.GZ");
+            s.setCasesensitive(false);
+            results = selectionString(s);
+            assertEquals("FFFTFFFTTFFF", results);
+
+            s = (FilenameSelector)getInstance();
+            Parameter param1 = new Parameter();
+            param1.setName("name");
+            param1.setValue("**/*.bz2");
+            Parameter[] params = {param1};
+            s.setParameters(params);
+            results = selectionString(s);
+            assertEquals("FFTFFFFFFTTF", results);
+
+        }
+        finally {
+            cleanupBed();
+        }
+
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/ModifiedSelectorTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/ModifiedSelectorTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/ModifiedSelectorTest.java	(revision 16139)
@@ -0,0 +1,670 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+
+// Java
+import java.io.File;
+import java.io.FileWriter;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.text.RuleBasedCollator;
+
+// Ant
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.Parameter;
+
+// The classes to test
+import org.apache.tools.ant.types.selectors.modifiedselector.*;
+
+
+/**
+ * Unit tests for ModifiedSelector.
+ *
+ * @version 2003-09-13
+ * @since  Ant 1.6
+ */
+public class ModifiedSelectorTest extends BaseSelectorTest {
+
+    /** Package of the CacheSelector classes. */
+    private static String pkg = "org.apache.tools.ant.types.selectors.modifiedselector";
+
+
+    public ModifiedSelectorTest(String name) {
+        super(name);
+    }
+
+
+    /**
+     * Factory method from base class. This should be overriden in child
+     * classes to return a specific Selector class (like here).
+     */
+    public BaseSelector getInstance() {
+        return new ModifiedSelector();
+    }
+
+
+    /** Test right use of cache names. */
+    public void testValidateWrongCache() {
+        String name = "this-is-not-a-valid-cache-name";
+        try {
+            ModifiedSelector.CacheName cacheName = new ModifiedSelector.CacheName();
+            cacheName.setValue(name);
+            fail("CacheSelector.CacheName accepted invalid value.");
+        } catch (BuildException be) {
+            assertEquals(name + " is not a legal value for this attribute",
+                         be.getMessage());
+        }
+    }
+
+
+    /** Test right use of cache names. */
+    public void testValidateWrongAlgorithm() {
+        String name = "this-is-not-a-valid-algorithm-name";
+        try {
+            ModifiedSelector.AlgorithmName algoName
+                = new ModifiedSelector.AlgorithmName();
+            algoName.setValue(name);
+            fail("CacheSelector.AlgorithmName accepted invalid value.");
+        } catch (BuildException be) {
+            assertEquals(name + " is not a legal value for this attribute",
+                         be.getMessage());
+        }
+    }
+
+
+    /** Test right use of comparator names. */
+    public void testValidateWrongComparator() {
+        String name = "this-is-not-a-valid-comparator-name";
+        try {
+            ModifiedSelector.ComparatorName compName
+                = new ModifiedSelector.ComparatorName();
+            compName.setValue(name);
+            fail("ModifiedSelector.ComparatorName accepted invalid value.");
+        } catch (BuildException be) {
+            assertEquals(name + " is not a legal value for this attribute",
+                         be.getMessage());
+        }
+    }
+
+
+    /**
+     * Propertycache must have a set 'cachefile' attribute.
+     * The default in ModifiedSelector "cache.properties" is set by the selector.
+     */
+    public void testPropcacheInvalid() {
+        Cache cache = new PropertiesfileCache();
+        if (cache.isValid())
+            fail("PropertyfilesCache does not check its configuration.");
+    }
+
+
+    /**
+     * Tests whether the seldirs attribute is used.
+     */
+    public void testSeldirs() {
+        ModifiedSelector s = (ModifiedSelector)getSelector();
+        try {
+            makeBed();
+
+            StringBuffer sbTrue  = new StringBuffer();
+            StringBuffer sbFalse = new StringBuffer();
+            for (int i=0; i<filenames.length; i++) {
+                if (files[i].isDirectory()) {
+                    sbTrue.append("T");
+                    sbFalse.append("F");
+                } else {
+                    sbTrue.append("T");
+                    sbFalse.append("T");
+                }
+            }
+
+
+            s.setSeldirs(true);
+            performTests(s, sbTrue.toString());
+            s.getCache().delete();
+
+            s.setSeldirs(false);
+            performTests(s, sbFalse.toString());
+            s.getCache().delete();
+
+        } finally {
+            cleanupBed();
+            if (s!=null) s.getCache().delete();
+        }
+    }
+
+
+    /**
+     * Complex test scenario using default values (DigestAlgorithm with MD5,
+     * PropertiesfileCache with file=cache.properties, EqualComparator
+     * and update=true). <ol>
+     * <li> try fist time --> should select all </li>
+     * <li> try second time --> should select no files (only directories) </li>
+     * <li> modify timestamp of one file and content of a nother one </li>
+     * <li> try third time --> should select only the file with modified
+     *      content </li>
+     */
+    public void testScenario1() {
+        BFT bft = null;
+        ModifiedSelector s = null;
+        try {
+            //
+            // *****  initialize test environment (called "bed")  *****
+            //
+            makeBed();
+            String results = null;
+
+            // Configure the selector - only defaults are used
+            s = (ModifiedSelector)getSelector();
+
+            //
+            // *****  First Run  *****
+            // the first call should get all files, because nothing is in
+            // the cache
+            //
+            performTests(s, "TTTTTTTTTTTT");
+
+            //
+            // *****  Second Run  *****
+            // the second call should get no files, because no content
+            // has changed
+            //
+            performTests(s, "TFFFFFFFFFFT");
+
+            //
+            // *****  make some files dirty  *****
+            //
+
+            // these files are made dirty --> 3+4 with different content
+            String f2name = "tar/bz2/asf-logo-huge.tar.bz2";
+            String f3name = "asf-logo.gif.md5";
+            String f4name = "copy.filterset.filtered";
+
+            // AccessObject to the test-Ant-environment
+            bft = new BFT();
+            // give some values (via property file) to that environment
+            bft.writeProperties("f2name="+f2name);
+            bft.writeProperties("f3name="+f3name);
+            bft.writeProperties("f4name="+f4name);
+            // call the target for making the files dirty
+            bft.doTarget("modifiedselectortest-makeDirty");
+
+
+            //
+            // *****  Third Run  *****
+            // third call should get only those files, which CONTENT changed
+            // (no timestamp changes required!)
+            results = selectionString(s);
+
+            //
+            // *****  Check the result  *****
+            //
+
+            // Mark all files which should be selected as (T)rue and all others
+            // as (F)alse. Directories are always selected so they always are
+            // (T)rue.
+            StringBuffer expected = new StringBuffer();
+            for (int i=0; i<filenames.length; i++) {
+                String ch = "F";
+                if (files[i].isDirectory()) ch = "T";
+                // f2name shouldn't be selected: only timestamp has changed!
+                if (filenames[i].equalsIgnoreCase(f3name)) ch = "T";
+                if (filenames[i].equalsIgnoreCase(f4name)) ch = "T";
+                expected.append(ch);
+            }
+
+            assertEquals(
+                "Wrong files selected. Differing files: "       // info text
+                + resolve(diff(expected.toString(), results)),  // list of files
+                expected.toString(),                            // expected result
+                results                                         // result
+            );
+
+        } finally {
+            // cleanup the environment
+            cleanupBed();
+            if (s!=null) s.getCache().delete();
+            if (bft!=null) bft.deletePropertiesfile();
+        }
+    }
+
+
+
+    /**
+     * This scenario is based on scenario 1, but does not use any
+     * default value and its based on <custom> selector. Used values are:<ul>
+     * <li><b>Cache: </b> Propertyfile,
+     *                    cachefile={java.io.tmpdir}/mycache.txt </li>
+     * <li><b>Algorithm: </b> Digest
+     *                    algorithm=SHA, Provider=null </li>
+     * <li><b>Comparator: </b> java.text.RuleBasedCollator
+     * <li><b>Update: </b> true </li>
+     */
+    public void testScenario2() {
+        ExtendSelector s = new ExtendSelector();
+        BFT bft = new BFT();
+        String cachefile = System.getProperty("java.io.tmpdir")+"/mycache.txt";
+        try {
+            makeBed();
+
+            s.setClassname("org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector");
+
+            s.addParam(createParam("cache.cachefile", cachefile));
+            //s.addParam(createParam("algorithm.provider","---")); // i don't know any valid
+            s.addParam(createParam("cache","propertyfile"));
+            s.addParam(createParam("update","true"));
+            s.addParam(createParam("comparator","rule"));
+            s.addParam(createParam("algorithm.name","sha"));
+            s.addParam(createParam("algorithm","digest"));
+
+            // first and second run
+            performTests(s, "TTTTTTTTTTTT");
+            performTests(s, "TFFFFFFFFFFT");
+            // make dirty
+            String f2name = "tar/bz2/asf-logo-huge.tar.bz2";
+            String f3name = "asf-logo.gif.md5";
+            String f4name = "copy.filterset.filtered";
+            bft.writeProperties("f2name="+f2name);
+            bft.writeProperties("f3name="+f3name);
+            bft.writeProperties("f4name="+f4name);
+            bft.doTarget("modifiedselectortest-makeDirty");
+            // third run
+            String results = selectionString(s);
+            StringBuffer expected = new StringBuffer();
+            for (int i=0; i<filenames.length; i++) {
+                String ch = "F";
+                if (files[i].isDirectory()) ch = "T";
+                if (filenames[i].equalsIgnoreCase(f3name)) ch = "T";
+                if (filenames[i].equalsIgnoreCase(f4name)) ch = "T";
+                expected.append(ch);
+            }
+            assertEquals(
+                "Wrong files selected. Differing files: "       // info text
+                + resolve(diff(expected.toString(), results)),  // list of files
+                expected.toString(),                            // expected result
+                results                                         // result
+            );
+        } finally {
+            // cleanup the environment
+            cleanupBed();
+            (new java.io.File(cachefile)).delete();
+            if (bft!=null) bft.deletePropertiesfile();
+        }
+    }
+
+
+    /** Checks whether a cache file is created. */
+    public void testCreatePropertiesCacheDirect() {
+        File basedir   = getSelector().getProject().getBaseDir();
+        File cachefile = new File(basedir, "cachefile.properties");
+
+        PropertiesfileCache cache = new PropertiesfileCache();
+        cache.setCachefile(cachefile);
+
+        cache.put("key", "value");
+        cache.save();
+
+        assertTrue("Cachefile not created.", cachefile.exists());
+
+        cache.delete();
+        assertFalse("Cachefile not deleted.", cachefile.exists());
+    }
+
+
+    /** Checks whether a cache file is created. */
+    public void testCreatePropertiesCacheViaModifiedSelector() {
+        File basedir   = getSelector().getProject().getBaseDir();
+        File cachefile = new File(basedir, "cachefile.properties");
+        try {
+
+            // initialize test environment (called "bed")
+            makeBed();
+
+            // Configure the selector
+            ModifiedSelector s = (ModifiedSelector)getSelector();
+            s.addParam("cache.cachefile", cachefile);
+
+            ModifiedSelector.CacheName cacheName = new ModifiedSelector.CacheName();
+            cacheName.setValue("propertyfile");
+            s.setCache(cacheName);
+
+            s.setUpdate(true);
+
+            // does the selection
+            String results = selectionString(s);
+
+            // evaluate correctness
+            assertTrue("Cache file is not created.", cachefile.exists());
+        } finally {
+            cleanupBed();
+            if (cachefile!=null) cachefile.delete();
+        }
+    }
+
+
+    /**
+     * In earlier implementations there were problems with the <i>order</i>
+     * of the <param>s. The scenario was <pre>
+     *   <custom class="ModifiedSelector">
+     *       <param name="cache.cachefile" value="mycache.properties" />
+     *       <param name="cache" value="propertyfiles" />
+     *   </custom>
+     * </pre> It was important first to set the cache and then to set
+     * the cache's configuration parameters. That results in the reorganized
+     * configure() method of ModifiedSelector. This testcase tests that.
+     */
+    public void testCreatePropertiesCacheViaCustomSelector() {
+        File cachefile = org.apache.tools.ant.util.FileUtils.newFileUtils()
+                         .createTempFile("tmp-cache-", ".properties", null);
+        try {
+            // initialize test environment (called "bed")
+            makeBed();
+
+            // Configure the selector
+
+            ExtendSelector s = new ExtendSelector();
+            s.setClassname("org.apache.tools.ant.types.selectors.modifiedselector.ModifiedSelector");
+            s.addParam(createParam("update", "true"));
+            s.addParam(createParam("cache.cachefile", cachefile.getAbsolutePath()));
+            s.addParam(createParam("cache", "propertyfile"));
+
+            // does the selection
+            String results = selectionString(s);
+
+            // evaluate correctness
+            assertTrue("Cache file is not created.", cachefile.exists());
+        } finally {
+            cleanupBed();
+            if (cachefile!=null) cachefile.delete();
+        }
+    }
+
+
+    public void testEqualComparatorViaSelector() {
+        ModifiedSelector s = (ModifiedSelector)getSelector();
+        ModifiedSelector.ComparatorName compName = new ModifiedSelector.ComparatorName();
+        compName.setValue("equal");
+        s.setComparator(compName);
+        try {
+            performTests(s, "TTTTTTTTTTTT");
+        } finally {
+            s.getCache().delete();
+        }
+    }
+
+
+    public void testRuleComparatorViaSelector() {
+        ModifiedSelector s = (ModifiedSelector)getSelector();
+        ModifiedSelector.ComparatorName compName = new ModifiedSelector.ComparatorName();
+        compName.setValue("rule");
+        s.setComparator(compName);
+        try {
+            performTests(s, "TTTTTTTTTTTT");
+        } finally {
+            s.getCache().delete();
+        }
+    }
+
+
+    public void testHashvalueAlgorithm() {
+        HashvalueAlgorithm algo = new HashvalueAlgorithm();
+        doTest(algo);
+    }
+
+    public void testDigestAlgorithmMD5() {
+        DigestAlgorithm algo = new DigestAlgorithm();
+        algo.setAlgorithm("MD5");
+        doTest(algo);
+    }
+
+    public void testDigestAlgorithmSHA() {
+        DigestAlgorithm algo = new DigestAlgorithm();
+        algo.setAlgorithm("SHA");
+        doTest(algo);
+    }
+
+
+    public void testPropertyfileCache() {
+        PropertiesfileCache cache = new PropertiesfileCache();
+        File cachefile = new File("cache.properties");
+        cache.setCachefile(cachefile);
+        doTest(cache);
+        assertFalse("Cache file not deleted.", cachefile.exists());
+    }
+
+
+    public void testEqualComparator() {
+        EqualComparator comp = new EqualComparator();
+        doTest(comp);
+    }
+
+
+    public void testRuleComparator() {
+        RuleBasedCollator comp = (RuleBasedCollator)RuleBasedCollator.getInstance();
+        doTest(comp);
+    }
+
+
+    public void testScenarioCoreSelectorDefaults() {
+        doScenarioTest("modifiedselectortest-scenario-coreselector-defaults", "cache.properties");
+    }
+
+
+
+    public void testSceanrioCoreSelectorSettings() {
+        doScenarioTest("modifiedselectortest-scenario-coreselector-settings", "core.cache.properties");
+    }
+
+
+    public void testScenarioCustomSelectorSettings() {
+        doScenarioTest("modifiedselectortest-scenario-customselector-settings", "core.cache.properties");
+    }
+
+
+    public void doScenarioTest(String target, String cachefilename) {
+        BFT bft = new BFT();
+        bft.setUp();
+        File basedir = bft.getProject().getBaseDir();
+        File cachefile = new File(basedir, cachefilename);
+        try {
+            // do the actions
+            bft.doTarget("modifiedselectortest-scenario-clean");
+            bft.doTarget(target);
+
+            // the directories to check
+            File to1 = new File(basedir, "selectortest/to-1");
+            File to2 = new File(basedir, "selectortest/to-2");
+            File to3 = new File(basedir, "selectortest/to-3");
+
+            // do the checks
+            assertTrue("Cache file not created.", cachefile.exists());
+            assertTrue("Not enough files copied on first time.", to1.list().length>5);
+            assertTrue("Too much files copied on second time.", to2.list().length==0);
+            assertTrue("Too much files copied on third time.", to3.list().length==2);
+        // don't catch the JUnit exceptions
+        } finally {
+            bft.doTarget("modifiedselectortest-scenario-clean");
+            bft.deletePropertiesfile();
+            bft.tearDown();
+            cachefile.delete();
+        }
+    }
+
+
+    //  ====================  Test interface semantic  ===================
+
+
+    /**
+     * This method does some common test for algorithm implementations.
+     * An algorithm must return always the same value for the same file and
+     * it must not return <i>null</i>.
+     *
+     * @param algo   configured test object
+     */
+    protected void doTest(Algorithm algo) {
+        assertTrue("Algorithm not proper configured.", algo.isValid());
+        try {
+            makeBed();
+
+            for (int i=0; i<files.length; i++) {
+                File file = files[i];  // must not be a directory
+                if (file.isFile()) {
+                    // get the Hashvalues
+                    String hash1 = algo.getValue(file);
+                    String hash2 = algo.getValue(file);
+                    String hash3 = algo.getValue(file);
+                    String hash4 = algo.getValue(file);
+                    String hash5 = algo.getValue(new File(file.getAbsolutePath()));
+
+                    // Assert !=null and equality
+                    assertNotNull("Hashvalue was null for "+file.getAbsolutePath(), hash1);
+                    assertNotNull("Hashvalue was null for "+file.getAbsolutePath(), hash2);
+                    assertNotNull("Hashvalue was null for "+file.getAbsolutePath(), hash3);
+                    assertNotNull("Hashvalue was null for "+file.getAbsolutePath(), hash4);
+                    assertNotNull("Hashvalue was null for "+file.getAbsolutePath(), hash5);
+                    assertEquals("getHashvalue() returned different value for "+file.getAbsolutePath(), hash1, hash2);
+                    assertEquals("getHashvalue() returned different value for "+file.getAbsolutePath(), hash1, hash3);
+                    assertEquals("getHashvalue() returned different value for "+file.getAbsolutePath(), hash1, hash4);
+                    assertEquals("getHashvalue() returned different value for "+file.getAbsolutePath(), hash1, hash5);
+                }//if-isFile
+            }//for
+        } finally {
+            cleanupBed();
+        }
+    }
+
+
+    /**
+     * This method does some common test for cache implementations.
+     * A cache must return a stored value and a valid iterator.
+     * After calling the delete() the cache must be empty.
+     *
+     * @param algo   configured test object
+     */
+    protected void doTest(Cache cache) {
+        assertTrue("Cache not proper configured.", cache.isValid());
+
+        String key1   = "key1";
+        String value1 = "value1";
+        String key2   = "key2";
+        String value2 = "value2";
+
+        // given cache must be empty
+        Iterator it1 = cache.iterator();
+        assertFalse("Cache is not empty", it1.hasNext());
+
+        // cache must return a stored value
+        cache.put(key1, value1);
+        cache.put(key2, value2);
+        assertEquals("cache returned wrong value", value1, cache.get(key1));
+        assertEquals("cache returned wrong value", value2, cache.get(key2));
+
+        // test the iterator
+        Iterator it2 = cache.iterator();
+        Object   returned = it2.next();
+        boolean ok = (key1.equals(returned) || key2.equals(returned));
+        String msg = "Iterator returned unexpected value."
+                   + "  key1.equals(returned)="+key1.equals(returned)
+                   + "  key2.equals(returned)="+key2.equals(returned)
+                   + "  returned="+returned
+                   + "  ok="+ok;
+        assertTrue(msg, ok);
+
+        // clear the cache
+        cache.delete();
+        Iterator it3 = cache.iterator();
+        assertFalse("Cache is not empty", it1.hasNext());
+    }
+
+
+    /**
+     * This method does some common test for comparator implementations.
+     *
+     * @param algo   configured test object
+     */
+    protected void doTest(Comparator comp) {
+        Object o1 = new String("string1");
+        Object o2 = new String("string2");
+        Object o3 = new String("string2"); // really "2"
+
+        assertTrue("Comparator gave wrong value.", comp.compare(o1, o2) != 0);
+        assertTrue("Comparator gave wrong value.", comp.compare(o1, o3) != 0);
+        assertTrue("Comparator gave wrong value.", comp.compare(o2, o3) == 0);
+    }
+
+
+    //  ========================  Helper methods  ========================
+
+
+    private Parameter createParam(String name, String value) {
+        Parameter p = new Parameter();
+        p.setName(name);
+        p.setValue(value);
+        return p;
+    }
+
+
+    private class BFT extends org.apache.tools.ant.BuildFileTest {
+        BFT() { super("nothing"); }
+        BFT(String name) {
+            super(name);
+        }
+        String propfile = "ModifiedSelectorTest.properties";
+
+        boolean isConfigured = false;
+
+        public void setUp() {
+            configureProject("src/etc/testcases/types/selectors.xml");
+            isConfigured = true;
+        }
+
+        public void tearDown() { }
+
+        public void doTarget(String target) {
+            if (!isConfigured) setUp();
+            executeTarget(target);
+        }
+
+        public void writeProperties(String line) {
+            if (!isConfigured) setUp();
+            File dir = getProject().getBaseDir();
+            File file = new File(dir, propfile);
+            try {
+                java.io.FileWriter out =
+                    new java.io.FileWriter(file.getAbsolutePath(), true);
+                out.write(line);
+                out.write(System.getProperty("line.separator"));
+                out.flush();
+                out.close();
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        }
+
+        public void deletePropertiesfile() {
+            if (!isConfigured) setUp();
+            new File(getProject().getBaseDir(), propfile).delete();
+        }
+
+        public org.apache.tools.ant.Project getProject() {
+            return super.getProject();
+        }
+    }//class-BFT
+
+}//class-ModifiedSelectorTest
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/PresentSelectorTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/PresentSelectorTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/PresentSelectorTest.java	(revision 16139)
@@ -0,0 +1,164 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.*;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.IdentityMapper;
+import org.apache.tools.ant.util.GlobPatternMapper;
+
+import java.io.File;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+/**
+ * Tests Present Selectors
+ *
+ */
+public class PresentSelectorTest extends BaseSelectorTest {
+
+    private Project project;
+
+    public PresentSelectorTest(String name) {
+        super(name);
+    }
+
+    /**
+     * Factory method from base class. This is overriden in child
+     * classes to return a specific Selector class.
+     */
+    public BaseSelector getInstance() {
+        return new PresentSelector();
+    }
+
+    /**
+     * Test the code that validates the selector.
+     */
+    public void testValidate() {
+        PresentSelector s = (PresentSelector)getInstance();
+        try {
+            s.createMapper();
+            s.createMapper();
+            fail("PresentSelector allowed more than one nested mapper.");
+        } catch (BuildException be1) {
+            assertEquals("Cannot define more than one mapper",
+                    be1.getMessage());
+        }
+
+        s = (PresentSelector)getInstance();
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("PresentSelector did not check for required fields");
+        } catch (BuildException be2) {
+            assertEquals("The targetdir attribute is required.",
+                    be2.getMessage());
+        }
+
+    }
+
+    /**
+     * Tests to make sure that the selector is selecting files correctly.
+     */
+    public void testSelectionBehaviour() {
+        PresentSelector s;
+        String results;
+        Mapper m;
+        Mapper.MapperType identity = new Mapper.MapperType();
+        identity.setValue("identity");
+        Mapper.MapperType glob = new Mapper.MapperType();
+        glob.setValue("glob");
+        Mapper.MapperType merge = new Mapper.MapperType();
+        merge.setValue("merge");
+        Mapper.MapperType flatten = new Mapper.MapperType();
+        flatten.setValue("flatten");
+
+        try {
+            makeBed();
+
+            s = (PresentSelector)getInstance();
+            s.setTargetdir(beddir);
+            results = selectionString(s);
+            assertEquals("TTTTTTTTTTTT", results);
+
+            s = (PresentSelector)getInstance();
+            s.setTargetdir(beddir);
+            m = s.createMapper();
+            m.setType(identity);
+            results = selectionString(s);
+            assertEquals("TTTTTTTTTTTT", results);
+
+            s = (PresentSelector)getInstance();
+            File subdir = new File("src/etc/testcases/taskdefs/expected");
+            s.setTargetdir(subdir);
+            m = s.createMapper();
+            m.setType(flatten);
+            results = selectionString(s);
+        if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
+                assertEquals("TTTTTFFFFFFF", results);
+            } else {
+                assertEquals("TTTTTTTTTTTF", results);
+            }
+
+            s = (PresentSelector)getInstance();
+            s.setTargetdir(beddir);
+            m = s.createMapper();
+            m.setType(merge);
+            m.setTo("asf-logo.gif.gz");
+            results = selectionString(s);
+            assertEquals("TTTTTTTTTTTT", results);
+
+            s = (PresentSelector)getInstance();
+            subdir = new File(beddir, "tar/bz2");
+            s.setTargetdir(subdir);
+            m = s.createMapper();
+            m.setType(glob);
+            m.setFrom("*.bz2");
+            m.setTo("*.tar.bz2");
+            results = selectionString(s);
+            assertEquals("FFTFFFFFFFFF", results);
+
+            try {
+                makeMirror();
+
+                s = (PresentSelector)getInstance();
+                subdir = getProject().resolveFile("selectortest2");
+                s.setTargetdir(subdir);
+                results = mirrorSelectionString(s);
+                assertEquals("TTTFFTTTTTTT", results);
+                results = selectionString(s);
+                assertEquals("TTTFFTTTTTTT", results);
+
+
+            }
+            finally {
+                cleanupMirror();
+            }
+
+        }
+        finally {
+            cleanupBed();
+        }
+
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/README
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/README	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/README	(revision 16139)
@@ -0,0 +1,96 @@
+A DESCRIPTION OF THE SELECTOR TEST FRAMEWORK
+
+When writing tests for selectors, I found that I wanted to have some
+standard way of working with a set of files and testing whether one or
+another of them was selected. To that end, I created a base class called
+BaseSelectorTest that does most of the heavy lifting. Of course, you can
+test your selectors any way you want, but if you want to reuse this code,
+read on.
+
+What BaseSelectorTest does is use an ant build file
+"src/etc/testcases/types/selector.xml" to copy a tree of files out of
+"src/etc/testcases/taskdefs/expected" into a "selectortest" directories.
+Then it takes a list of 12 of the files and directories in this tree, and
+applies whatever selector you pass in to each one. It passes back to your
+test a 12 character long string indicating which of the 12 files and
+directories was selected, using 'T' for selected and 'F' for not selected.
+In the Test class for your selector, you override the getInstance() method
+to create your own type of selector, and set the elements of your selector
+a variety of ways to ensure that the string of T's and F's returned when
+the selector is applied to those 12 files is correct.
+
+So, for example, DepthSelectorTest.java extends BaseSelectorTest and has
+the following code:
+
+
+    public BaseSelector getInstance() {
+        return new DepthSelector();
+    }
+
+
+    public void testSelectionBehaviour() {
+        DepthSelector s;
+        String results;
+
+
+        try {
+            makeBed();
+
+
+            s = (DepthSelector)getInstance();
+            s.setMin(20);
+            s.setMax(25);
+            results = selectionString(s);
+            assertEquals("FFFFFFFFFFFF", results);
+
+
+            s = (DepthSelector)getInstance();
+            s.setMin(0);
+            results = selectionString(s);
+            assertEquals("TTTTTTTTTTTT", results);
+
+
+            s = (DepthSelector)getInstance();
+            s.setMin(1);
+            results = selectionString(s);
+            assertEquals("FFFFFTTTTTTT", results);
+
+
+The first test says that none of the 12 files or directories will match if
+the depth range for the selector is between 20 and 25 (that would be one
+deep directory tree!). The second says that all files and directories
+match if the minimum depth is set to 0 and the maximum isn't specified. The
+third test says that if the minumum depth is 1, the first 5 entries in the
+list of 12 will not be selected and the rest will.
+
+
+You can find the 12 files and directories that are tested for selection in
+the BaseSelectorTest class. I used a fixed list so that if someone added
+new files to the src/etc/testcases/types directory it wouldn't break my
+tests:
+
+
+    protected String[] filenames = {".","asf-logo.gif.md5","asf-
+    logo.gif.bz2",
+            "asf-logo.gif.gz","copy.filterset.filtered","zip/asf-
+            logo.gif.zip",
+            "tar/asf-logo.gif.tar","tar/asf-logo-huge.tar.gz",
+            "tar/gz/asf-logo.gif.tar.gz","tar/bz2/asf-logo.gif.tar.bz2",
+            "tar/bz2/asf-logo-huge.tar.bz2","tar/bz2"};
+
+
+If you wish to use this set of files and directories to test your selector,
+you can reuse the BaseSelectorTest with no change to it.
+
+You may find you need to alter the build file so that you get some
+variation in the files that your selector can work with. Most of the core
+selectors have required that kind of modification. If you do that, make
+sure that it doesn't alter the output strings on the other selector test,
+or if it does that you update their expected return results.
+
+You may also want to alter the set of files you look at in a particular
+selector test. Since the filelist in BaseSelectorTest is protected, you
+should be able to override it as you need to. Or you can alter the fileset
+in BaseSelectorTest itself, provided you update the test strings in all the
+other unit tests.
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/SizeSelectorTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/SizeSelectorTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/SizeSelectorTest.java	(revision 16139)
@@ -0,0 +1,205 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.util.*;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.types.Parameter;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+/**
+ * Tests Size Selectors
+ *
+ */
+public class SizeSelectorTest extends BaseSelectorTest {
+
+    private Project project;
+
+    public SizeSelectorTest(String name) {
+        super(name);
+    }
+
+    /**
+     * Factory method from base class. This is overriden in child
+     * classes to return a specific Selector class.
+     */
+    public BaseSelector getInstance() {
+        return new SizeSelector();
+    }
+
+    /**
+     * Test the code that validates the selector.
+     */
+    public void testValidate() {
+        SizeSelector s = (SizeSelector)getInstance();
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("SizeSelector did not check for required fields");
+        } catch (BuildException be1) {
+            assertEquals("The value attribute is required, and must "
+                    + "be positive", be1.getMessage());
+        }
+
+        s = (SizeSelector)getInstance();
+        s.setValue(-10);
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("SizeSelector did not check for value being in the "
+                    + "allowable range");
+        } catch (BuildException be2) {
+            assertEquals("The value attribute is required, and must "
+                    + "be positive", be2.getMessage());
+        }
+
+        s = (SizeSelector)getInstance();
+        Parameter param = new Parameter();
+        param.setName("garbage in");
+        param.setValue("garbage out");
+        Parameter[] params = {param};
+        s.setParameters(params);
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("SizeSelector did not check for valid parameter element");
+        } catch (BuildException be3) {
+            assertEquals("Invalid parameter garbage in", be3.getMessage());
+        }
+
+        s = (SizeSelector)getInstance();
+        param = new Parameter();
+        param.setName("value");
+        param.setValue("garbage out");
+        params[0] = param;
+        s.setParameters(params);
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("SizeSelector accepted bad value as parameter");
+        } catch (BuildException be4) {
+            assertEquals("Invalid size setting garbage out",
+                    be4.getMessage());
+        }
+
+        s = (SizeSelector)getInstance();
+        Parameter param1 = new Parameter();
+        Parameter param2 = new Parameter();
+        param1.setName("value");
+        param1.setValue("5");
+        param2.setName("units");
+        param2.setValue("garbage out");
+        params = new Parameter[2];
+        params[0] = param1;
+        params[1] = param2;
+        try {
+            s.setParameters(params);
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("SizeSelector accepted bad units as parameter");
+        } catch (BuildException be5) {
+            assertEquals("garbage out is not a legal value for this attribute",
+                    be5.getMessage());
+        }
+
+    }
+
+    /**
+     * Tests to make sure that the selector is selecting files correctly.
+     */
+    public void testSelectionBehaviour() {
+        SizeSelector s;
+        String results;
+
+        SizeSelector.ByteUnits kilo = new SizeSelector.ByteUnits();
+        kilo.setValue("K");
+        SizeSelector.ByteUnits kibi = new SizeSelector.ByteUnits();
+        kibi.setValue("Ki");
+        SizeSelector.ByteUnits tibi = new SizeSelector.ByteUnits();
+        tibi.setValue("Ti");
+        SizeSelector.SizeComparisons less = new SizeSelector.SizeComparisons();
+        less.setValue("less");
+        SizeSelector.SizeComparisons equal = new SizeSelector.SizeComparisons();
+        equal.setValue("equal");
+        SizeSelector.SizeComparisons more = new SizeSelector.SizeComparisons();
+        more.setValue("more");
+
+
+        try {
+            makeBed();
+
+            s = (SizeSelector)getInstance();
+            s.setValue(10);
+            s.setWhen(less);
+            results = selectionString(s);
+            assertEquals("TFFFFFFFFFFT", results);
+
+            s = (SizeSelector)getInstance();
+            s.setValue(10);
+            s.setWhen(more);
+            results = selectionString(s);
+            assertEquals("TTTTTTTTTTTT", results);
+
+            s = (SizeSelector)getInstance();
+            s.setValue(32);
+            s.setWhen(equal);
+            results = selectionString(s);
+            assertEquals("TTFFTFFFFFFT", results);
+
+            s = (SizeSelector)getInstance();
+            s.setValue(7);
+            s.setWhen(more);
+            s.setUnits(kilo);
+            results = selectionString(s);
+            assertEquals("TFTFFTTTTTTT", results);
+
+            s = (SizeSelector)getInstance();
+            s.setValue(7);
+            s.setWhen(more);
+            s.setUnits(kibi);
+            results = selectionString(s);
+            assertEquals("TFTFFFTTFTTT", results);
+
+            s = (SizeSelector)getInstance();
+            s.setValue(99999);
+            s.setWhen(more);
+            s.setUnits(tibi);
+            results = selectionString(s);
+            assertEquals("TFFFFFFFFFFT", results);
+
+            s = (SizeSelector)getInstance();
+            Parameter param1 = new Parameter();
+            Parameter param2 = new Parameter();
+            Parameter param3 = new Parameter();
+            param1.setName("value");
+            param1.setValue("20");
+            param2.setName("units");
+            param2.setValue("Ki");
+            param3.setName("when");
+            param3.setValue("more");
+            Parameter[] params = {param1,param2,param3};
+            s.setParameters(params);
+            results = selectionString(s);
+            assertEquals("TFFFFFFTFFTT", results);
+        }
+        finally {
+            cleanupBed();
+        }
+
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/TypeSelectorTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/TypeSelectorTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/types/selectors/TypeSelectorTest.java	(revision 16139)
@@ -0,0 +1,100 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.types.selectors;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildFileTest;
+import org.apache.tools.ant.types.Parameter;
+import org.apache.tools.ant.util.JavaEnvUtils;
+
+import java.text.SimpleDateFormat;
+import java.text.ParsePosition;
+import java.util.Date;
+
+import junit.framework.TestCase;
+import junit.framework.AssertionFailedError;
+
+/**
+ * Tests Type Selectors.
+ *
+ */
+public class TypeSelectorTest extends BaseSelectorTest {
+
+    public TypeSelectorTest(String name) {
+        super(name);
+    }
+
+    /**
+     * Factory method from base class. This is overriden in child
+     * classes to return a specific Selector class.
+     */
+    public BaseSelector getInstance() {
+        return new TypeSelector();
+    }
+
+    /**
+     * Test the code that validates the selector.
+     */
+    public void testValidate() {
+        TypeSelector s = (TypeSelector)getInstance();
+        try {
+            s.isSelected(basedir,filenames[0],files[0]);
+            fail("TypeSelector did not check for required fields");
+        } catch (BuildException be1) {
+            assertEquals("The type attribute is required"
+                    , be1.getMessage());
+        }
+    }
+
+    /**
+     * Tests to make sure that the selector is selecting files correctly.
+     */
+    public void testSelectionBehaviour() {
+        TypeSelector s;
+        String results;
+
+        TypeSelector.FileType directory = new
+                TypeSelector.FileType();
+        directory.setValue("dir");
+        TypeSelector.FileType file = new
+                TypeSelector.FileType();
+        file.setValue("file");
+
+        try {
+            makeBed();
+
+            s = (TypeSelector)getInstance();
+            s.setType(directory);
+            results = selectionString(s);
+            assertEquals("TFFFFFFFFFFT", results);
+
+            s = (TypeSelector)getInstance();
+            s.setType(file);
+            results = selectionString(s);
+            assertEquals("FTTTTTTTTTTF", results);
+
+
+        }
+        finally {
+            cleanupBed();
+        }
+
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/ClasspathUtilsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/ClasspathUtilsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/ClasspathUtilsTest.java	(revision 16139)
@@ -0,0 +1,66 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Enumeration;
+import junit.framework.TestCase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.Path;
+
+
+/**
+ * Test case for ClasspathUtils
+ *
+ */
+public class ClasspathUtilsTest extends TestCase {
+
+    private Project p;
+
+    public ClasspathUtilsTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        p = new Project();
+        p.init();
+    }
+
+
+    public void testOnlyOneInstance() {
+        Enumeration enumeration;
+        String list = "";
+        ClassLoader c = ClasspathUtils.getUniqueClassLoaderForPath(p, (Path) null, false);
+        try {
+            enumeration = c.getResources(
+                "org/apache/tools/ant/taskdefs/defaults.properties");
+        } catch (IOException e) {
+            throw new BuildException(
+                "Could not get the defaults.properties resource");
+        }
+        int count = 0;
+        while (enumeration.hasMoreElements()) {
+            list = list + " " + enumeration.nextElement();
+            count++;
+        }
+        assertTrue("Should be only one and not " + count + " " + list, count == 1);
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/CollectionUtilsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/CollectionUtilsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/CollectionUtilsTest.java	(revision 16139)
@@ -0,0 +1,100 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.Stack;
+import java.util.Vector;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for org.apache.tools.ant.util.CollectionUtils.
+ *
+ */
+public class CollectionUtilsTest extends TestCase {
+
+    public CollectionUtilsTest(String name) {
+        super(name);
+    }
+
+    public void testVectorEquals() {
+        assertTrue(!CollectionUtils.equals(null, new Vector()));
+        assertTrue(!CollectionUtils.equals(new Vector(), null));
+        assertTrue(CollectionUtils.equals(new Vector(), new Vector()));
+        Vector v1 = new Vector();
+        Stack s2 = new Stack();
+        v1.addElement("foo");
+        s2.push("foo");
+        assertTrue(CollectionUtils.equals(v1, s2));
+        assertTrue(CollectionUtils.equals(s2, v1));
+        v1.addElement("bar");
+        assertTrue(!CollectionUtils.equals(v1, s2));
+        assertTrue(!CollectionUtils.equals(s2, v1));
+        s2.push("bar");
+        assertTrue(CollectionUtils.equals(v1, s2));
+        assertTrue(CollectionUtils.equals(s2, v1));
+        s2.push("baz");
+        assertTrue(!CollectionUtils.equals(v1, s2));
+        assertTrue(!CollectionUtils.equals(s2, v1));
+        v1.addElement("baz");
+        assertTrue(CollectionUtils.equals(v1, s2));
+        assertTrue(CollectionUtils.equals(s2, v1));
+        v1.addElement("zyzzy");
+        s2.push("zyzzy2");
+        assertTrue(!CollectionUtils.equals(v1, s2));
+        assertTrue(!CollectionUtils.equals(s2, v1));
+    }
+
+    public void testDictionaryEquals() {
+        assertTrue(!CollectionUtils.equals(null, new Hashtable()));
+        assertTrue(!CollectionUtils.equals(new Hashtable(), null));
+        assertTrue(CollectionUtils.equals(new Hashtable(), new Properties()));
+        Hashtable h1 = new Hashtable();
+        Properties p2 = new Properties();
+        h1.put("foo", "");
+        p2.put("foo", "");
+        assertTrue(CollectionUtils.equals(h1, p2));
+        assertTrue(CollectionUtils.equals(p2, h1));
+        h1.put("bar", "");
+        assertTrue(!CollectionUtils.equals(h1, p2));
+        assertTrue(!CollectionUtils.equals(p2, h1));
+        p2.put("bar", "");
+        assertTrue(CollectionUtils.equals(h1, p2));
+        assertTrue(CollectionUtils.equals(p2, h1));
+        p2.put("baz", "");
+        assertTrue(!CollectionUtils.equals(h1, p2));
+        assertTrue(!CollectionUtils.equals(p2, h1));
+        h1.put("baz", "");
+        assertTrue(CollectionUtils.equals(h1, p2));
+        assertTrue(CollectionUtils.equals(p2, h1));
+        h1.put("zyzzy", "");
+        p2.put("zyzzy2", "");
+        assertTrue(!CollectionUtils.equals(h1, p2));
+        assertTrue(!CollectionUtils.equals(p2, h1));
+        p2.put("zyzzy", "");
+        h1.put("zyzzy2", "");
+        assertTrue(CollectionUtils.equals(h1, p2));
+        assertTrue(CollectionUtils.equals(p2, h1));
+        h1.put("dada", "1");
+        p2.put("dada", "2");
+        assertTrue(!CollectionUtils.equals(h1, p2));
+        assertTrue(!CollectionUtils.equals(p2, h1));
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/DOMElementWriterTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/DOMElementWriterTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/DOMElementWriterTest.java	(revision 16139)
@@ -0,0 +1,95 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Tests for org.apache.tools.ant.util.DOMElementWriter.
+ *
+ */
+public class DOMElementWriterTest extends TestCase {
+
+    private DOMElementWriter w = new DOMElementWriter();
+
+    public DOMElementWriterTest(String name) {
+        super(name);
+    }
+
+    public void testIsReference() {
+        assertTrue("&#20;", w.isReference("&#20;"));
+        assertTrue("&#x20;", w.isReference("&#x20;"));
+        assertTrue("&#xA0;", w.isReference("&#xA0;"));
+        assertTrue("&#A0;", !w.isReference("&#A0;"));
+        assertTrue("20;", !w.isReference("20;"));
+        assertTrue("&#20", !w.isReference("&#20"));
+        assertTrue("&quot;", w.isReference("&quot;"));
+        assertTrue("&apos;", w.isReference("&apos;"));
+        assertTrue("&gt;", w.isReference("&gt;"));
+        assertTrue("&lt;", w.isReference("&lt;"));
+        assertTrue("&amp;", w.isReference("&amp;"));
+    }
+
+    public void testEncode() {
+        assertEquals("&#20;", w.encode("&#20;"));
+        assertEquals("&#x20;", w.encode("&#x20;"));
+        assertEquals("&#xA0;", w.encode("&#xA0;"));
+        assertEquals("&amp;#A0;", w.encode("&#A0;"));
+        assertEquals("20;", w.encode("20;"));
+        assertEquals("&amp;#20", w.encode("&#20"));
+        assertEquals("&quot;", w.encode("&quot;"));
+        assertEquals("&apos;", w.encode("&apos;"));
+        assertEquals("&gt;", w.encode("&gt;"));
+        assertEquals("&lt;", w.encode("&lt;"));
+        assertEquals("&amp;", w.encode("&amp;"));
+        assertEquals("&quot;", w.encode("\""));
+        assertEquals("&lt;", w.encode("<"));
+        assertEquals("&amp;", w.encode("&"));
+        assertEquals("", w.encode("\u0017"));
+        assertEquals("&#20;\"20;&", w.encodedata("&#20;\"20;&"));
+        assertEquals("", w.encodedata("\u0017"));
+    }
+
+    public void testIsLegalCharacter() {
+        assertTrue("0x00", !w.isLegalCharacter('\u0000'));
+        assertTrue("0x09", w.isLegalCharacter('\t'));
+        assertTrue("0x0A", w.isLegalCharacter('\n'));
+        assertTrue("0x0C", w.isLegalCharacter('\r'));
+        assertTrue("0x1F", !w.isLegalCharacter('\u001F'));
+        assertTrue("0x20", w.isLegalCharacter('\u0020'));
+        assertTrue("0xD7FF", w.isLegalCharacter('\uD7FF'));
+        assertTrue("0xD800", !w.isLegalCharacter('\uD800'));
+        assertTrue("0xDFFF", !w.isLegalCharacter('\uDFFF'));
+        assertTrue("0xE000", w.isLegalCharacter('\uE000'));
+        assertTrue("0xFFFD", w.isLegalCharacter('\uFFFD'));
+        assertTrue("0xFFFE", !w.isLegalCharacter('\uFFFE'));
+    }
+
+    public void testCDATAEndEncoding() {
+        assertEquals("]>", w.encodedata("]>"));
+        assertEquals("]]", w.encodedata("]]"));
+        assertEquals("&#x5d;&#x5d;&gt;", w.encodedata("]]>"));
+        assertEquals("&#x5d;&#x5d;&gt;A", w.encodedata("]]>A"));
+        assertEquals("A&#x5d;&#x5d;&gt;", w.encodedata("A]]>"));
+        assertEquals("A&#x5d;&#x5d;&gt;A", w.encodedata("A]]>A"));
+        assertEquals("A&#x5d;&#x5d;&gt;B&#x5d;&#x5d;&gt;C",
+                     w.encodedata("A]]>B]]>C"));
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/DateUtilsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/DateUtilsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/DateUtilsTest.java	(revision 16139)
@@ -0,0 +1,91 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.util.Date;
+import java.util.Calendar;
+import java.util.TimeZone;
+
+import junit.framework.TestCase;
+
+/**
+ * TestCase for DateUtils.
+ *
+ */
+public class DateUtilsTest extends TestCase {
+    public DateUtilsTest(String s) {
+        super(s);
+    }
+
+    public void testElapsedTime(){
+        String text = DateUtils.formatElapsedTime(50*1000);
+        assertEquals("50 seconds", text);
+        text = DateUtils.formatElapsedTime(65*1000);
+        assertEquals("1 minute 5 seconds", text);
+        text = DateUtils.formatElapsedTime(120*1000);
+        assertEquals("2 minutes 0 seconds", text);
+        text = DateUtils.formatElapsedTime(121*1000);
+        assertEquals("2 minutes 1 second", text);
+    }
+
+    public void testDateTimeISO(){
+        TimeZone timeZone = TimeZone.getTimeZone("GMT+1");
+        Calendar cal = Calendar.getInstance(timeZone);
+        cal.set(2002,1,23,10,11,12);
+        String text = DateUtils.format(cal.getTime(),
+                DateUtils.ISO8601_DATETIME_PATTERN);
+        assertEquals("2002-02-23T09:11:12", text);
+    }
+
+    public void testDateISO(){
+        TimeZone timeZone = TimeZone.getTimeZone("GMT");
+        Calendar cal = Calendar.getInstance(timeZone);
+        cal.set(2002,1,23);
+        String text = DateUtils.format(cal.getTime(),
+                DateUtils.ISO8601_DATE_PATTERN);
+        assertEquals("2002-02-23", text);
+    }
+
+    public void testTimeISODate(){
+        // make sure that elapsed time in set via date works
+        TimeZone timeZone = TimeZone.getTimeZone("GMT+1");
+        Calendar cal = Calendar.getInstance(timeZone);
+        cal.set(2002,1,23, 21, 11, 12);
+        String text = DateUtils.format(cal.getTime(),
+                DateUtils.ISO8601_TIME_PATTERN);
+        assertEquals("20:11:12", text);
+    }
+
+    public void testTimeISO(){
+        // make sure that elapsed time in ms works
+        long ms = (20*3600 + 11*60 + 12)*1000;
+        String text = DateUtils.format(ms,
+                DateUtils.ISO8601_TIME_PATTERN);
+        assertEquals("20:11:12", text);
+    }
+
+    public void testPhaseOfMoon() {
+        TimeZone timeZone = TimeZone.getTimeZone("GMT");
+        Calendar cal = Calendar.getInstance(timeZone);
+        // should be full moon
+        cal.set(2002, 2, 27);
+        assertEquals(4, DateUtils.getPhaseOfMoon(cal));
+        // should be new moon
+        cal.set(2002, 2, 12);
+        assertEquals(0, DateUtils.getPhaseOfMoon(cal));
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/FileUtilsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/FileUtilsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/FileUtilsTest.java	(revision 16139)
@@ -0,0 +1,457 @@
+/*
+ * Copyright  2001-2005 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import junit.framework.TestCase;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.condition.Os;
+
+/**
+ * Tests for org.apache.tools.ant.util.FileUtils.
+ *
+ */
+public class FileUtilsTest extends TestCase {
+
+    private FileUtils fu;
+    private File removeThis;
+    private String root;
+
+    public FileUtilsTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        fu = FileUtils.newFileUtils();
+        // Windows adds the drive letter in uppercase, unless you run Cygwin
+        root = new File(File.separator).getAbsolutePath().toUpperCase();
+    }
+
+    public void tearDown() {
+        if (removeThis != null && removeThis.exists()) {
+            removeThis.delete();
+        }
+    }
+
+    public void testSetLastModified() throws IOException {
+        removeThis = new File("dummy");
+        FileOutputStream fos = new FileOutputStream(removeThis);
+        fos.write(new byte[0]);
+        fos.close();
+        long modTime = removeThis.lastModified();
+        assertTrue(modTime != 0);
+
+        /*
+         * Sleep for some time to make sure a touched file would get a
+         * more recent timestamp according to the file system's
+         * granularity (should be > 2s to account for Windows FAT).
+         */
+        try {
+            Thread.sleep(5000);
+        } catch (InterruptedException ie) {
+            fail(ie.getMessage());
+        }
+
+        fu.setFileLastModified(removeThis, -1);
+        long secondModTime = removeThis.lastModified();
+        assertTrue(secondModTime > modTime);
+
+        // number of milliseconds in a day
+        final int millisperday=24 * 3600 * 1000;
+        // in a previous version, the date of the file was set to 123456
+        // milliseconds since 01.01.1970
+        // it did not work on a computer running JDK 1.4.1_02 + Windows 2000
+        fu.setFileLastModified(removeThis, secondModTime + millisperday);
+        long thirdModTime = removeThis.lastModified();
+        /*
+         * I would love to compare this with 123456, but depending on
+         * the filesystems granularity it can take an arbitrary value.
+         *
+         * Just assert the time has changed.
+         */
+        assertTrue(thirdModTime != secondModTime);
+    }
+
+    public void testResolveFile() {
+        /*
+         * Start with simple absolute file names.
+         */
+        assertEquals(File.separator,
+                     fu.resolveFile(null, "/").getPath());
+        assertEquals(File.separator,
+                     fu.resolveFile(null, "\\").getPath());
+
+        if (!Os.isFamily("unix")) {
+        /*
+         * throw in drive letters
+         */
+        String driveSpec = "C:";
+        assertEquals(driveSpec + "\\",
+                     fu.resolveFile(null, driveSpec + "/").getPath());
+        assertEquals(driveSpec + "\\",
+                     fu.resolveFile(null, driveSpec + "\\").getPath());
+        String driveSpecLower = "c:";
+        assertEquals(driveSpec + "\\",
+                     fu.resolveFile(null, driveSpecLower + "/").getPath());
+        assertEquals(driveSpec + "\\",
+                     fu.resolveFile(null, driveSpecLower + "\\").getPath());
+        /*
+         * promised to eliminate consecutive slashes after drive letter.
+         */
+        assertEquals(driveSpec + "\\",
+                     fu.resolveFile(null, driveSpec + "/////").getPath());
+        assertEquals(driveSpec + "\\",
+                     fu.resolveFile(null, driveSpec + "\\\\\\\\\\\\").getPath());
+        }
+
+        if (Os.isFamily("netware")) {
+            /*
+             * throw in NetWare volume names
+             */
+            String driveSpec = "SYS:";
+            assertEquals(driveSpec,
+                         fu.resolveFile(null, driveSpec + "/").getPath());
+            assertEquals(driveSpec,
+                         fu.resolveFile(null, driveSpec + "\\").getPath());
+            String driveSpecLower = "sys:";
+            assertEquals(driveSpec,
+                         fu.resolveFile(null, driveSpecLower + "/").getPath());
+            assertEquals(driveSpec,
+                         fu.resolveFile(null, driveSpecLower + "\\").getPath());
+            /*
+             * promised to eliminate consecutive slashes after drive letter.
+             */
+            assertEquals(driveSpec,
+                         fu.resolveFile(null, driveSpec + "/////").getPath());
+            assertEquals(driveSpec,
+                         fu.resolveFile(null, driveSpec + "\\\\\\\\\\\\").getPath());
+        }
+
+        /*
+         * Now test some relative file name magic.
+         */
+        assertEquals(localize("/1/2/3/4"),
+                     fu.resolveFile(new File(localize("/1/2/3")), "4").getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     fu.resolveFile(new File(localize("/1/2/3")), "./4").getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     fu.resolveFile(new File(localize("/1/2/3")), ".\\4").getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     fu.resolveFile(new File(localize("/1/2/3")), "./.\\4").getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     fu.resolveFile(new File(localize("/1/2/3")), "../3/4").getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     fu.resolveFile(new File(localize("/1/2/3")), "..\\3\\4").getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     fu.resolveFile(new File(localize("/1/2/3")), "../../5/.././2/./3/6/../4").getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     fu.resolveFile(new File(localize("/1/2/3")), "..\\../5/..\\./2/./3/6\\../4").getPath());
+
+        try {
+            fu.resolveFile(new File(localize("/1")), "../../b");
+            fail("successfully crawled beyond the filesystem root");
+        } catch (BuildException e) {
+            // Expected Exception caught
+        }
+
+    }
+
+    public void testNormalize() {
+        /*
+         * Start with simple absolute file names.
+         */
+        assertEquals(File.separator,
+                     fu.normalize("/").getPath());
+        assertEquals(File.separator,
+                     fu.normalize("\\").getPath());
+
+        if (!Os.isFamily("unix")) {
+        /*
+         * throw in drive letters
+         */
+        String driveSpec = "C:";
+        assertEquals(driveSpec,
+                     fu.normalize(driveSpec).getPath());
+        assertEquals(driveSpec + "\\",
+                     fu.normalize(driveSpec + "/").getPath());
+        assertEquals(driveSpec + "\\",
+                     fu.normalize(driveSpec + "\\").getPath());
+        String driveSpecLower = "c:";
+        assertEquals(driveSpec + "\\",
+                     fu.normalize(driveSpecLower + "/").getPath());
+        assertEquals(driveSpec + "\\",
+                     fu.normalize(driveSpecLower + "\\").getPath());
+        /*
+         * promised to eliminate consecutive slashes after drive letter.
+         */
+        assertEquals(driveSpec + "\\",
+                     fu.normalize(driveSpec + "/////").getPath());
+        assertEquals(driveSpec + "\\",
+                     fu.normalize(driveSpec + "\\\\\\\\\\\\").getPath());
+        }
+        if (Os.isFamily("netware")) {
+            /*
+             * throw in NetWare volume names
+             */
+            String driveSpec = "SYS:";
+            assertEquals(driveSpec,
+                         fu.normalize(driveSpec).getPath());
+            assertEquals(driveSpec,
+                         fu.normalize(driveSpec + "/").getPath());
+            assertEquals(driveSpec,
+                         fu.normalize(driveSpec + "\\").getPath());
+            String driveSpecLower = "sys:";
+            assertEquals(driveSpec,
+                         fu.normalize(driveSpecLower).getPath());
+            assertEquals(driveSpec,
+                         fu.normalize(driveSpecLower + "/").getPath());
+            assertEquals(driveSpec,
+                         fu.normalize(driveSpecLower + "\\").getPath());
+            assertEquals(driveSpec + "\\junk",
+                         fu.normalize(driveSpecLower + "\\junk").getPath());
+            /*
+             * promised to eliminate consecutive slashes after drive letter.
+             */
+            assertEquals(driveSpec,
+                         fu.normalize(driveSpec + "/////").getPath());
+            assertEquals(driveSpec,
+                         fu.normalize(driveSpec + "\\\\\\\\\\\\").getPath());
+        }
+
+        /*
+         * Now test some relative file name magic.
+         */
+        assertEquals(localize("/1/2/3/4"),
+                     fu.normalize(localize("/1/2/3/4")).getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     fu.normalize(localize("/1/2/3/./4")).getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     fu.normalize(localize("/1/2/3/.\\4")).getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     fu.normalize(localize("/1/2/3/./.\\4")).getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     fu.normalize(localize("/1/2/3/../3/4")).getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     fu.normalize(localize("/1/2/3/..\\3\\4")).getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     fu.normalize(localize("/1/2/3/../../5/.././2/./3/6/../4")).getPath());
+        assertEquals(localize("/1/2/3/4"),
+                     fu.normalize(localize("/1/2/3/..\\../5/..\\./2/./3/6\\../4")).getPath());
+
+        try {
+            fu.normalize("foo");
+            fail("foo is not an absolute path");
+        } catch (BuildException e) {
+            // Expected exception caught
+        }
+
+        try {
+            fu.normalize(localize("/1/../../b"));
+            fail("successfully crawled beyond the filesystem root");
+        } catch (BuildException e) {
+            // Expected exception caught
+        }
+    }
+
+    /**
+     * Test handling of null arguments.
+     */
+    public void testNullArgs() {
+        try {
+            fu.normalize(null);
+            fail("successfully normalized a null-file");
+        } catch (NullPointerException npe) {
+            // Expected exception caught
+        }
+
+        File f = fu.resolveFile(null, "a");
+        assertEquals(f, new File("a"));
+    }
+
+    /**
+     * Test createTempFile
+     */
+    public void testCreateTempFile() {
+        File parent = new File((new File("/tmp")).getAbsolutePath());
+        File tmp1 = fu.createTempFile("pre", ".suf", parent);
+        assertTrue("new file", !tmp1.exists());
+
+        String name = tmp1.getName();
+        assertTrue("starts with pre", name.startsWith("pre"));
+        assertTrue("ends with .suf", name.endsWith(".suf"));
+        assertEquals("is inside parent dir",
+                     parent.getAbsolutePath(),
+                     tmp1.getParent());
+
+        File tmp2 = fu.createTempFile("pre", ".suf", parent);
+        assertTrue("files are different",
+                   !tmp1.getAbsolutePath().equals(tmp2.getAbsolutePath()));
+
+        // null parent dir
+        File tmp3 = fu.createTempFile("pre", ".suf", null);
+        String  tmploc = System.getProperty("java.io.tmpdir");
+        assertEquals((new File(tmploc, tmp3.getName())).getAbsolutePath(),
+                     tmp3.getAbsolutePath());
+    }
+
+    /**
+     * Test contentEquals
+     */
+    public void testContentEquals() throws IOException {
+        assertTrue("Non existing files", fu.contentEquals(new File("foo"),
+                                                          new File("bar")));
+        assertTrue("One exists, the other one doesn\'t",
+                   !fu.contentEquals(new File("foo"), new File("build.xml")));
+        assertTrue("Don\'t compare directories",
+                   !fu.contentEquals(new File("src"), new File("src")));
+        assertTrue("File equals itself",
+                   fu.contentEquals(new File("build.xml"),
+                                    new File("build.xml")));
+        assertTrue("Files are different",
+                   !fu.contentEquals(new File("build.xml"),
+                                     new File("docs.xml")));
+    }
+
+    /**
+     * Test createNewFile
+     */
+    public void testCreateNewFile() throws IOException {
+        removeThis = new File("dummy");
+        assertTrue(!removeThis.exists());
+        fu.createNewFile(removeThis);
+        assertTrue(removeThis.exists());
+    }
+
+    /**
+     * Test removeLeadingPath.
+     */
+    public void testRemoveLeadingPath() {
+        assertEquals("bar", fu.removeLeadingPath(new File("/foo"),
+                                                 new File("/foo/bar")));
+        assertEquals("bar", fu.removeLeadingPath(new File("/foo/"),
+                                                 new File("/foo/bar")));
+        assertEquals("bar", fu.removeLeadingPath(new File("\\foo"),
+                                                 new File("\\foo\\bar")));
+        assertEquals("bar", fu.removeLeadingPath(new File("\\foo\\"),
+                                                 new File("\\foo\\bar")));
+        assertEquals("bar", fu.removeLeadingPath(new File("c:/foo"),
+                                                 new File("c:/foo/bar")));
+        assertEquals("bar", fu.removeLeadingPath(new File("c:/foo/"),
+                                                 new File("c:/foo/bar")));
+        assertEquals("bar", fu.removeLeadingPath(new File("c:\\foo"),
+                                                 new File("c:\\foo\\bar")));
+        assertEquals("bar", fu.removeLeadingPath(new File("c:\\foo\\"),
+                                                 new File("c:\\foo\\bar")));
+        assertEqualsIgnoreDriveCase(fu.normalize("/bar").getAbsolutePath(),
+                     fu.removeLeadingPath(new File("/foo"), new File("/bar")));
+        assertEqualsIgnoreDriveCase(fu.normalize("/foobar").getAbsolutePath(),
+                     fu.removeLeadingPath(new File("/foo"), new File("/foobar")));
+        // bugzilla report 19979
+        assertEquals("", fu.removeLeadingPath(new File("/foo/bar"),
+                                              new File("/foo/bar")));
+        assertEquals("", fu.removeLeadingPath(new File("/foo/bar"),
+                                              new File("/foo/bar/")));
+        assertEquals("", fu.removeLeadingPath(new File("/foo/bar/"),
+                                              new File("/foo/bar/")));
+        assertEquals("", fu.removeLeadingPath(new File("/foo/bar/"),
+                                              new File("/foo/bar")));
+
+        String expected = "foo/bar".replace('\\', File.separatorChar)
+            .replace('/', File.separatorChar);
+        assertEquals(expected, fu.removeLeadingPath(new File("/"),
+                                                    new File("/foo/bar")));
+        assertEquals(expected, fu.removeLeadingPath(new File("c:/"),
+                                                    new File("c:/foo/bar")));
+        assertEquals(expected, fu.removeLeadingPath(new File("c:\\"),
+                                                    new File("c:\\foo\\bar")));
+    }
+
+    /**
+     * test toUri
+     */
+    public void testToURI() {
+        String dosRoot = null;
+        if (Os.isFamily("dos")) {
+            dosRoot = System.getProperty("user.dir").charAt(0) + ":/";
+        }
+        else
+        {
+            dosRoot = "";
+        }
+        if (Os.isFamily("dos")) {
+            assertEquals("file:///C:/foo", fu.toURI("c:\\foo"));
+        }
+        if (Os.isFamily("netware")) {
+            assertEquals("file:///SYS:/foo", fu.toURI("sys:\\foo"));
+        }
+        assertEquals("file:///" + dosRoot + "foo", fu.toURI("/foo"));
+        assertEquals("file:./foo",  fu.toURI("./foo"));
+        assertEquals("file:///" + dosRoot + "foo", fu.toURI("\\foo"));
+        assertEquals("file:./foo",  fu.toURI(".\\foo"));
+        assertEquals("file:///" + dosRoot + "foo%20bar", fu.toURI("/foo bar"));
+        assertEquals("file:///" + dosRoot + "foo%20bar", fu.toURI("\\foo bar"));
+        assertEquals("file:///" + dosRoot + "foo%23bar", fu.toURI("/foo#bar"));
+        assertEquals("file:///" + dosRoot + "foo%23bar", fu.toURI("\\foo#bar"));
+    }
+
+    /**
+     * test fromUri
+     */
+    public void testFromURI() {
+        if (Os.isFamily("netware")) {
+            assertEqualsIgnoreDriveCase("SYS:\\foo", fu.fromURI("file:///sys:/foo"));
+        }
+        if (Os.isFamily("dos")) {
+            assertEqualsIgnoreDriveCase("C:\\foo", fu.fromURI("file:///c:/foo"));
+        }
+        assertEqualsIgnoreDriveCase(localize("/foo"), fu.fromURI("file:///foo"));
+        assertEquals("." + File.separator + "foo",
+                     fu.fromURI("file:./foo"));
+        assertEqualsIgnoreDriveCase(localize("/foo bar"), fu.fromURI("file:///foo%20bar"));
+        assertEqualsIgnoreDriveCase(localize("/foo#bar"), fu.fromURI("file:///foo%23bar"));
+    }
+
+    /**
+     * adapt file separators to local conventions
+     */
+    private String localize(String path) {
+        path = root + path.substring(1);
+        return path.replace('\\', File.separatorChar).replace('/', File.separatorChar);
+    }
+    /**
+     * convenience method
+     * normalize brings the drive in uppercase
+     * the drive letter is in lower case under cygwin
+     * calling this method allows tests where normalize is called to pass under cygwin
+     */
+    private void assertEqualsIgnoreDriveCase(String s1, String s2) {
+        if (Os.isFamily("dos") && s1.length()>=1 && s2.length()>=1) {
+            StringBuffer sb1= new StringBuffer(s1);
+            StringBuffer sb2= new StringBuffer(s2);
+            sb1.setCharAt(0,Character.toUpperCase(s1.charAt(0)));
+            sb2.setCharAt(0,Character.toUpperCase(s2.charAt(0)));
+            assertEquals(sb1.toString(),sb2.toString());
+        }   else {
+            assertEquals(s1,s2);
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/GlobPatternMapperTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/GlobPatternMapperTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/GlobPatternMapperTest.java	(revision 16139)
@@ -0,0 +1,99 @@
+/*
+ * Copyright  2000,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Tests for org.apache.tools.ant.util;GlobPatternMapper.
+ *
+ */
+public class GlobPatternMapperTest extends TestCase {
+
+    public GlobPatternMapperTest(String name) {
+        super(name);
+    }
+
+    public void testNoPatternAtAll() {
+        GlobPatternMapper m = new GlobPatternMapper();
+        m.setFrom("foobar");
+        m.setTo("baz");
+        assertNull("Shouldn\'t match foobar", m.mapFileName("plonk"));
+        String[] result = m.mapFileName("foobar");
+        assertNotNull("Should match foobar", result);
+        assertEquals("only one result for foobar", 1, result.length);
+        assertEquals("baz", result[0]);
+    }
+
+    public void testPostfixOnly() {
+        GlobPatternMapper m = new GlobPatternMapper();
+        m.setFrom("*foo");
+        m.setTo("*plonk");
+        assertNull("Shouldn\'t match *foo", m.mapFileName("bar.baz"));
+        String[] result = m.mapFileName("bar.foo");
+        assertNotNull("Should match *.foo", result);
+        assertEquals("only one result for bar.foo", 1, result.length);
+        assertEquals("bar.plonk", result[0]);
+
+        // Try a silly case
+        m.setTo("foo*");
+        result = m.mapFileName("bar.foo");
+        assertEquals("foobar.", result[0]);
+    }
+
+    public void testPrefixOnly() {
+        GlobPatternMapper m = new GlobPatternMapper();
+        m.setFrom("foo*");
+        m.setTo("plonk*");
+        assertNull("Shouldn\'t match foo*", m.mapFileName("bar.baz"));
+        String[] result = m.mapFileName("foo.bar");
+        assertNotNull("Should match foo*", result);
+        assertEquals("only one result for foo.bar", 1, result.length);
+        assertEquals("plonk.bar", result[0]);
+
+        // Try a silly case
+        m.setTo("*foo");
+        result = m.mapFileName("foo.bar");
+        assertEquals(".barfoo", result[0]);
+    }
+
+    public void testPreAndPostfix() {
+        GlobPatternMapper m = new GlobPatternMapper();
+        m.setFrom("foo*bar");
+        m.setTo("plonk*pling");
+        assertNull("Shouldn\'t match foo*bar", m.mapFileName("bar.baz"));
+        String[] result = m.mapFileName("foo.bar");
+        assertNotNull("Should match foo*bar", result);
+        assertEquals("only one result for foo.bar", 1, result.length);
+        assertEquals("plonk.pling", result[0]);
+
+        // and a little longer
+        result = m.mapFileName("foo.baz.bar");
+        assertNotNull("Should match foo*bar", result);
+        assertEquals("only one result for foo.baz.bar", 1, result.length);
+        assertEquals("plonk.baz.pling", result[0]);
+
+        // and a little shorter
+        result = m.mapFileName("foobar");
+        assertNotNull("Should match foo*bar", result);
+        assertEquals("only one result for foobar", 1, result.length);
+        assertEquals("plonkpling", result[0]);
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/JAXPUtilsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/JAXPUtilsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/JAXPUtilsTest.java	(revision 16139)
@@ -0,0 +1,42 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+
+/**
+ * JAXPUtils test case
+ */
+public class JAXPUtilsTest extends TestCase {
+    public JAXPUtilsTest(String name){
+        super(name);
+    }
+
+    public void testGetSystemId(){
+        File file = null;
+        if ( File.separatorChar == '\\' ){
+            file = new File("d:\\jdk");
+        } else {
+            file = new File("/user/local/bin");
+        }
+        String systemid = JAXPUtils.getSystemId(file);
+        assertTrue("SystemIDs should start by file:///", systemid.startsWith("file:///"));
+        assertTrue("SystemIDs should not start with file:////", !systemid.startsWith("file:////"));
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/JavaEnvUtilsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/JavaEnvUtilsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/JavaEnvUtilsTest.java	(revision 16139)
@@ -0,0 +1,137 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.File;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.TestCase;
+
+import org.apache.tools.ant.taskdefs.condition.Os;
+
+/**
+ * TestCase for JavaEnvUtils.
+ *
+ */
+public class JavaEnvUtilsTest extends TestCase {
+    public JavaEnvUtilsTest(String s) {
+        super(s);
+    }
+
+    public void testGetExecutableNetware() {
+        if (Os.isName("netware")) {
+            assertEquals("java", JavaEnvUtils.getJreExecutable("java"));
+            assertEquals("javac", JavaEnvUtils.getJdkExecutable("javac"));
+            assertEquals("foo", JavaEnvUtils.getJreExecutable("foo"));
+            assertEquals("foo", JavaEnvUtils.getJdkExecutable("foo"));
+        }
+    }
+
+    public void testGetExecutableWindows() {
+        if (Os.isFamily("windows")) {
+            FileUtils fileUtils = FileUtils.newFileUtils();
+            String javaHome =
+                fileUtils.normalize(System.getProperty("java.home"))
+                .getAbsolutePath();
+
+            String j = JavaEnvUtils.getJreExecutable("java");
+            assertTrue(j.endsWith(".exe"));
+            assertTrue(j+" is absolute", (new File(j)).isAbsolute());
+            try {
+                assertTrue(j+" is normalized and in the JRE dir",
+                           j.startsWith(javaHome));
+            } catch (AssertionFailedError e) {
+                // java.home is bogus
+                assertEquals("java.exe", j);
+            }
+
+            j = JavaEnvUtils.getJdkExecutable("javac");
+            assertTrue(j.endsWith(".exe"));
+            try {
+                assertTrue(j+" is absolute", (new File(j)).isAbsolute());
+                String javaHomeParent =
+                    fileUtils.normalize(javaHome+"/..").getAbsolutePath();
+                assertTrue(j+" is normalized and in the JDK dir",
+                           j.startsWith(javaHomeParent));
+
+                if (JavaEnvUtils.getJavaVersion() == JavaEnvUtils.JAVA_1_0 ||
+                    JavaEnvUtils.getJavaVersion() == JavaEnvUtils.JAVA_1_1) {
+                    assertTrue(j+" is normalized and in the JRE dir",
+                               j.startsWith(javaHome));
+                } else {
+                    assertTrue(j+" is normalized and not in the JRE dir",
+                               !j.startsWith(javaHome));
+                }
+
+            } catch (AssertionFailedError e) {
+                // java.home is bogus
+                assertEquals("javac.exe", j);
+            }
+
+            assertEquals("foo.exe", JavaEnvUtils.getJreExecutable("foo"));
+            assertEquals("foo.exe", JavaEnvUtils.getJdkExecutable("foo"));
+        }
+    }
+
+    public void testGetExecutableMostPlatforms() {
+        if (!Os.isName("netware") && !Os.isFamily("windows")) {
+            FileUtils fileUtils = FileUtils.newFileUtils();
+            String javaHome =
+                fileUtils.normalize(System.getProperty("java.home"))
+                .getAbsolutePath();
+
+            // could still be OS/2
+            String extension = Os.isFamily("dos") ? ".exe" : "";
+
+            String j = JavaEnvUtils.getJreExecutable("java");
+            if (!extension.equals("")) {
+                assertTrue(j.endsWith(extension));
+            }
+            assertTrue(j+" is absolute", (new File(j)).isAbsolute());
+            assertTrue(j+" is normalized and in the JRE dir",
+                       j.startsWith(javaHome));
+
+            j = JavaEnvUtils.getJdkExecutable("javac");
+            if (!extension.equals("")) {
+                assertTrue(j.endsWith(extension));
+            }
+            assertTrue(j+" is absolute", (new File(j)).isAbsolute());
+
+            String javaHomeParent =
+                fileUtils.normalize(javaHome+"/..").getAbsolutePath();
+            assertTrue(j+" is normalized and in the JDK dir",
+                       j.startsWith(javaHomeParent));
+
+            if (JavaEnvUtils.getJavaVersion() == JavaEnvUtils.JAVA_1_0 ||
+                JavaEnvUtils.getJavaVersion() == JavaEnvUtils.JAVA_1_1 ||
+                Os.isFamily("mac")) {
+                assertTrue(j+" is normalized and in the JRE dir",
+                           j.startsWith(javaHome));
+            } else {
+                assertTrue(j+" is normalized and not in the JRE dir",
+                           !j.startsWith(javaHome));
+            }
+
+            assertEquals("foo"+extension,
+                         JavaEnvUtils.getJreExecutable("foo"));
+            assertEquals("foo"+extension,
+                         JavaEnvUtils.getJdkExecutable("foo"));
+        }
+
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/LazyFileOutputStreamTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/LazyFileOutputStreamTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/LazyFileOutputStreamTest.java	(revision 16139)
@@ -0,0 +1,68 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.File;
+import java.io.IOException;
+import junit.framework.TestCase;
+
+/**
+ * @since Ant 1.6
+ */
+public class LazyFileOutputStreamTest extends TestCase {
+    private LazyFileOutputStream los;
+    private final static File f = new File("test.txt");
+
+    public LazyFileOutputStreamTest(String s) {
+        super(s);
+    }
+
+    public void setUp() {
+        los = new LazyFileOutputStream(f);
+    }
+
+    public void tearDown() throws IOException {
+        try {
+            los.close();
+        } finally {
+            f.delete();
+        }
+    }
+
+    public void testNoFileWithoutWrite() throws IOException {
+        los.close();
+        assertTrue(f + " has not been written.", !f.exists());
+    }
+
+    public void testOpen() throws IOException {
+        los.open();
+        los.close();
+        assertTrue(f + " has been written.", f.exists());
+    }
+
+    public void testSingleByte() throws IOException {
+        los.write(0);
+        los.close();
+        assertTrue(f + " has been written.", f.exists());
+    }
+
+    public void testByteArray() throws IOException {
+        los.write(new byte[] {0});
+        los.close();
+        assertTrue(f + " has been written.", f.exists());
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/LoaderUtilsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/LoaderUtilsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/LoaderUtilsTest.java	(revision 16139)
@@ -0,0 +1,42 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.File;
+import junit.framework.TestCase;
+
+/**
+ * @since Ant 1.6
+ */
+public class LoaderUtilsTest extends TestCase {
+
+    public LoaderUtilsTest(String name) {
+        super(name);
+    }
+
+    public void testGetXyzSource() {
+        File f1 = LoaderUtils.getClassSource(LoaderUtils.class);
+        assertNotNull(f1);
+
+        File f2 = LoaderUtils.getResourceSource(null,
+                                                "org/apache/tools/ant/taskdefs/defaults.properties");
+        assertNotNull(f2);
+
+        assertEquals(f1.getAbsolutePath(), f2.getAbsolutePath());
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/PackageNameMapperTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/PackageNameMapperTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/PackageNameMapperTest.java	(revision 16139)
@@ -0,0 +1,39 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+ package org.apache.tools.ant.util;
+
+import java.io.File;
+import junit.framework.TestCase;
+
+public class PackageNameMapperTest extends TestCase {
+    public PackageNameMapperTest(String name) { super(name); }
+
+    public void testMapping() {
+        PackageNameMapper mapper = new PackageNameMapper();
+        mapper.setFrom("*.java");
+        mapper.setTo("TEST-*.xml");
+        String file = fixupPath("org/apache/tools/ant/util/PackageNameMapperTest.java");
+        String result = mapper.mapFileName(file)[0];
+
+        assertEquals("TEST-org.apache.tools.ant.util.PackageNameMapperTest.xml",
+          result);
+    }
+
+    private String fixupPath(String file) {
+        return file.replace('/', File.separatorChar);
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/ReferencesTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/ReferencesTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/ReferencesTest.java	(revision 16139)
@@ -0,0 +1,46 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import junit.framework.TestCase;
+
+
+/**
+ * this test just makes sure that we really do create weak references
+ * on java1.2+, and is written to not import the WeakishReference12 class
+ * because then we'd have to make the class conditional on java1.2+.
+ */
+public class ReferencesTest extends TestCase{
+
+    public ReferencesTest(String s) {
+        super(s);
+    }
+
+    /**
+     * look at the type of a reference we have created
+     */
+    public static void testReferencesAreSoft() {
+        boolean isJava11=JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1);
+        WeakishReference reference = WeakishReference.createReference(new Object());
+        if ((reference.getClass().getName().indexOf("HardReference") > 0 )
+            && !isJava11) {
+            fail("We should be creating soft references in this version of Java");
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/ResourceUtilsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/ResourceUtilsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/ResourceUtilsTest.java	(revision 16139)
@@ -0,0 +1,62 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.taskdefs.Echo;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceFactory;
+
+import java.io.File;
+
+import junit.framework.TestCase;
+
+/**
+ * Tests for org.apache.tools.ant.util.ResourceUtils.
+ */
+public class ResourceUtilsTest extends TestCase
+    implements ResourceFactory, FileNameMapper {
+
+    private Echo taskINeedForLogging = new Echo();
+
+    public ResourceUtilsTest(String name) {
+        super(name);
+        taskINeedForLogging.setProject(new Project());
+    }
+
+    public void testNoDuplicates() {
+        Resource r = new Resource("samual vimes", true, 1, false);
+        Resource[] toNew =
+            ResourceUtils.selectOutOfDateSources(taskINeedForLogging,
+                                                 new Resource[] {r},
+                                                 this, this);
+        assertEquals(1, toNew.length);
+    }
+
+    /* ============ ResourceFactory interface ====================== */
+    public Resource getResource(String name) {
+        return new Resource(name); // implies lastModified == 0
+    }
+
+    /* ============ FileNameMapper interface ======================= */
+    public void setFrom(String s) {}
+    public void setTo(String s) {}
+    public String[] mapFileName(String s) {
+        return new String[] {"fred colon", "carrot ironfoundersson"};
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/StringUtilsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/StringUtilsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/StringUtilsTest.java	(revision 16139)
@@ -0,0 +1,58 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.util.Vector;
+
+import junit.framework.TestCase;
+
+/**
+ * Test for StringUtils
+ */
+public class StringUtilsTest extends TestCase {
+    public StringUtilsTest(String s) {
+        super(s);
+    }
+
+    public void testSplit(){
+        final String data = "a,b,,";
+        Vector res = StringUtils.split(data, ',');
+        assertEquals(4, res.size());
+        assertEquals("a", res.elementAt(0));
+        assertEquals("b", res.elementAt(1));
+        assertEquals("", res.elementAt(2));
+        assertEquals("", res.elementAt(3));
+    }
+
+    public void testSplitLines(){
+        final String data = "a\r\nb\nc\nd\ne";
+        Vector res = StringUtils.lineSplit(data);
+        assertEquals(5, res.size());
+        assertEquals("a\r", res.elementAt(0));
+        assertEquals("b", res.elementAt(1));
+        assertEquals("c", res.elementAt(2));
+        assertEquals("d", res.elementAt(3));
+        assertEquals("e", res.elementAt(4));
+    }
+
+    public void testReplace() {
+        final String data = "abcabcabca";
+        String res = StringUtils.replace(data, "a", "");
+        assertEquals("bcbcbc", res);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/UnPackageNameMapperTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/UnPackageNameMapperTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/UnPackageNameMapperTest.java	(revision 16139)
@@ -0,0 +1,39 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.ant.util;
+
+import java.io.File;
+import junit.framework.TestCase;
+
+public class UnPackageNameMapperTest extends TestCase {
+    public UnPackageNameMapperTest(String name) { super(name); }
+
+    public void testMapping() {
+        UnPackageNameMapper mapper = new UnPackageNameMapper();
+        mapper.setFrom("TEST-*.xml");
+        mapper.setTo("*.java");
+        String file ="TEST-org.apache.tools.ant.util.UnPackageNameMapperTest.xml";
+        String result = mapper.mapFileName(file)[0];
+        String expected = fixupPath("org/apache/tools/ant/util/UnPackageNameMapperTest.java");
+
+        assertEquals(expected, result);
+    }
+
+    private String fixupPath(String file) {
+        return file.replace('/', File.separatorChar);
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/facade/FacadeTaskHelperTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/facade/FacadeTaskHelperTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/facade/FacadeTaskHelperTest.java	(revision 16139)
@@ -0,0 +1,62 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.facade;
+
+import junit.framework.TestCase;
+
+/**
+ * @since Ant 1.5
+ */
+public class FacadeTaskHelperTest extends TestCase {
+
+    public FacadeTaskHelperTest(String name) {
+        super(name);
+    }
+
+    public void testPrecedenceRules() {
+        FacadeTaskHelper fth = new FacadeTaskHelper("foo");
+        assertEquals("foo", fth.getImplementation());
+
+        fth.setMagicValue("bar");
+        assertEquals("bar", fth.getImplementation());
+
+        fth = new FacadeTaskHelper("foo", "bar");
+        assertEquals("bar", fth.getImplementation());
+
+        fth = new FacadeTaskHelper("foo", null);
+        assertEquals("foo", fth.getImplementation());
+
+        fth = new FacadeTaskHelper("foo");
+        fth.setMagicValue("bar");
+        fth.setImplementation("baz");
+        assertEquals("baz", fth.getImplementation());
+    }
+
+    public void testHasBeenSet() {
+        FacadeTaskHelper fth = new FacadeTaskHelper("foo");
+        assertTrue("nothing set", !fth.hasBeenSet());
+        fth.setMagicValue(null);
+        assertTrue("magic has not been set", !fth.hasBeenSet());
+        fth.setMagicValue("foo");
+        assertTrue("magic has been set", fth.hasBeenSet());
+        fth.setMagicValue(null);
+        assertTrue(!fth.hasBeenSet());
+        fth.setImplementation("baz");
+        assertTrue("set explicitly", fth.hasBeenSet());
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/facade/ImplementationSpecificArgumentTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/facade/ImplementationSpecificArgumentTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/facade/ImplementationSpecificArgumentTest.java	(revision 16139)
@@ -0,0 +1,58 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.facade;
+
+import junit.framework.TestCase;
+
+/**
+ * @since Ant 1.5
+ */
+public class ImplementationSpecificArgumentTest extends TestCase {
+
+    public ImplementationSpecificArgumentTest(String name) {
+        super(name);
+    }
+
+    public void testDependsOnImplementation() {
+        ImplementationSpecificArgument ia =
+            new ImplementationSpecificArgument();
+        ia.setLine("A B");
+        String[] parts = ia.getParts();
+        assertNotNull(parts);
+        assertEquals(2, parts.length);
+        assertEquals("A", parts[0]);
+        assertEquals("B", parts[1]);
+
+        parts = ia.getParts(null);
+        assertNotNull(parts);
+        assertEquals(2, parts.length);
+        assertEquals("A", parts[0]);
+        assertEquals("B", parts[1]);
+
+        ia.setImplementation("foo");
+        parts = ia.getParts(null);
+        assertNotNull(parts);
+        assertEquals(0, parts.length);
+
+        parts = ia.getParts("foo");
+        assertNotNull(parts);
+        assertEquals(2, parts.length);
+        assertEquals("A", parts[0]);
+        assertEquals("B", parts[1]);
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/JakartaOroMatcherTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/JakartaOroMatcherTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/JakartaOroMatcherTest.java	(revision 16139)
@@ -0,0 +1,34 @@
+/*
+ * Copyright  2000,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.regexp;
+
+/**
+ * Tests for the jakarta-oro implementation of the RegexpMatcher interface.
+ *
+ */
+public class JakartaOroMatcherTest extends RegexpMatcherTest {
+
+    public RegexpMatcher getImplementation() {
+        return new JakartaOroMatcher();
+    }
+
+    public JakartaOroMatcherTest(String name) {
+        super(name);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/JakartaOroRegexpTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/JakartaOroRegexpTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/JakartaOroRegexpTest.java	(revision 16139)
@@ -0,0 +1,34 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.regexp;
+
+/**
+ * Tests for the jakarta-oro implementation of the Regexp interface.
+ *
+ */
+public class JakartaOroRegexpTest extends RegexpTest {
+
+    public Regexp getRegexpImplementation() {
+        return new JakartaOroRegexp();
+    }
+
+    public JakartaOroRegexpTest(String name) {
+        super(name);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/JakartaRegexpMatcherTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/JakartaRegexpMatcherTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/JakartaRegexpMatcherTest.java	(revision 16139)
@@ -0,0 +1,64 @@
+/*
+ * Copyright  2000-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.regexp;
+
+import java.io.IOException;
+
+import junit.framework.AssertionFailedError;
+
+import org.apache.tools.ant.taskdefs.condition.Os;
+
+/**
+ * Tests for the jakarta-regexp implementation of the RegexpMatcher interface.
+ *
+ */
+public class JakartaRegexpMatcherTest extends RegexpMatcherTest {
+
+    public RegexpMatcher getImplementation() {
+        return new JakartaRegexpMatcher();
+    }
+
+    public JakartaRegexpMatcherTest(String name) {
+        super(name);
+    }
+
+    public void testWindowsLineSeparator2() throws IOException {
+        try {
+            super.testWindowsLineSeparator2();
+            fail("Should trigger when this bug is fixed. {@since 1.2}");
+        } catch (AssertionFailedError e) {
+        }
+    }
+
+    /**
+     * Fails for the same reason as "default" mode in doEndTest2.
+     */
+    public void testUnixLineSeparator() throws IOException {
+        try {
+            super.testUnixLineSeparator();
+            fail("Should trigger once this bug is fixed. {@since 1.2}");
+        } catch (AssertionFailedError e) {
+        }
+    }
+
+
+    /**
+     * Fails for "default" mode.
+     */
+    protected void doEndTest2(String text) {}
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/JakartaRegexpRegexpTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/JakartaRegexpRegexpTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/JakartaRegexpRegexpTest.java	(revision 16139)
@@ -0,0 +1,63 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.regexp;
+
+import java.io.IOException;
+
+import junit.framework.AssertionFailedError;
+
+import org.apache.tools.ant.taskdefs.condition.Os;
+
+/**
+ * Tests for the jakarta-regexp implementation of the Regexp interface.
+ *
+ */
+public class JakartaRegexpRegexpTest extends RegexpTest {
+
+    public Regexp getRegexpImplementation() {
+        return new JakartaRegexpRegexp();
+    }
+
+    public JakartaRegexpRegexpTest(String name) {
+        super(name);
+    }
+
+    public void testWindowsLineSeparator2() throws IOException {
+        try {
+            super.testWindowsLineSeparator2();
+            fail("Should trigger when this bug is fixed. {@since 1.2}");
+        } catch (AssertionFailedError e){
+        }
+    }
+
+    /**
+     * Fails for the same reason as "default" mode in doEndTest2.
+     */
+    public void testUnixLineSeparator() throws IOException {
+        try {
+            super.testUnixLineSeparator();
+            fail("Should trigger once this bug is fixed. {@since 1.2}");
+        } catch (AssertionFailedError e){
+        }
+    }
+
+    /**
+     * Fails for "default" mode.
+     */
+    protected void doEndTest2(String text) {}
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/Jdk14RegexpMatcherTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/Jdk14RegexpMatcherTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/Jdk14RegexpMatcherTest.java	(revision 16139)
@@ -0,0 +1,69 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.regexp;
+
+import java.io.IOException;
+
+import junit.framework.AssertionFailedError;
+
+/**
+ * Tests for the JDK 1.4 implementation of the RegexpMatcher interface.
+ *
+ */
+public class Jdk14RegexpMatcherTest extends RegexpMatcherTest {
+
+    public RegexpMatcher getImplementation() {
+        return new Jdk14RegexpMatcher();
+    }
+
+    public Jdk14RegexpMatcherTest(String name) {
+        super(name);
+    }
+
+    public void testParagraphCharacter() throws IOException {
+        try {
+            super.testParagraphCharacter();
+            fail("Should trigger once fixed. {@since JDK 1.4RC1}");
+        } catch (AssertionFailedError e){
+        }
+    }
+
+    public void testLineSeparatorCharacter() throws IOException {
+        try {
+            super.testLineSeparatorCharacter();
+            fail("Should trigger once fixed. {@since JDK 1.4RC1}");
+        } catch (AssertionFailedError e){
+        }
+    }
+
+    public void testStandaloneCR() throws IOException {
+        try {
+            super.testStandaloneCR();
+            fail("Should trigger once fixed. {@since JDK 1.4RC1}");
+        } catch (AssertionFailedError e){
+        }
+    }
+
+    public void testWindowsLineSeparator() throws IOException {
+        try {
+            super.testWindowsLineSeparator();
+            fail("Should trigger once fixed. {@since JDK 1.4RC1}");
+        } catch (AssertionFailedError e){
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/Jdk14RegexpRegexpTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/Jdk14RegexpRegexpTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/Jdk14RegexpRegexpTest.java	(revision 16139)
@@ -0,0 +1,70 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.regexp;
+
+import java.io.IOException;
+
+import junit.framework.AssertionFailedError;
+
+/**
+ * Tests for the JDK 1.4 implementation of the Regexp interface.
+ *
+ */
+public class Jdk14RegexpRegexpTest extends RegexpTest {
+
+    public Regexp getRegexpImplementation() {
+        return new Jdk14RegexpRegexp();
+    }
+
+    public Jdk14RegexpRegexpTest(String name) {
+        super(name);
+    }
+
+    public void testParagraphCharacter() throws IOException {
+        try {
+            super.testParagraphCharacter();
+            fail("Should trigger once fixed. {@since JDK 1.4RC1}");
+        } catch (AssertionFailedError e){
+        }
+    }
+
+    public void testLineSeparatorCharacter() throws IOException {
+        try {
+            super.testLineSeparatorCharacter();
+            fail("Should trigger once fixed. {@since JDK 1.4RC1}");
+        } catch (AssertionFailedError e){
+        }
+    }
+
+    public void testStandaloneCR() throws IOException {
+        try {
+            super.testStandaloneCR();
+            fail("Should trigger once fixed. {@since JDK 1.4RC1}");
+        } catch (AssertionFailedError e){
+        }
+    }
+
+    public void testWindowsLineSeparator() throws IOException {
+        try {
+            super.testWindowsLineSeparator();
+            fail("Should trigger once fixed. {@since JDK 1.4RC1}");
+        } catch (AssertionFailedError e){
+        }
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/RegexpMatcherTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/RegexpMatcherTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/RegexpMatcherTest.java	(revision 16139)
@@ -0,0 +1,205 @@
+/*
+ * Copyright  2000-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.regexp;
+
+import java.io.*;
+import java.util.Vector;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+/**
+ * Tests for all implementations of the RegexpMatcher interface.
+ *
+ */
+public abstract class RegexpMatcherTest extends TestCase {
+
+    public final static String UNIX_LINE = "\n";
+
+    private RegexpMatcher reg;
+
+    public abstract RegexpMatcher getImplementation();
+
+    protected final RegexpMatcher getReg() {return reg;}
+
+    public RegexpMatcherTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        reg = getImplementation();
+    }
+
+    public void testMatches() {
+        reg.setPattern("aaaa");
+        assertTrue("aaaa should match itself", reg.matches("aaaa"));
+        assertTrue("aaaa should match xaaaa", reg.matches("xaaaa"));
+        assertTrue("aaaa shouldn\'t match xaaa", !reg.matches("xaaa"));
+        reg.setPattern("^aaaa");
+        assertTrue("^aaaa shouldn\'t match xaaaa", !reg.matches("xaaaa"));
+        assertTrue("^aaaa should match aaaax", reg.matches("aaaax"));
+        reg.setPattern("aaaa$");
+        assertTrue("aaaa$ shouldn\'t match aaaax", !reg.matches("aaaax"));
+        assertTrue("aaaa$ should match xaaaa", reg.matches("xaaaa"));
+        reg.setPattern("[0-9]+");
+        assertTrue("[0-9]+ should match 123", reg.matches("123"));
+        assertTrue("[0-9]+ should match 1", reg.matches("1"));
+        assertTrue("[0-9]+ shouldn\'t match \'\'", !reg.matches(""));
+        assertTrue("[0-9]+ shouldn\'t match a", !reg.matches("a"));
+        reg.setPattern("[0-9]*");
+        assertTrue("[0-9]* should match 123", reg.matches("123"));
+        assertTrue("[0-9]* should match 1", reg.matches("1"));
+        assertTrue("[0-9]* should match \'\'", reg.matches(""));
+        assertTrue("[0-9]* should match a", reg.matches("a"));
+        reg.setPattern("([0-9]+)=\\1");
+        assertTrue("([0-9]+)=\\1 should match 1=1", reg.matches("1=1"));
+        assertTrue("([0-9]+)=\\1 shouldn\'t match 1=2", !reg.matches("1=2"));
+    }
+
+    public void testGroups() {
+        reg.setPattern("aaaa");
+        Vector v = reg.getGroups("xaaaa");
+        assertEquals("No parens -> no extra groups", 1, v.size());
+        assertEquals("Trivial match with no parens", "aaaa",
+                     (String) v.elementAt(0));
+
+        reg.setPattern("(aaaa)");
+        v = reg.getGroups("xaaaa");
+        assertEquals("Trivial match with single paren", 2, v.size());
+        assertEquals("Trivial match with single paren, full match", "aaaa",
+                     (String) v.elementAt(0));
+        assertEquals("Trivial match with single paren, matched paren", "aaaa",
+                     (String) v.elementAt(0));
+
+        reg.setPattern("(a+)b(b+)");
+        v = reg.getGroups("xaabb");
+        assertEquals(3, v.size());
+        assertEquals("aabb", (String) v.elementAt(0));
+        assertEquals("aa", (String) v.elementAt(1));
+        assertEquals("b", (String) v.elementAt(2));
+    }
+
+    public void testBugzillaReport14619() {
+        reg.setPattern("^(.*)/src/((.*/)*)([a-zA-Z0-9_\\.]+)\\.java$");
+        Vector v = reg.getGroups("de/tom/src/Google.java");
+        assertEquals(5, v.size());
+        assertEquals("de/tom", v.elementAt(1));
+        assertEquals("", v.elementAt(2));
+        assertEquals("", v.elementAt(3));
+        assertEquals("Google", v.elementAt(4));
+    }
+
+    public void testCaseInsensitiveMatch() {
+        reg.setPattern("aaaa");
+        assertTrue("aaaa doesn't match AAaa", !reg.matches("AAaa"));
+        assertTrue("aaaa matches AAaa ignoring case",
+                   reg.matches("AAaa", RegexpMatcher.MATCH_CASE_INSENSITIVE));
+    }
+
+
+// make sure there are no issues concerning line separator interpretation
+// a line separator for regex (perl) is always a unix line (ie \n)
+
+    public void testParagraphCharacter() throws IOException {
+        reg.setPattern("end of text$");
+        assertTrue("paragraph character", !reg.matches("end of text\u2029"));
+    }
+
+    public void testLineSeparatorCharacter() throws IOException {
+        reg.setPattern("end of text$");
+        assertTrue("line-separator character", !reg.matches("end of text\u2028"));
+    }
+
+    public void testNextLineCharacter() throws IOException {
+        reg.setPattern("end of text$");
+        assertTrue("next-line character", !reg.matches("end of text\u0085"));
+    }
+
+    public void testStandaloneCR() throws IOException {
+        reg.setPattern("end of text$");
+        assertTrue("standalone CR", !reg.matches("end of text\r"));
+    }
+
+    public void testWindowsLineSeparator() throws IOException {
+        reg.setPattern("end of text$");
+        assertTrue("Windows line separator", !reg.matches("end of text\r\n"));
+    }
+
+    public void testWindowsLineSeparator2() throws IOException {
+        reg.setPattern("end of text\r$");
+        assertTrue("Windows line separator", reg.matches("end of text\r\n"));
+    }
+
+    public void testUnixLineSeparator() throws IOException {
+        reg.setPattern("end of text$");
+        assertTrue("Unix line separator", reg.matches("end of text\n"));
+    }
+
+
+    public void testMultiVersusSingleLine() throws IOException {
+        StringBuffer buf = new StringBuffer();
+        buf.append("Line1").append(UNIX_LINE);
+        buf.append("starttest Line2").append(UNIX_LINE);
+        buf.append("Line3 endtest").append(UNIX_LINE);
+        buf.append("Line4").append(UNIX_LINE);
+        String text = buf.toString();
+
+        doStartTest1(text);
+        doStartTest2(text);
+        doEndTest1(text);
+        doEndTest2(text);
+    }
+
+    protected void doStartTest1(String text) {
+        reg.setPattern("^starttest");
+        assertTrue("^starttest in default mode", !reg.matches(text));
+        assertTrue("^starttest in single line mode",
+               !reg.matches(text, RegexpMatcher.MATCH_SINGLELINE));
+        assertTrue("^starttest in multi line mode",
+               reg.matches(text, RegexpMatcher.MATCH_MULTILINE));
+    }
+
+    protected void doStartTest2(String text) {
+        reg.setPattern("^Line1");
+        assertTrue("^Line1 in default mode", reg.matches(text));
+        assertTrue("^Line1 in single line mode",
+               reg.matches(text, RegexpMatcher.MATCH_SINGLELINE));
+        assertTrue("^Line1 in multi line mode",
+               reg.matches(text, RegexpMatcher.MATCH_MULTILINE));
+    }
+
+    protected void doEndTest1(String text) {
+        reg.setPattern("endtest$");
+        assertTrue("endtest$ in default mode", !reg.matches(text));
+        assertTrue("endtest$ in single line mode",
+               !reg.matches(text, RegexpMatcher.MATCH_SINGLELINE));
+        assertTrue("endtest$ in multi line mode",
+               reg.matches(text, RegexpMatcher.MATCH_MULTILINE));
+    }
+
+    protected void doEndTest2(String text) {
+        reg.setPattern("Line4$");
+        assertTrue("Line4$ in default mode", reg.matches(text));
+        assertTrue("Line4$ in single line mode",
+               reg.matches(text, RegexpMatcher.MATCH_SINGLELINE));
+        assertTrue("Line4$ in multi line mode",
+               reg.matches(text, RegexpMatcher.MATCH_MULTILINE));
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/RegexpTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/RegexpTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/ant/util/regexp/RegexpTest.java	(revision 16139)
@@ -0,0 +1,62 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.ant.util.regexp;
+
+/**
+ * Tests for all implementations of the Regexp interface.
+ *
+ */
+public abstract class RegexpTest extends RegexpMatcherTest {
+
+    private static final String test = "abcdefg-abcdefg";
+    private static final String pattern = "ab([^d]*)d([^f]*)f";
+
+    public RegexpTest(String name) {
+        super(name);
+    }
+
+    public final RegexpMatcher getImplementation() {
+        return getRegexpImplementation();
+    }
+
+    public abstract Regexp getRegexpImplementation();
+
+    public void testSubstitution() {
+        Regexp reg = (Regexp) getReg();
+        reg.setPattern(pattern);
+        assertTrue(reg.matches(test));
+        assertEquals("abedcfg-abcdefg", reg.substitute(test, "ab\\2d\\1f",
+                                                       Regexp.MATCH_DEFAULT));
+    }
+
+    public void testReplaceFirstSubstitution() {
+        Regexp reg = (Regexp) getReg();
+        reg.setPattern(pattern);
+        assertTrue(reg.matches(test));
+        assertEquals("abedcfg-abcdefg", reg.substitute(test, "ab\\2d\\1f",
+                                                       Regexp.REPLACE_FIRST));
+    }
+
+    public void testReplaceAllSubstitution() {
+        Regexp reg = (Regexp) getReg();
+        reg.setPattern(pattern);
+        assertTrue(reg.matches(test));
+        assertEquals("abedcfg-abedcfg", reg.substitute(test, "ab\\2d\\1f",
+                                                       Regexp.REPLACE_ALL));
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/mail/MailMessageTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/mail/MailMessageTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/mail/MailMessageTest.java	(revision 16139)
@@ -0,0 +1,733 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.mail;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.ServerSocket;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.mail.MailMessage;
+
+import junit.framework.TestCase;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.mail.MailMessage.
+ *
+ * @since Ant 1.6
+ */
+public class MailMessageTest extends TestCase {
+
+    // 27224 = magic (a random port which is unlikely to be in use)
+    private static int TEST_PORT = 27224;
+
+    private String local = null;
+
+    public MailMessageTest(String name) {
+        super(name);
+    }
+
+    public void setUp() {
+        try {
+            local = InetAddress.getLocalHost().getHostName();
+        } catch (java.net.UnknownHostException uhe) {
+            // ignore
+        }
+    }
+
+    /**
+     *  Test an example that is similar to the one given in the API
+     *  If this testcase takes >90s to complete, it is very likely that
+     *  the two threads are blocked waiting for each other and Thread.join()
+     *  timed out.
+     */
+    public void testAPIExample() {
+
+        ServerThread testMailServer = new ServerThread();
+        Thread server = new Thread(testMailServer);
+        server.start();
+
+        ClientThread testMailClient = new ClientThread();
+
+        testMailClient.from("Mail Message <EmailTaskTest@ant.apache.org>");
+        testMailClient.to("to@you.com");
+        testMailClient.cc("cc1@you.com");
+        testMailClient.cc("cc2@you.com");
+        testMailClient.bcc("bcc@you.com");
+        testMailClient.setSubject("Test subject");
+        testMailClient.setMessage( "test line 1\n" +
+            "test line 2" );
+
+        Thread client = new Thread(testMailClient);
+        client.start();
+
+        try {
+            server.join(60 * 1000); // 60s
+            client.join(30 * 1000); // a further 30s
+        } catch (InterruptedException ie ) {
+            fail( "InterruptedException: " + ie );
+        }
+
+        String result = testMailServer.getResult();
+        String expectedResult = "220 test SMTP EmailTaskTest\r\n" +
+        "HELO " + local + "\r\n" +
+        "250 " + local + " Hello " + local + " [127.0.0.1], pleased to meet you\r\n" +
+        "MAIL FROM: <EmailTaskTest@ant.apache.org>\r\n" +
+        "250\r\n" +
+        "RCPT TO: <to@you.com>\r\n" +
+        "250\r\n" +
+        "RCPT TO: <cc1@you.com>\r\n" +
+        "250\r\n" +
+        "RCPT TO: <cc2@you.com>\r\n" +
+        "250\r\n" +
+        "RCPT TO: <bcc@you.com>\r\n" +
+        "250\r\n" +
+        "DATA\r\n" +
+        "354\r\n" +
+        "Subject: Test subject\r\n" +
+        "From: Mail Message <EmailTaskTest@ant.apache.org>\r\n" +
+        "To: to@you.com\r\n" +
+        "Cc: cc1@you.com, cc2@you.com\r\n" +
+        "X-Mailer: org.apache.tools.mail.MailMessage (ant.apache.org)\r\n" +
+        "\r\n" +
+        "test line 1\r\n" +
+        "test line 2\r\n" +
+        "\r\n" +
+        ".\r\n" +
+        "250\r\n" +
+        "QUIT\r\n" +
+        "221\r\n";
+        for (int icounter = 0; icounter<expectedResult.length(); icounter++) {
+            if (icounter < result.length()) {
+                if (expectedResult.charAt(icounter) != result.charAt(icounter)) {
+                    System.out.println("posit " + icounter + " expected "
+                        + expectedResult.charAt(icounter)
+                    + " result " + result.charAt(icounter));
+                }
+            }
+        }
+        if (expectedResult.length()>result.length()) {
+            System.out.println("excedent of expected result "
+                + expectedResult.substring(result.length()));
+        }
+        if (expectedResult.length()<result.length()) {
+            System.out.println("excedent of result "
+                + result.substring(expectedResult.length()));
+        }
+        assertEquals(expectedResult.length(), result.length());
+        assertEquals(expectedResult, result); // order of headers cannot be guaranteed
+        if (testMailClient.isFailed()) {
+            fail(testMailClient.getFailMessage());
+        }
+    }
+
+    /**
+     *  Test a MailMessage with no cc or bcc lines
+     */
+    public void testToOnly() {
+        ServerThread testMailServer = new ServerThread();
+        Thread server = new Thread(testMailServer);
+        server.start();
+
+        ClientThread testMailClient = new ClientThread();
+
+        testMailClient.from("Mail Message <EmailTaskTest@ant.apache.org>");
+        testMailClient.to("to@you.com");
+        testMailClient.setSubject("Test subject");
+        testMailClient.setMessage( "test line 1\n" +
+            "test line 2" );
+
+        Thread client = new Thread(testMailClient);
+        client.start();
+
+        try {
+            server.join(60 * 1000); // 60s
+            client.join(30 * 1000); // a further 30s
+        } catch (InterruptedException ie ) {
+            fail("InterruptedException: " + ie);
+        }
+
+        String result = testMailServer.getResult();
+        String expectedResult = "220 test SMTP EmailTaskTest\r\n" +
+        "HELO " + local + "\r\n" +
+        "250 " + local + " Hello " + local + " [127.0.0.1], pleased to meet you\r\n" +
+        "MAIL FROM: <EmailTaskTest@ant.apache.org>\r\n" +
+        "250\r\n" +
+        "RCPT TO: <to@you.com>\r\n" +
+        "250\r\n" +
+        "DATA\r\n" +
+        "354\r\n" +
+        "Subject: Test subject\r\n" +
+            "From: Mail Message <EmailTaskTest@ant.apache.org>\r\n" +
+            "To: to@you.com\r\n" +
+        "X-Mailer: org.apache.tools.mail.MailMessage (ant.apache.org)\r\n" +
+        "\r\n" +
+        "test line 1\r\n" +
+        "test line 2\r\n" +
+        "\r\n" +
+        ".\r\n" +
+        "250\r\n" +
+        "QUIT\r\n" +
+        "221\r\n";
+        assertEquals(expectedResult.length(), result.length());
+        assertEquals(expectedResult, result); // order of headers cannot be guaranteed
+        if (testMailClient.isFailed()) {
+            fail(testMailClient.getFailMessage());
+        }
+    }
+
+
+    /**
+     *  Test a MailMessage with no to or bcc lines
+     */
+    public void testCcOnly() {
+        ServerThread testMailServer = new ServerThread();
+        Thread server = new Thread(testMailServer);
+        server.start();
+
+        ClientThread testMailClient = new ClientThread();
+
+        testMailClient.from("Mail Message <EmailTaskTest@ant.apache.org>");
+        testMailClient.cc("cc@you.com");
+        testMailClient.setSubject("Test subject");
+        testMailClient.setMessage( "test line 1\n" +
+            "test line 2" );
+
+        Thread client = new Thread(testMailClient);
+        client.start();
+
+        try {
+            server.join(60 * 1000); // 60s
+            client.join(30 * 1000); // a further 30s
+        } catch (InterruptedException ie ) {
+            fail( "InterruptedException: " + ie );
+        }
+
+        String result = testMailServer.getResult();
+        String expectedResult = "220 test SMTP EmailTaskTest\r\n" +
+        "HELO " + local + "\r\n" +
+        "250 " + local + " Hello " + local + " [127.0.0.1], pleased to meet you\r\n" +
+        "MAIL FROM: <EmailTaskTest@ant.apache.org>\r\n" +
+        "250\r\n" +
+        "RCPT TO: <cc@you.com>\r\n" +
+        "250\r\n" +
+        "DATA\r\n" +
+        "354\r\n" +
+        "Subject: Test subject\r\n" +
+            "From: Mail Message <EmailTaskTest@ant.apache.org>\r\n" +
+            "Cc: cc@you.com\r\n" +
+        "X-Mailer: org.apache.tools.mail.MailMessage (ant.apache.org)\r\n" +
+        "\r\n" +
+        "test line 1\r\n" +
+        "test line 2\r\n" +
+        "\r\n" +
+        ".\r\n" +
+        "250\r\n" +
+        "QUIT\r\n" +
+        "221\r\n";
+        assertEquals(expectedResult.length(), result.length());
+        assertEquals(expectedResult, result);
+        if (testMailClient.isFailed()) {
+            fail(testMailClient.getFailMessage());
+        }
+    }
+
+
+    /**
+     *  Test a MailMessage with no to or cc lines
+     */
+    public void testBccOnly() {
+        ServerThread testMailServer = new ServerThread();
+        Thread server = new Thread(testMailServer);
+        server.start();
+
+        ClientThread testMailClient = new ClientThread();
+
+        testMailClient.from("Mail Message <EmailTaskTest@ant.apache.org>");
+        testMailClient.bcc("bcc@you.com");
+        testMailClient.setSubject("Test subject");
+        testMailClient.setMessage( "test line 1\n" +
+            "test line 2" );
+
+        Thread client = new Thread(testMailClient);
+        client.start();
+
+        try {
+            server.join(60 * 1000); // 60s
+            client.join(30 * 1000); // a further 30s
+        } catch (InterruptedException ie ) {
+            fail( "InterruptedException: " + ie );
+        }
+
+        String result = testMailServer.getResult();
+        String expectedResult = "220 test SMTP EmailTaskTest\r\n" +
+        "HELO " + local + "\r\n" +
+        "250 " + local + " Hello " + local + " [127.0.0.1], pleased to meet you\r\n" +
+        "MAIL FROM: <EmailTaskTest@ant.apache.org>\r\n" +
+        "250\r\n" +
+        "RCPT TO: <bcc@you.com>\r\n" +
+        "250\r\n" +
+        "DATA\r\n" +
+        "354\r\n" +
+        "Subject: Test subject\r\n" +
+        "From: Mail Message <EmailTaskTest@ant.apache.org>\r\n" +
+        "X-Mailer: org.apache.tools.mail.MailMessage (ant.apache.org)\r\n" +
+        "\r\n" +
+        "test line 1\r\n" +
+        "test line 2\r\n" +
+        "\r\n" +
+        ".\r\n" +
+        "250\r\n" +
+        "QUIT\r\n" +
+        "221\r\n";
+        assertEquals( expectedResult.length(), result.length() );
+        assertEquals( expectedResult, result );
+        if ( testMailClient.isFailed() ) {
+            fail( testMailClient.getFailMessage() );
+        }
+    }
+
+
+    /**
+     *  Test a MailMessage with no subject line
+     *  Subject is an optional field (RFC 822 s4.1)
+     */
+    public void testNoSubject() {
+        ServerThread testMailServer = new ServerThread();
+        Thread server = new Thread(testMailServer);
+        server.start();
+
+        ClientThread testMailClient = new ClientThread();
+
+        testMailClient.from("Mail Message <EmailTaskTest@ant.apache.org>");
+        testMailClient.to("to@you.com");
+        testMailClient.setMessage( "test line 1\n" +
+            "test line 2" );
+
+        Thread client = new Thread(testMailClient);
+        client.start();
+
+        try {
+            server.join(60 * 1000); // 60s
+            client.join(30 * 1000); // a further 30s
+        } catch (InterruptedException ie ) {
+            fail( "InterruptedException: " + ie );
+        }
+
+        String result = testMailServer.getResult();
+        String expectedResult = "220 test SMTP EmailTaskTest\r\n" +
+        "HELO " + local + "\r\n" +
+        "250 " + local + " Hello " + local + " [127.0.0.1], pleased to meet you\r\n" +
+        "MAIL FROM: <EmailTaskTest@ant.apache.org>\r\n" +
+        "250\r\n" +
+        "RCPT TO: <to@you.com>\r\n" +
+        "250\r\n" +
+        "DATA\r\n" +
+        "354\r\n" +
+        "From: Mail Message <EmailTaskTest@ant.apache.org>\r\n" +
+            "To: to@you.com\r\n" +
+        "X-Mailer: org.apache.tools.mail.MailMessage (ant.apache.org)\r\n" +
+        "\r\n" +
+        "test line 1\r\n" +
+        "test line 2\r\n" +
+        "\r\n" +
+        ".\r\n" +
+        "250\r\n" +
+        "QUIT\r\n" +
+        "221\r\n";
+        assertEquals( expectedResult.length(), result.length() );
+        assertEquals( expectedResult, result );
+        if ( testMailClient.isFailed() ) {
+            fail( testMailClient.getFailMessage() );
+        }
+    }
+
+
+    /**
+     *  Test a MailMessage with empty body message
+     */
+    public void testEmptyBody() {
+        ServerThread testMailServer = new ServerThread();
+        Thread server = new Thread(testMailServer);
+        server.start();
+
+        ClientThread testMailClient = new ClientThread();
+
+        testMailClient.from("Mail Message <EmailTaskTest@ant.apache.org>");
+        testMailClient.to("to@you.com");
+        testMailClient.setSubject("Test subject");
+        testMailClient.setMessage("");
+
+        Thread client = new Thread(testMailClient);
+        client.start();
+
+        try {
+            server.join(60 * 1000); // 60s
+            client.join(30 * 1000); // a further 30s
+        } catch (InterruptedException ie ) {
+            fail( "InterruptedException: " + ie );
+        }
+
+        String result = testMailServer.getResult();
+        String expectedResult = "220 test SMTP EmailTaskTest\r\n" +
+        "HELO " + local + "\r\n" +
+        "250 " + local + " Hello " + local + " [127.0.0.1], pleased to meet you\r\n" +
+        "MAIL FROM: <EmailTaskTest@ant.apache.org>\r\n" +
+        "250\r\n" +
+        "RCPT TO: <to@you.com>\r\n" +
+        "250\r\n" +
+        "DATA\r\n" +
+        "354\r\n" +
+        "Subject: Test subject\r\n" +
+            "From: Mail Message <EmailTaskTest@ant.apache.org>\r\n" +
+            "To: to@you.com\r\n" +
+        "X-Mailer: org.apache.tools.mail.MailMessage (ant.apache.org)\r\n" +
+        "\r\n" +
+        "\r\n" +
+        "\r\n" +
+        ".\r\n" +
+        "250\r\n" +
+        "QUIT\r\n" +
+        "221\r\n";
+        assertEquals(expectedResult.length(), result.length());
+        assertEquals(expectedResult, result);
+        if (testMailClient.isFailed()) {
+            fail(testMailClient.getFailMessage());
+        }
+    }
+
+
+    /**
+     *  Test a MailMessage with US-ASCII character set
+     *  The next four testcase can be kinda hard to debug as Ant will often
+     *  print the junit failure in US-ASCII.
+     */
+    public void testAsciiCharset() {
+
+        ServerThread testMailServer = new ServerThread();
+        Thread server = new Thread(testMailServer);
+        server.start();
+
+        ClientThread testMailClient = new ClientThread();
+
+        testMailClient.from("Mail Message <EmailTaskTest@ant.apache.org>");
+        testMailClient.to("Ceki G\u00fclc\u00fc <abuse@mail-abuse.org>");
+        testMailClient.setSubject("Test subject");
+        testMailClient.setMessage("");
+
+        Thread client = new Thread(testMailClient);
+        client.start();
+
+        try {
+            server.join(60 * 1000); // 60s
+            client.join(30 * 1000); // a further 30s
+        } catch (InterruptedException ie ) {
+            fail("InterruptedException: " + ie);
+        }
+
+        String result = testMailServer.getResult();
+        String expectedResult = "220 test SMTP EmailTaskTest\r\n" +
+        "HELO " + local + "\r\n" +
+        "250 " + local + " Hello " + local + " [127.0.0.1], pleased to meet you\r\n" +
+        "MAIL FROM: <EmailTaskTest@ant.apache.org>\r\n" +
+        "250\r\n" +
+        "RCPT TO: <abuse@mail-abuse.org>\r\n" +
+        "250\r\n" +
+        "DATA\r\n" +
+        "354\r\n" +
+        "Subject: Test subject\r\n" +
+            "From: Mail Message <EmailTaskTest@ant.apache.org>\r\n" +
+            "To: Ceki G\u00fclc\u00fc <abuse@mail-abuse.org>\r\n" +
+        "X-Mailer: org.apache.tools.mail.MailMessage (ant.apache.org)\r\n" +
+        "\r\n" +
+        "\r\n" +
+        "\r\n" +
+        ".\r\n" +
+        "250\r\n" +
+        "QUIT\r\n" +
+        "221\r\n";
+        ByteArrayOutputStream baos1 = new ByteArrayOutputStream();
+        ByteArrayOutputStream baos2 = new ByteArrayOutputStream();
+        PrintStream bos1 = new PrintStream(baos1, true);
+        PrintStream bos2 = new PrintStream(baos2, true);
+
+        bos1.print(expectedResult);
+        bos2.print(result);
+
+        assertEquals( "expected message length != actual message length "
+            + "in testAsciiCharset()", expectedResult.length(), result.length() );
+        assertEquals( "baos1 and baos2 should be the same in testAsciiCharset()",
+            baos1.toString(), baos2.toString() ); // order of headers cannot be guaranteed
+        if (testMailClient.isFailed()) {
+            fail(testMailClient.getFailMessage());
+        }
+    }
+
+
+
+
+    /**
+     * A private test class that pretends to be a mail transfer agent
+     */
+    private class ServerThread implements Runnable {
+
+        private StringBuffer sb = null;
+        private boolean loop = false;
+        ServerSocket ssock = null;
+        Socket sock = null;
+        BufferedWriter out = null;
+        BufferedReader in = null;
+        private boolean data = false;  // state engine: false=envelope, true=message
+
+        public void run() {
+
+            try {
+                ssock = new ServerSocket(TEST_PORT);
+                sock = ssock.accept(); // wait for connection
+                in = new BufferedReader( new InputStreamReader(
+                    sock.getInputStream()) );
+                out = new BufferedWriter( new OutputStreamWriter(
+                    sock.getOutputStream() ) );
+                sb = new StringBuffer();
+                send( "220 test SMTP EmailTaskTest\r\n" );
+                loop = true;
+                while ( loop ) {
+                    String response = in.readLine();
+                    if ( response == null ) {
+                        loop = false;
+                        break;
+                    }
+                    sb.append( response + "\r\n" );
+
+                    if ( !data && response.startsWith( "HELO" ) ) {
+                        send( "250 " + local + " Hello " + local + " " +
+                        "[127.0.0.1], pleased to meet you\r\n" );
+                    } else if ( !data && response.startsWith("MAIL") ) {
+                        send( "250\r\n" );
+                    } else if ( !data && response.startsWith("RCPT")) {
+                        send( "250\r\n" );
+                    } else if (!data && response.startsWith("DATA")) {
+                        send( "354\r\n" );
+                        data = true;
+                    } else if (data && response.equals(".") ) {
+                        send( "250\r\n" );
+                        data = false;
+                    } else if (!data && response.startsWith("QUIT")) {
+                        send( "221\r\n" );
+                        loop = false;
+                    } else if (!data) {
+                        //throw new IllegalStateException("Command unrecognized: "
+                        //    + response);
+                        send( "500 5.5.1 Command unrecognized: \"" +
+                            response + "\"\r\n" );
+                        loop = false;
+                    } else {
+                        // sb.append( response + "\r\n" );
+                    }
+
+                } // while
+            } catch (IOException ioe) {
+                fail();
+            } finally {
+                disconnect();
+            }
+        }
+
+        private void send(String retmsg) throws IOException {
+            out.write( retmsg );
+            out.flush();
+            sb.append( retmsg );
+        }
+
+        private void disconnect() {
+            if (out != null) {
+                try {
+                    out.flush();
+                    out.close();
+                    out = null;
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+            if (in != null) {
+                try {
+                    in.close();
+                    in = null;
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+            if (sock != null) {
+                try {
+                    sock.close();
+                    sock = null;
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+            if (ssock != null) {
+                try {
+                    ssock.close();
+                    ssock = null;
+                } catch (IOException e) {
+                    // ignore
+                }
+            }
+        }
+
+        public synchronized String getResult() {
+            loop = false;
+            return sb.toString();
+        }
+
+    }
+
+    /**
+     *  A private test class that wraps MailMessage
+     */
+    private class ClientThread implements Runnable {
+
+        private MailMessage msg;
+        private boolean fail = false;
+        private String failMessage = null;
+
+        protected String from = null;
+        protected String subject = null;
+        protected String message = null;
+
+        protected Vector replyToList = new Vector();
+        protected Vector toList = new Vector();
+        protected Vector ccList = new Vector();
+        protected Vector bccList = new Vector();
+
+
+        public void run() {
+            for (int i = 9; i > 0; i--) {
+                try {
+                    msg = new MailMessage("localhost", TEST_PORT);
+                } catch (java.net.ConnectException ce) {
+                    try {
+                        Thread.sleep(10 * 1000);
+                    } catch (InterruptedException ie) {
+                        // ignore
+                    }
+                } catch (IOException ioe) {
+                    fail = true;
+                    failMessage = "IOException: " + ioe;
+                    return;
+                }
+                if (msg != null) {
+                    break;
+                }
+            }
+
+            if (msg == null) {
+                fail = true;
+                failMessage = "java.net.ConnectException: Connection refused";
+                return;
+            }
+
+            try {
+                msg.from(from);
+
+                Enumeration e;
+
+                e = replyToList.elements();
+                while (e.hasMoreElements()) {
+                    msg.replyto(e.nextElement().toString());
+                }
+
+                e = toList.elements();
+                while (e.hasMoreElements()) {
+                    msg.to(e.nextElement().toString());
+                }
+
+                e = ccList.elements();
+                while (e.hasMoreElements()) {
+                    msg.cc(e.nextElement().toString());
+                }
+
+                e = bccList.elements();
+                while (e.hasMoreElements()) {
+                    msg.bcc(e.nextElement().toString());
+                }
+
+                if (subject != null) {
+                    msg.setSubject(subject);
+                }
+
+                if (message != null ) {
+                    PrintStream out = msg.getPrintStream();
+                    out.println( message );
+                }
+
+                msg.sendAndClose();
+            } catch (IOException ioe) {
+                fail = true;
+                failMessage = "IOException: " + ioe;
+                return;
+            }
+        }
+
+        public boolean isFailed() {
+            return fail;
+        }
+
+        public String getFailMessage() {
+            return failMessage;
+        }
+
+        public void replyTo(String replyTo) {
+            replyToList.add(replyTo);
+        }
+
+        public void to(String to) {
+            toList.add(to);
+        }
+
+        public void cc(String cc) {
+            ccList.add(cc);
+        }
+
+        public void bcc(String bcc) {
+            bccList.add(bcc);
+        }
+
+        public void setSubject(String subject) {
+            this.subject = subject;
+        }
+
+        public void from(String from) {
+            this.from = from;
+        }
+
+        public void setMessage(String message) {
+            this.message = message;
+        }
+
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/tar/TarEntryTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/tar/TarEntryTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/tar/TarEntryTest.java	(revision 16139)
@@ -0,0 +1,37 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.tar;
+
+import junit.framework.TestCase;
+
+/**
+ * @since Ant 1.6
+ */
+public class TarEntryTest extends TestCase {
+
+    public TarEntryTest(String name) {
+        super(name);
+    }
+
+    /**
+     * demonstrates bug 18105 on OSes with os.name shorter than 7.
+     */
+    public void testFileConstructor() {
+        new TarEntry(new java.io.File("/foo"));
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/tar/TarRoundTripTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/tar/TarRoundTripTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/tar/TarRoundTripTest.java	(revision 16139)
@@ -0,0 +1,59 @@
+/*
+ * Copyright  2003-2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.apache.tools.tar;
+
+import java.io.IOException;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import junit.framework.TestCase;
+
+public class TarRoundTripTest extends TestCase {
+
+    private static final String LONG_NAME
+        = "this/path/name/contains/more/than/one/hundred/characters/in/order/"
+            + "to/test/the/GNU/long/file/name/capability/round/tripped";
+
+    public TarRoundTripTest(String name) {
+        super(name);
+    }
+
+    /**
+     * test round-tripping long (GNU) entries
+     */
+    public void testLongRoundTripping() throws IOException {
+        TarEntry original = new TarEntry(LONG_NAME);
+        assertEquals("over 100 chars", true, LONG_NAME.length() > 100);
+        assertEquals("original name", LONG_NAME, original.getName());
+
+
+        ByteArrayOutputStream buff = new ByteArrayOutputStream();
+        TarOutputStream tos = new TarOutputStream(buff);
+        tos.setLongFileMode(TarOutputStream.LONGFILE_GNU);
+        tos.putNextEntry(original);
+        tos.closeEntry();
+        tos.close();
+
+        TarInputStream tis
+            = new TarInputStream(new ByteArrayInputStream(buff.toByteArray()));
+        TarEntry tripped = tis.getNextEntry();
+        assertEquals("round-tripped name", LONG_NAME, tripped.getName());
+        assertNull("no more entries", tis.getNextEntry());
+        tis.close();
+    }
+}
+
+
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/zip/AsiExtraFieldTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/zip/AsiExtraFieldTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/zip/AsiExtraFieldTest.java	(revision 16139)
@@ -0,0 +1,140 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.zip;
+
+import junit.framework.TestCase;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.zip.AsiExtraField.
+ *
+ */
+public class AsiExtraFieldTest extends TestCase implements UnixStat {
+    public AsiExtraFieldTest(String name) {
+        super(name);
+    }
+
+    /**
+     * Test file mode magic.
+     */
+    public void testModes() {
+        AsiExtraField a = new AsiExtraField();
+        a.setMode(0123);
+        assertEquals("plain file", 0100123, a.getMode());
+        a.setDirectory(true);
+        assertEquals("directory", 040123, a.getMode());
+        a.setLinkedFile("test");
+        assertEquals("symbolic link", 0120123, a.getMode());
+    }
+
+    /**
+     * Test content.
+     */
+    public void testContent() {
+        AsiExtraField a = new AsiExtraField();
+        a.setMode(0123);
+        a.setUserId(5);
+        a.setGroupId(6);
+        byte[] b = a.getLocalFileDataData();
+
+        // CRC manually calculated, sorry
+        byte[] expect = {(byte)0xC6, 0x02, 0x78, (byte)0xB6, // CRC
+                         0123, (byte)0x80,                   // mode
+                         0, 0, 0, 0,                         // link length
+                         5, 0, 6, 0};                        // uid, gid
+        assertEquals("no link", expect.length, b.length);
+        for (int i=0; i<expect.length; i++) {
+            assertEquals("no link, byte "+i, expect[i], b[i]);
+        }
+
+        a.setLinkedFile("test");
+        expect = new byte[] {0x75, (byte)0x8E, 0x41, (byte)0xFD, // CRC
+                             0123, (byte)0xA0,                   // mode
+                             4, 0, 0, 0,                         // link length
+                             5, 0, 6, 0,                         // uid, gid
+                             (byte)'t', (byte)'e', (byte)'s', (byte)'t'};
+        b = a.getLocalFileDataData();
+        assertEquals("no link", expect.length, b.length);
+        for (int i=0; i<expect.length; i++) {
+            assertEquals("no link, byte "+i, expect[i], b[i]);
+        }
+
+    }
+
+    /**
+     * Test reparse
+     */
+    public void testReparse() throws Exception {
+        // CRC manually calculated, sorry
+        byte[] data = {(byte)0xC6, 0x02, 0x78, (byte)0xB6, // CRC
+                       0123, (byte)0x80,                   // mode
+                       0, 0, 0, 0,                         // link length
+                       5, 0, 6, 0};                        // uid, gid
+        AsiExtraField a = new AsiExtraField();
+        a.parseFromLocalFileData(data, 0, data.length);
+        assertEquals("length plain file", data.length,
+                     a.getLocalFileDataLength().getValue());
+        assertTrue("plain file, no link", !a.isLink());
+        assertTrue("plain file, no dir", !a.isDirectory());
+        assertEquals("mode plain file", FILE_FLAG | 0123, a.getMode());
+        assertEquals("uid plain file", 5, a.getUserId());
+        assertEquals("gid plain file", 6, a.getGroupId());
+
+        data = new byte[] {0x75, (byte)0x8E, 0x41, (byte)0xFD, // CRC
+                           0123, (byte)0xA0,                   // mode
+                           4, 0, 0, 0,                         // link length
+                           5, 0, 6, 0,                         // uid, gid
+                           (byte)'t', (byte)'e', (byte)'s', (byte)'t'};
+        a = new AsiExtraField();
+        a.parseFromLocalFileData(data, 0, data.length);
+        assertEquals("length link", data.length,
+                     a.getLocalFileDataLength().getValue());
+        assertTrue("link, is link", a.isLink());
+        assertTrue("link, no dir", !a.isDirectory());
+        assertEquals("mode link", LINK_FLAG | 0123, a.getMode());
+        assertEquals("uid link", 5, a.getUserId());
+        assertEquals("gid link", 6, a.getGroupId());
+        assertEquals("test", a.getLinkedFile());
+
+        data = new byte[] {(byte)0x8E, 0x01, (byte)0xBF, (byte)0x0E, // CRC
+                           0123, (byte)0x40,                         // mode
+                           0, 0, 0, 0,                               // link
+                           5, 0, 6, 0};                          // uid, gid
+        a = new AsiExtraField();
+        a.parseFromLocalFileData(data, 0, data.length);
+        assertEquals("length dir", data.length,
+                     a.getLocalFileDataLength().getValue());
+        assertTrue("dir, no link", !a.isLink());
+        assertTrue("dir, is dir", a.isDirectory());
+        assertEquals("mode dir", DIR_FLAG | 0123, a.getMode());
+        assertEquals("uid dir", 5, a.getUserId());
+        assertEquals("gid dir", 6, a.getGroupId());
+
+        data = new byte[] {0, 0, 0, 0,                           // bad CRC
+                           0123, (byte)0x40,                     // mode
+                           0, 0, 0, 0,                           // link
+                           5, 0, 6, 0};                          // uid, gid
+        a = new AsiExtraField();
+        try {
+            a.parseFromLocalFileData(data, 0, data.length);
+            fail("should raise bad CRC exception");
+        } catch (Exception e) {
+            assertEquals("bad CRC checksum 0 instead of ebf018e",
+                         e.getMessage());
+        }
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/zip/ExtraFieldUtilsTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/zip/ExtraFieldUtilsTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/zip/ExtraFieldUtilsTest.java	(revision 16139)
@@ -0,0 +1,113 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.zip;
+
+import junit.framework.TestCase;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.zip.ExtraFieldUtils.
+ *
+ */
+public class ExtraFieldUtilsTest extends TestCase implements UnixStat {
+    public ExtraFieldUtilsTest(String name) {
+        super(name);
+    }
+
+    private AsiExtraField a;
+    private UnrecognizedExtraField dummy;
+    private byte[] data;
+    private byte[] aLocal;
+
+    public void setUp() {
+        a = new AsiExtraField();
+        a.setMode(0755);
+        a.setDirectory(true);
+        dummy = new UnrecognizedExtraField();
+        dummy.setHeaderId(new ZipShort(1));
+        dummy.setLocalFileDataData(new byte[0]);
+        dummy.setCentralDirectoryData(new byte[] {0});
+
+        aLocal = a.getLocalFileDataData();
+        byte[] dummyLocal = dummy.getLocalFileDataData();
+        data = new byte[4 + aLocal.length + 4 + dummyLocal.length];
+        System.arraycopy(a.getHeaderId().getBytes(), 0, data, 0, 2);
+        System.arraycopy(a.getLocalFileDataLength().getBytes(), 0, data, 2, 2);
+        System.arraycopy(aLocal, 0, data, 4, aLocal.length);
+        System.arraycopy(dummy.getHeaderId().getBytes(), 0, data,
+                         4+aLocal.length, 2);
+        System.arraycopy(dummy.getLocalFileDataLength().getBytes(), 0, data,
+                         4+aLocal.length+2, 2);
+        System.arraycopy(dummyLocal, 0, data,
+                         4+aLocal.length+4, dummyLocal.length);
+
+    }
+
+    /**
+     * test parser.
+     */
+    public void testParse() throws Exception {
+        ZipExtraField[] ze = ExtraFieldUtils.parse(data);
+        assertEquals("number of fields", 2, ze.length);
+        assertTrue("type field 1", ze[0] instanceof AsiExtraField);
+        assertEquals("mode field 1", 040755,
+                     ((AsiExtraField) ze[0]).getMode());
+        assertTrue("type field 2", ze[1] instanceof UnrecognizedExtraField);
+        assertEquals("data length field 2", 0,
+                     ze[1].getLocalFileDataLength().getValue());
+
+        byte[] data2 = new byte[data.length-1];
+        System.arraycopy(data, 0, data2, 0, data2.length);
+        try {
+            ExtraFieldUtils.parse(data2);
+            fail("data should be invalid");
+        } catch (Exception e) {
+            assertEquals("message",
+                         "data starting at "+(4+aLocal.length)+" is in unknown format",
+                         e.getMessage());
+        }
+    }
+
+    /**
+     * Test merge methods
+     */
+    public void testMerge() {
+        byte[] local =
+            ExtraFieldUtils.mergeLocalFileDataData(new ZipExtraField[] {a, dummy});
+        assertEquals("local length", data.length, local.length);
+        for (int i=0; i<local.length; i++) {
+            assertEquals("local byte "+i, data[i], local[i]);
+        }
+
+        byte[] dummyCentral = dummy.getCentralDirectoryData();
+        byte[] data2 = new byte[4 + aLocal.length + 4 + dummyCentral.length];
+        System.arraycopy(data, 0, data2, 0, 4 + aLocal.length + 2);
+        System.arraycopy(dummy.getCentralDirectoryLength().getBytes(), 0,
+                         data2, 4+aLocal.length+2, 2);
+        System.arraycopy(dummyCentral, 0, data2,
+                         4+aLocal.length+4, dummyCentral.length);
+
+
+        byte[] central =
+            ExtraFieldUtils.mergeCentralDirectoryData(new ZipExtraField[] {a, dummy});
+        assertEquals("central length", data2.length, central.length);
+        for (int i=0; i<central.length; i++) {
+            assertEquals("central byte "+i, data2[i], central[i]);
+        }
+
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/zip/ZipEntryTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/zip/ZipEntryTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/zip/ZipEntryTest.java	(revision 16139)
@@ -0,0 +1,117 @@
+/*
+ * Copyright  2001-2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.zip;
+
+import junit.framework.TestCase;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.zip.ZipEntry.
+ *
+ */
+public class ZipEntryTest extends TestCase {
+
+    public ZipEntryTest(String name) {
+        super(name);
+    }
+
+    /**
+     * test handling of extra fields
+     *
+     * @since 1.1
+     */
+    public void testExtraFields() {
+        AsiExtraField a = new AsiExtraField();
+        a.setDirectory(true);
+        a.setMode(0755);
+        UnrecognizedExtraField u = new UnrecognizedExtraField();
+        u.setHeaderId(new ZipShort(1));
+        u.setLocalFileDataData(new byte[0]);
+
+        ZipEntry ze = new ZipEntry("test/");
+        ze.setExtraFields(new ZipExtraField[] {a, u});
+        byte[] data1 = ze.getExtra();
+        ZipExtraField[] result = ze.getExtraFields();
+        assertEquals("first pass", 2, result.length);
+        assertSame(a, result[0]);
+        assertSame(u, result[1]);
+
+        UnrecognizedExtraField u2 = new UnrecognizedExtraField();
+        u2.setHeaderId(new ZipShort(1));
+        u2.setLocalFileDataData(new byte[] {1});
+
+        ze.addExtraField(u2);
+        byte[] data2 = ze.getExtra();
+        result = ze.getExtraFields();
+        assertEquals("second pass", 2, result.length);
+        assertSame(a, result[0]);
+        assertSame(u2, result[1]);
+        assertEquals("length second pass", data1.length+1, data2.length);
+
+        UnrecognizedExtraField u3 = new UnrecognizedExtraField();
+        u3.setHeaderId(new ZipShort(2));
+        u3.setLocalFileDataData(new byte[] {1});
+        ze.addExtraField(u3);
+        result = ze.getExtraFields();
+        assertEquals("third pass", 3, result.length);
+
+        ze.removeExtraField(new ZipShort(1));
+        byte[] data3 = ze.getExtra();
+        result = ze.getExtraFields();
+        assertEquals("fourth pass", 2, result.length);
+        assertSame(a, result[0]);
+        assertSame(u3, result[1]);
+        assertEquals("length fourth pass", data2.length, data3.length);
+
+        try {
+            ze.removeExtraField(new ZipShort(1));
+            fail("should be no such element");
+        } catch (java.util.NoSuchElementException nse) {
+        }
+    }
+
+    public void testUnixMode() {
+        ZipEntry ze = new ZipEntry("foo");
+        assertEquals(0, ze.getPlatform());
+        ze.setUnixMode(0755);
+        assertEquals(3, ze.getPlatform());
+        assertEquals(0755,
+                     (ze.getExternalAttributes() >> 16) & 0xFFFF);
+        assertEquals(0, ze.getExternalAttributes()  & 0xFFFF);
+
+        ze.setUnixMode(0444);
+        assertEquals(3, ze.getPlatform());
+        assertEquals(0444,
+                     (ze.getExternalAttributes() >> 16) & 0xFFFF);
+        assertEquals(1, ze.getExternalAttributes()  & 0xFFFF);
+
+        ze = new ZipEntry("foo/");
+        assertEquals(0, ze.getPlatform());
+        ze.setUnixMode(0777);
+        assertEquals(3, ze.getPlatform());
+        assertEquals(0777,
+                     (ze.getExternalAttributes() >> 16) & 0xFFFF);
+        assertEquals(0x10, ze.getExternalAttributes()  & 0xFFFF);
+
+        ze.setUnixMode(0577);
+        assertEquals(3, ze.getPlatform());
+        assertEquals(0577,
+                     (ze.getExternalAttributes() >> 16) & 0xFFFF);
+        assertEquals(0x11, ze.getExternalAttributes()  & 0xFFFF);
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/zip/ZipLongTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/zip/ZipLongTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/zip/ZipLongTest.java	(revision 16139)
@@ -0,0 +1,81 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.zip;
+
+import junit.framework.TestCase;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.zip.ZipLong.
+ *
+ */
+public class ZipLongTest extends TestCase {
+
+    public ZipLongTest(String name) {
+        super(name);
+    }
+
+    /**
+     * Test conversion to bytes.
+     */
+    public void testToBytes() {
+        ZipLong zl = new ZipLong(0x12345678);
+        byte[] result = zl.getBytes();
+        assertEquals("length getBytes", 4, result.length);
+        assertEquals("first byte getBytes", 0x78, result[0]);
+        assertEquals("second byte getBytes", 0x56, result[1]);
+        assertEquals("third byte getBytes", 0x34, result[2]);
+        assertEquals("fourth byte getBytes", 0x12, result[3]);
+    }
+
+    /**
+     * Test conversion from bytes.
+     */
+    public void testFromBytes() {
+        byte[] val = new byte[] {0x78, 0x56, 0x34, 0x12};
+        ZipLong zl = new ZipLong(val);
+        assertEquals("value from bytes", 0x12345678, zl.getValue());
+    }
+
+    /**
+     * Test the contract of the equals method.
+     */
+    public void testEquals() {
+        ZipLong zl = new ZipLong(0x12345678);
+        ZipLong zl2 = new ZipLong(0x12345678);
+        ZipLong zl3 = new ZipLong(0x87654321);
+
+        assertTrue("reflexive", zl.equals(zl));
+
+        assertTrue("works", zl.equals(zl2));
+        assertTrue("works, part two", !zl.equals(zl3));
+
+        assertTrue("symmetric", zl2.equals(zl));
+
+        assertTrue("null handling", !zl.equals(null));
+        assertTrue("non ZipLong handling", !zl.equals(new Integer(0x1234)));
+    }
+
+    /**
+     * Test sign handling.
+     */
+    public void testSign() {
+        ZipLong zl = new ZipLong(new byte[] {(byte)0xFF, (byte)0xFF, (byte)0xFF, (byte)0xFF});
+        assertEquals(0x00000000FFFFFFFFl, zl.getValue());
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/apache/tools/zip/ZipShortTest.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/apache/tools/zip/ZipShortTest.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/apache/tools/zip/ZipShortTest.java	(revision 16139)
@@ -0,0 +1,79 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.apache.tools.zip;
+
+import junit.framework.TestCase;
+
+/**
+ * JUnit 3 testcases for org.apache.tools.zip.ZipShort.
+ *
+ */
+public class ZipShortTest extends TestCase {
+
+    public ZipShortTest(String name) {
+        super(name);
+    }
+
+    /**
+     * Test conversion to bytes.
+     */
+    public void testToBytes() {
+        ZipShort zs = new ZipShort(0x1234);
+        byte[] result = zs.getBytes();
+        assertEquals("length getBytes", 2, result.length);
+        assertEquals("first byte getBytes", 0x34, result[0]);
+        assertEquals("second byte getBytes", 0x12, result[1]);
+    }
+
+    /**
+     * Test conversion from bytes.
+     */
+    public void testFromBytes() {
+        byte[] val = new byte[] {0x34, 0x12};
+        ZipShort zs = new ZipShort(val);
+        assertEquals("value from bytes", 0x1234, zs.getValue());
+    }
+
+    /**
+     * Test the contract of the equals method.
+     */
+    public void testEquals() {
+        ZipShort zs = new ZipShort(0x1234);
+        ZipShort zs2 = new ZipShort(0x1234);
+        ZipShort zs3 = new ZipShort(0x5678);
+
+        assertTrue("reflexive", zs.equals(zs));
+
+        assertTrue("works", zs.equals(zs2));
+        assertTrue("works, part two", !zs.equals(zs3));
+
+        assertTrue("symmetric", zs2.equals(zs));
+
+        assertTrue("null handling", !zs.equals(null));
+        assertTrue("non ZipShort handling", !zs.equals(new Integer(0x1234)));
+    }
+
+    /**
+     * Test sign handling.
+     */
+    public void testSign() {
+        ZipShort zs = new ZipShort(new byte[] {(byte)0xFF, (byte)0xFF});
+        assertEquals(0x0000FFFF, zs.getValue());
+    }
+
+}
Index: /release-kits/shared/ant/src/testcases/org/example/junit/Output.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/example/junit/Output.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/example/junit/Output.java	(revision 16139)
@@ -0,0 +1,36 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.example.junit;
+
+import junit.framework.TestCase;
+
+/**
+ * Not really a test of Ant but a test that is run by the test of the
+ * junit task.  Confused?
+ *
+ * @since Ant 1.5
+ */
+public class Output extends TestCase {
+
+    public Output(String s) {
+        super(s);
+    }
+
+    public void testOutput() {
+        System.out.println("foo");
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/example/junit/ThreadedOutput.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/example/junit/ThreadedOutput.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/example/junit/ThreadedOutput.java	(revision 16139)
@@ -0,0 +1,42 @@
+/*
+ * Copyright  2002,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+package org.example.junit;
+
+import junit.framework.TestCase;
+
+/**
+ * Not really a test of Ant but a test that is run by the test of the
+ * junit task.  Confused?
+ *
+ * @since Ant 1.5
+ */
+public class ThreadedOutput extends TestCase {
+
+    public ThreadedOutput(String s) {
+        super(s);
+    }
+
+    public void testOutput() throws InterruptedException {
+        Thread t = new Thread(new Runnable() {
+                public void run() {
+                    System.out.println("foo");
+                }
+            });
+        t.start();
+        t.join();
+    }
+}
Index: /release-kits/shared/ant/src/testcases/org/example/tasks/TaskdefTestContainerTask.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/example/tasks/TaskdefTestContainerTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/example/tasks/TaskdefTestContainerTask.java	(revision 16139)
@@ -0,0 +1,24 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.example.tasks;
+
+import org.apache.tools.ant.taskdefs.Sequential;
+
+public class TaskdefTestContainerTask extends Sequential {
+    public TaskdefTestContainerTask() {}
+}
Index: /release-kits/shared/ant/src/testcases/org/example/tasks/TaskdefTestSimpleTask.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/example/tasks/TaskdefTestSimpleTask.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/example/tasks/TaskdefTestSimpleTask.java	(revision 16139)
@@ -0,0 +1,44 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.example.tasks;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+
+public class TaskdefTestSimpleTask extends Task {
+
+    public class Echo {
+        Echo() {}
+        private String message = null;
+        public void setMessage(String s) {message = s;}
+    }
+
+    public TaskdefTestSimpleTask() {}
+
+    private Echo echo;
+    public Echo createEcho() {
+        echo = new Echo();
+        return echo;
+    }
+
+    public void execute() {
+        log("simpletask: "+echo.message, Project.MSG_INFO);
+    }
+
+}
+
Index: /release-kits/shared/ant/src/testcases/org/example/types/TypedefTestType.java
===================================================================
--- /release-kits/shared/ant/src/testcases/org/example/types/TypedefTestType.java	(revision 16139)
+++ /release-kits/shared/ant/src/testcases/org/example/types/TypedefTestType.java	(revision 16139)
@@ -0,0 +1,24 @@
+/*
+ * Copyright  2001,2004 The Apache Software Foundation
+ *
+ *  Licensed 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.
+ *
+ */
+
+package org.example.types;
+
+import org.apache.tools.ant.ProjectComponent;
+
+public class TypedefTestType extends ProjectComponent {
+}
+
Index: /release-kits/shared/ant/welcome.html
===================================================================
--- /release-kits/shared/ant/welcome.html	(revision 16139)
+++ /release-kits/shared/ant/welcome.html	(revision 16139)
@@ -0,0 +1,352 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
+<HTML>
+<HEAD>
+	<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=windows-1252">
+	<TITLE>Welcome to Apache Ant 1.6</TITLE>
+</HEAD>
+<BODY LANG="en-US" BGCOLOR="#ffffff" DIR="LTR">
+<H1>Welcome to Apache Ant 1.6</H1>
+<P><BR><BR>
+</P>
+<H2>Your life just got better. 
+</H2>
+<P>Not in big ways. Your social life isn't going to be helped, though
+with any luck you may now have more time for one. Nor is it going to
+take less time to write your Java code -although we note that running
+<A HREF="http://xdoclet.sf.net/" TARGET="other">XDoclet</A> under Ant
+lets you avoid writing so much code. Nor is a new release of Ant
+likely to provide a fundamental kick-start to the currently somewhat
+subdued technology and software industries. 
+</P>
+<P>No, Ant1.6 will not fundamentally change your life. But if you do
+have to get software out on time -"roughly what you asked for,
+roughly when you asked", then Ant1.6 provides lots of little
+improvements over the existing version. 
+</P>
+<P>Before we look at those details, lets look at the world of The
+Automated Build.</P>
+<P>Firstly, we'd like to thank everyone for all those awards that
+have been flowing in. The JavaWorld Editors' Choice Award for "Most
+Useful Java Community-Developed Technology", The Java
+Developer's Journal "Editors Choice Award", and Java Pro
+Reader's Choice award for "Most Valuable Java Deployment
+Technology." Wow. That's a lot of awards. Aardman Animations
+keep all their Wallace and Gromit -related oscars in a cabinet in
+their tea room. If the Apache organization had a tea room, those Ant
+awards would be forcing all the other (excellent) Apache products to
+fight hard for their cabinet space. 
+</P>
+<P>All those awards come for a reason: everyone, at least everyone
+working on any project of moderate complexity, needs to control their
+build process. Ant is one of the best ways to do it in Java, and,
+over the past four years, it has moved from a tool used simply to
+build Tomcat cross-platform, to a tool used across many open source
+projects, and now to a tool used by almost all Java projects. Indeed,
+pretty much the only competitor in the Java space is a sibling
+project under the Apache banner, <A HREF="http://maven.apache.org/" TARGET="other">Maven</A>.
+One of the obvious signs of Ant's success is that all the popular
+IDEs, from the Open Source -Emacs JDE, Eclipse, NetBeans and jEdit -
+to the commercial: IntelliJ IDEA, Borland JBuilder- all ship with
+built in Ant support. This lets you use your favourite IDE for what
+it is good at: editing text, creating Java source, refactoring
+existing code, debugging and the like, and you can turn to Ant for
+co-ordinating the build-test-deploy/deliver process. That Ant based
+process can be triggered from keystrokes in the IDE, command line
+invocations for those so inclined, and in automated scheduled builds
+so the machines can keep an eye on the engineers. Another sign is how
+Ant is helping the Java aisle of bookstores fight back against
+attempts by books about Macromedia Flash to take over all the space
+-there are now seven or eight books on the subject, with more on the
+way. Germany and Korea have their own native language books too,
+which shows how global the tool is -in use and in development terms. 
+</P>
+<P>The other metric of success is the pre-announcement hints from our
+distant software colleagues in Redmond, Microsoft, of a new build
+tool, "MSBuild", which "might be the single most
+important feature innovation in our pipeline", according to one
+MS developer. That is surely the greatest metric of success: XML
+based build tools are now viewed as so essential to the modern build
+process, that Microsoft has to come up with a competitor to Ant to
+win Java developers over to .NET. Let's hope they discover we like
+ubiquitous JUnit testing too, and refactoring IDEs that create and
+run the tests for us. 
+</P>
+<P>Success comes at a price, of course. One price is all those
+support calls. We try and stay on top of the bug reports, but one
+thing we cannot do is fix inconsistencies or things that seem like
+defects if they stand a significant chance of breaking existing
+builds. Its sad, but there are lots of little minor faults with Ant
+that we don't dare fix because, well, things might break. For
+example, why don't if= and unless= clauses also support
+<code>if="${property}"</code> clauses? Alternatively, why isn't it an
+error to use a property that isn't defined. Everyone that has ever
+seen directories called ${build.dir} popping up the source tree will
+understand why that behaviour is not always what you want. Well, we
+could fix these things, but we won't, because backwards compatibility
+is sacred. 
+</P>
+<P>That is the other price of success: all those users who have
+existing build files they want to work. And all those IDEs that host
+Ant, and who want an easy upgrade to a new version. This means we
+have lost a lot of the flexibility we used to have in the early days
+of the project, when different versions of Ant could have completely
+different property evaluation algorithms and nobody would bat an
+eyelid. Now, even the most obscure bug fix ends up generating 'you
+broke my build complaints'. 
+</P>
+<P>This explains why there will not be the 'incompatible upgrade'
+version of Ant, Ant2.0, that has long been discussed on our web site.
+</P>
+<H2>Where is Ant2.0?</H2>
+<P>For years we have been discussing Ant2.0, the complete rewrite
+version that would be cleaner and faster, and slightly incompatible
+with Ant1.x. It would be the opportunity to take the lessons from the
+1.x line, and support them cleanly. We even got as far as having
+multiple implementations of new Ant engines in the CVS repository,
+especially Mutant and Myrmidion. But we always seemed to have a hard
+time making progress -everyone was too busy using and firefighting
+Ant1.x that nobody got time to work on the 2.x codebase. Which is a
+shame, as all the proposals had interesting ideas.</P>
+<P>After Ant1.5 shipped, the future of Ant effectively resolved into
+one of evolution rather than revolution. There will be no Ant2.0 with
+a complete new engine underneath. There will be no need to run XSL
+transforms over existing build files to move them to the Ant2.0
+world. Instead Ant1.x is getting better underneath the build file
+-improving its internal design while retaining five-nines backwards
+compatibility with existing build files. 
+</P>
+<P>And that is what we have been up to.</P>
+<P>Under the hood, Ant1.6 contains some of the most major reworkings
+of the core Ant system yet seen. We haven't finished yet, and are
+holding back some of the more visible developments so we can see what
+works before their release in a product forces us to maintain them.
+But the underlying parts of Ant are now set up for the next stage in
+development. 
+</P>
+<P>Whether we call the next version of Ant 1.7 or 2.0 is something we
+have yet to decide. Maybe we should call it 3.0 just to surprise
+people.</P>
+<H2>What has changed</H2>
+<P>Look at the <A HREF="WHATSNEW" TARGET="other">WHATSNEW</A>
+document to get a full list of changes. Here are some of the core
+conceptual differences.</P>
+<H3>No more Java1.1</H3>
+<P>We got fed up of jumping through reflection hoops to do everything
+from weak references to setting file timestamps. After consultation
+with the Ant user mail list, Ant1.6 only runs on Java1.2 or later. It
+can still cross compile to Java1.1 if that is what you have to do. We
+haven't completely purged all 1.1 references in the docs, or 1.1
+support from the source, but that will come over time.</P>
+<H3>New classloader use.</H3>
+<P>This is going to make people nervous. If there is one thing Java
+developers have learned over time, only the very naive, the very
+brave, or the very competent do things with classloaders. We will let
+the Ant users decide what category to put us in, but before everyone
+panics, Costin, of Tomcat fame, did a lot of the work here. You don't
+write application servers without understanding classloaders inside
+and out. 
+</P>
+<P>The impact of these changes will trickle out over Ant versions. In
+1.6, the key features are 
+</P>
+<OL>
+	<LI><P>We have got rid of the bit in the batch file/shell script
+	that built up a really big classpath environment variable from
+	everything in ANT_HOME/lib. Now that is done in a launcher class
+	that does the work then calls tools.ant.Main as before.</P>
+	<LI><P>You can add new library directories to that classloader with
+	the -lib option on the command line. This option is interpreted by
+	the launcher class, so will not work with IDEs and other apps that
+	use the inner entry point.</P>
+	<LI><P>We have broken up optional.jar into many-many jar files, such
+	as ant-commons-logging.jar, ant-xalan2.jar, etc etc, and a
+	nodeps.jar for optional stuff without any dependencies. This creates
+	a lot of jar files.</P>
+	<LI><P>You can now &lt;taskdef&gt; existing tasks -like &lt;junit&gt;-
+	by including the specific ant jar <I>and</I> the dependent libraries
+	(i.e. junit.jar) in the declaration. This solves the problem of
+	ANT_HOME/lib needing to contain every jar possibly needed by every
+	user/project. You still have to declare the tasks one by one,
+	something we will fix in Ant1.7</P>
+</OL>
+<H3>Adapters</H3>
+<P>These are Java classes that <I>adapt</I>&gt; arbitrary Java
+classes into ant tasks or types. There has always been some of this
+stuff inside Ant, but now you can &lt;taskdef&gt; a task by naming
+not just the implementation class, but the adapter class. An adapter
+is essentially a meta task implementation -something that can be used
+to create new tasks dynamically. Which, when you consider that the
+core of Ant is fundamentally an XML to java mapping system and a
+simple workflow engine, may let you do very unusual things with Ant. 
+</P>
+<H3>Antlib: Ant libraries</H3>
+<P>This is something we will expand in future. Till now you could
+declare tasks and types with &lt;taskdef&gt; and &lt;typedef&gt;. If
+they were in a jar, you could write a properties file and name the
+resource path of the file in the jar. If you wanted to have both
+tasks and types, you had name a shared classloader. If you wanted to
+add more things -such as conditions or mappers, you were out of luck.</P>
+<P>Antlibs are Ant Libraries, JAR files containing the code to extend
+Ant, and an XML description file to describe how Ant is extended.
+Before anyone panics at 'yet another XML descriptor syntax' to learn:
+you may already know the syntax. We call it "Ant build files".
+Actually it is a subset: it can only contain those task declarations
+that are derived from org.apache.tools.ant.taskdefs.AntlibDefinition.
+That includes &lt;taskdef&gt; and &lt;typedef&gt;, and <I>any other
+task you choose to derive. </I>We are experimenting with scripting
+and some kind of task predefinition declarations in antlibs. With the
+latter, you will be able to write a predefined task -such as a
+&lt;javac&gt; derivative with the compiler options set, and then use
+it any of your build files. This is all too experimental to get into
+Ant1.6 -expect it in the successor. For now, start using antlibs and
+use the &lt;taskdef&gt; task to load them into your projects.</P>
+<H3>XML Namespace aware</H3>
+<P>Ant finally adopts XML namespaces. This is to address build file
+scalability; antlibs can be imported into their own namespaces, and
+so you can avoid namespace clashes with other libraries. If you do
+not know what namespaces are, do not worry -they are not compulsory.</P>
+
+<h3>All tasks can go in at the toplevel</h3>
+<p>
+ 
+Prior to Ant1.6, only three tasks were allowed outside 
+targets : &lt;taskdef&gt;,&lt;typedef&gt; and &lt;property&gt;.
+Ant 1.6 puts an end to this distinction; anything can go in at the top
+level. This is partly because there were many more tasks that merited the 
+option based on the original rationale of "global initialization tasks":
+&lt;import&gt; and &lt;antlib&gt; were the new additions, but existing
+tasks like &lt;condition&gt;, &lt;available&gt;, &lt;xmlproperties&gt; 
+and &lt;loadproperties&gt; had equal rights. 
+</p>
+<p>
+Rather that expand the set slightly, now all tasks are allowed outside
+targets. This gives external tasks the same rights as built in code,
+eliminates sporadic bug reports, and annoying error messages. It gives
+users the ability to write build files without any targets at all; the
+top-level declarations are processed in sequence. 
+</p>
+
+On a style note, we strongly advocate using this feature carefully. It
+is best if zero-side-effect, initialization-only tasks get put into the
+top level. Remember also that all top level statements are processed in
+order, before any targets are executed. Even tasks at the end of the
+file will get executed before targets declared above them. 
+
+<H2>New Tasks</H2>
+<P>As usual, the task base is growing and expanding. These days the
+ant core is resisting adopting many of the highly worthy donations of
+tasks from people, because they make maintenance and firefighting
+worse. Our current stance is that except in special circumstances,
+Ant tasks to support third party open source projects, should live
+with the projects themselves. This keeps them in sync with the
+libraries they integrate with, avoids GPL/Apache licensing issues,
+and reduces the Ant team's support workload, letting them focus on
+the core. The antlib mechanism is intended to make it easier for
+people to load tasks from libraries for this very reason.</P>
+<P>That said, we are pleased to introduce many new tasks. Of
+particular interest may be the SSH tasks, which let one deploy code
+to remote servers securely. Now you really can do live updates with
+Ant -if the operations team will let you. The other one that is quite
+interesting is &lt;subant&gt;. This is an extension of the &lt;ant&gt;
+task, to take an entire fileset of directories and run their build
+files. This is incredibly useful in very large projects. This does
+not mean that we are advocating the many-build-file development
+pattern, but in a sufficiently complex project it happens anyway.
+&lt;subant&gt; keeps things manageable.</P>
+<H2>What else</H2>
+<P>So, what is new in Ant1.6? Lots of stuff. You will have to look at
+the <A HREF="WHATSNEW">whatsnew</A> file to see, but here are some
+key points. 
+</P>
+<OL>
+	<LI><P STYLE="margin-bottom: 0in">Bug fixes. We know, some things
+	were broken in 1.5. In ant1.6 we have moved the bugs, fixing the
+	ones we could, and no doubt adding different ones. Hopefully the
+	total bug count has decreased. 
+	</P>
+	<LI><P STYLE="margin-bottom: 0in">New platforms: Open VMS and HP's
+	NonStop Kernel (Tandem) OS. OpenVMS is very different from the rest;
+	Read the &lt;exec&gt; task documentation carefully. 
+	</P>
+	<LI><P STYLE="margin-bottom: 0in">Spawning. &lt;java&gt; and &lt;exec&gt;
+	started applications can outlive Ant if you set spawn=true. Note
+	that the moment you do so, Ant cannot bind to their input or output,
+	for obvious reasons. 
+	</P>
+	<LI><P>Synchronisation with Java versions (heh, thought by moving
+	javah's entry point that you could hide from us? Think again).</P>
+	<LI><P>Synchronization with third party libraries. Of special note:
+	we have moved to the Apache commons-net.jar, the successor to
+	NetComponents for telnet and FTP as well as Apache BSF, the
+	successor to IBM BSF, for script.</P>
+</OL>
+<P>There are many more enhancements, so we hope you will find your
+build projects easier. We have, as usual, jumped through hoops to
+keep existing builds working. If your build file stops working, and
+it isn't something listed on the 'changes that may break your build'
+part of the WHATSNEW file, or something we know about on bugzilla,
+please don't hesitate to file a new bug report, preferably one with a
+replicable test and a patch to fix the problem. Please, please,
+please, do a search on bugzilla first. You do not want to be the
+seventy-third person to complain that Ant1.6 doesn't do something
+that it should. 
+</P>
+<P>Thanks, 
+</P>
+<P>The Ant development team. 
+</P>
+<H3>Acknowledgements</H3>
+<UL>
+	<LI><P>Many thanks for Antoine to being the build manager for this
+	release! 
+	</P>
+	<LI><P>Thank you to everyone who supplies the components we use in
+	Ant, particularly JUnit, commons-logging, log4J, bcel, ORO, Xerces, and Xalan. 
+	</P>
+	<LI><P>Everyone who has supplied bug reports, especially those with
+	patches and tests.</P>
+	<LI><P>IDE projects who incorporate Ant into their products. Not
+	only does this help Ant's success, you find lots of interesting
+	integration defects. Special mention to the Eclipse team for fixing
+	our memory leaks :)</P>
+</UL>
+<H3>Call to Action</H3>
+
+<P>
+
+It is an interesting time for Java. .NET is a serious challenger, and
+will get better. A core strength of Java over .NET is its community. It
+is the community that gave the world leading edge development tools and
+other core components: Ant, JUnit, XDoclet, hsqldb, Hibernate, Struts,
+etc. These things weren't created by JCP committees, or built according
+to the strategic vision of a Fortune 100 company. They were written by
+Java developers, for Java developers, usually to meet their own tactical
+goals.
+
+</P>
+
+<P>If Java is to survive -and we think it ought to- everyone who can
+needs to become active members of that community. It could be helping
+with Ant, but it could just as easily be helping with any other open
+source Java project, be hosted by Apache, FSF, Sourceforge or someone
+else, be it server-side, client-side or mobile-side. It could be an
+existing project, or it could be your own idea as to how things could
+be better. The key is: things will only be better if you put in the
+time to make it so. 
+</P>
+<H3>Call to Inaction</H3>
+<P>A special message to whoever it is in charge of commands in
+tools.jar: stop moving your entry points! In Ant1.5 we had to deal
+with the 'classic' javac entry point going away in Java1.4.0,
+seemingly coming back later. In Java 1.4.2, the javah entry point
+moved. The traditional command line invocation mechanism has been
+replaced by hosted invocation -Ant, Maven, IDEs, etc, and moving
+entry points around breaks these host applications. Even if we get a
+bug fix out in Ant a few weeks after the Java release, it takes
+months for this to trickle down to end users, especially via IDEs and
+other distributions. For example, Sun's own Java Web Services
+Developer Pack ships with Ant1.5.1, and so cannot run &lt;javah&gt;
+on a 1.4.2 installation. 
+</P>
+</BODY>
+</HTML>
